LINUX中USB驱动架构—USB驱动程序框架
usb/usb-skeleton.c文件为我们提供了一个最基础的USB驱动程序,即USB 程序框架,它可被看作一个最简单的USB设备驱动实例。
usb_driver结构体
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,.supports_autosuspend = 1,
};
所支持的USB设备的列表数组为skel_table[],id_table的结构为:
static 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_driver的注册和注销发生在USB骨架程序的模块加载与卸载函数内,其分别调用了 usb_register()和usb_deregister(),不过这个注册和注销的代码却不用写出来,直接用一个快捷宏 module_usb_driver即可。
在usb_driver的probe()成员函数中,会根据usb_interface的成员寻找第一个批量输入和输出端点,将端点地址、缓冲区等信息存入为USB骨架程序定义的usb_skel结构体中,并将usb_skel实例的指针传入 usb_set_intfdata()中以作为USB接口的私有数据,最后,它会注册USB设备。
static int skel_probe(struct usb_interface *interface,const struct usb_device_id *id)
{struct usb_skel *dev;struct usb_host_interface *iface_desc;struct usb_endpoint_descriptor *endpoint;size_t buffer_size;int i;int retval = -ENOMEM;/* allocate memory for our device state and initialize it */dev = kzalloc(sizeof(*dev), GFP_KERNEL);if (!dev)goto error;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);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 */iface_desc = interface->cur_altsetting;for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {endpoint = &iface_desc->endpoint[i].desc;if (!dev->bulk_in_endpointAddr &&usb_endpoint_is_bulk_in(endpoint)) {/* we found a bulk in endpoint */buffer_size = usb_endpoint_maxp(endpoint);dev->bulk_in_size = buffer_size;dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);if (!dev->bulk_in_buffer)goto error;dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL);if (!dev->bulk_in_urb)goto error;}if (!dev->bulk_out_endpointAddr &&usb_endpoint_is_bulk_out(endpoint)) {/* we found a bulk out endpoint */dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;}}if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {dev_err(&interface->dev,"Could not find both bulk-in and bulk-out endpoints\n");goto error;}/* save our data pointer in this interface device */usb_set_intfdata(interface, dev);/* we can register the device now, as it is ready */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:if (dev)/* this frees allocated memory */kref_put(&dev->kref, skel_delete);return retval;
}
usb_skel结构体可以被看作是一个私有数据结构体,应该根据具体的设备量身定制。
USB骨架程序的断开函数会完成与probe()函数相反的工作,即设置接口数据为NULL,注销USB设备。
usb_register_dev(interface,&skel_class)中的第二个参数skel_class包含了字符设备的 file_operations结构体指针,而这个结构体中的成员实现也是USB字符设备的另一个组成成分。
open()成员函数的实现非常简单,它根据usb_driver和次设备号 通过usb_find_interface()获得USB接口,之后通过usb_get_intfdata()获得接口的私有数据并赋予file- >private_data
接下来要分析的是读写函数,前面已经提到,在访问USB设备的时候,贯穿其中的“中枢神经”是URB 结构体。
设备写函数在urb小节已经给出,下面是skel_read读函数
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;dev = file->private_data;/* if we cannot read at all, return EOF */if (!dev->bulk_in_urb || !count)return 0;/* no concurrent readers */rv = mutex_lock_interruptible(&dev->io_mutex);if (rv < 0)return rv;if (dev->disconnected) { /* disconnect() was called */rv = -ENODEV;goto exit;}/* if IO is under way, we must not touch things */
retry:spin_lock_irq(&dev->err_lock);ongoing_io = dev->ongoing_read;spin_unlock_irq(&dev->err_lock);if (ongoing_io) {/* nonblocking IO shall not wait */if (file->f_flags & O_NONBLOCK) {rv = -EAGAIN;goto exit;}/** IO may take forever* hence wait in an interruptible state*/rv = wait_event_interruptible(dev->bulk_in_wait, (!dev->ongoing_read));if (rv < 0)goto exit;}/* errors must be reported */rv = dev->errors;if (rv < 0) {/* any error is reported once */dev->errors = 0;/* to preserve notifications about reset */rv = (rv == -EPIPE) ? rv : -EIO;/* report it */goto exit;}/** if the buffer is filled we may satisfy the read* else we need to start IO*/if (dev->bulk_in_filled) {/* we had read data */size_t available = dev->bulk_in_filled - dev->bulk_in_copied;size_t chunk = min(available, count);if (!available) {/** all data has been used* actual IO needs to be done*/rv = skel_do_read_io(dev, count);if (rv < 0)goto exit;elsegoto retry;}/** data is available* chunk tells us how much shall be copied*/if (copy_to_user(buffer,dev->bulk_in_buffer + dev->bulk_in_copied,chunk))rv = -EFAULT;elserv = chunk;dev->bulk_in_copied += chunk;/** if we are asked for more than we have,* we start IO but don't wait*/if (available < count)skel_do_read_io(dev, count - chunk);} else {/* no data in the buffer */rv = skel_do_read_io(dev, count);if (rv < 0)goto exit;elsegoto retry;}
exit:mutex_unlock(&dev->io_mutex);return rv;
}