usb通信的核心原理(mtp 协议部分研究 pc端)

原本以为是用户态的文件系统,不过后来研究是直接利用内核usb devios内存访问。大致在usb_hcd_map_urb_for_dma看了下堆栈的情况:

[  580.372069]  ? usb_hcd_map_urb_for_dma+0x5/0x4b0
[  580.372070]  ? usb_hcd_submit_urb+0x164/0x2e0
[  580.372072]  ? usb_hcd_map_urb_for_dma+0x5/0x4b0
[  580.372074]  ? usb_hcd_submit_urb+0x164/0x2e0
[  580.372075]  usb_submit_urb+0x1e2/0x5e0
[  580.372076]  ? idr_alloc_u32+0x93/0xd0
[  580.372078]  usb_start_wait_urb+0x71/0x180
[  580.372079]  usb_control_msg+0xe3/0x140
[  580.372080]  usb_get_string+0x6a/0xc0
[  580.372082]  usb_string_sub+0x6f/0x100
[  580.372083]  usb_string+0xda/0x1b0
[  580.372083]  ? usb_cache_string+0x2f/0xb0
[  580.372084]  usb_cache_string+0x4a/0xb0
[  580.372085]  usb_create_sysfs_intf_files+0x66/0xa0
[  580.372087]  usb_bus_notify+0x4a/0x80
[  580.372089]  blocking_notifier_call_chain+0x68/0x90
[  580.372090]  device_add+0x3a6/0x860
[  580.372091]  ? __kmalloc+0x430/0x470
[  580.372093]  ? usb_cache_string+0x61/0xb0
[  580.372094]  ? usb_cache_string+0x7f/0xb0
[  580.372095]  ? _cond_resched+0x19/0x30
[  580.372097]  usb_set_configuration+0x484/0x830
[  580.372099]  usb_generic_driver_probe+0x43/0x60
[  580.372100]  usb_probe_device+0x3f/0xd0
[  580.372101]  really_probe+0x357/0x460
[  580.372103]  driver_probe_device+0xe9/0x160
[  580.372104]  __device_attach_driver+0x71/0xd0
[  580.372106]  ? driver_allows_async_probing+0x50/0x50
[  580.372108]  bus_for_each_drv+0x84/0xd0
[  580.372109]  __device_attach+0xed/0x170
[  580.372111]  device_initial_probe+0x13/0x20
[  580.372112]  bus_probe_device+0x8f/0xa0
[  580.372114]  device_add+0x3cf/0x860
[  580.372115]  usb_new_device.cold+0x12d/0x304
[  580.372117]  elfcorehdr_read+0x40/0x40
[  580.372118]  port_event+0x57f/0x860
[  580.372120]  ? pm_runtime_autosuspend_expiration.part.0+0x2e/0x40
[  580.372122]  hub_event+0x152/0x3b0
[  580.372124]  process_one_work+0x220/0x3c0
[  580.372125]  worker_thread+0x4d/0x3f0
[  580.372125]  ? process_one_work+0x3c0/0x3c0
[  580.372126]  kthread+0x12b/0x150
[  580.372128]  ? set_kthread_struct+0x40/0x40
[  580.372129]  ret_from_fork+0x1f/0x30

int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
gfp_t mem_flags)
map里面是这个数据的传递

	urb->transfer_dma = dma_map_single(
			hcd->self.sysdev,
			urb->transfer_buffer,
			urb->transfer_buffer_length,
			dir);

int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl, int nents,
enum dma_data_direction dir, unsigned long attrs);

dma_addr_t dma_map_page_attrs(struct device *dev, struct page *page,
size_t offset, size_t size, enum dma_data_direction dir,
unsigned long attrs);


