USB客户驱动简析

Linux USB 驱动开发(五)—— USB驱动程序开发过程简单总结 设备驱动程序是操作系统内核和机器硬件之间的接口,由一组函数和一些私有数据组成,是应用程序和硬件设备之间的桥梁。在应用程序看来,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。       设备驱动程序是内核的一部分,主要完成以下功能:对设备的初始化和释放;把数据从内核传送到硬件设备和从硬件设备读取数据;读取应用程序数据传送给设备文件和回送应用程序请求的数据;检测和处理 阅读详情

内核为我们提供了一个最基础的USB驱动,即USB骨架程序,是一个最简单的USB设备驱动实例

通过分析该驱动可以了解 USB设备驱动主要工作、如何读写数据。

drivers\usb\usb-skeleton.c

//定义厂商ID、产品ID
#define USB_SKEL_VENDOR_ID	0xfff0
#define USB_SKEL_PRODUCT_ID	0xfff0

/* Get a minor range for your devices from the usb maintainer */
#define USB_SKEL_MINOR_BASE	192

//设备信息
struct usb_skel {
	struct usb_device	*udev;			/* the usb device for this device */
	struct usb_interface	*interface;		/* the interface for this device */
	struct semaphore	limit_sem;		/* limiting the number of writes in progress */
	struct usb_anchor	submitted;		//用于撤回提交
	
	//批量IN端点 : 向处理器提交数据
	struct urb		*bulk_in_urb;		/* the urb to read data with 用于读数据的URB*/
	unsigned char   *bulk_in_buffer;	/* the buffer to receive data */
	size_t			bulk_in_size;		/* the size of the receive buffer */
	size_t			bulk_in_filled;		//缓冲区中的字节数
	size_t			bulk_in_copied;		//already copied to user space  已经拷贝到用户空间的字节数
	__u8			bulk_in_endpointAddr;	/* the address of the bulk in endpoint */
	
	//批量OUT端点 :接收处理器传递过来的数据
	__u8			bulk_out_endpointAddr;	/* 批量输出端点的地址*/
	
	int			errors;			/* the last request tanked */
	bool			ongoing_read;		/* a read is going on */
	spinlock_t		err_lock;		/* lock for errors */
	struct kref		kref;
	struct mutex		io_mutex;		/* synchronize I/O with disconnect */
	unsigned long		disconnected:1;
	wait_queue_head_t	bulk_in_wait;		/* to wait for an ongoing read */
};

//read操作 提交URB的回调函数
static void skel_read_bulk_callback(struct urb *urb)
{
	struct usb_skel *dev;
	unsigned long flags;

	dev = urb->context;

	spin_lock_irqsave(&dev->err_lock, flags);
	//求情失败
	if (urb->status) {
		if (!(urb->status == -ENOENT ||
		    urb->status == -ECONNRESET ||
		    urb->status == -ESHUTDOWN))
			dev_err(&dev->interface->dev,
				"%s - nonzero write bulk status received: %d\n",
				__func__, urb->status);

		dev->errors = urb->status;
	} else {
		//请求成功
		//如果请求的数据被成功收到,记录实际传输长度
		dev->bulk_in_filled = urb->actual_length;
	}
	dev->ongoing_read = 0;
	spin_unlock_irqrestore(&dev->err_lock, flags);

	wake_up_interruptible(&dev->bulk_in_wait);
}	

