1. 程式人生 > >USB驅動的一點了解(gadget)

USB驅動的一點了解(gadget)

參考以下部落格:

http://blog.csdn.net/myarrow/article/details/7012230          Linux USB驅動詳解
http://blog.csdn.net/myarrow/article/details/7013198        Linux USB驅動工作流程
http://www.cnblogs.com/general001/articles/2319552.html        Linux下USB驅動框架分析
http://blog.csdn.net/gotosola/article/details/7473730        Linux usb驅動程式全註釋
http://m.blog.csdn.net/blog/wuyuwei45/9047035     Android/Linux USB Gadget:三層架構
http://blog.chinaunix.net/uid-25909619-id-3153939.html    基於android4.0的usb gadget分析 (系列 USB 驅動分析)
http://blog.csdn.net/airk000/article/details/7887645    Android4.0 USB掛載核心驅動層流程分析(一)
http://blog.csdn.net/successcw/article/details/17137361     linux usb gadget
---------------------------------------------------------------------------------------------------------------------------------------------------------------------

一、主機驅動(略寫)

1,Linux usb裝置驅動框架:

分兩種,主機驅動與gadget驅動,前者控制插入其中的USB裝置,後者控制USB裝置(嵌入式)如何與主機通訊,如下:

2.usb系統一般由三個部分組成,主機,usb hub,usb裝置

在任何的usb系統中僅有一個主機

所有的usb device都連線在hub埠上

3.USB 四種傳輸方式

(1)控制傳輸模式(Control)

(2)等時傳輸方式(lsochronous)

(3)中斷傳輸模式(Interrupt)

(4)批量傳輸模式(bulk)

4. usb裝置組成
(1)一個usb裝置由可以有一個或多個配置(任一時刻,只能有一個配置生效)
(2)一個配置通常可以有一個或多個介面
(3)一個介面通常可以有一個或多個端點
通常所盡的usb裝置驅動是指介面驅動,即一個介面對應一個驅動。
所以Linux usb裝置有四大描述符,分別為裝置描述符,配置描述符,介面描述符,端點描述符


5.urb主要用於Linux host與裝置進行資料傳輸

所有USB通訊均為請求-->響應模式,USB裝置不會主動向Host傳送資料。
寫資料:USB裝置驅動傳送urb請求給USB裝置,USB裝置不需要回資料。
讀資料:USB裝置驅動傳送urb請求給USB裝置,USB裝置需要回資料。

urb的生命週期:
(1)由usb裝置驅動建立
(2)分配到usb裝置的指定端點
(3)由Usb裝置驅動提交到usb core
(4)由Usb core提交到usb 主機控制器
(5)由Usb主機控制器控制裝置進行資料傳輸
(6)當urb完成的時候,usb主機控制器驅動通知usb 裝置驅動

6.driver/usb/usb-skeleton.c

在Linux kernel原始碼目錄中driver/usb/usb-skeleton.c為我們提供了一個最基礎的USB驅動程式。我們稱為USB骨架。通過它我們僅需要修改極少的部分,就可以完成一個USB裝置的驅動,略。

---------------------------------------------------------------------------------------------------------------------------------------------------------------------

二.gadget驅動


1.Linux USB Gadget分三層架構:
(1). USB Gadget 功能層:
BSP/Driver開發者通常是要實現這一層,從而實現一個具體的裝置驅動,如Anddroid在此層實現了 adb,mtp,mass_storage 等
瀏覽參考關注此層程式碼時,會發現“composite”是此層的關鍵字,此層中關鍵的資料結構是:struct  usb_composite_driver
這一層的驅動檔案一般為:driver/usb/gadget/android.c(android實現的)或driver/usb/gadget/serial.c(傳統Linux實現的USB轉串列埠)

(2). USB 裝置層
這一層是Linux核心開發維護者實現的,與我們沒太大關係,不用我們操心,我們只關心其的一些介面就行。
瀏覽參考關注此層時,會發現“gadget”是此層的關鍵字
此層的關鍵資料結構是: usb_gadget_driver,usb_composite_dev。 這層主要的一個驅動檔案為:driver/usb/gadget/composite.c

(3). USB 裝置控制器驅動層:
這一層主要是與CPU、CPU USB控制器有關,與硬體緊密相關
這一層也比較頭痛,主要它和USB控制器牽扯在一起,涉及有 暫存器、時鐘、DMA等等。但是這一層往往是由晶片廠商去實現。
我們一般僅需在板級檔案中處理好所需要的USB介面即可。這層的關鍵字就是“UDC”,
主要驅動檔案命名含“udc”關鍵字,一般與CPU或晶片廠商有關,如driver/usb/gadget/xxx_udc.c。

USB Gadget功能層呼叫USB裝置層的介面,USB裝置層呼叫USB裝置控制器驅動層的介面
然後USB裝置控制器驅動層回撥USB裝置層,USB裝置層回撥USB Gadget功能層

2.重要的結構體

struct android_dev {
	const char *name;
	struct android_usb_function **functions;	//該android_dev支援的功能如adb,大容量儲存
	struct usb_composite_dev *cdev;				//符合裝置的dev
	struct device *dev;							//支援裝置驅動模型的dev 
	bool enabled;
	int disable_depth;
	struct mutex mutex;							//操作該結構體是用到的互斥鎖
	struct android_usb_platform_data *pdata;
	bool connected;								//記錄當前的連線狀態
	bool sw_connected;							//記錄切換後的連線狀態
	bool suspended;
	bool sw_suspended;
	char pm_qos[5];
	struct pm_qos_request pm_qos_req_dma;
	struct work_struct work;					//支援的工作佇列
	/* A list of struct android_configuration */
	struct list_head configs;
	int configs_num;
	/* A list node inside the android_dev_list */
	struct list_head list_item;
};

struct android_usb_function {
	char *name;						//該功能的名字
	void *config;					//該功能所依賴的配置
	struct device *dev;				//為裝置驅動模型準備的device
	char *dev_name;
	struct device_attribute **attributes;
	struct android_dev *android_dev;
	/* Optional: initialization during gadget bind */
	int (*init)(struct android_usb_function *, struct usb_composite_dev *);	//在繫結的時候,執行的初始化函式
	/* Optional: cleanup during gadget unbind */
	void (*cleanup)(struct android_usb_function *);							//在解除繫結時,執行清除的函式
	/* Optional: called when the function is added the list of enabled functions */
	void (*enable)(struct android_usb_function *);
	/* Optional: called when it is removed */
	void (*disable)(struct android_usb_function *);

	int (*bind_config)(struct android_usb_function *,						//把該function繫結到特定的配置上,此時function相當一個介面
			   struct usb_configuration *);

	/* Optional: called when the configuration is removed */
	void (*unbind_config)(struct android_usb_function *,
			      struct usb_configuration *);
	/* Optional: handle ctrl requests before the device is configured */
	int (*ctrlrequest)(struct android_usb_function *,
					struct usb_composite_dev *,
					const struct usb_ctrlrequest *);
};

static struct usb_gadget_driver composite_driver = {
	.unbind		= composite_unbind,
	.setup		= composite_setup,
	.disconnect	= composite_disconnect,
	.suspend	= composite_suspend,
	.resume		= composite_resume,
	.driver	= {
		.owner		= THIS_MODULE,
	},
};

struct usb_gadget {
	/* readonly to gadget driver */
	const struct usb_gadget_ops	*ops;
	struct usb_ep			*ep0;
	struct list_head		ep_list;	/* of usb_ep */
	enum usb_device_speed		speed;
	enum usb_device_speed		max_speed;
	unsigned			sg_supported:1;
	unsigned			is_otg:1;
	unsigned			is_a_peripheral:1;
	unsigned			b_hnp_enable:1;
	unsigned			a_hnp_support:1;
	unsigned			a_alt_hnp_support:1;
	unsigned			host_request:1;
	unsigned			otg_srp_reqd:1;
	const char			*name;
	struct device			dev;
	u8				usb_core_id;
	bool				l1_supported;
	bool                remote_wakeup;
};


static struct android_usb_function *supported_functions[] = {
	&mbim_function,
	&ecm_qc_function,
#ifdef CONFIG_SND_PCM
	&audio_function,
#endif
	&rmnet_smd_function,
	&rmnet_sdio_function,
	&rmnet_smd_sdio_function,
	&rmnet_function,
	&gps_function,
	&diag_function,
	&qdss_function,
	&serial_function,
	&adb_function,			//支援的adb功能
	&ccid_function,
	&acm_function,
	&mtp_function,
	&ptp_function,
	&rndis_function,		//Remote Network Driver Interface Specification,既是RemoteNDIS,既是遠端網路驅動介面規範。
							//基於USB實現RNDIS實際上就是TCP/IP over USB,就是在USB裝置上跑TCP/IP,讓USB裝置看上去像一塊網絡卡 
	&rndis_qc_function,
	&ecm_function,			//ecm
	&ncm_function,
	&mass_storage_function,	//支援的大容量儲存功能
	&accessory_function,
#ifdef CONFIG_SND_PCM
	&audio_source_function,
#endif
	&uasp_function,
	NULL
};

static struct usb_composite_driver android_usb_driver = {
	.name		= "android_usb",	//該複合裝置驅動的名稱
	.dev		= &device_desc,		//裝置描述符
	.strings	= dev_strings,
	.unbind		= android_usb_unbind,
	.max_speed	= USB_SPEED_SUPER
};

static struct usb_device_descriptor device_desc = {
	.bLength              = sizeof(device_desc),
	.bDescriptorType      = USB_DT_DEVICE,
	.bcdUSB               = __constant_cpu_to_le16(0x0200),
	.bDeviceClass         = USB_CLASS_PER_INTERFACE,
	.idVendor             = __constant_cpu_to_le16(VENDOR_ID),
	.idProduct            = __constant_cpu_to_le16(PRODUCT_ID),
	.bcdDevice            = __constant_cpu_to_le16(0xffff),
	.bNumConfigurations   = 1,
};

driver/usb/gadget/f_mass_storage.c
struct fsg_config {
	unsigned nluns;					//最大支援的LUN數量(LUN:Logical units)
	struct fsg_lun_config {			//每個LUN的引數
		const char *filename;		//此LUN的名字,如果不是可移除的(removable)則不需要
		char ro;					//FALSE(TRUE),設定read-only,如果是CD-ROM則不可能掛載成R/W
		char removable;				//TRUE(FALSE),說明此LUN可移除
		char cdrom;					//FALSE(TRUE),此LUN是否為CD-ROM
		char nofua;					//FALSE(TRUE),此LUN是否可忽略
	} luns[FSG_MAX_LUNS];

	const char		*lun_name_format;//建議為lun%d形式,如果不只是一個LUN,可以用%d來引導,必須是整型數字,如果不符合,可能將出現不可預知的錯誤
	const char		*thread_name;	//預設名字是"file_storage",是應該叫核心執行緒名字吧

	/* Callback functions. */
	const struct fsg_operations	*ops;
	/* Gadget's private data. */
	void			*private_data;

	const char *vendor_name;		/*  8 characters or less */
	const char *product_name;		/* 16 characters or less */
	u16 release;

	char			can_stall;
};

3.呼叫流程
./arch/arm/boot/dts/msm9625-v2.1.dtsi

	[email protected] {
		compatible = "qcom,android-usb";
		reg = <0xfe8078c8 0xc8>;
		qcom,android-usb-swfi-latency = <100>;
	};

Android.c (drivers\usb\gadget)
static int __init init(void)
	composite_driver.setup = android_setup;//composite_driver 是一個全域性的結構體,android.c中重新實現了它的setup和disconnect方法
	composite_driver.disconnect = android_disconnect;
	composite_driver.suspend = android_suspend;
	composite_driver.resume = android_resume;
	ret = platform_driver_register(&android_platform_driver);

static struct platform_driver android_platform_driver = {
	.driver = {
		.name = "android_usb",
		.of_match_table = usb_android_dt_match,
	},
	.probe = android_probe,
	.remove = android_remove,
	.id_table = android_id_table,
};

static int __devinit android_probe(struct platform_device *pdev)
	android_class = class_create(THIS_MODULE, "android_usb");//建立一個class,建立這個 class 的目的是支援使用者空間的 udev,自動建立裝置節點,/sys/devices/virtual/android_usb/
	android_dev = kzalloc(sizeof(*android_dev), GFP_KERNEL);//分配一個該結構體的例項
	android_dev->functions = supported_functions;		//初始化該結構體的 functions 成員,supported_functions 結構體見上面
	INIT_LIST_HEAD(&android_dev->configs);			//初始化dev的 enabled_functions 成員連結串列頭
	INIT_WORK(&android_dev->work, android_work);		//初始化dev的work成員,支援的工作佇列
	mutex_init(&android_dev->mutex);			//初始化dev的成員mutex互斥鎖
	composite_driver.usb_core_id = pdata->usb_core_id;
	//繼續初始化 android_dev 中的成員dev ,支援動態建立裝置節點,併為該裝置建立屬性,/sys/devices/virtual/android_usb/android0
	ret = android_create_device(android_dev, composite_driver.usb_core_id);	---- 
	ret = usb_composite_probe(&android_usb_driver, android_bind);	//這個函式才是函式的開始,到裡面牽涉的東西會越來越多

driver/usb/gadget/composite.c
/*將註冊進來的 usb_composite_driver (這裡是 android_usb_driver)與usb_gadget_driver(這裡是composite_driver)聯絡起來,
然後呼叫 udc core 提供的函式: usb_gadget_probe_driver ,並提供 callback function:  composite_bind 給udc core*/	
int usb_composite_probe(struct usb_composite_driver *driver, int (*bind)(struct usb_composite_dev *cdev))
	composite_driver.function =  (char *) driver->name;
	composite_driver.driver.name = driver->name;
	composite_driver.max_speed = driver->max_speed;
	composite = driver;
	composite_gadget_bind = bind;
	retval = usb_gadget_probe_driver(&composite_driver, composite_bind);	//向USB裝置層進行探測和註冊

driver/usb/gadget/udc-core.c
//usb_gadget_probe_driver() 函式是每一個USB裝置控制器驅動要實現的,這個函式與硬體緊密相關	
int usb_gadget_probe_driver(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *))	
	ret = usb_gadget_udc_start(udc->gadget, driver);
	
static inline int usb_gadget_udc_start(struct usb_gadget *gadget,struct usb_gadget_driver *driver)
	gadget->ops->udc_start(gadget, driver);		
	/*這個函式是udc core層實現的一個介面函式,具體實現在對應的 usb device controller 中,但由於不知 msm9625 是哪個 ???udc.c 故 udc_start 不分析了
	將 usb_gadget_driver 和 usb_gadget 對應起來,並呼叫bind(這裡為 composite_bind)*/
		

static int composite_bind(struct usb_gadget *gadget)
	struct usb_composite_dev	*cdev;		//新建 usb_composite_dev
	cdev->gadget = gadget;				//建立 usb_gadget 與 usb_composite_dev 的關係
	set_gadget_data(gadget, cdev);								
	cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);	//給這個端點分配一個請求物件,ep0 usb request
	cdev->driver = composite;//這裡指定 usb_composite_dev 的driver為 usb_composite_probe 註冊進來的 usb_composite_driver: android_usb_driver
	status = composite_gadget_bind(cdev);						
	/*這裡呼叫的是 usb_composite_probe 傳進來的 android_bind, android_bind 會呼叫 usb_add_config 來新增 config
	具體的每一個function的新增會在呼叫 usb_add_config 時傳進來的 callback function 中完成
	(這裡是 android_bind_config , 這個函式會呼叫每一個需要新增的function的 bind_config, 而 bind_config 呼叫的是 usb_add_function)
	*/

***********

/*初始化所有支援的 usb 裝置型別, 繼續建立 mtp,ptp, rndis, acm, ffs, mass_storage等屬性結點。
儲存來自 cdev 的 manufacturer_id, product_id, manufacturer_string, product_string, serial_string 資訊。
設定裝置自供電能力特性*/

static int android_bind(struct usb_composite_dev *cdev)
	usb_gadget_disconnect(gadget);		//初始化前確保是斷開的
	ret = android_init_functions(dev->functions, cdev);
	id = usb_string_id(cdev);    		//通過讀取裝置ID來填充一下驅動引數
	strlcpy(manufacturer_string, "Android", sizeof(manufacturer_string) - 1);	//填充一下預設的廠商和產品資訊,根據說明意思是可以通過上層改變
	gcnum = usb_gadget_controller_number(gadget);    //識別一下控制晶片,返回一個BCD值

static int android_init_functions(struct android_usb_function **functions, struct usb_composite_dev *cdev)
	f->dev_name = kasprintf(GFP_KERNEL, "f_%s", f->name);	//拿 mass_storage_function 來說,dev_name 就是 f_mass_storage 了
	f->dev = device_create(android_class, dev->dev, MKDEV(0, index), f, f->dev_name);	//建立裝置
	if (f->init)     //f->init指向 mass_storage_function_init,所以是在這個時候執行的

4.rndis部分

static struct android_usb_function rndis_function = {
	.name		= "rndis",
	.init		= rndis_function_init,
	.cleanup	= rndis_function_cleanup,
	.bind_config	= rndis_function_bind_config,
	.unbind_config	= rndis_function_unbind_config,
	.attributes	= rndis_function_attributes,
};

//rndis_bind_config_vendor() 是和 android usb 層溝通的橋樑函式,也是整個 f_rndis.c 檔案的唯一入口函式
static int rndis_function_bind_config(struct android_usb_function *f,struct usb_configuration *c)
	return rndis_bind_config_vendor(c, rndis->ethaddr, rndis->vendorID, rndis->manufacturer);

int rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],u32 vendorID, const char *manufacturer)
	status = rndis_init();		//通過呼叫 rndis_init(),向下打通了 kernel/drivers/usb/gadget/rndis.c 層
	rndis->vendorID = vendorID;	//在設定好 f_rndis 成員變數,也就是分配好必須的資源
	rndis->port.func.set_alt = rndis_set_alt;	//對於控制id,呼叫 usb_ep_enable() ,對於資料id, 呼叫 gether_connect()
	rndis->port.func.setup = rndis_setup;		//使用CDC命令封裝機制來實現一個 RPC呼叫,只檢查一種 USB_DIR_OUT, 一種 USB_DIR_IN
	rrndis->port.func.bind = rndis_bind;
	rndis->port.func.disable = rndis_disable;	//釋放資源,gether_disconnect() 斷開gether連線,呼叫 usb_ep_disable() 關閉端點
	status = usb_add_function(c, &rndis->port.func);//把該usb功能加入到配置中去,對應 USB規範可知,每個 usb 配置必須包含一個或多個usb功能		


//usb_add_function() 將呼叫 rndis->port.func.bind 函式並返回其值,實際就是呼叫 rndis_bind() 函式。
int usb_add_function(struct usb_configuration *config,struct usb_function *function)	
	value = function->bind(config, function);
	return value;
	
//rndis_bind() 進行乙太網功能驅動的初始化和繫結操作
static int rndis_bind(struct usb_configuration *c, struct usb_function *f)	
	status = usb_interface_id(c, f);	//分配usb 未使用的介面id值,是 drivers/usb/gadget/composite.c 的通用功能函式
	ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc);	//分配和配置必要的 usb 端點資源
	rndis->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);	//分配並返回一個 usb_request 物件指標
	rndis->notify_req->complete = rndis_response_complete;		//usb請求執行結束後,回撥函式  rndis_response_complete()		---- 1
	status = rndis_register(rndis_response_available, rndis);	//rndis_response_available() 函式傳送 RNDIS RESPONSE_AVAILABLE 訊息到端點。
	
static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req)
	status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC);	//如果 usb_request 返回狀態正常,則通過 usb_ep_queue() 放入佇列
	

 f_rndis.c 溝通的下層是  drivers/usb/gadget/u_ether.c。	

5.adb 部分
adb:
	
static struct android_usb_function adb_function = {
	.name		= "adb",
	.enable		= adb_android_function_enable,
	.disable	= adb_android_function_disable,
	.init		= adb_function_init,
	.cleanup	= adb_function_cleanup,
	.bind_config	= adb_function_bind_config,
};	
	
static int adb_function_bind_config(struct android_usb_function *f, struct usb_configuration *c)
	return adb_bind_config(c);

static int adb_bind_config(struct usb_configuration *c)
	dev->cdev = c->cdev;
	dev->function.name = "adb";
	dev->function.descriptors = fs_adb_descs;
	dev->function.hs_descriptors = hs_adb_descs;
	dev->function.bind = adb_function_bind;
	return usb_add_function(c, &dev->function);
	
int usb_add_function(struct usb_configuration *config,struct usb_function *function)	
	value = function->bind(config, function);	---- 即呼叫 adb_function_bind
	
static int adb_function_bind(struct usb_configuration *c, struct usb_function *f)	
	ret = adb_create_bulk_endpoints(dev, &adb_fullspeed_in_desc, &adb_fullspeed_out_desc);
	

static int adb_create_bulk_endpoints(struct adb_dev *dev,
				struct usb_endpoint_descriptor *in_desc,
				struct usb_endpoint_descriptor *out_desc)	
	ep = usb_ep_autoconfig(cdev->gadget, in_desc);			---- 分配gadget初始化註冊的endpoint
	req = adb_request_new(dev->ep_out, ADB_BULK_BUFFER_SIZE);	---- (主要呼叫 usb_ep_alloc_request)來分配對應的endpoint的request
	req->complete = adb_complete_out;	---- 指定request的complete函式,這個函式會在usb device controller對應的endpoint收發中斷完成之後被呼叫
	

6.mass_storage 部分
static int mass_storage_function_init(struct android_usb_function *f,struct usb_composite_dev *cdev)
	config->fsg.nluns = 2;				//支援2個儲存裝置
	config->fsg.luns[1].cdrom = 1;		//支援 cdrom
	config->fsg.luns[1].ro = 1;			//read only
	config->fsg.luns[1].removable = 0;	//不可移除
	name[1] = "lun0";					
	common = fsg_common_init(NULL, cdev, &config->fsg);	//根據配置建立裝置
	err = sysfs_create_link(&f->dev->kobj,&common->luns[i].dev.kobj,name[i]);	//建立裝置連結