USB gadget 驅動 printer.c 分析


1. modprobe g_printer idVendor=0x0525 idProduct=0xa4a8 modprobe后面也可以加模塊參數

2. prn_example從stdout獲取數據然后通過USB發送出去,下面讓他將文件中的內容發送出去:
# cat data_file | prn_example -write_data

3.pdev = device_create(usb_gadget_class, NULL, devt, NULL, "g_printer%d", dev->minor); /*這個應該是在gadget類下創建一個device: struct device結構*/

4. 在調用bind后,經過重重函數調用,現在設備終於飽滿了,既有配置了,也有接口了,接口里也有相應的端點了。各層的關系也都聯系起來了。

5.核心結構體

/*將配置分組到gadget中*/
struct usb_composite_driver {
    const char    *name;
    const struct usb_device_descriptor    *dev;
    struct usb_gadget_strings    **strings;
    enum usb_device_speed        max_speed;
    unsigned        needs_serial:1; 
    
    /*用於分配整個設備共享的資源,例如字符串ID,並使用@usb_add_config()添加其配置。 
    返回負的errno值可能會失敗; 它應該在成功初始化時返回零。*/
    int        (*bind)(struct usb_composite_dev *cdev);
    /*與@bind相反*/
    int        (*unbind)(struct usb_composite_dev *);

    /*可選的驅動斷開方法*/
    void    (*disconnect)(struct usb_composite_dev *);

    /* global suspend hooks */
    void    (*suspend)(struct usb_composite_dev *);
    void    (*resume)(struct usb_composite_dev *);
    /*hidg_driver:指向全局composite_driver_template*/
    struct usb_gadget_driver    gadget_driver; 
};
/*代表一個復合的USB gadget*/
struct usb_composite_dev {
    /*只讀,作為此gadget的USB外設USB控制器的抽象*/
    struct usb_gadget        *gadget;
    /*用於控制響應; 緩沖區是預先分配的*/
    struct usb_request        *req;
    /*用於OS描述符響應,緩沖區是預先分配的*/
    struct usb_request        *os_desc_req;
    /*當前active的配置*/
    struct usb_configuration    *config;

    /* OS String is a custom (yet popular) extension to the USB standard. */
    u8    qw_sign[OS_STRING_QW_SIGN_LEN];
    u8    b_vendor_code;
    struct usb_configuration    *os_desc_config;
    unsigned int    use_os_string:1;

    /* private: */
    /* internals */
    unsigned int    suspended:1;
    struct usb_device_descriptor    desc;
    struct list_head    configs; /*usb_add_config_only()中將add的config添加到這個鏈表中*/
    struct list_head    gstrings;
    struct usb_composite_driver    *driver;
    u8    next_string_id;
    char    *def_manufacturer;

    /* the gadget driver won't enable the data pullup
     * while the deactivation count is nonzero.
     */
    unsigned    deactivations;

    /* the composite driver won't complete the control transfer's
     * data/status stages till delayed_status is zero.
     */
    int    delayed_status;

    /* protects deactivations and delayed_status counts*/
    spinlock_t    lock;

    /* public: */
    unsigned int    setup_pending:1; /*true when setup request is queued but not completed*/
    unsigned int    os_desc_pending:1;
};
/*代表usb從設備的驅動程序 */
struct usb_gadget_driver {
    /*描述此gadget功能的字符串*/
    char    *function; /*String describing the gadget's function*/
    /*此驅動能處理的最大速率*/
    enum usb_device_speed    max_speed;
    /*驅動的bind的回調*/
    int        (*bind)(struct usb_gadget *gadget, struct usb_gadget_driver *driver);
    /*驅動程序從gadget解除綁定時調用,通常來自rmmod(報告斷開連接后),在允許睡眠的環境中調用*/
    void    (*unbind)(struct usb_gadget *);
    /** @setup:由未被硬件級驅動程序處理的ep0控制請求調用。 大多數調用必須由gadget驅動程序處理,
    包括描述符和配置管理。 設置數據的16位成員按USB字節順序排列。 在in_interrupt被調用; 這可能不
    會睡覺。 驅動程序將對ep0的響應排隊,或將負數返回到停止。*/
    int        (*setup)(struct usb_gadget *,const struct usb_ctrlrequest *);
    /*@disconnect:在主機斷開連接后停止所有傳輸后調用。 可能在中斷上下文中調用; 這可能不能睡眠。 
    某些設備無法檢測到斷開連接,因此除非作為控制器關閉的一部分,否則可能無法調用此函數。*/
    void    (*disconnect)(struct usb_gadget *);
    void    (*suspend)(struct usb_gadget *);
    void    (*resume)(struct usb_gadget *);
    void    (*reset)(struct usb_gadget *);