// 填充URB,提交URB
static int skel_do_read_io(struct usb_skel *dev, size_t count)
{
	int rv;

	// 填充URB 准备读
	/*
	struct usb_skel 
		struct urb		*bulk_in_urb
	
	struct usb_skel
		struct usb_device	*udev;
	
	//创建IN方向的批量管道 看代码可以知道 所谓的管道 其实就是一个数字,理解为编号
		//参1 usb设备  参2 批量输入端点地址
	usb_sndbulkpipe(dev->udev, dev->bulk_in_endpointAddr)
	
	dev->bulk_in_buffer :  the buffer to receive data
	min(dev->bulk_in_size, count),: 准备读的数据大小
	skel_read_bulk_callback 读完回调函数:使用异步URB提交,提交完了就返回到调用它的线程,并不等待数据是否传成功传送到了应用。
	struct usb_skel 设备信息
	
	
		
												struct usb_skel
										+-------struct usb_device	*udev;
										|
	struct urb                          |
		struct usb_device *dev----------+
		unsigned int pipe
		//USB设备数据缓存区
		void *transfer_buffer
		//缓存大小
		u32 transfer_buffer_length;
		//回调函数
		usb_complete_t complete;
		//回调函数参数
		void *context;	
	
	
	*/
	usb_fill_bulk_urb(dev->bulk_in_urb,
			dev->udev,
			usb_rcvbulkpipe(dev->udev,
				dev->bulk_in_endpointAddr),
			dev->bulk_in_buffer,
			min(dev->bulk_in_size, count),
			skel_read_bulk_callback,
			dev);

...
	/* submit bulk in urb, which means no data to deliver */
	dev->bulk_in_filled = 0;
	dev->bulk_in_copied = 0;

	//异步提交URB
	/*
	执行后在回调中 查看  urb->status
		0 : 
			输入URB: 请求的数据被成功收到
			输出URB:数据被成功发送
		else 失败
	*/
	rv = usb_submit_urb(dev->bulk_in_urb, GFP_KERNEL);
	
	...
	
	return rv;
	
}

/* 应用程序读 /dev/skel%d 
同样使用异步URB提交,提交完了就返回到调用它的线程,并不等待数据是否传成功上传给应用

1 获取usb设备信息
2 如果缓冲区有数据,copy_to_user拷贝数据到用户空间
3 如果缓存中没有数据
	3.1设置用于批量输入端点的URB,主要是:
		管道信息(批量输入端点地址)
		接收数据缓存地址 
		缓存大小 
		回调函数 
	3.2 提交URB,请求数据
4 调动回调
	如果请求的数据被成功收到,记录实际请求到的数据size

*/
static ssize_t skel_read(struct file *file, char *buffer, size_t count,
			 loff_t *ppos)
{
	struct usb_skel *dev;
	int rv;
	bool ongoing_io;

	//获取usb设备信息
	dev = file->private_data;

	/* if IO is under way, we must not touch things */
retry:

...

	/*
	 * if the buffer is filled we may satisfy the read
	 * else we need to start IO
	 如果缓冲区被填满,我们可以满足读取,否则我们需要开始IO
	 */

	//缓冲区有数据
	if (dev->bulk_in_filled) {
		//当前待读取数据量
		size_t available = dev->bulk_in_filled - dev->bulk_in_copied;
		size_t chunk = min(available, count);

		//没有数据可读
		if (!available) {
			//提交数据请求URB 拿数据
			rv = skel_do_read_io(dev, count);
			if (rv < 0)
				goto exit;
			else
				goto retry;
		}

		//拷贝数据到用户空间
		/*
		buffer : 目的地址
		dev->bulk_in_buffer + dev->bulk_in_copied :源地址
		chunk : 拷贝数据大小
		*/
		if (copy_to_user(buffer, dev->bulk_in_buffer + dev->bulk_in_copied, chunk))
			rv = -EFAULT;
		else
			rv = chunk;

		//已拷贝数据大小 ++
		dev->bulk_in_copied += chunk;
	
		//剩下可用数据小于用户需要的数据
		if (available < count){
			// 填充URB,提交URB
			skel_do_read_io(dev, count - chunk);
		}
	} else {
		//缓存中没有数据
		// 填充URB,提交URB  请求数据
		rv = skel_do_read_io(dev, count);
		if (rv < 0)
			goto exit;
		else
			goto retry;
	}
exit:
	mutex_unlock(&dev->io_mutex);
	return rv;
}



