内核为我们提供了一个最基础的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;
}