    /* FIXME support safe rmmod */ /*===>目前內核不支持安全的rmmod!!!*/
    struct device_driver    driver;

    /*應該綁定此驅動程序的UDC名稱。 如果udc_name為NULL,則此驅動程序將綁定到任何可用的UDC。*/
    char    *udc_name;
    struct list_head    pending; /*掛載在等待udc的鏈表上*/
    unsigned   match_existing_only:1; /*決定在沒有available的UDC時,釋放將綁定請求的driver掛載到pending鏈表上
    see usb_gadget_probe_driver()*/
};
/*代表一個usb從設備*/
struct usb_gadget {
    /*sysfs_notify()使用的工作隊列*/
    struct work_struct work;
    struct usb_udc *udc;
    /*用於訪問特定於硬件的操作的函數指針,對於gadget driver來說是只讀的*/
    const struct usb_gadget_ops    *ops; /*指向全局的:renesas_usb3_gadget_ops*/
    struct usb_ep *ep0; /*端點0,控制端點*/
    struct list_head ep_list;    /* of usb_ep, List of other endpoints supported by the device.*/
    enum usb_device_speed speed;
    enum usb_device_speed max_speed;
    enum usb_device_state state;
    const char    *name;
    struct device    dev;
    unsigned    out_epnum; /*分別為輸入輸出端點數*/
    unsigned    in_epnum;
    unsigned    mA;
    struct usb_otg_caps        *otg_caps;

    unsigned    sg_supported:1;
    unsigned    is_otg:1; /*標識是不是otg設備*/
    unsigned    is_a_peripheral:1;
    unsigned    b_hnp_enable:1;
    unsigned    a_hnp_support:1;
    unsigned    a_alt_hnp_support:1;
    unsigned    hnp_polling_support:1;
    unsigned    host_request_flag:1;
    unsigned    quirk_ep_out_aligned_size:1;
    unsigned    quirk_altset_not_supp:1;
    unsigned    quirk_stall_not_supp:1;
    unsigned    quirk_zlp_not_supp:1;
    unsigned    quirk_avoids_skb_reserve:1;
    unsigned    is_selfpowered:1; /*表明此gadget是否為self-powered*/
    unsigned    deactivated:1;
    unsigned    connected:1;
    unsigned    lpm_capable:1;
};

我們發現這些bind就是一個目的,初始化struct usb_composite_dev結構,使其逐漸豐滿。因為這個結構代表一個USB設備。
經過合適的初始化后設備才能正確的工作。經過重重初始化,三層總算整合在了一起了。這三層最終形成了一個飽滿的
struct usb_composite_dev結構。這個結構包含USB設備運行各種信息。包括:配置,接口,端點等。

6.作為host稱為USB主機控制器驅動,作為gadget時稱為USB設備控制器驅動(UDC)

7.USB設備控制器驅動,需要關心四個核心的數據結構,這些數據結構包括描述一個USB設備控制器的usb_gadget、UDC操作usb_gadget_ops、
描述一個端點的usb_ep以及描述端點操作的usb_ep_ops結構體。UDC驅動圍繞這些數據結構及其成員函數展開

8.打印機的設備位置為:# ls -l /devices/virtual/usb_printer_gadget/

9.由於普通USB設備只有一個上游端口,因此它們只有一個這樣的驅動程序。 控制器驅動程序可以支持任意數量的不同gadget驅動程序,
但一次只能使用其中一個。

10.host端驅動與USB主機控制器驅動的交互通過 struct urb, 而作為gadget時,驅動與USB設備控制器之間的交互使用 struct usb_request來 ########
描述一次IO請求。