//应用程序写 /dev/skel%d 
/*
使用异步URB提交,提交完了就返回到调用它的线程,并不等待数据是否传成功传送到了外围设备。
1 创建一个URB
2 分配USB设备数据传输缓冲区,并将用户空间数据拷贝到该空间
3 填充URB
	如重点是 
		管道信息(批量输出端点地址)
		发送数据缓存地址 
		缓存大小
		回调函数 
		
4 提交URB,数据被成功发送
*/
static ssize_t skel_write(struct file *file, const char *user_buffer,
			  size_t count, loff_t *ppos)
{
	struct usb_skel *dev;
	int retval = 0;
	struct urb *urb = NULL;
	char *buf = NULL;
	size_t writesize = min(count, (size_t)MAX_TRANSFER);
	
	/* 获取当前USB设备信息
		 在 open()中绑定
	*/
	dev = file->private_data;

	/* verify that we actually have some data to write */
	if (count == 0)
		goto exit;

...

	/* 创建一个URB
		creates a new urb for a USB driver to use
	*/
	urb = usb_alloc_urb(0, GFP_KERNEL);


	/*  allocate a DMA-consistent buffer,  USB设备数据传输缓冲区 DMA缓存
	
	 * @dev: device the buffer will be used with
	 * @size: requested buffer size
	 * @mem_flags: affect whether allocation may block
	 * @dma: used to return DMA address of buffer
	*/
	buf = usb_alloc_coherent(dev->udev, writesize, GFP_KERNEL,
				 &urb->transfer_dma);

	//拷贝用户空间数据 到 DMA缓存
	if (copy_from_user(buf, user_buffer, writesize)) {
		retval = -EFAULT;
		goto error;
	}

...
	// 填充URB
	/*
	
	struct urb
	
	struct usb_skel
		struct usb_device	*udev;
	
	//创建OUT方向的批量管道 看代码可以知道 所谓的管道 其实就是一个数字,理解为编号
		//参1 usb设备  参2 批量输出端点地址
	usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr)
	
	buf : USB设备DMA缓存
	writesize: 准备写的数据大小
	写完回调函数:使用异步URB提交,提交完了就返回到调用它的线程,并不等待数据是否传成功传送到了外围设备。
	struct usb_skel 设备信息
	
	
												struct usb_skel
										+-------struct usb_device	*udev;
										|
	struct urb                          |
		struct usb_device *dev----------+
		unsigned int pipe
		//USB设备数据缓存区
		void *transfer_buffer
		//缓存大小
		u32 transfer_buffer_length;
		//回调函数
		usb_complete_t complete;
		//回调函数参数
		void *context;	
	*/
	usb_fill_bulk_urb(urb, dev->udev,  usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), buf, writesize, skel_write_bulk_callback, dev);
	
	/*
	struct urb 
		//dma addr for transfer_buffer
		dma_addr_t transfer_dma;
		
	Transfer_dma是有效的,所以最好利用它进行数据传输
	*/
	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
	
...

	/* send the data out the bulk port 从批量端口发送数据 */
	//异步提交URB 
	/*
		执行后在回调中 查看  urb->status
			0 : 
				输入URB: 请求的数据被成功收到
				输出URB:数据被成功发送
			else 失败
	*/
	retval = usb_submit_urb(urb, GFP_KERNEL);

...

	return writesize;
}


/*
当应用程序打开 /dev/skel%d 设备文件的时候,调用 skel_open()
工作: 根据文件次设备号,获取usb设备接口信息,再通过接口信息 获取 usb设备信息,将其绑定到 file

	struct file
		//needed for tty driver, and maybe others 
		void	*private_data; ------------------------- struct usb_skel 
*/
static int skel_open(struct inode *inode, struct file *file)
{
	struct usb_skel *dev;
	struct usb_interface *interface;
	int subminor;
	int retval = 0;

	//文件所对应的次设备号
	subminor = iminor(inode);

	//获取接口
	interface = usb_find_interface(&skel_driver, subminor);
	if (!interface) {
		pr_err("%s - error, can't find device for minor %d\n",
			__func__, subminor);
		retval = -ENODEV;
		goto exit;
	}

	//获取设备信息
	dev = usb_get_intfdata(interface);
	if (!dev) {
		retval = -ENODEV;
		goto exit;
	}
...
	/* save our object in the file's private structure */
	/*
	struct file
		//needed for tty driver, and maybe others 
		void	*private_data; ------------------------- struct usb_skel 
	*/
	file->private_data = dev;

exit:
	return retval;
}




//字符设备操作集
static const struct file_operations skel_fops = {
	.owner =	THIS_MODULE,
	.read =		skel_read,
	.write =	skel_write,
	.open =		skel_open,
	.release =	skel_release,
	.flush =	skel_flush,
	.llseek =	noop_llseek,
};

/*
 * usb class driver info in order to get a minor number from the usb core,
 * and to have the device registered with the driver core
 */
static struct usb_class_driver skel_class = {
	.name =		"skel%d",
	.fops =		&skel_fops,
	.minor_base =	USB_SKEL_MINOR_BASE,
};

prob工作:
在这里插入图片描述

