二.USB驱动分析
内核代码分析包括USB驱动框架、鼠标驱动、键盘驱动、U盘驱动。
USB驱动编写的主要框架usb-skeleton.c
USB鼠标驱动 usbmouse.c
USB键盘驱动usbkbd.c
USB Mass Storage是一类USB存储设备, U盘便是其中之一,主要分析的驱动文件是usb.c
1.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,
};
#define USB_SKEL_VENDOR_ID 0xfff0
#define USB_SKEL_PRODUCT_ID 0xfff0
static struct usb_device_id skel_table[] = {
{ USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
{ }
};
MODULE_DEVICE_TABLE(usb, skel_table);
由上面代码可见,通过USB_DEVICE宏定义了设备支持项。
对上面usb_driver的注册和注销发送在USB骨架程序的模块加载和卸载函数中。
static int __init usb_skel_init(void)
{
int result;
result = usb_register(&skel_driver); //将该驱动挂在USB总线上
if (result)
err("usb_register failed. Error number %d", result);
return result;
}
一个设备被安装或者有设备插入后,当USB总线上经过match匹配成功,就会调用设备驱动程序中的probe探测函数,向探测函数传递设备的信息,以便确定驱动程序是否支持该设备。
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;
dev = kzalloc(sizeof(*dev), GFP_KERNEL); //分配内存
if (!dev) {
err("Out of memory");
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_completion(&dev->bulk_in_completion); //初始化完成量
dev->udev = usb_get_dev(interface_to_usbdev(interface)); //获取usb_device结构体
dev->interface = interface; //获取usb_interface结构体
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)) { //如果该端点为批量输入端点
buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); //缓冲大小
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) {
err("Could not allocate bulk_in_buffer");
goto error;
}
dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL); //分配urb空间
if (!dev->bulk_in_urb) {
err("Could not allocate bulk_in_urb");
goto error;
}
}
if (!dev->bulk_out_endpointAddr &&
usb_endpoint_is_bulk_out(endpoint)) { //如果该端点为批量输出端点
dev->bulk_out_endpointAddr = endpoint->bEndpointAddress; //端点地址
}
}
if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {//都不是批量端点
err("Could not find both bulk-in and bulk-out endpoints");
goto error;
}
usb_set_intfdata(interface, dev); //将特定设备结构体设置为接口的私有数据
retval = usb_register_dev(interface, &skel_class); //注册USB设备
if (retval) {
err("Not able to get a minor for this device.");
usb_set_intfdata(interface, NULL);
goto error;
}
dev_info(&interface->dev,
"USB Skeleton device now attached to USBSkel-%d",
interface->minor);
return 0;
error:
if (dev)
kref_put(&dev->kref, skel_delete);
return retval;
}
通过上面分析,我们知道,usb_driver的probe函数中根据usb_interface的成员寻找第一个批量输入和输出的端点,将端点地址、缓冲区等信息存入USB骨架程序定义的usb_skel结构体中,并将usb_skel通过usb_set_intfdata传为USB接口的私有数据,最后注册USB设备。