接下来的工作是向系统注册一些以后会用的的信息。首先我们来说明一下usb-set_intfdata(),他向内核注册一个data,这个data结构可以是任意的,在这段程序用向内核注册了一个usb_skel结构,就是我们刚刚看到的被初始化的那个,这个data可以在以后用usb_get_intfdata来得到。
usb_set_intfdata(interface, dev);
retval = usb_register_dev(interface, &skel_class);
然后我们向这个interface注册一个skel_class结构。这个结构又是什么?我们就来看看这到底是个什么东西:
static struct usb_class_driver skel_class = {
.name = "skel%d",
.fops = &skel_fops,
.minor_base = USB_SKEL_MINOR_BASE,
};
它其实是一个系统定义的结构,里面包含了一名字、一个文件操作结构体还有一个次设备号的基准值。事实上它定义真正完成对设备IO操作的函数。所以他的核心内容应该是skel_fops。这里补充一些我个人的估计:因为usb设备可以有多个interface,每个interface所定义的IO操作可能不一样,所以想系统注册的usb_class_driver要求注册到某一个interface,因此usb_register_dev的第一个参数是interface,而第二个参数就是某一个usb_class_driver。通常情况下,linux系统用主设备好来识别某类设备的的驱动程序,用次设备号管理识别具体的设备,驱动程序可以依照次设备好来区分不同的设备,所以,这里的次设备好其实是用来管理不同的interface的,但由于这个范例只有一个interface,在代码上无法求证这个猜想。
static struct file_operations skel_fops = {
.owner = THIS_MODULE,
.read = skel_read,
.write = skel_write,
.open = skel_open,
.release = skel_release,
};
这个文件操作结构中定义了对设备的读写、打开释放(USB设备通常使用这个术语release)。他们都是函数指针,分别指向skel_read、skel_write、skel_open、skel_release这四个函数,这四个函数应该有开发人员自己实现。
当设备被拔出集线器时,usb子系统会自动地调用disconnect,他做的事情不多,最重要的是注销class_driver(交还次设备号)和interface的data:
dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
/* give back our minor */
usb_deregister_dev(interface, &skel_class);
然后他会用kref_put(&dev->kref, skel_delete)进行清理,kref_put的细节参见前文。
到目前为止,我们已经分析完usb子系统要求的各个主要操作,下一部分我们在讨论一下对USB设备的IO操作。