ok6410usb 驅動架構分析
目前為止,寫驅動嘛,雖然我隻是菜鳥,但還是直到一些常識,故我從常識開始分析,
一,USB控制器,也就時主機測,看待裝置,是一個裝置 -》配置 -》接口 -》端點的順序來配置usb并和usb裝置通信的
二,在usb裝置内部運作着固件程式,來對usb控制器發送過來的資訊進行解包,并執行相應的操作(不确定這句話的正确性,但覺得是對的)
三,在usb裝置内部存儲着裝置的配置資訊,比如有多少個配置了,配置有多少個接口,接口有多少個端點,還有一些辨別符(廠商号,用的什麼協定之類的用來表明我是個什麼子的裝置,我應該和哪個驅動比對又或者加載什麼樣的驅動)正是通過了這些usb裝置内部存儲的資訊,我們才能配置usb驅動
越來越感覺,想實作一個驅動,即使是我這樣,先湊出來的也一定要對協定和原理了解,這倆周真的是舉步維艱
下面分析usb-skeleton驅動
static const struct file_operations skel_fops = { //usb的操作
.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 = { //初始化usb_class_diver 用于配置usb的操作
.name = "skel%d",
.fops = &skel_fops,
.minor_base = USB_SKEL_MINOR_BASE,
};
static int skel_probe(struct usb_interface *interface,
const struct usb_device_id *id) //在hub探測到新裝置添加後,并探測到其對應我們的id_table後,運作probe,其實從這裡也可以間接的分析出,裝置内部自己存儲了自己由幾個配置,幾個接口,幾個端點,不然也不會傳過來usb_interface啊
{
struct usb_skel *dev;//自定義的usb資料 每種特定的usb裝置都不一樣
struct usb_host_interface *iface_desc; //我們要從傳過來的usb_interface,選出我們自己的設定
//簡而言之, 也接口中有多個設定,每個設定中的端點并不同,我們從中選出我們需要的設定,然後再擷取端點
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) {
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));
dev->interface = 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 = 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)) {//是否位輸出端點
/* we found a 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;
}
/* 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);//注冊usb裝置
if (retval) {
/* something prevented us from registering this driver */
err("Not able to get a minor for this device.");
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;
}
static void skel_disconnect(struct usb_interface *interface)//在斷開usb時做的操作
{
struct usb_skel *dev;
int minor = interface->minor; //擷取裝置号
dev = usb_get_intfdata(interface);//通過interface擷取dev(自定義資料,我們會在probe使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->interface = NULL; //取消dev與接口的關聯
mutex_unlock(&dev->io_mutex);
usb_kill_anchored_urbs(&dev->submitted);//關閉所有的urb
/* decrement our usage count */
kref_put(&dev->kref, skel_delete);//減小引用計數
dev_info(&interface->dev, "USB Skeleton #%d now disconnected", minor);
}
static void skel_draw_down(struct usb_skel *dev) //等待所有urb完成,若逾時, 則強制取消
{
int time;
time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000);
if (!time)
usb_kill_anchored_urbs(&dev->submitted);
usb_kill_urb(dev->bulk_in_urb);
}
static int skel_suspend(struct usb_interface *intf, pm_message_t message)//挂起操作
{
struct usb_skel *dev = usb_get_intfdata(intf);
if (!dev)
return 0;
skel_draw_down(dev);
return 0;
}
static int skel_resume(struct usb_interface *intf)
{
return 0;
}
static int skel_pre_reset(struct usb_interface *intf) //相當于加鎖把,在解鎖(即複位結束前,不允許其他程序操作)
{
struct usb_skel *dev = usb_get_intfdata(intf);
mutex_lock(&dev->io_mutex);
skel_draw_down(dev);
return 0;
}
static int skel_post_reset(struct usb_interface *intf)
{
struct usb_skel *dev = usb_get_intfdata(intf);
/* we are sure no URBs are active - no locking needed */
dev->errors = -EPIPE;
mutex_unlock(&dev->io_mutex);
return 0;
}
static struct usb_driver skel_driver = { //usb_driver結構體,
.name = "skeleton",
.probe = skel_probe,
.disconnect = skel_disconnect,
.suspend = skel_suspend,
.resume = skel_resume,
.pre_reset = skel_pre_reset, //衆所周知, 當usb裝置第一次被探測到時, usb裝置會被reset, pre_reset會被usb_reset_device調用,直到post_reset被調用,即複位結束
.post_reset = skel_post_reset,
.id_table = skel_table,
.supports_autosuspend = 1,
};
static int __init usb_skel_init(void) //驅動加載, 加__init的作用應該是當執行完一次後,就會把這個函數從記憶體中删掉
{
int result;
/* register this driver with the USB subsystem */
result = usb_register(&skel_driver); //注冊usb驅動, 根絕id_table進行比對, 在hub裡會識别裝置并添加新裝置
if (result)
err("usb_register failed. Error number %d", result);
return result;
}
static void __exit usb_skel_exit(void)//驅動寫在, __exit的作用和__init的作用類似
{
/* deregister this driver with the USB subsystem */
usb_deregister(&skel_driver);
}
module_init(usb_skel_init); //驅動加載, 沒什麼好說的
module_exit(usb_skel_exit); //驅動解除安裝
MODULE_LICENSE("GPL");
一, 不保證其正确性
二, 還有一些疑問
一個usb接口有多個設定,我如何确定我的驅動在probe裡收到interface後怎麼直到我需要哪個設定呢?
一個接口有多個端點,如果有多個輸入端點,我在擷取usb資訊時,應該用哪個端點呢?
接下來分析usb控制器驅動。。。