编写USB驱动程序步骤: 1所有usb驱动都必须创建主要结构体struct usb_driver struct usb_driver ->struct module *owner (有他可正确对该驱动程序引用计数,应为THIS_MODULE) ->const char *name (驱动名字,运行时可在查看 /sys/bus/usb/drivers/) ->const struct usb_device_id *id_table (包含该驱动可支持的所有不同类型的驱动设备,没添探测回调函数不会被调用) ->int (*probe)(struct usb_interface *intf,const struct usb_device_id *id) (usb驱动探测函数,确认后struct usb_interface 应恰当初始化,然后返0,如果出错则返负值) ->void(*disconnect)(struct usb_interface *intf) (当struct usb_interface 被从系统中移除或驱动正从usb核心中卸载时,usb核心将调用此函数) 代码实例: static struct usb_driver skel_driver={ .owner = THIS_MODULE, .name = "skeleton", .id_table = skel_table, .probe = skel_probe, .disconnect = skel_disconnect, }; ↓ 2usb_register()注册将struct usb_driver 注册到usb核心,传统是在usb驱动程序模块初始化代码中完成该工作的 static int __init usb_skel_init(void) { ... usb_register(&skel_driver); ... } ↓ 3struct usb_device_id usb核心用该表判断哪个设备该使用哪个驱动程序,热插拔脚本使用它来确定当一个特定的设备插入到系统时该自动装载哪个驱动程序。 ->__u16 match_flags(确定设备和结构体中下列字段中哪一个相匹配) ->__u16 idVendor(设备的usb制造商id) ->__u16 idProduct(设备的usb产品id) ↓ 4USB骨架程序的关键几点如下: 1. USB驱动的注册和注销 Usb驱动程序在注册时会发送一个命令给usb_register,通常在驱动程序的初始化函数里。 当要从系统卸载驱动程序时,需要注销usb子系统。即需要usb_unregister 函数处理。 2 当usb设备插入时,为了使linux-hotplug(Linux中PCI、USB等设备热插拔支持)系统自动装载驱动程序,你需要创建一个MODULE_DEVICE_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_DEVICE宏利用厂商ID和产品ID为我们提供了一个设备的唯一标识。当系统插入一个ID匹配的USB设备到USB总线时,驱动会在USB core中注册。驱动程序中probe 函数也就会被调用。usb_device 结构指针、接口号和接口ID都会被传递到函数中。 3 static void * skel_probe(struct usb_device *dev,unsigned int ifnum, const struct usb_device_id *id) 驱动程序需要确认插入的设备是否可以被接受,如果不接受,或者在初始化的过程中发生任何错误,probe函数返回一个NULL值。否则返回一个含有设备驱动程序状态的指针。通过这个指针,就可以访问所有结构中的回调函数。 4 在骨架驱动程序里,最后一点是我们要注册devfs。我们创建一个缓冲用来保存那些被发送给usb设备的数据和那些从设备上接受的数据,同时USB urb 被初始化,并且我们在devfs子系统中注册设备,允许devfs用户访问我们的设备。注册过程如下:
/* initialize the devfs node for this device and register it */sprintf(name, "skel%d", skel->;minor); skel->devfs = devfs_register (usb_devfs_handle, name,DEVFS_FL_DEFAULT, USB_MAJOR,USB_SKEL_MINOR_BASE + skel->minor, S_IFCHR | S_IRUSR | S_IWUSR |S_IRGRP | S_IWGRP | S_IROTH, &skel_fops, NULL); 如果devfs_register函数失败,不用担心,devfs子系统会将此情况报告给用户。 当然最后,如果设备从usb总线拔掉,设备指针会调用disconnect 函数。驱动程序就需要清除那些被分配了的所有私有数据、关闭urbs,并且从devfs上注销调自己。 /* remove our devfs node */devfs_unregister(skel->;devfs); ↓ 5其他 1 struct usb_host_endpoint(描述usb端点) →(包含) struct usb_endpoint_descriptor(含真正端点信息,数据格式,是真正驱动关心的字段) 端点描述符: bEndpointAddress = 81(in)(第8位为1是输入设备)(usb的端点地址,包含端点方向) bmAttibutes = 03(interrupt)(端点类型,为中断传输) wMaxPacketSize = 0008(每次传8个字节)(端点每次可处理最大字节长度) bInterval = 08(8ms)(如端点为中断,该值为轮询间隔) 2 usb端点捆绑为接口,usb接口只处理一种usb逻辑连接,如鼠标键盘等. 一个usb设备可有多接口,usb扬声器:一个usb键盘用于按键,一个usb音频流,则需两个不同的驱动程序。 usb驱动 通常将struct usb_interface 转成 struct usb_device 用函数 interface_to_usbdev转 3 struct usb_interface 描述usb接口 →struct usb_host_interface * altsetting(接口结构体数组,包含所有可能用于该接口的可选设置) →struct usb_host_endpoint →unsigned num_altsetting(可选设置的数量) →struct usb_host_interface * cur_altsetting(接口当前活动设置) →int minor(usb核心分配给接口的次设备号,成功调用usb_register_dev有效) 4 usb设备非常复杂,由许多不同逻辑单元组成,简单关系如下: 设备通常有一个以上的配置 配置经常有一个以上接口 接口通常有一个以上设置 接口通常有一个以上端点 设备描述-》配置描述-》接口描述-》端点描述 5 usb sysfs设备命名方案 根集线器-集线器端口号:配置。接口 对于usb hub树中层次更高的字树命名方案 根集线器-集线器端口号-集线器端口号:配置。接口 6 linux内核的代码通过一个成为urb(usb请求块)和所有usb设备通信. 用struct urb描述(include/linux/usb.h中定义) ->urb用异步同usb设备特定usb端点发送/接收数据,使用类似网络代码中的struct skbuff -> urb 被动态创建,随时可被驱动程序或usb核心取消,内部有引用计数,可被多次调用,使他们可在最后一个使用者释放他们时自动地销毁 -> urb使得流处理或其他复杂的重叠的通信成为可能,获得高数据传输速度。 ->usb_alloc_urb() 创建urb包 usb_free_urb() 释放urb包 ->usb_fill_int_urb()正确初始化将发送到usb设备的中断端点urb usb_fill_bulk_urb() .. .. .. ... 批量传输端点urb usb_fill_control_urb() .. .. .. ... 控制端点urb 等时urb在提交给核心时必须手动初始化(很不幸,没函数) ->usb_submit_urb()urb被usb驱动正确创建和初始化后,就可提交到usb核心,发送到usb设备上了,如果调用成功,函数返0,urb控制权转给usb核心 ->usb_kill_urb() or usb_unlink_urb()取消已经被提交给核心的urb
|