[10406.090808] Call Trace:
[10406.090809]  dump_stack+0x74/0x92
[10406.090812]  kum_hook_map_handler+0xe/0x12 [kum_core]
[10406.090815]  pre_handler_kretprobe+0x97/0x170
[10406.090816]  ? usb_submit_urb+0x1/0x5e0
[10406.090817]  kprobe_ftrace_handler+0x11e/0x1f0
[10406.090818]  ? usb_submit_urb+0x5/0x5e0
[10406.090819]  0xffffffffc08bf0e3
[10406.090820] RIP: 0010:usb_submit_urb+0x1/0x5e0
[10406.090821] Code: e8 34 1e a4 ff 41 5c 5d c3 48 8b 7f 60 e8 27 1e a4 ff 4c 89 e7 e8 1f 1e a4 ff 41 5c 5d c3 66 66 2e 0f 1f 84 00 00 00 00 00 e8 <5b> 5d a2 29 55 48 89 e5 41 57 41 56 41 55 41 54 53 48 83 ec 18 89
[10406.090822] RSP: 0018:ffffb8dfc2b93ad0 EFLAGS: 00000246 ORIG_RAX: 0000000000000000
[10406.090823] RAX: ffffb8dfc2b93ae8 RBX: ffffb8dfc2b93b5c RCX: 0000000080000480
[10406.090824] RDX: ffffffff98a9e03c RSI: 0000000000000c00 RDI: ffff8af2060d9b40
[10406.090825] RBP: ffffb8dfc2b93b30 R08: 00000000000000c0 R09: 0000000000000000
[10406.090825] R10: ffff8af2060d9b40 R11: ffff8af10b51f8a0 R12: ffff8af2060d9b40
[10406.090826] R13: ffff8af101ddc348 R14: 0000000000001388 R15: ffffb8dfc2b93ad8
[10406.090827]  ? usb_submit_urb+0x5/0x5e0
[10406.090828]  ? usb_start_wait_urb+0x71/0x180
[10406.090829]  ? usb_submit_urb+0x5/0x5e0
[10406.090829]  ? usb_start_wait_urb+0x71/0x180
[10406.090831]  usb_control_msg+0xe3/0x140
[10406.090832]  usb_get_string+0x6a/0xc0
[10406.090852]  usb_string_sub+0x6f/0x100
[10406.090853]  usb_string+0xda/0x1b0
[10406.090854]  ? usb_cache_string+0x2f/0xb0
[10406.090855]  usb_cache_string+0x4a/0xb0
[10406.090856]  usb_new_device+0x81/0x1f0
[10406.090858]  elfcorehdr_read+0x40/0x40
[10406.090859]  port_event+0x57f/0x860
[10406.090861]  ? pm_runtime_autosuspend_expiration.part.0+0x2e/0x40
[10406.090863]  hub_event+0x152/0x3b0
[10406.090864]  process_one_work+0x220/0x3c0
[10406.090865]  worker_thread+0x4d/0x3f0
[10406.090866]  ? process_one_work+0x3c0/0x3c0
[10406.090867]  kthread+0x12b/0x150
[10406.090868]  ? set_kthread_struct+0x40/0x40
[10406.090870]  ret_from_fork+0x1f/0x30
[10406.092769] CPU: 1 PID: 12020 Comm: kworker/1:2 Kdump: loaded Tainted: G           OE     5.11.0-43-generic #47~20.04.2-Ubuntu



[10406.107131] CPU: 1 PID: 12020 Comm: kworker/1:2 Kdump: loaded Tainted: G           OE     5.11.0-43-generic #47~20.04.2-Ubuntu
[10406.107133] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 07/22/2020
[10406.107134] Workqueue: usb_hub_wq hub_event
[10406.107138] Call Trace:
[10406.107139]  dump_stack+0x74/0x92
[10406.107143]  kum_hook_map_handler+0xe/0x12 [kum_core]
[10406.107146]  pre_handler_kretprobe+0x97/0x170
[10406.107148]  ? usb_submit_urb+0x1/0x5e0
[10406.107149]  kprobe_ftrace_handler+0x11e/0x1f0
[10406.107151]  ? usb_submit_urb+0x5/0x5e0
[10406.107152]  0xffffffffc08bf0e3
[10406.107153] RIP: 0010:usb_submit_urb+0x1/0x5e0
[10406.107154] Code: e8 34 1e a4 ff 41 5c 5d c3 48 8b 7f 60 e8 27 1e a4 ff 4c 89 e7 e8 1f 1e a4 ff 41 5c 5d c3 66 66 2e 0f 1f 84 00 00 00 00 00 e8 <5b> 5d a2 29 55 48 89 e5 41 57 41 56 41 55 41 54 53 48 83 ec 18 89
[10406.107155] RSP: 0018:ffffb8dfc2b93c00 EFLAGS: 00000246 ORIG_RAX: 0000000000000000
[10406.107157] RAX: ffffb8dfc2b93c18 RBX: ffffb8dfc2b93c8c RCX: 0000000080000180
[10406.107157] RDX: ffffffff98a9e03c RSI: 0000000000000c00 RDI: ffff8af2060d9540
[10406.107158] RBP: ffffb8dfc2b93c60 R08: 00000000000000c0 R09: 0000000000000000
[10406.107159] R10: ffff8af2060d9540 R11: ffff8af20d777678 R12: ffff8af2060d9540
[10406.107159] R13: ffff8af101ddc170 R14: 00000000000003e8 R15: ffffb8dfc2b93c08
[10406.107161]  ? usb_submit_urb+0x5/0x5e0
[10406.107161]  ? usb_start_wait_urb+0x71/0x180
[10406.107163]  ? usb_submit_urb+0x5/0x5e0
[10406.107163]  ? usb_start_wait_urb+0x71/0x180
[10406.107164]  usb_control_msg+0xe3/0x140
[10406.107166]  hub_ext_port_status+0x86/0x110
[10406.107167]  port_event+0x7b/0x860
[10406.107169]  ? __switch_to_xtra+0x119/0x510
[10406.107171]  hub_event+0x152/0x3b0
[10406.107172]  process_one_work+0x220/0x3c0
[10406.107174]  worker_thread+0x249/0x3f0
[10406.107175]  ? process_one_work+0x3c0/0x3c0
[10406.107175]  kthread+0x12b/0x150
[10406.107177]  ? set_kthread_struct+0x40/0x40
[10406.107178]  ret_from_fork+0x1f/0x30


