"./drivers/usb/usb-skeleton.c"是內核提供給usb設備驅動開發者的海量存儲usb設備的模板程序, 程序不長, 通用性卻很強,十分經典, 深刻理解這個文件能夠幫助咱們更好的理解usb子系統以及usb設備驅動框架, 寫出更好的usb海量存儲設備驅動。node
既然是一個usb設備驅動的模板,那麼就少不了構造一個usb_driver對象並將其註冊到內核中,併發
650 static struct usb_driver skel_driver = { 651 .name = "skeleton", 652 .probe = skel_probe, 653 .disconnect = skel_disconnect, 654 .suspend = skel_suspend, 655 .resume = skel_resume, 656 .pre_reset = skel_pre_reset, 657 .post_reset = skel_post_reset, 658 .id_table = skel_table, 659 .supports_autosuspend = 1, 660 }; 661 662 module_usb_driver(skel_driver);
關於這個對象的域,在上一篇已經解釋了,這裏,咱們主要關心的是skel_table,它決定了這個驅動匹配到哪一個設備,從下面的定義能夠看出,這個驅動是按照device進行匹配的,框架
30 static const struct usb_device_id skel_table[] = { 31 { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) }, 32 { } /* Terminating entry */ 33 }; 34 MODULE_DEVICE_TABLE(usb, skel_table);
接下來,看一下這個驅動對於資源類的定義,這但是整個驅動程序的紐帶,管理着整個驅動程序各個函數與接口共用的資源, 不得不說這個註釋真的是內核中少有的詳細, skeleton主要是針對海量存儲設備的,因此其資源對象中封裝了不少緩衝區的信息VS中斷設備只要一個urb便可搞定數據傳輸問題async
49 struct usb_skel { 50 struct usb_device *udev; /* the usb device for this device */ 51 struct usb_interface *interface; /* the interface for this device */ 52 struct semaphore limit_sem; /* limiting the number of writes in progress 53 struct usb_anchor submitted; /* in case we need to retract our submission 54 struct urb *bulk_in_urb; /* the urb to read data with */ 55 unsigned char *bulk_in_buffer; /* the buffer to receive data */ 56 size_t bulk_in_size; /* the size of the receive buffer */ 57 size_t bulk_in_filled; /* number of bytes in the buffer */ 58 size_t bulk_in_copied; /* already copied to user space */ 59 __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ 60 __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ 61 int errors; /* the last request tanked */ 62 bool ongoing_read; /* a read is going on */ 63 spinlock_t err_lock; /* lock for errors */ 64 struct kref kref; 65 struct mutex io_mutex; /* synchronize I/O with disconnect */ 66 wait_queue_head_t bulk_in_wait; /* to wait for an ongoing read */ 67 };
struct usb_skel
--50-->驅動操做的usb_device對象
--51-->驅動操做的usb_interface對象, 這兩個都是設備信息, VS i2c-s3c2410.c 經過將設備信息在probe中拷貝出來以保存到驅動資源對象中, 這裏也是一樣的思路. struct usb_interface->dev域之於usb_skel以及其餘接口函數, 至關於struct device域之於s3c24xx_i2c以及其餘接口函數, 都是在各個接口函數中流動的
--54-->使用的urb對象
--55-->用於接收數據的buf指針
--56-->標識要接收數據長度的域
--57-->標識當前緩衝區有多少有效數據的域
--58-->標識當前緩衝區已經被拷貝走多少數據的域,skeleton不會清空緩衝區,而是使用各類長度表示來決定已經佔用了多少,超出長度的部分,是否被清零無所謂。他們之間的關係見下圖
--59-->bulk設備的輸入端點
--60-->bulk設備的輸出端點
--62-->設備可讀標誌位,0表示可讀,1表示不可讀
--64-->kref供內核引用計數用函數
usb_skeleton還參考內核中已有的to_platform_device等結構封裝了一個to_skel_dev, 這種寫法值得借鑑post
68 #define to_skel_dev(d) container_of(d, struct usb_skel, kref)
匹配成功後,按照套路就該請probe上場了學習
490 static int skel_probe(struct usb_interface *interface, 491 const struct usb_device_id *id) 492 { 493 struct usb_skel *dev; 494 struct usb_host_interface *iface_desc; 495 struct usb_endpoint_descriptor *endpoint; 496 size_t buffer_size; 497 int i; 498 int retval = -ENOMEM; 499 500 /* allocate memory for our device state and initialize it */ 501 dev = kzalloc(sizeof(*dev), GFP_KERNEL); 506 kref_init(&dev->kref); 507 sema_init(&dev->limit_sem, WRITES_IN_FLIGHT); 508 mutex_init(&dev->io_mutex); 509 spin_lock_init(&dev->err_lock); 510 init_usb_anchor(&dev->submitted); 511 init_waitqueue_head(&dev->bulk_in_wait); 512 513 dev->udev = usb_get_dev(interface_to_usbdev(interface)); 514 dev->interface = interface; 515 516 /* set up the endpoint information */ 517 /* use only the first bulk-in and bulk-out endpoints */ 518 iface_desc = interface->cur_altsetting; 519 for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { 520 endpoint = &iface_desc->endpoint[i].desc; 521 522 if (!dev->bulk_in_endpointAddr && 523 usb_endpoint_is_bulk_in(endpoint)) { 524 /* we found a bulk in endpoint */ 525 buffer_size = usb_endpoint_maxp(endpoint); 526 dev->bulk_in_size = buffer_size; 527 dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; 528 dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); 534 dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL); 540 } 542 if (!dev->bulk_out_endpointAddr && 543 usb_endpoint_is_bulk_out(endpoint)) { 544 /* we found a bulk out endpoint */ 545 dev->bulk_out_endpointAddr = endpoint->bEndpointAddress; 546 } 547 } 553 554 /* save our data pointer in this interface device */ 555 usb_set_intfdata(interface, dev); 556 557 /* we can register the device now, as it is ready */ 558 retval = usb_register_dev(interface, &skel_class); 566 567 /* let the user know what node this device is now attached to */ 568 dev_info(&interface->dev, 569 "USB Skeleton device now attached to USBSkel-%d", 570 interface->minor); 571 return 0; 578 } 579
skel_probe
--501-->爲資源對象申請空間, 注意這裏的寫法: dev = kzalloc(sizeof(*dev), GFP_KERNEL);
--506-->初始化usb_skel->kref
--507-->初始化usb_skel->limit_sem
--508-->初始化usb_skel->io_mutex);
--509-->初始化usb_skel->err_lock);
--510-->初始化usb_skel->submitted);
--511-->初始化usb_skel->bulk_in_wait
--513-->初始化usb_skel->udev,將匹配到的usb_device地址存儲下來
--514-519-->初始化usb_skel對象其餘域.
--555-->將咱們的資源對象藏到interface->dev->p->driver_data中
--558-->註冊一個usb_device對象到內核、申請一個次設備號並建立設備文件, ==>intf->usb_dev = device_create(usb_class->class, &intf->dev,MKDEV(USB_MAJOR, minor), class_driver,"%s", temp);this
經過上面的分析, 咱們發現了一個skeleton和usbmouse不同的地方:skeleton構造了usb_class_driver對象並使用usb_register_dev註冊一個usb設備, 而usbmouse做爲input子系統, 僅須要input_register(input_dev)便可, 不用usb設備的註冊問題, 產生這個差異的緣由是skeleton是針對bulk urb設備的, 而usbmouse是針對interrupt urb設備的。對於bulk設備,咱們會對設備進行讀寫操做,而不只僅是讀操做,因此在bulk urb設備驅動中要實現相應的操做方法集並綁定到設備文件一塊兒註冊到內核,這個工做就是由usb_register_dev來完成。爲了使用這個函數,咱們須要構造一個usb_class_driver對象,其中最重要的就是本節要討論的skel_fops了。這個域也是struct file_operations類型的,全部的讀寫方法的實現都要註冊到這個域中。
既然提到fops,咱們主要關心的就三個方法的實現:open, read和write,考慮到read和write在操做邏輯相似,因此本文只討論open和readspa
83 static int skel_open(struct inode *inode, struct file *file) 84 { 85 struct usb_skel *dev; 86 struct usb_interface *interface; 87 int subminor; 88 int retval = 0; 89 90 subminor = iminor(inode); 91 92 interface = usb_find_interface(&skel_driver, subminor); 100 dev = usb_get_intfdata(interface); 106 retval = usb_autopm_get_interface(interface); 110 /* increment our usage count for the device */ 111 kref_get(&dev->kref); 112 113 /* save our object in the file's private structure */ 114 file->private_data = dev; 115 117 return retval; 118 }
skel_open()
--90-->從inode中獲取次設備號
--92-->根據skel_driver對象和次設備號獲取usb_interface對象,至此就找到了設備
--100-->從interface->dev->p->driver_data中獲取資源對象的地址,這個地址是在probe--555--中藏到這的
--110-->引用計數加一
--114-->關鍵,將以前得到的資源對象的地址藏在file->private_data中,這樣在全部的cdev接口之間均可以使用資源對象了,和將資源對象地址藏到interface中以便在usb_driver的接口函數之間流動的思想是同樣的。指針
打開了設備,接下來就能夠讀寫了,skeleton中對於讀操做的關鍵函數調用關係以下,咱們依照這個調用樹依次分析
skel_read()
skel_do_read_io(dev, count)
usb_fill_bulk_urb(...);
usb_submit_urb(dev->bulk_in_urb, GFP_KERNEL);
首先是skel_read(),這個函數是應用層讀設備時回調的函數,它試圖實現這樣一個功能: 若是內核緩衝區有數據就將適當的數據拷貝給應用層, 若是沒有就調用skel_do_read_io來向設備請求數據
226 static ssize_t skel_read(struct file *file, char *buffer, size_t count, 227 loff_t *ppos) 228 { 229 struct usb_skel *dev; 230 int rv; 231 bool ongoing_io; 232 233 dev = file->private_data; 255 if (ongoing_io) { 256 /* nonblocking IO shall not wait */ 257 if (file->f_flags & O_NONBLOCK) { 258 rv = -EAGAIN; 259 goto exit; 260 } 265 rv = wait_event_interruptible(dev->bulk_in_wait, (!dev->ongoing_read)); 266 if (rv < 0) 267 goto exit; 268 } 269 270 /* errors must be reported */ 271 rv = dev->errors; 272 if (rv < 0) { 273 /* any error is reported once */ 274 dev->errors = 0; 275 /* to preserve notifications about reset */ 276 rv = (rv == -EPIPE) ? rv : -EIO; 277 /* report it */ 278 goto exit; 279 } 286 if (dev->bulk_in_filled) { 287 /* we had read data */ 288 size_t available = dev->bulk_in_filled - dev->bulk_in_copied; 289 size_t chunk = min(available, count); 290 291 if (!available) { 296 rv = skel_do_read_io(dev, count); 297 if (rv < 0) 298 goto exit; 299 else 300 goto retry; 301 } 307 if (copy_to_user(buffer, 308 dev->bulk_in_buffer + dev->bulk_in_copied, 309 chunk)) 310 rv = -EFAULT; 311 else 312 rv = chunk; 313 314 dev->bulk_in_copied += chunk; 320 if (available < count) 321 skel_do_read_io(dev, count - chunk); 322 } else { 323 /* no data in the buffer */ 324 rv = skel_do_read_io(dev, count); 325 if (rv < 0) 326 goto exit; 327 else 328 goto retry; 329 } 330 exit: 331 mutex_unlock(&dev->io_mutex); 332 return rv; 333 }
skel_read()
--233-->都是套路,先將藏在file_private_data中的資源對象拿出來
--255-268-->資源對象中的可讀標誌位,不可讀的時候,判斷IO是否容許阻塞,若是不容許就直接返回,容許阻塞就使用資源對象中的等待隊列頭,將進程加入等待隊列,使用的是interruptible版本的wait,若是睡眠中的進程是被中斷喚醒的,那麼rv==-1,函數直接返回。
--286-->執行到這一行只有一個狀況:設備可讀了!若是緩衝區滿執行第一個語句塊,不然執行下面的語句塊
--288-->緩衝區滿時, 獲取可拷貝的數據的大小.
--289-->在可拷貝的大小和指望拷貝的大小中取小者給chunk
--291-->可拷貝的數據爲0, 而usb_skel->bulk_in_filled被置位才能進入這裏, 因此只有一種狀況: 緩衝區的數據已經拷貝完了
--292-->既然數據已經拷貝完畢, 調用skel_do_read_io發起請求
--300-->請求了數據,設備也反饋了,可是什麼數據都沒有,重試
307-->從內核緩衝區usb_skel->bulk_in_buffer + usb_skel->bulk_in_copied開始(就是剩餘未拷貝數據的首地址)拷貝chunk byte的數據到應用層
--314-->更新usb_skel->bulk_in_copied的值
--320-->若是可拷貝數據的大小<指望拷貝的大小, 那麼顯然剛纔chunk=availible, 已經將全部的數據拷貝到應用層, 可是還不能知足應用層的需求, 調用skel_do_read_io來繼續向設備索取數據, 固然, 索取的大小是沒知足的部分, 即count-chunk
--324-->usb_skel->bulk_in_filled沒有被置位, 表示內核緩衝區沒有數據, 調用skel_do_read_io索取數據, 固然, 索取的大小是所有數據, 即count
剛纔也說了, 若是緩衝區不能知足應用層需求的時候, 就會調用下面這個函數向bulk usb設備請求數據, 獲得數據後將數據放到緩衝區並將相應的標誌位置1/置0
189 static int skel_do_read_io(struct usb_skel *dev, size_t count) 190 { 191 int rv; 193 /* prepare a read */ 194 usb_fill_bulk_urb(dev->bulk_in_urb,dev->udev,usb_rcvbulkpipe(dev->udev,dev->bulk_in_endpointAddr),dev->bulk_in_buffer, min(dev->bulk_in_size, count),skel_read_bulk_callback,dev); 204 dev->ongoing_read = 1; 206 207 /* submit bulk in urb, which means no data to deliver */ 208 dev->bulk_in_filled = 0; 209 dev->bulk_in_copied = 0; 210 211 /* do it */ 212 rv = usb_submit_urb(dev->bulk_in_urb, GFP_KERNEL); 223 return rv; 224 }
skel_do_read_io()
--194-->向usb核心提交一個urb, 將資源對象dev藏在urb->context中隨着urb實參傳入回調函數, 和usb_fill_int_urb不一樣, usb_fill_bulk_urb註冊的時候須要將緩衝區首地址和請求數據的大小和urb綁定到一塊兒一同提交, 這樣才知道向bulk設備請求的數據的大小, bulk設備有數據返回的時候才知道放哪.
--204-->將usb_skel->ongoing_read置1, 表示沒有數據可讀
--208-->將usb_skel->bulk_in_filled置0, 表示內核緩衝區沒有數據可讀
--209-->將usb_skel->bulk_in_copied置0, 表示沒有任何數據已被拷貝
--212-->作好準備工做以後, 命令usb核心發送urb
請求被髮出後, usb總線就會靜待設備的反饋, 設備有反饋後就會回調urb的註冊函數, 咱們看看這個回調函數都作了什麼
163 static void skel_read_bulk_callback(struct urb *urb) 164 { 165 struct usb_skel *dev; 166 167 dev = urb->context; 168 169 spin_lock(&dev->err_lock); 170 /* sync/async unlink faults aren't errors */ 181 dev->bulk_in_filled = urb->actual_length; 183 dev->ongoing_read = 0; 184 spin_unlock(&dev->err_lock); 185 186 wake_up_interruptible(&dev->bulk_in_wait); 187 }
skel_read_bulk_callback
--167-->套路, 先把資源對象拿出來
--181-->將表示設備反饋的數據長度urb->actual_length賦值給usb_skel->bulk_in_filled, 表示緩衝區有數據了
--183-->將usb_skel->ongoing_read置0, 表示可讀了!
--186-->喚醒由於沒有數據可讀而陷入睡眠的進程
分析到這裏, 應用層就能夠經過usb_skeleton驅動從USB海量存儲設備中獲取數據了!!!寫入數據的思路是同樣的, 我這裏就不羅嗦了.
除了對緩衝區管理的巧妙, usb_skeleton.c中對於併發控制技術的使用也值得學習, 在構造資源對象usb_skel的時候, 這個驅動使用了semaphore ,spinlock,mutex三種經常使用的併發控制鎖機制, 接下來咱們討論一下內核大牛們是如何在不一樣應用場景中使用這些技術的.
semaphore是以進程爲單位的, 其典型特色就是當一個進程不能獲取信號量的時候, 會進陷入睡眠讓出CPU, 因此中斷上下文不能使用semaphore。在usb_skeleton.c中,semaphore在以下場景中被使用
335 static void skel_write_bulk_callback(struct urb *urb) 336 { 358 up(&dev->limit_sem); 359 } 361 static ssize_t skel_write(struct file *file, const char *user_buffer, 362 size_t count, loff_t *ppos) 363 { 376 /* 377 * limit the number of URBs in flight to stop a user from using up all 378 * RAM 379 */ 380 if (!(file->f_flags & O_NONBLOCK)) { 381 if (down_interruptible(&dev->limit_sem)) { 382 retval = -ERESTARTSYS; 383 goto exit; 384 } 385 } else { 386 if (down_trylock(&dev->limit_sem)) { 387 retval = -EAGAIN; 388 goto exit; 389 } 390 } 467 return retval; 468 }
當不能獲取臨界資源時,使用spinlock的進程不會陷入睡眠, 而是忙等,因此spinlock能夠用在中斷上下文,可是若是不能獲取資源又不出讓CPU,會浪費系統資源,因此被spinlock保護的臨界區不能太長。usb_skeleton主要在如下場景中使用了spinlock
226 static ssize_t skel_read(struct file *file, char *buffer, size_t count, 227 loff_t *ppos) 228 { 250 retry: 251 spin_lock_irq(&dev->err_lock); 252 ongoing_io = dev->ongoing_read; 253 spin_unlock_irq(&dev->err_lock); 332 return rv; 333 }
mutex只是用來保證互斥,在不使用trylock的時候,和semaphore同樣會在得不到鎖的時候進入睡眠。usb_skeleton在如下場景中使用mutex
226 static ssize_t skel_read(struct file *file, char *buffer, size_t count, 227 loff_t *ppos) 228 { 239 /* no concurrent readers */ 240 rv = mutex_lock_interruptible(&dev->io_mutex); 330 exit: 331 mutex_unlock(&dev->io_mutex); 332 return rv; 333 }