11.內核文檔翻譯:
https://01.org/linuxgraphics/gfx-docs/drm/driver-api/usb/gadget.html?highlight=usb_composite_probe#c.usb_composite_probe
gadget驅動的生命周期
gadget驅動程序向硬件發出端點I/O請求,而無需了解硬件的許多細節,但驅動程序設置/配置代碼需要處理一些差異。 像這樣使用API:

1.為特定設備端USB控制器硬件注冊驅動程序,例如PCI PDA(USB 2.0)上的net2280,Linux PDA中的sa11x0或pxa25x,依此類推。 此時,
設備邏輯上處於USB ch9初始狀態(已連接attached),沒有電源且無法使用(因為它還不支持枚舉)。 任何主機都不應該看到該設備,
因為即使VBUS電源可用,它也不會激活主機用來檢測設備的數據線上拉。
2.注冊實現某些更高級別設備功能的gadget驅動程序。 然后將bind()綁定到usb_gadget,它會在檢測到VBUS后的某個時間激活數據線上拉。
3.硬件驅動程序現在可以開始枚舉。 它處理的步驟是接受USB電源和set_address請求。 其他步驟由gadget驅動程序處理。 如果在主機開
始枚舉之前卸載gadget驅動程序模塊,則跳過步驟7之前的步驟。
4.gadget驅動程序的setup()調用返回usb描述符,既基於總線接口硬件提供的內容,也基於正在實現的功能。 這可能涉及備用設置或配置,
除非硬件阻止此類操作。 對於OTG設備,每個配置描述符包括OTG描述符。
5.當USB主機發出set_configuration調用時,gadget驅動程序處理枚舉的最后一步。 它使能該配置中使用的所有端點,使所有接口都處於
默認設置。 這涉及使用硬件端點列表,根據端點描述符啟用每個端點。 它還可能涉及使用usb_gadget_vbus_draw()來從VBUS中獲取更多的
電流(可選Rcar3沒有實現),如該配置所允許的那樣。 對於OTG設備,設置配置還可能涉及通過用戶界面報告HNP功能。
6.執行實際工作並執行數據傳輸,可能涉及更改接口設置或切換到新配置,直到設備與主機斷開連接disconnect()。 將任意數量的傳輸請求
排隊到每個端點。 在斷開連接之前,它可能會被suspend和resume幾次。 斷開連接時,驅動程序將返回步驟3(上方)。
7.卸載gadget驅動程序模塊時,驅動程序的unbind()將會被回調。 這樣可以控制器驅動程序被卸載。

驅動程序通常會被安排,以便只加載gadget驅動程序模塊(或將其靜態鏈接到Linux內核)允許外圍設備被枚舉,但某些驅動程序將推遲枚舉,
直到某些更高級別的組件(如用戶模式守護程序)使能它。 請注意,在此最低級別,沒有關於如何實現ep0配置邏輯的策略,除了它應遵守
USB規范。 這些問題屬於gadget驅動程序領域,包括了解某些USB控制器強加的實施限制,或者了解復合設備可能恰好通過集成可重用組件來
構建。

請注意,OTG設備的上述生命周期可能略有不同。 除了在每個配置中提供額外的OTG描述符之外,只有與HNP相關的差異對於驅動程序代碼特
別可見。 它們涉及SET_CONFIGURATION請求期間的報告要求,以及在某些掛起回調期間調用HNP的選項。 此外,SRP稍微改變了
usb_gadget_wakeup的語義。

struct usb_request 描述一次IO請求。

12.Gadget 功能驅動層, Gadget功能驅動層是USB Gadget軟件結構的最上層。主要是實現USB設備的功能,
這一層通常與linux內核的其他子系統有密切的聯系。

13.mod_gadget.c:這個是2.0的UDC驅動,renesas_usb3.c: 應該3.0的usb gadget的UDC驅動。

14.UDC層還需要提供USB設備的中斷處理程序,中斷處理尤其重要。因為所有的USB傳輸都是由主機發起,而有沒有USB傳輸完全由USB中斷判
定,所以USB中斷處理程序是整個軟件架構的核心。

15.在用戶程序中,select()和poll()本質上是一樣的, 不同只是引入的方式不同,前者是在BSD UNIX中引入的,后者是在System V中引入的。
用的比較廣泛的是select

16.DECLARE_USB_FUNCTION_INIT(printer, gprinter_alloc_inst, gprinter_alloc);