/*
当集线器驱动完成枚举动作,唤醒 khubd线程,该线程就会执行 该USB设备驱动的 prob()
工作:
根据设备接口信息 获取接口下的目标 批量输入端点 批量输出端点等信息,并注册该usb设备
*/
static int skel_probe(struct usb_interface *interface,
		      const struct usb_device_id *id)
{
	//设备信息
	struct usb_skel *dev;
	//端点描述符
	struct usb_endpoint_descriptor *bulk_in, *bulk_out;
	int retval;

	/* allocate memory for our device state and initialize it */
	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev)
		return -ENOMEM;

	kref_init(&dev->kref);
	sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
	mutex_init(&dev->io_mutex);
	spin_lock_init(&dev->err_lock);
	init_usb_anchor(&dev->submitted);
	init_waitqueue_head(&dev->bulk_in_wait);

	/* 拿到目标 usb_device 和 usb接口
	
	struct usb_skel
		
	
	*/
	dev->udev = usb_get_dev(interface_to_usbdev(interface));
	dev->interface = usb_get_intf(interface);

	/* set up the endpoint information */
	/* use only the first bulk-in and bulk-out endpoints */
	
	/* 
	遍历该接口下的所有端点,我们当前只查找一个批量输入端点和一个批量输出端点 拿到后存储返回
	批量输入端点 : 存放到  bulk_in
	批量输出端点 :  存放到  bulk_out
	
	struct usb_interface
		struct usb_host_interface *cur_altsetting;
	
	struct usb_endpoint_descriptor *bulk_in
	
	struct usb_endpoint_descriptor *bulk_out
	*/
	retval = usb_find_common_endpoints(interface->cur_altsetting,
			&bulk_in, &bulk_out, NULL, NULL);
	if (retval) {
		dev_err(&interface->dev,
			"Could not find both bulk-in and bulk-out endpoints\n");
		goto error;
	}

	/*
	struct usb_skel	
		size_t	bulk_in_size; //the size of the receive buffer
		__u8	bulk_in_endpointAddr; //the address of the bulk in endpoint
		unsigned char	*bulk_in_buffer;	//the buffer to receive data
	*/
	dev->bulk_in_size = usb_endpoint_maxp(bulk_in);//获取批量输入端点的最大数据包大小
	dev->bulk_in_endpointAddr = bulk_in->bEndpointAddress;//批量输入端点地址
	dev->bulk_in_buffer = kmalloc(dev->bulk_in_size, GFP_KERNEL);//接收数据缓存
	if (!dev->bulk_in_buffer) {
		retval = -ENOMEM;
		goto error;
	}
	
	//创建一个新的urb供驱动程序使用
	/*
	struct usb_skel	
		struct urb	*bulk_in_urb; // the urb to read data with 
	*/
	dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL);
	if (!dev->bulk_in_urb) {
		retval = -ENOMEM;
		goto error;
	}

	/* 设置批量输出端点地址
	struct usb_skel
		__u8	bulk_out_endpointAddr;	//the address of the bulk out endpoint 
	*/
	dev->bulk_out_endpointAddr = bulk_out->bEndpointAddress;

	/* save our data pointer in this interface device */
	/*
	struct usb_skel
	|	struct usb_interface	*interface;	//the interface for this device 
	|		struct device dev;
	+-----------void	*driver_data;

	*/
	usb_set_intfdata(interface, dev);

	/* we can register the device now, as it is ready
	   注册 USB设备,将 字符设备 /dev/skel%d 提供给用户空间, 应用程序操作 /dev/skel%d 和 该USB设备交换数据
		注意:
						//全局 字符设备操作集数组
						static const struct file_operations *usb_minors[MAX_USB_MINORS];
														 |
														 |
														 |
	static struct usb_class_driver skel_class            |
		.fops =		&skel_fops---------------------------+
	
	struct usb_skel
		struct usb_interface	*interface;	//the interface for this device 
			int minor;	//数组下标
	*/
	retval = usb_register_dev(interface, &skel_class);
	if (retval) {
		/* something prevented us from registering this driver */
		dev_err(&interface->dev,
			"Not able to get a minor for this device.\n");
		usb_set_intfdata(interface, NULL);
		goto error;
	}

	/* let the user know what node this device is now attached to */
	dev_info(&interface->dev,
		 "USB Skeleton device now attached to USBSkel-%d",
		 interface->minor);
	return 0;