static int get_port_status(struct usb_device *hdev, int port1,
			   void *data, u16 value, u16 length)
{
	int i, status = -ETIMEDOUT;

	for (i = 0; i < USB_STS_RETRIES &&
			(status == -ETIMEDOUT || status == -EPIPE); i++) {
		status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
			USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, value,
			port1, data, length, USB_STS_TIMEOUT);
	}
	return status;
}


int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
		    __u8 requesttype, __u16 value, __u16 index, void *data,
		    __u16 size, int timeout)
{
	struct usb_ctrlrequest *dr;
	int ret;

	dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
	if (!dr)
		return -ENOMEM;

	dr->bRequestType = requesttype;
	dr->bRequest = request;
	dr->wValue = cpu_to_le16(value);
	dr->wIndex = cpu_to_le16(index);
	dr->wLength = cpu_to_le16(size);

	ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout);

	/* Linger a bit, prior to the next control message. */
	if (dev->quirks & USB_QUIRK_DELAY_CTRL_MSG)
		msleep(200);

	kfree(dr);

	return ret;
}
EXPORT_SYMBOL_GPL(usb_control_msg);


static int usb_internal_control_msg(struct usb_device *usb_dev,
				    unsigned int pipe,
				    struct usb_ctrlrequest *cmd,
				    void *data, int len, int timeout)
{
	struct urb *urb;
	int retv;
	int length;

	urb = usb_alloc_urb(0, GFP_NOIO);
	if (!urb)
		return -ENOMEM;

	usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char *)cmd, data,
			     len, usb_api_blocking_completion, NULL);

	retv = usb_start_wait_urb(urb, timeout, &length);
	if (retv < 0)
		return retv;
	else
		return length;
}

控制传输的命令定义

#define USB_REQ_GET_STATUS		0x00
#define USB_REQ_CLEAR_FEATURE		0x01
#define USB_REQ_SET_FEATURE		0x03
#define USB_REQ_SET_ADDRESS		0x05
#define USB_REQ_GET_DESCRIPTOR		0x06
#define USB_REQ_SET_DESCRIPTOR		0x07
#define USB_REQ_GET_CONFIGURATION	0x08
#define USB_REQ_SET_CONFIGURATION	0x09
#define USB_REQ_GET_INTERFACE		0x0A
#define USB_REQ_SET_INTERFACE		0x0B
#define USB_REQ_SYNCH_FRAME		0x0C
#define USB_REQ_SET_SEL			0x30
#define USB_REQ_SET_ISOCH_DELAY		0x31

#define USB_REQ_SET_ENCRYPTION		0x0D	/* Wireless USB */
#define USB_REQ_GET_ENCRYPTION		0x0E
#define USB_REQ_RPIPE_ABORT		0x0E
#define USB_REQ_SET_HANDSHAKE		0x0F
#define USB_REQ_RPIPE_RESET		0x0F
#define USB_REQ_GET_HANDSHAKE		0x10
#define USB_REQ_SET_CONNECTION		0x11
#define USB_REQ_SET_SECURITY_DATA	0x12
#define USB_REQ_GET_SECURITY_DATA	0x13
#define USB_REQ_SET_WUSB_DATA		0x14
#define USB_REQ_LOOPBACK_DATA_WRITE	0x15
#define USB_REQ_LOOPBACK_DATA_READ	0x16
#define USB_REQ_SET_INTERFACE_DS	0x17

