• libusb 的底層追蹤 (libusb thread support and the relation with kernel usbfs)


    因為做 project,無意間找到了Greg KH 大師級的文章-- [Snooping the USB Data Stream] 文中有一段提到 kernel 對 usbfs 的支援,讓 application 可直接透過 usbfs 對 device 發出 usb transfer,實做於devio.c, inode.c, and devices.c 等三個 kernel sources. (note: 我這裡的 kernel version 是 2.6.26)


    而另一方面,在 application library 端,就是依靠 usbfs 的幫忙,發展出 [libusb project] 1.0.0  版,但在 debian testing 的套件裏,還只包到 libusb-0.1.12,若要 library header 檔,請安裝 libusb-dev 套件。

    從 0.1.12 到 1.0.0 做了許多改變,除了 API 幾乎重新定之外,最明顯的是增加了 thread 的支援,但是兩者在最底層的系統呼叫,都是使用了 usbfs 提供的 I/O control。因為 1.0.0 的設計蠻複雜的,所以我還是以 0.1.12 來追蹤(其實是功力不夠啦!)以下就是一些 0.1.12 追蹤的筆記:

    in libusb-0.1.12:

    usb_bulk_read()          
    usb_interrupt_read()
    usb_bulk_write()   與    usb_interrupt_write() 
           :                      :
           V                      V
       USB_URB_TYPE_BULK      USB_URB_TYPE_INTERRUPT
                     :           :
                     :           :
                     V           V
                   usb_urb_transfer()

    usb_urb_transfer() 大致上流程,僅僅提供了 synchronous 的傳送方式(就是呼叫之後就等待它完成)---在 0.1.12 的介面上並沒有提供 asynchronous 的方式(就是呼叫後就離開,將來 urb 收/送完成後,系統會呼叫 complete function),注意:這是 libusb-0.1.12 並沒有提供 asynchronous 的函數,但是 kernel 的 IOCTL_USB_SUBMITURB 工作方式卻都是 asynchronous 的動作,等下追蹤 kernel 的部份時就會知道了。
    usb_urb_transfer() 用 IOCTL_USB_SUBMITURB 送出 urb 之後,然後一直重複使用 IOCTL_USB_REAPURBNDELAY 來收取completed urb  ,並且把使用者傳入的 timeout 切成 1ms 的單位用 select() 來等待。結果有幾種:
    1. select() 等到了 I/O 動作,REAPURB 得到了某個 completed urb ,返回值是所收送的 data 長度。
    2. select() 等不到 I/O 動作,重複 1ms 的 select()等待,一直到 timeout 了,返回值是 -ETIMEDOUT

    usb_urb_transfer() 有一段註解,是這樣說的,直接節錄下來:

    #define URB_USERCONTEXT_COOKIE        ((void *)0x1)

      /*
       * HACK: The use of urb.usercontext is a hack to get threaded applications
       * sort of working again. Threaded support is still not recommended, but
       * this should allow applications to work in the common cases. Basically,
       * if we get the completion for an URB we're not waiting for, then we update
       * the usercontext pointer to 1 for the other threads URB and it will see
       * the change after it wakes up from the the timeout. Ugly, but it works.
       */

    雖然這裡說 Threaded application 可以 work ! 根據這段註解說,使用了 urb.usercontext 來"標示" reapped urb---若不是我們的 urb 就把該 urb.usercontext 設定為 URB_USERCONTEXT_COOKIE,以便讓另一個 thread 可以 reap:但是另一個 thread 可以 reap 到這個作了 cookie 記號的 urb 嗎?

    追蹤到這裡,我們不得不往 kernel 的 devio.c 追蹤,要徹底了解 kernel 提供 usbfs 的動作才能解答這個問題...

    首先從  IOCTL_USB_SUBMITURB 開始找線索,因為這個是 libusb 定義的 I/O control code,kernel 裏相對應的是 USBDEVFS_SUBMITURB I/O control code,負責處理這個 I/O control code  的是 proc_submiturb(ps, p); 其中 ps 與 p 分別是

       struct dev_state *ps = file->private_data;
       void __user *p = (void __user *)arg;

    arg 是 user 由 ioctl system call 傳入的 argument pointer,這裡傳入 user urb。
    ps 是 usbdev_open() 時 allocated 得到的,定義為 :

    struct dev_state {
        struct list_head list;      /* state list */
        struct usb_device *dev;
        struct file *file;
        spinlock_t lock;            /* protects the async urb lists */
        struct list_head async_pending;
        struct list_head async_completed;
        
    wait_queue_head_t wait;     /* wake up if a request completed */
        unsigned int discsignr;
        struct pid *disc_pid;
        uid_t disc_uid, disc_euid;
        void __user *disccontext;
        unsigned long ifclaimed;
        u32 secid;
    };


    可以把這個資料結構看成 process 對於這個 device 的傳送 urb 的狀態紀錄. 其中兩個 list 分別是 urb 送出後就把對應的 async 由 async_pending 紀錄,等 urb complete 之後就把對應的 async 由 async_completed 紀錄,async 是甚麼呢?對於每個 urb 都有一個對應的 async data structure,是在 proc_do_submiturb() 時 allocate 得到的 。async 的資料結構為:

    struct async {
        struct list_head asynclist;
        struct dev_state *ps;
        struct pid *pid;
        uid_t uid, euid;
        unsigned int signr;
        unsigned int ifnum;
        void __user *userbuffer;
        void __user *userurb;
        struct urb *urb;

        int status;
        u32 secid;
    };   

    對每個 urb 都有一個 async 紀錄,在 proc_do_submiturb() 時 allocate 得到,同時它也會紀錄 user urb 的位置,將來可以把 urb 所得的資料 copy 回 user urb。


    整個 urb 的流程為: 
    proc_submiturb()
    : 把 user's urb(user space) 拷貝一份到我們的 uurb (kernel space)
    ->proc_do_submiturb(): 根據 bulk,interrupt..等 type 分別 initial 一些欄位,然後 allocate async data structure (裡面還包含 urb),然後放入 ps->async_pending queue 做紀錄,接著呼叫 usb_submit_urb() (之後就交給 usb host controller 處理了)然後不等結果就返回, 所以我們說 kernel 這裡是以 asynchronous 的方式處理 urb !

    等 usb host controller 把 urb 處理完後,會呼叫 async_complete(),async_complete() 將 async 紀錄從 ps->async_pending 移到 ps->completed.

    另一方面 user 要透過 IOCTL_USB_REAPURBNDELAY "收割" 已完成的 urb ,對應到 kernel 為 USBDEVFS_REAPURBNDELAY。這個 I/O control 會呼叫 proc_reapurbnonblock(),它會巡視 ps->completed 是否有 async 紀錄,若無則返回 -EAGAIN,若有則把找到的 urb (kernel space) 資料拷貝到 user space,並設定 IOCTL_URB_REAPURBNDELAY 時傳入的 arg 指向 user space urb,至此,kernel 傳送的部份已經完成。

    好,kernel 的部份至此大致了解,接下來討論我們的疑問,我們分兩種情形討論:
    1. 每一個 thread 都使用同一個 open handle, 也就是在 kernel 裡面同一份 ps:
      當 IOCTL_USB_REAPURBNDELAY 時,在 kernel 裡,會取出 ps->completd 上已完成 urb ,但是並不知道是哪一個 thread 的 urb ,因此 libusb 使用 cookie 作記號;
    在 usb_urb_transfer() 時,每一個 thread 在自己的 thread stack 上宣告一個 user space urb,如果第一個 thread reap 到不屬於自己的 urb 就打上 cookie 並繼續從 kernel reap 其他的 urb,此時當第二個 thread reap urb 時,就從 ps->completed 再抓一個 ... of course ,這樣第二個 thread 就收不到該收的 urb 了,錯誤就產生了。

    2. 
    每一個 thread 各自開了 open handle, 也就是在 kernel 裡面對應各自的 ps:
      這樣每個 thread 都對應了自己的 ps->completed list,所收的 urb 不會混淆,好像也不需要 cookie 了喔!但是這樣有一個缺點:bulk transfer 之前要 claim interface---就是每個 thread 要使用 usb_urb_transfer() 時必須先 claim interface...,所以原來已經 claim interface 的 thread 要 release interface,那就要把之前該 thread 的 urb 結束掉才行囉,這樣多個 thread "interleave urbs transfer" 的本意就沒了啊!

    所以結論是:若想以 thread 的方式使用 usblib-0.1.12,大概自己要動手改 usb_urb_transfer() 的部份,否則就直接用 usblib-1.0.0 。

  • 相关阅读:
    mailto发送邮件
    使用css实现一个持续的动画效果
    documentFragment添加节点
    删除数组的第一个元素,不要直接修改数组,结果返回新的数组
    js数组去重
    css定位position(侧边栏导航)
    mongoexport导出mongodb数据库中的数据
    textarea头部不顶行问题和textarea禁止拉伸
    HTML meta标签
    textarea
  • 原文地址:https://www.cnblogs.com/leino11121/p/2381914.html
Copyright © 2020-2023  润新知