error:
	/* this frees allocated memory */
	kref_put(&dev->kref, skel_delete);

	return retval;
}

//USB设备拔出时 调用
static void skel_disconnect(struct usb_interface *interface)
{
	struct usb_skel *dev;
	int minor = interface->minor;

	dev = usb_get_intfdata(interface);
	usb_set_intfdata(interface, NULL);

	/* give back our minor */
	usb_deregister_dev(interface, &skel_class);

	/* prevent more I/O from starting */
	mutex_lock(&dev->io_mutex);
	dev->disconnected = 1;
	mutex_unlock(&dev->io_mutex);

	usb_kill_anchored_urbs(&dev->submitted);

	/* decrement our usage count */
	kref_put(&dev->kref, skel_delete);

	dev_info(&interface->dev, "USB Skeleton #%d now disconnected", minor);
}



//当USB核心监测到某个设备的属性和该 usb_device_id信息一致时,就会执行 该驱动的 probe()
static const struct usb_device_id skel_table[] = {
	{ USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
	{ }					/* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, skel_table);

//usb 设备驱动
static struct usb_driver skel_driver = {
	.name =		"skeleton",
	.probe =	skel_probe,
	.disconnect =	skel_disconnect,
	.suspend =	skel_suspend,
	.resume =	skel_resume,
	.pre_reset =	skel_pre_reset,
	.post_reset =	skel_post_reset,
	.id_table =	skel_table,//USB设备的ID信息
	.supports_autosuspend = 1,
};


//向内核注册 USB 驱动
module_usb_driver(skel_driver);

drivers\usb\core\usb.c

/**
 * usb_find_common_endpoints() -- look up common endpoint descriptors
 * @alt:	alternate setting to search
 * @bulk_in:	pointer to descriptor pointer, or NULL
 * @bulk_out:	pointer to descriptor pointer, or NULL
 * @int_in:	pointer to descriptor pointer, or NULL
 * @int_out:	pointer to descriptor pointer, or NULL
 *
 * Search the alternate setting's endpoint descriptors for the first bulk-in,
 * bulk-out, interrupt-in and interrupt-out endpoints and return them in the
 * provided pointers (unless they are NULL).
 *
 * If a requested endpoint is not found, the corresponding pointer is set to
 * NULL.
 *
 * 如果找到所有请求的描述符,则返回:0,否则返回-ENXIO。
 */
 	/* 
	
遍历该接口下的所有端点,我们当前只查找一个大容量输入端点和一个大容量输出端点 拿到后存储返回
	
	struct usb_interface
		struct usb_host_interface *cur_altsetting;//备用设置
	
	struct usb_endpoint_descriptor *bulk_in  批量输入端点描述符 数组
	
	struct usb_endpoint_descriptor *bulk_out 批量输出端点描述符 数组
	
	NULL 中断输入端点描述符 数组
	
	NULL 中断输出端点描述符 数组
	
	*/
int usb_find_common_endpoints(struct usb_host_interface *alt,
		struct usb_endpoint_descriptor **bulk_in,
		struct usb_endpoint_descriptor **bulk_out,
		struct usb_endpoint_descriptor **int_in,
		struct usb_endpoint_descriptor **int_out)
{
	struct usb_endpoint_descriptor *epd;
	int i;

	if (bulk_in)
		*bulk_in = NULL;
	if (bulk_out)
		*bulk_out = NULL;
	if (int_in)
		*int_in = NULL;
	if (int_out)
		*int_out = NULL;

	/* 遍历该接口下的所有端点

	struct usb_interface
		struct usb_host_interface *cur_altsetting;//备用设置
			//接口描述符
			struct usb_interface_descriptor	desc;
				//端点数量
				__u8  bNumEndpoints;
	*/
	for (i = 0; i < alt->desc.bNumEndpoints; ++i) {
		/* 获取端点描述符
		struct usb_interface
			struct usb_host_interface *cur_altsetting;//备用设置
				//端点数组
				struct usb_host_endpoint *endpoint;
					//端点描述符
					struct usb_endpoint_descriptor	desc;
		*/
		epd = &alt->endpoint[i].desc;

		/* 判断目标 端点描述符传输方向类型,并存存放到对应数组
			输入端点 : 存放到  bulk_in[0]
			输出端点 :  存放到  bulk_out[0]
			
		端点描述符
		struct usb_endpoint_descripto
		
		struct usb_endpoint_descriptor *bulk_in  批量输入端点描述符 数组
	
		struct usb_endpoint_descriptor *bulk_out 批量输出端点描述符 数组
		
		NULL 中断输入端点描述符 数组
	
		NULL 中断输出端点描述符 数组
		
		*/
		if (match_endpoint(epd, bulk_in, bulk_out, int_in, int_out))
			return 0;
	}

	return -ENXIO;
}
EXPORT_SYMBOL_GPL(usb_find_common_endpoints);




/*
端点描述符
struct usb_endpoint_descripto

struct usb_endpoint_descriptor *bulk_in  批量输入端点描述符 数组

struct usb_endpoint_descriptor *bulk_out 批量输出端点描述符 数组

NULL 中断输入端点描述符 数组

NULL 中断输出端点描述符 数组

*/
static bool match_endpoint(struct usb_endpoint_descriptor *epd,
		struct usb_endpoint_descriptor **bulk_in,
		struct usb_endpoint_descriptor **bulk_out,
		struct usb_endpoint_descriptor **int_in,
		struct usb_endpoint_descriptor **int_out)
{
	/*
	获取当前端点的传输类型,并存存放到对应数组
	*/
	switch (usb_endpoint_type(epd)) {
		
	//如果是批量传输类型端点
	case USB_ENDPOINT_XFER_BULK:
		/*
		判断该端点方向
			输入端点 : 存放到  bulk_in[0]
			输出端点 :  存放到  bulk_out[0]
		*/
		if (usb_endpoint_dir_in(epd)) {
			if (bulk_in && !*bulk_in) {
				//获取该
				*bulk_in = epd;
				break;
			}
		} else {
			if (bulk_out && !*bulk_out) {
				*bulk_out = epd;
				break;
			}
		}

		return false;
...
	default:
		return false;
	}
	return (!bulk_in || *bulk_in) && (!bulk_out || *bulk_out) &&
			(!int_in || *int_in) && (!int_out || *int_out);
}

kernel\include\linux\usb.h

/**
 * usb_fill_bulk_urb - macro to help initialize a bulk urb
 * @urb: pointer to the urb to initialize.
 * @dev: pointer to the struct usb_device for this urb.
 * @pipe: the endpoint pipe
 * @transfer_buffer: pointer to the transfer buffer
 * @buffer_length: length of the transfer buffer
 * @complete_fn: pointer to the usb_complete_t function
 * @context: what to set the urb context to.
 *
 * Initializes a bulk urb with the proper information needed to submit it
 * to a device.
 */
 
/*

struct urb

struct usb_skel
	struct usb_device	*udev;

//创建OUT方向的批量管道 看代码可以知道 所谓的管道 其实就是一个数字,理解为编号
usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr)

buf :USB设备DMA缓存
writesize: 准备写的数据大小
写完回调函数:使用异步URB提交,提交完了就返回到调用它的线程,并不等待数据是否传成功传送到了外围设备。
struct usb_skel 设备信息

*/
static inline void usb_fill_bulk_urb(struct urb *urb,
				     struct usb_device *dev,
				     unsigned int pipe,
				     void *transfer_buffer,
				     int buffer_length,
				     usb_complete_t complete_fn,
				     void *context)
{
	
	/*
	
											struct usb_skel
										+-------struct usb_device	*udev;
										|
	struct urb                          |
		struct usb_device *dev----------+
		unsigned int pipe
		//USB设备数据缓存区
		void *transfer_buffer
		//缓存大小
		u32 transfer_buffer_length;
		//回调函数
		usb_complete_t complete;
		//回调函数参数
		void *context;			
				
	*/
	urb->dev = dev;
	urb->pipe = pipe;
	urb->transfer_buffer = transfer_buffer;
	urb->transfer_buffer_length = buffer_length;
	urb->complete = complete_fn;
	urb->context = context;
}
Linux USB 驱动开发(一)—— USB设备基础概念 在终端用户看来,USB设备为主机提供了多种多样的附加功能,如文件传输,声音播放等,但对USB主机来说,它与所有USB设备的接口都是一致的。一个USB设备由3个功能模块组成:USB总线接口、USB逻辑设备和功能单元: a -- 这里的USB总线接口指的是USB设备中的串行接口引擎(SIE); b -- USB逻辑设备被USB系统软件看作是一个端点的集合; c -- 功能单元被客户软件看作是一个 阅读详情

相关推荐

(一)USB驱动程序_USB基础知识

深入,并且广泛 -沉默犀牛 USB设备驱动分类 USB驱动程序可以粗分为两类: 一、主机(Host)系统上的驱动程序 ,这个驱动程序控制插入其中的USB设备 二、设备(Device)上的驱动程序,这个驱动程序控制USB设备如何与主机通信 为了举一个形象的例子,我得先展示一张图片,更细致的介绍一下以上的两种分类: Host Device USB设备驱动(Mass sto...

感谢您来到姜立明的技术探索空间 1万+

USB驱动之USB网络共享

一 编译USB/IP组件 USB/IP组件包含两部分:USB/IP协议栈和USB/IP驱动模块 USB/IP协议栈源码位于linux_kernel/tools/usb/usbip USB/IP驱动模块位于linux_kernel/drivers/usb/usbip 1. 1.1编译USB/IP协议栈 1 进入USB/IP协议栈目录 cd linux_kernel/tools/usb/usbip...

weixin_33968104的博客 4998

c语言+usb驱动开发,usb驱动程序分析

usb驱动是linux内核中比较复杂的驱动之一,因此,大多数usb教程建议从usb-skeleton开始学习usb驱动。个人认为这是相当正确的,usb-sekelton提供了一个usb驱动开发的模板,而且代码量较少,很适合初学者的学习。记住,对于c语言的程序设计说,数据结构是整个程序的灵魂。因此,分析别人编写的代码的简洁的入口点就是高清代码中主要数据结构之间的关系。分析以usb-skeleton为...

weixin_36165049的博客 2431

USB URB

URB定义:usb 请求块(usb request block, urb)是usb设备驱动中用来描述与usb设备通信所用的基本载体和核心数据结构,非常类似于网络设备驱动中的sk_buff结构体,是usb主机与设备通信的电波。struct urb {      /* private

君子当自强不息 1万+

我的交叉验证翻车实录:从‘炼丹’到可靠评估,我是怎么用五折验证拯救我的图像分割模型的

本文分享了作者在医学图像分割模型评估中从失败到成功的实战经验。通过引入五折交叉验证方法,解决了传统数据划分的局限性,显著提升了模型评估的稳定性和泛化能力。文章详细介绍了Python实现、数据准备策略和训练流水线改造,为AI医疗影像分析提供了可靠的技术方案。

weixin_42625070的博客 271

UR5双臂Gazebo仿真(Python)

近来在Gazebo上搭了一个ur5双臂仿真平台。 差不多就是以下这样。 使用rostopic list查看一下话题,如下 显然,采用/left_arm_controller/follow_joint_trajectory/goal和/right_arm_controller/follow_joint_trajectory/goal两个话题来控制双臂的运动。 现在的问题是,如何保证双臂运动的一致性,即如何能保证双臂能同时接受到以上两个消息。 思路一:设置/left_arm_controlle

qq_42823342的博客 2233

七,USB设备驱动 - 分析USB储存驱动程序

usb设备驱动demo

滴水藏海 1532

PID参数自整定实战解析:从继电反馈到智能调节的完整实现

本文深入解析PID参数自整定的实战应用,从继电反馈原理到智能调节算法的完整实现。通过详细讲解振荡波形特征提取、参数计算的工程化改进及工业场景调试技巧,帮助工程师快速掌握PID自整定技术,提升控制系统性能。特别针对嵌入式系统优化和噪声环境下的稳定策略提供了实用解决方案。

weixin_28731223的博客 461

编写Linux usb 键盘驱动的笔记

编写linux usb 设备驱动参考: \\10.150.50.230\zhangjiaqi\linux-5.8.5\Documentation\driver-api\usb\writing_usb_driver.rst \\10.150.50.230\zhangjiaqi\linux-5.8.5\drivers\usb\usb-skeleton.c //usb设备驱动标准框架,这是一个usb磁盘驱动程序,用户app通过open,wrire,read读写磁盘(u盘); 写linux ...

qq_43418840的博客 934

ADS交叉耦合振荡器VCO仿真

本文主要参考视频中如何使用进行ADS交叉耦合振荡器仿真和分析,由于视频中有诸多错误和视频清晰度较低,虽然视频博主及时改正,为了节省学习时间,可以直接参考本文。

qq_32589131的博客 5425

Open3D (C++) KD tree的使用【2025最新版】

Open3D (C++) KD tree的使用,博客长期更新,本文最近更新时间为:2025年1月6日。

点云侠的博客 1566

Linux系统下usbmon抓包全攻略:从内核配置到Wireshark解析(含tcpdump实战命令)

本文详细介绍了在Linux系统下使用usbmon进行USB抓包的完整流程,从内核配置、模块加载、设备识别,到利用tcpdump进行二进制抓包和Wireshark高级解析。文章提供了实战命令和自定义解析器编写方法,帮助开发者构建免费的软件USB协议分析环境,高效进行外设调试与协议分析。

t8u9v0的博客 893

5-EP4CE10F17C8-引脚配置

EP4CE10F17C8-引脚配置

weixin_44479192的博客 720

linux usb设备驱动(1)

/* * USB Skeleton driver - 2.2 *功能:探测插入usb设备的次数,主要根据linux内核源码usb_skeleton.c修改,本事例以插入手机为例,因为系统原来已经有了u盘设备的驱动 * * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) * * This program

冰雪的专栏 527

CMake入门教程【基础篇】CMake+Visual Studio2022构建C++项目

CMake+Visual Studio2022搭建C++环境

小鱼酱的博客 4971

FAST-LIVO2 源码精读(十八):实时性优化、漂移排查与工程落地

FAST-LIVO2 的工程价值,不在于某一个孤立的技巧,而在于它把"激光的几何精度、惯性的高频预测、视觉的纹理与色彩"用一套简洁的滤波框架紧紧拧在一起,又在实时性、鲁棒性、可扩展性之间取得了难得的平衡。先用内建计时表定位瓶颈,再按降采样、并行、滑窗、编译优化、双频输出逐项加速;遇到漂移先看现象、循诊断树定位根因、回到对应章节取解法。十八篇至此收束。

PX4-SLAM-PNC 217

windows USB 设备驱动开发-USB 客户端驱动程序验证程序

USB 客户端驱动程序验证程序是 USB 3.0 驱动程序堆栈的一项功能,包含在 Windows 8 中。 启用验证程序后,USB 驱动程序堆栈会失败或修改客户端驱动程序执行的某些操作。 这些失败模拟的错误条件,否则可能难以找到,并可能导致以后产生不良结果。 模拟故障使你有机会确保驱动程序能够正常处理故障。 客户端可以通过错误处理代码处理错误或执行不同的代码路径。

王马的博客 1690

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

windows USB 设备驱动开发-总章

windows usb驱动开发

王马的博客 5305

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

windows USB 设备驱动开发-USB 等时传输

windows USB 设备驱动开发,等时传输。

王马的博客 2196

windows USB 驱动开发-URB结构

windows USB 设备驱动开发, USB 驱动的URB分配和提交

王马的博客 1918

Linux驱动开发——USB设备驱动

Linux驱动开发——USB设备驱动

qq_52479948的博客 3775

windows USB 设备驱动程序开发-USB 设备模拟 (一)

Windows 操作系统中 (UDE) 支持 USB 模拟设备,用于开发模拟通用串行总线 (USB) 主机控制器驱动程序和连接的虚拟 USB 设备。

王马的博客 4075

USB驱动笔记

总线,USB,驱动

abc353940924的博客 1397

WINCE USB客户端驱动切换

今天遇到一个比较特殊的客户要求,需要应用程序可以控制USB从设备,只有在应用程序使能之后,用户才能连接到电脑,说是怕用户随意拷贝或者破坏设备里存储的资料,当然界面是封装的,可不能避免用户通过连接电脑进行拷贝等操作。目前设备连接电脑有两种方式,同步方式(Active Sync)和U

cenewman的专栏 1716

windows USB 设备驱动开发-USB描述符

windows USB 设备驱动开发 USB其它描述符

王马的博客 1716

windows USB 设备驱动开发-USB带宽

本文讨论如何仔细管理 USB 带宽的指导。 每个 USB 客户端驱动程序都有责任最大程度地减少其使用的 USB 带宽,并尽快将未使用的带宽返回到可用带宽池。

王马的博客 1833
上一篇: USB子系统简述
下一篇: 嵌入式系统IO体系简述
Ma浩然
博客等级 码龄9年 1万+粉丝 462原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ma浩然

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值