DECLARE_USB_FUNCTION_INIT(printer, gprinter_alloc_inst, gprinter_alloc);
/*==========================上面等效下面======================================*/
static struct usb_function_driver printerusb_func = {
    .name = "printer",
    .mod  = THIS_MODULE,
    .alloc_inst = gprinter_alloc_inst,
    .alloc_func = gprinter_alloc,
};
MODULE_ALIAS("usbfunc:printer"); /*用它來加載驅動模塊*/

static int __init printermod_init(void)
{
    return usb_function_register(&printerusb_func);
}
static void __exit printermod_exit(void)
{
    usb_function_unregister(&printerusb_func);
}
module_init(printermod_init);
module_exit(printermod_exit);
module_usb_composite_driver(printer_driver);
/*-------module_usb_composite_driver(printer_driver);等效如下----------*/
static int __init printer_driver_init(void)
{
    return usb_composite_probe(&printer_driver);
}
module_init(printer_driver_init);
static void __exit printer_driver_exit(void)
{
    usb_composite_unregister(&printer_driver);
}
module_exit(printer_driver_exit);

/*----------------------------------------------------------------------*/

 

 

17.module_param_named(maximum_line_test, max_test, int, 0); 此定義下,該參數的外部可見參數名為:maximum_line_test,內部全局變量名為:max_test */

18.usb2.0的gadget的UDC注冊過程:

usbhs_probe  //[usb/renesas_usbhs/common.c]
    usbhs_mod_probe //[usb/renesas_usbhs/mod.c]
        usbhs_mod_gadget_probe //[usb/renesas_usbhs/mod_gadget.c]
            usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) //[usb/gadget/udc/core.c]  將新gadget添加到udc類驅動程序列表中
                udc = kzalloc(sizeof(*udc), GFP_KERNEL);
                udc->dev.class = udc_class;
                udc->dev.groups = usb_udc_attr_groups;
                list_add_tail(&udc->list, &udc_list); 將此udc放到全局鏈表udc_list中
                usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
                udc->vbus = true;
                check_pending_gadget_drivers(udc) //[usb/gadget/udc/core.c] 匹配一個usb_gadget_driver
                    list_for_each_entry(driver, &gadget_driver_pending_list, pending) //在gadget_driver_pending_list查找pending的usb_gadget_driver
                    if (!driver->udc_name || strcmp(driver->udc_name, dev_name(&udc->dev)) == 0) //usb_udc和usb_gadget_driver通過名字進行匹配
                        ret = udc_bind_to_driver(udc, driver);
                    
=====>UDC也是以一個usb_gadget的形式注冊的。
19.f_printer.c中function注冊過程:
DECLARE_USB_FUNCTION_INIT
int __init printermod_init(void)
    usb_function_register(&printerusb_func) //這個注冊僅僅是將usb_function_driver放到全局鏈表func_list中
        list_add_tail(&newf->list, &func_list); //[usb/gadget/functions.c] 

20.printer.c中:

printer_driver_init
    usb_composite_probe(struct usb_composite_driver *driver); //傳參為全局變量&printer_driver
        driver->gadget_driver = composite_driver_template;
        由usb_composite_driver構造出一個usb_gadget_driver
        usb_gadget_probe_driver(struct usb_gadget_driver *driver) //傳參為構造出來的usb_gadget_driver
            它里面去匹配已經注冊的UDC,若匹配不到就掛載在gadget_driver_pending_list上。
            list_for_each_entry(udc, &udc_list, list) //在udc_list上查找已經注冊的usb_udc
            list_add_tail(&driver->pending, &gadget_driver_pending_list); //若匹配不到就將usb_gadget_driver放到鏈表gadget_driver_pending_list中。
            udc_bind_to_driver(udc, driver); //若找到就將此usb_udc綁定到usb_gadget_driver中
                udc->driver = driver;
                driver->bind(udc->gadget, driver); //調用usb_gadget_driver的bind(),也就是composite_bind,是模板composite_driver_template中的,
                    參數是usb_gadget和usb_gadget_driver
                    struct usb_composite_dev *cdev = kzalloc(sizeof *cdev, GFP_KERNEL); //分配一個usb_composite_dev結構
                    cdev->gadget = usb_gadget;
                    composite_dev_prepare(struct usb_composite_driver *composite, struct usb_composite_dev *cdev)
                        cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); //預先分配ep0使用的req
                        cdev->req->complete = composite_setup_complete; //指定ep0的完成回調函數
                        usb_ep_autoconfig_reset(gadget); //執行一次端點的rest
                        composite->bind(cdev); //調用usb_composite_driver的bind(),也就是printer_bind,其調用流程如① ###########
                        update_unchanged_dev_desc(usb_device_descriptor *new, usb_device_descriptor *old) //更新設備描述符
                usb_gadget_udc_start(udc); //使能usb設備控制器
                    udc->gadget->ops->udc_start(udc->gadget, udc->driver) //也就是調用usbhsg_gadget_ops.usbhsg_gadget_start,做一些設備控制器的初始化
                        usbhsg_try_start
                            usbhsg_update_pullup
                usb_udc_connect_control(udc);
                    usb_gadget_connect
                        gadget->ops->pullup(gadget, 1); //也就是調用usbhsg_gadget_ops.pullup,使能Dp上拉
                kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); //上報App




