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);
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中的模組引數宣告。