/* specific requests for USB Power Delivery */
#define USB_REQ_GET_PARTNER_PDO		20
#define USB_REQ_GET_BATTERY_STATUS	21
#define USB_REQ_SET_PDO			22
#define USB_REQ_GET_VDM			23
#define USB_REQ_SEND_VDM		24
//__u8 requesttype,
//usb_control_msg
#define USB_DIR_OUT			0		/* to device */
#define USB_DIR_IN			0x80		/* to host */

/*
 * USB types, the second of three bRequestType fields
 */
#define USB_TYPE_MASK			(0x03 << 5)
#define USB_TYPE_STANDARD		(0x00 << 5)
#define USB_TYPE_CLASS			(0x01 << 5)
#define USB_TYPE_VENDOR			(0x02 << 5)
#define USB_TYPE_RESERVED		(0x03 << 5)
struct urb {
	/* private: usb core and host controller only fields in the urb */
	struct kref kref;		/* reference count of the URB */
	int unlinked;			/* unlink error code */
	void *hcpriv;			/* private data for host controller */
	atomic_t use_count;		/* concurrent submissions counter */
	atomic_t reject;		/* submissions will fail */

	/* public: documented fields in the urb that can be used by drivers */
	struct list_head urb_list;	/* list head for use by the urb's
					 * current owner */
	struct list_head anchor_list;	/* the URB may be anchored */
	struct usb_anchor *anchor;
	struct usb_device *dev;		/* (in) pointer to associated device */
	struct usb_host_endpoint *ep;	/* (internal) pointer to endpoint */
	unsigned int pipe;		/* (in) pipe information */
	unsigned int stream_id;		/* (in) stream ID */
	int status;			/* (return) non-ISO status */
	unsigned int transfer_flags;	/* (in) URB_SHORT_NOT_OK | ...*/
	void *transfer_buffer;		/* (in) associated data buffer */
	dma_addr_t transfer_dma;	/* (in) dma addr for transfer_buffer */
	struct scatterlist *sg;		/* (in) scatter gather buffer list */
	int num_mapped_sgs;		/* (internal) mapped sg entries */
	int num_sgs;			/* (in) number of entries in the sg list */
	u32 transfer_buffer_length;	/* (in) data buffer length */
	u32 actual_length;		/* (return) actual transfer length */
	unsigned char *setup_packet;	/* (in) setup packet (control only) */
	dma_addr_t setup_dma;		/* (in) dma addr for setup_packet */
	int start_frame;		/* (modify) start frame (ISO) */
	int number_of_packets;		/* (in) number of ISO packets */
	int interval;			/* (modify) transfer interval
					 * (INT/ISO) */
	int error_count;		/* (return) number of ISO errors */
	void *context;			/* (in) context for completion */
	usb_complete_t complete;	/* (in) completion routine */
	struct usb_iso_packet_descriptor iso_frame_desc[0];
					/* (in) ISO ONLY */
};

这个 USB_REQ_LOOPBACK_DATA_WRITE并没有在写数据的时候起作用,通过代码我们可以知道, 数据进入 usb_hcd_map_urb_for_dma 后是吧数据的虚拟地址转换成了物理地址。之后找到对应的device的domain来关联设备的。
int iommu_attach_device(struct iommu_domain *domain, struct device *dev);

struct iommu_group {
	struct kobject kobj;
	struct kobject *devices_kobj;
	struct list_head devices;
	struct mutex mutex;
	struct blocking_notifier_head notifier;
	void *iommu_data;
	void (*iommu_data_release)(void *iommu_data);
	char *name;
	int id;
	struct iommu_domain *default_domain;
	struct iommu_domain *domain;
	struct list_head entry;
};
struct iommu_domain {
	unsigned type;
	const struct iommu_ops *ops;
	unsigned long pgsize_bitmap;	/* Bitmap of page sizes in use */
	iommu_fault_handler_t handler;
	void *handler_token;
	struct iommu_domain_geometry geometry;
	void *iova_cookie;
};
#define IOMMU_READ	(1 << 0)
#define IOMMU_WRITE	(1 << 1)
#define IOMMU_CACHE	(1 << 2) /* DMA cache coherency */
#define IOMMU_NOEXEC	(1 << 3)
#define IOMMU_MMIO	(1 << 4) /* e.g. things like MSI doorbells */

通过iommu_group 关联 到struct device结构。

不过看了下,iommu最后发现于mtp无关。

数据读写的路径

经过一系列的研究发现,读写并不经过一般的文件系统,而是直接到了usb驱动devio 设备控制逻辑里面。

static long usbdev_ioctl(struct file *file, unsigned int cmd,
			unsigned long arg)
{
	int ret;

	ret = usbdev_do_ioctl(file, cmd, (void __user *)arg);

	return ret;
}
static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
				void __user *p)
{
	struct usb_dev_state *ps = file->private_data;
	struct inode *inode = file_inode(file);
	struct usb_device *dev = ps->dev;
	int ret = -ENOTTY;

	if (!(file->f_mode & FMODE_WRITE))
		return -EPERM;

经过switch cmd进入 proc_submiturb

	switch (cmd) {
	case USBDEVFS_CONTROL:
		snoop(&dev->dev, "%s: CONTROL\n", __func__);
		ret = proc_control(ps, p);
		if (ret >= 0)
			inode->i_mtime = current_time(inode);
		break;




	case USBDEVFS_SUBMITURB:
		snoop(&dev->dev, "%s: SUBMITURB\n", __func__);
		ret = proc_submiturb(ps, p);
		if (ret >= 0)
			inode->i_mtime = current_time(inode);
		break;
static int proc_submiturb(struct usb_dev_state *ps, void __user *arg)
{
	struct usbdevfs_urb uurb;
	sigval_t userurb_sigval;

	if (copy_from_user(&uurb, arg, sizeof(uurb)))
		return -EFAULT;

	memset(&userurb_sigval, 0, sizeof(userurb_sigval));
	userurb_sigval.sival_ptr = arg;

	return proc_do_submiturb(ps, &uurb,
			(((struct usbdevfs_urb __user *)arg)->iso_frame_desc),
			arg, userurb_sigval);
}

struct usbdevfs_urb {
	unsigned char type;
	unsigned char endpoint;
	int status;
	unsigned int flags;
	void __user *buffer;
	int buffer_length;
	int actual_length;
	int start_frame;
	union {
		int number_of_packets;	/* Only used for isoc urbs */
		unsigned int stream_id;	/* Only used with bulk streams */
	};
	int error_count;
	unsigned int signr;	/* signal to be sent on completion,
				  or 0 if none should be sent. */
	void __user *usercontext;
	struct usbdevfs_iso_packet_desc iso_frame_desc[0];
};

proc_do_submiturb会对数据进行整合复制,最后以不同的方式映射到内存中。

static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb,
			struct usbdevfs_iso_packet_desc __user *iso_frame_desc,
			void __user *arg, sigval_t userurb_sigval){

	uurb->buffer_length = le16_to_cpu(dr->wLength);
	uurb->buffer += 8;
......
if (num_sgs) {
		as->urb->sg = kmalloc_array(num_sgs,
					    sizeof(struct scatterlist),
					    GFP_KERNEL);
		if (!as->urb->sg) {
			ret = -ENOMEM;
			goto error;
		}
		as->urb->num_sgs = num_sgs;
		sg_init_table(as->urb->sg, as->urb->num_sgs);

		totlen = uurb->buffer_length;
		for (i = 0; i < as->urb->num_sgs; i++) {
			u = (totlen > USB_SG_SIZE) ? USB_SG_SIZE : totlen;
			buf = kmalloc(u, GFP_KERNEL);
			if (!buf) {
				ret = -ENOMEM;
				goto error;
			}
			sg_set_buf(&as->urb->sg[i], buf, u);

			if (!is_in) {
				if (copy_from_user(buf, uurb->buffer, u)) {
					ret = -EFAULT;
					goto error;
				}
				uurb->buffer += u;
			}
			totlen -= u;
		}
	} else if (uurb->buffer_length > 0) {
		if (as->usbm) {
			unsigned long uurb_start = (unsigned long)uurb->buffer;

			as->urb->transfer_buffer = as->usbm->mem +
					(uurb_start - as->usbm->vm_start);
		} else {
			as->urb->transfer_buffer = kmalloc(uurb->buffer_length,
					GFP_KERNEL);
			if (!as->urb->transfer_buffer) {
				ret = -ENOMEM;
				goto error;
			}
			if (!is_in) {
				if (copy_from_user(as->urb->transfer_buffer,
						   uurb->buffer,
						   uurb->buffer_length)) {
					ret = -EFAULT;
					goto error;
				}
			} else if (uurb->type == USBDEVFS_URB_TYPE_ISO) {
				/*
				 * Isochronous input data may end up being
				 * discontiguous if some of the packets are
				 * short. Clear the buffer so that the gaps
				 * don't leak kernel data to userspace.
				 */
				memset(as->urb->transfer_buffer, 0,
						uurb->buffer_length);
			}
		}
	}
.......
	ret = usb_submit_urb(as->urb, GFP_KERNEL);
}

	urb->transfer_flags &= ~(URB_DIR_MASK | URB_DMA_MAP_SINGLE |
			URB_DMA_MAP_PAGE | URB_DMA_MAP_SG | URB_MAP_LOCAL |
			URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL |
			URB_DMA_SG_COMBINED);
	urb->transfer_flags |= (is_out ? URB_DIR_OUT : URB_DIR_IN);
enum dma_data_direction {
	DMA_BIDIRECTIONAL = 0,
	DMA_TO_DEVICE = 1,
	DMA_FROM_DEVICE = 2,
	DMA_NONE = 3,
};
int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
			    gfp_t mem_flags)