printer.c中的usb_composite_driver.bind的調用流程:
printer_bind //① ###########
    usb_get_function_instance("printer");
        try_get_usb_function_instance(name);
            list_for_each_entry(fd, &func_list, list) 在全局鏈表func_list中通過name查找之前使用
                fi = fd->alloc_inst(); 找到對應的usb_function_driver並調用其alloc_inst()
                fi->fd = fd; return fi; 然后fi->fd指向找到的usb_function_driver結構體。
    usb_string_ids_tab(cdev, strings); //[usb/gadget/functions.c] 為每一個字符串都分配一個唯一的id,然后將這些id賦值給
    usb_device_descriptor中的iManufacturer,iProduct,iSerialNumber。
        gadget_is_otg //判斷是不是otg,如果是,還要為其分配otg描述符 usb_otg_descriptor_alloc
    usb_add_config(cdev, &printer_cfg_driver, printer_do_config);
        usb_add_config_only(cdev, config); //將此config添加到usb_composite_dev cdev->configs中
        printer_do_config //執行這個回調
            usb_ep_autoconfig_reset
            usb_gadget_set_selfpowered
            f_printer = usb_get_function(fi_printer);
                f = fi->fd->alloc_func(fi); //調用usb_function_instance的usb_function_driver的alloc_func,應該就
                    是gprinter_alloc,獲取到一個usb_function
                    gprinter_alloc中初始化usb_function的各個成員函數(bind,unbind,setup,set_alt),做一些鏈表的初始化
                f->fi = fi; return f; //usb_function的usb_function_instance指向此fi
            status = usb_add_function(c, f_printer);
                list_add_tail(&function->list, &config->functions); //將此usb_function掛接在usb_configuration的functions鏈表上
                function->bind(config, function) //調用function的bind進行綁定,也即是printer_func_bind
                    usb_interface_id  //分配一個沒有使用的接口id
                    in_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_in_desc); //根據端點描述符自動分配IN/OUT端點
                    out_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_out_desc);
                    usb_assign_descriptors    //對usb_function中的各個描述符賦值
                    for (i = 0; i < dev->q_len; i++) //為in_ep/out_ep分配usb_request,並將其插入隊列中
                        req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL);
                        list_add(&req->list, &dev->tx_reqs);
                        req = printer_req_alloc(dev->out_ep, USB_BUFSIZE, GFP_KERNEL);
                        list_add(&req->list, &dev->rx_reqs);
                    device_create(usb_gadget_class, NULL, devt, NULL, "g_printer%d", dev->minor); //創建設備類,為創建設備文件做准備
                    cdev_init(&dev->printer_cdev, &printer_io_operations); //在bind()中指定對用戶空間的接口printer_io_operations
                    ret = cdev_add(&dev->printer_cdev, devt, 1);
        usb_ep_autoconfig_reset(cdev->gadget); //執行一次reset ep的操作
    usb_composite_overwrite_options //用來以實際的值覆蓋USB_GADGET_COMPOSITE_OPTIONS中的模塊參數聲明。

 

 

 

 

 

 

 


















免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM