現從msg_bind()函數(drivers/usb/gadget/legacy/mass_storage.c)開始講起。
U盤的gadget驅動比較複雜,因爲它包含幾部分,包括gadget驅動、U盤相關的處理(SCSI命令的處理、lun概念等)以及虛擬文件系統的讀寫操作(因爲我們虛擬出來的U盤是用文件系統來保存的)等。
//不失一般性,刪除一些錯誤處理代碼
static int msg_bind(struct usb_composite_dev *cdev)
{
static const struct fsg_operations ops = {
.thread_exits = msg_thread_exits,
};
struct fsg_opts *opts;
struct fsg_config config;
int status;
fi_msg = usb_get_function_instance("mass_storage");
fsg_config_from_params(&config, &mod_data, fsg_num_buffers);
opts = fsg_opts_from_func_inst(fi_msg);
opts->no_configfs = true;
status = fsg_common_set_num_buffers(opts->common, fsg_num_buffers);
fsg_common_set_ops(opts->common, &ops);
status = fsg_common_set_cdev(opts->common, cdev, config.can_stall);
fsg_common_set_sysfs(opts->common, true);
status = fsg_common_create_luns(opts->common, &config);
fsg_common_set_inquiry_string(opts->common, config.vendor_name,
config.product_name);
status = usb_string_ids_tab(cdev, strings_dev);
msg_device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
if (gadget_is_otg(cdev->gadget) && !otg_desc[0]) {
struct usb_descriptor_header *usb_desc;
usb_desc = usb_otg_descriptor_alloc(cdev->gadget);
if (!usb_desc)
goto fail_string_ids;
usb_otg_descriptor_init(cdev->gadget, usb_desc);
otg_desc[0] = usb_desc;
otg_desc[1] = NULL;
}
status = usb_add_config(cdev, &msg_config_driver, msg_do_config);
usb_composite_overwrite_options(cdev, &coverwrite);
return 0;
}
內核有個特點,就是驅動分層,gadget也不例外,它有一系列的function驅動(源碼在drivers/usb/gadget/function目錄下),譬如f_mass_storage.c(usb_f_mass_storage.ko)就是實現U盤的功能,而f_hid.c(usb_f_hid.ko)則是實現HID鍵鼠功能,爲什麼這麼做呢?因爲一個USB設備不一定只有一種功能,譬如"U盤+網卡"、“攝像頭+串口”等等這種複合設備,其中drivers/usb/gadget/legacy/multi.c就是一個複合設備的例子。只是我們現在分析的mass_storage.c剛好只有一種功能(U盤),你覺得沒必要分層而已,畢竟驅動分層後變複雜了很多。你看 fi_msg = usb_get_function_instance("mass_storage"),不跟進去根本不知道它做了什麼,除非你比較熟悉gadget的api接口。它其實是會得到由drivers/usb/gadget/function/f_mass_storage.c這個驅動(usb_f_mass_storage.ko)所註冊的“usb功能實例”(struct usb_function_instance *), 而在f_mass_storage.c這邊是預先通過DECLARE_USB_FUNCTION_INIT宏(在include/linux/usb/composite.h中定義)進行註冊的:
// f_mass_storage.c
DECLARE_USB_FUNCTION_INIT(mass_storage, fsg_alloc_inst, fsg_alloc);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Michal Nazarewicz");
每調用DECLARE_USB_FUNCTION_INIT宏註冊一個function實例,就會add進一個全局鏈表變量的尾部(list_add_tail),所以當在其他驅動中調用usb_get_function_instance("mass_storage"),就可以通過“mass_storage”名稱在全局鏈表中找到對應的“usb功能實例”(struct usb_function_instance *)。
好了,知道這一層關係後,調用usb_get_function_instance("mass_storage")其實就是調用f_mass_storage.c的fsg_alloc_inst()函數:
//去掉錯誤處理代碼
static struct usb_function_instance *fsg_alloc_inst(void)
{
struct fsg_opts *opts;
struct fsg_lun_config config;
int rc;
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
...
mutex_init(&opts->lock);
opts->func_inst.free_func_inst = fsg_free_inst;
opts->common = fsg_common_setup(opts->common);
...
rc = fsg_common_set_num_buffers(opts->common,
CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS);
...
memset(&config, 0, sizeof(config));
config.removable = true;
rc = fsg_common_create_lun(opts->common, &config, 0, "lun.0",
(const char **)&opts->func_inst.group.cg_item.ci_name);
...
opts->lun0.lun = opts->common->luns[0];
opts->lun0.lun_id = 0;
config_group_init_type_name(&opts->lun0.group, "lun.0", &fsg_lun_type);
opts->default_groups[0] = &opts->lun0.group;
opts->func_inst.group.default_groups = opts->default_groups;
config_group_init_type_name(&opts->func_inst.group, "", &fsg_func_type);
return &opts->func_inst;
}
在fsg_alloc_inst()中我們看到它先malloc了一個struct fsg_opts對象,該對象在fsg(File Storage Gadget)中算是比較重要的:
struct fsg_opts {
struct fsg_common *common;
struct usb_function_instance func_inst;
struct fsg_lun_opts lun0;
struct config_group *default_groups[2];
bool no_configfs; /* for legacy gadgets */
/*
* Read/write access to configfs attributes is handled by configfs.
*
* This is to protect the data from concurrent access by read/write
* and create symlink/remove symlink.
*/
struct mutex lock;
int refcnt;
};
因爲它裏面保存了struct fsg_common * common和struct usb_function_instance func_inst結構,而no_configfs字段則是指明是否使用configfs配置,這個參數在msg_bind()會配置,我們在第二篇文章中有介紹過使用configfs進行配置gadget驅動,很是方便,只是代碼講解我選用了legacy gadgets來分析,就不細說configfs相關的代碼了。U盤gadget的主要全局變量都是聚集在struct fsg_common結構體中,f_mass_storage.c大部分函數都是傳入struct fsg_common *參數進去,是本驅動最重要的結構體!struct fsg_common * common就是在這個時候分配好空間(fsg_common_setup())的,並且把common用到的自旋鎖、完成量(completion)等等初始化一遍,方便後續使用。然後分配buffer,譬如32個16KB的緩衝區,用於U盤數據傳輸,多個緩衝區能增加數據讀寫吞吐量。最後調用fsg_common_create_lun()創建邏輯單元(logical units/LUNs),也是爲了後續U盤操作做準備。最後return &opts->func_inst返回struct usb_function_instance *。
後面兩個函數是關於configfs的,畢竟要支持從configfs配置驅動,肯定要有相應的api支持,後面遇到configs相關的代碼我都直接忽略。
ok,回到msg_bind(),我們這時得到了上述由fsg_alloc_inst()負責填充好並返回的struct usb_function_instance *fi_msg(全局變量),另外,我們都知道可以根據struct usb_function_instance *通過container_of()找回struct fsg_opts對象,方便後續繼續配置(初始化)。
另外在msg_bind()中我們還出現了一個struct fsg_config config結構體,該結構體主要用於配置U盤gadget支持的功能,譬如我們可以配置該U盤是否removable的,是否CD/ROM,是否只讀,是否nofua,有多少個邏輯塊(luns),是否支持端點掛起(stall),以及爲各個lun指定映射的文件路徑,因爲一個lun代表一個邏輯分區,既然我的U盤的底層存儲是基於文件系統,自然要爲每個分區都指定文件路徑。這些參數從哪定義?自然是加載驅動時把參數帶進去,譬如(modprobe g_mass_storage luns=2 file=/var/sdcard/disk.img,/var/sdcard/disk1.img 等等。爲什麼我會知道使用file=filename[,filename...],原因是內核有個文檔專門描述怎麼配置,具體在內核源碼目錄下Documentation/usb/mass-storage.txt)。struct fsg_config的具體定義如下:
struct fsg_config {
unsigned nluns;
struct fsg_lun_config luns[FSG_MAX_LUNS];
/* 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 */
char can_stall;
unsigned int fsg_num_buffers;
};
其實就是在msg_bind()->fsg_config_from_params()裏解釋參數,不深入了。在這裏又調用fsg_common_set_num_buffers()一次,之前在f_mass_storage.c的fsg_alloc_inst()也有初始化過,但沒有釋放之前的,貌似有內存泄漏了。後面通過fsg_common_create_luns()根據用戶設置的luns數量重新創建lun。fsg_common_set_inquiry_string()函數有意思,我們可以改變裏面的內容,讓PC插入我們的U盤時,下發SCSI INQUIRY命令時,返回的字符串。這個可以用Bus Hound或者USBlyzer抓包看到。usb_string_ids_tab(cdev, strings_dev)是利用strings_dev結構生成對應的字符串描述符,用於usb枚舉階段返回給usb host。接着是otg相關,我們跳過。最重要的函數來了,usb_add_config(cdev, &msg_config_driver, msg_do_config)。因爲調用完它之後,U盤就算初始化完畢!我們看下它的定義,它的具體實現我們不分析,有興趣的朋友可以自行研究:
//composite.c
/**
* usb_add_config() - add a configuration to a device.
* @cdev: wraps the USB gadget
* @config: the configuration, with bConfigurationValue assigned
* @bind: the configuration's bind function
* Context: single threaded during gadget setup
*
* One of the main tasks of a composite @bind() routine is to
* add each of the configurations it supports, using this routine.
*
* This function returns the value of the configuration's @bind(), which
* is zero for success else a negative errno value. Binding configurations
* assigns global resources including string IDs, and per-configuration
* resources such as interface IDs and endpoints.
*/
int usb_add_config(struct usb_composite_dev *cdev,
struct usb_configuration *config,
int (*bind)(struct usb_configuration *))
{
...
}
功能是“爲一個usb設備增加配置”,我們還記得usb的描述符結構吧?usb設備描述符->配置描述符->接口描述符->端點描述符....,其中,usb_add_config()的int (*bind)(struct usb_configuration *)函數指針就是用於“爲配置增加接口”,它會在調用usb_add_config()時被回調,對應我們的U盤gadget就是msg_do_config()。我們先來看兩個實參的定義:
static struct usb_configuration msg_config_driver = {
.label = "Linux File-Backed Storage",
.bConfigurationValue = 1,
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
};
static int msg_do_config(struct usb_configuration *c)
{
struct fsg_opts *opts;
int ret;
if (gadget_is_otg(c->cdev->gadget)) {
c->descriptors = otg_desc;
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
opts = fsg_opts_from_func_inst(fi_msg);
f_msg = usb_get_function(fi_msg);
if (IS_ERR(f_msg))
return PTR_ERR(f_msg);
ret = usb_add_function(c, f_msg);
if (ret)
goto put_func;
return 0;
put_func:
usb_put_function(f_msg);
return ret;
}
msg_config_driver結構主要是指定.bConfigurationValue = 1,使用第一個配置,因爲到時usb host(譬如PC)在枚舉本U盤階段,會下發SetConfiguration() 進行配置,這時U盤事實上要準備好usb傳輸相關的東西,如端點分配等,因爲緊接着PC就會用端點描述符上指定的端點進行通信,你得提前準備好。
上面的代碼片段msg_do_config中的fi_msg變量就是在msg_bind()中通過usb_get_function_instance("mass_storage")得到的全局變量。它先是struct usb_function *f_msg = usb_get_function(fi_msg),該函數事實上是調用了f_mass_storage.c註冊好的fsg_alloc():
static struct usb_function *fsg_alloc(struct usb_function_instance *fi)
{
struct fsg_opts *opts = fsg_opts_from_func_inst(fi);
struct fsg_common *common = opts->common;
struct fsg_dev *fsg;
fsg = kzalloc(sizeof(*fsg), GFP_KERNEL);
if (unlikely(!fsg))
return ERR_PTR(-ENOMEM);
mutex_lock(&opts->lock);
opts->refcnt++;
mutex_unlock(&opts->lock);
fsg->function.name = FSG_DRIVER_DESC;
fsg->function.bind = fsg_bind;
fsg->function.unbind = fsg_unbind;
fsg->function.setup = fsg_setup;
fsg->function.set_alt = fsg_set_alt;
fsg->function.disable = fsg_disable;
fsg->function.free_func = fsg_free;
fsg->common = common;
return &fsg->function;
}
這個函數超級重要,很明顯它的作用是填充一些回調函數,以及返回struct usb_function* f_msg,在我們前面的代碼分析就已經知道struct fsg_opts *opts和struct fsg_common *common結構早在fsg_alloc_inst()時就已經分配好空間了,這裏只是拿回指針進行填充(初始化)而已。其中fsg_setup和fsg_bind回調十分重要,未來會被composite.c框架回調,用於處理usb host下發usb枚舉以及U盤的實際讀寫操作。後面再單獨分析。
msg_do_config最後調用的一個函數是usb_add_function(c, f_msg),這樣就把上面填充的fsg_setup和fsg_bind等回調註冊到composite.c框架了。其中參數c是在static struct usb_configuration msg_config_driver基礎上通過usb_add_config()->usb_add_config_only()進行進一步配置,剩下就是composite.c框架處理的事情了。那fsg_setup()和fsg_bind()在什麼情況下會被回調:
/**
* usb_add_function() - add a function to a configuration
* @config: the configuration
* @function: the function being added
* Context: single threaded during gadget setup
*
* After initialization, each configuration must have one or more
* functions added to it. Adding a function involves calling its @bind()
* method to allocate resources such as interface and string identifiers
* and endpoints.
*
* This function returns the value of the function's bind(), which is
* zero for success else a negative errno value.
*/
int usb_add_function(struct usb_configuration *config,
struct usb_function *function)
{
...
...
/* REVISIT *require* function->bind? */
if (function->bind) {
value = function->bind(config, function);
...
} else
value = 0;
...
...
}
我們發現msg_do_config()在調用usb_add_function()後,fsg_bind()就會被回調,這時就越來越接近我們的初衷:“爲啥它能讓PC識別成“可移動磁盤”,以及它可以像市面上的U盤一樣能讀寫文件”。
好了,下一篇文章我們從fsg_bind()函數(usb/gadget/function/f_mass_storage.c)講起!