再往深就是三种映射类型

		if (urb->num_sgs) {
				int n;

				/* We don't support sg for isoc transfers ! */
				if (usb_endpoint_xfer_isoc(&urb->ep->desc)) {
					WARN_ON(1);
					return -EINVAL;
				}

				n = dma_map_sg(
						hcd->self.sysdev,
						urb->sg,
						urb->num_sgs,
						dir);
				if (n <= 0)
					ret = -EAGAIN;
				else
					urb->transfer_flags |= URB_DMA_MAP_SG;
				urb->num_mapped_sgs = n;
				if (n != urb->num_sgs)
					urb->transfer_flags |=
							URB_DMA_SG_COMBINED;
			} else if (urb->sg) {
				struct scatterlist *sg = urb->sg;
				urb->transfer_dma = dma_map_page(
						hcd->self.sysdev,
						sg_page(sg),
						sg->offset,
						urb->transfer_buffer_length,
						dir);
				if (dma_mapping_error(hcd->self.sysdev,
						urb->transfer_dma))
					ret = -EAGAIN;
				else
					urb->transfer_flags |= URB_DMA_MAP_PAGE;
			} else if (object_is_on_stack(urb->transfer_buffer)) {
				WARN_ONCE(1, "transfer buffer is on stack\n");
				ret = -EAGAIN;
			} else {
				urb->transfer_dma = dma_map_single(
						hcd->self.sysdev,
						urb->transfer_buffer,
						urb->transfer_buffer_length,
						dir);
				if (dma_mapping_error(hcd->self.sysdev,
						urb->transfer_dma))
					ret = -EAGAIN;
				else
					urb->transfer_flags |= URB_DMA_MAP_SINGLE;
			}
dma_addr_t dma_map_page_attrs(struct device *dev, struct page *page,
		size_t offset, size_t size, enum dma_data_direction dir,
		unsigned long attrs)
{
	const struct dma_map_ops *ops = get_dma_ops(dev);
	dma_addr_t addr;

	BUG_ON(!valid_dma_direction(dir));

	if (WARN_ON_ONCE(!dev->dma_mask))
		return DMA_MAPPING_ERROR;

	if (dma_map_direct(dev, ops) ||
	    arch_dma_map_page_direct(dev, page_to_phys(page) + offset + size))
		addr = dma_direct_map_page(dev, page, offset, size, dir, attrs);
	else
		addr = ops->map_page(dev, page, offset, size, dir, attrs);
	debug_dma_map_page(dev, page, offset, size, dir, addr);

	return addr;
}
static inline dma_addr_t dma_map_single_attrs(struct device *dev, void *ptr,
		size_t size, enum dma_data_direction dir, unsigned long attrs)
{
	/* DMA must never operate on areas that might be remapped. */
	if (dev_WARN_ONCE(dev, is_vmalloc_addr(ptr),
			  "rejecting DMA map of vmalloc memory\n"))
		return DMA_MAPPING_ERROR;
	debug_dma_map_single(dev, ptr, size);
	return dma_map_page_attrs(dev, virt_to_page(ptr), offset_in_page(ptr),
			size, dir, attrs);
}

引用:
https://blog.csdn.net/myarrow/article/details/8484113

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值