configfs配置usb gadget原理

configfs初始化

usb gadget configfs模块的初始化函数为gadget_cfs_init。该函数调用后,会向configfs注册一个子系统,子系统使用configfs_subsystem结构体描述。子系统中又可分为组,使用config_group描述,组内又有成员,使用config_item描述。usb gadget configfs就是configfs子系统中的一个成员,成员的名称为"usb_gadget",成员的类型使用config_item_type描述,成员类型中包含了初始化函数gadgets_ops。因此usb gadget configfs子系统最终通过调用gadgets_make进行初始化。当加载libcomposite.ko模块后,会在/sys/kernel/config/目录下生成一个usb_gadget目录。

static struct configfs_group_operations gadgets_ops = {
	.make_group     = &gadgets_make,
	.drop_item      = &gadgets_drop,
};

static const struct config_item_type gadgets_type = {
	.ct_group_ops   = &gadgets_ops,
	.ct_owner       = THIS_MODULE,
};

static struct configfs_subsystem gadget_subsys = {
	.su_group = {
		.cg_item = {
			.ci_namebuf = "usb_gadget",
			.ci_type = &gadgets_type,
		},
	},
	.su_mutex = __MUTEX_INITIALIZER(gadget_subsys.su_mutex),
};
static int __init gadget_cfs_init(void)
{
	int ret;

	config_group_init(&gadget_subsys.su_group);

	ret = configfs_register_subsystem(&gadget_subsys);

#ifdef CONFIG_USB_CONFIGFS_UEVENT
	android_class = class_create(THIS_MODULE, "android_usb");
	if (IS_ERR(android_class))
		return PTR_ERR(android_class);
#endif

	return ret;
}
module_init(gadget_cfs_init);

gadgets_make

gadgets_make函数的主要工作内容是设置复合设备数据结构usb_composite_dev和复合设备驱动数据结构usb_composite_driver

static struct config_group *gadgets_make(
		struct config_group *group, const char *name)
{
    ......
    /* 分配struct gadget_info结构体 */
    gi = kzalloc(sizeof(*gi), GFP_KERNEL);
    ......
	/* 设置group,gadgets_make最终返回的是gi->group */
	gi->group.default_groups = gi->default_groups;
	gi->group.default_groups[0] = &gi->functions_group;
	gi->group.default_groups[1] = &gi->configs_group;
	gi->group.default_groups[2] = &gi->strings_group;
	gi->group.default_groups[3] = &gi->os_desc_group;

    /* 设置functions_group,可配置function驱动的参数 */
	config_group_init_type_name(&gi->functions_group, "functions",
			&functions_type);
    /* 设置configs_group,可配置USB设备参数 */
	config_group_init_type_name(&gi->configs_group, "configs",
			&config_desc_type);
    /* 设置strings_group,可配置字符串参数 */
	config_group_init_type_name(&gi->strings_group, "strings",
			&gadget_strings_strings_type);
    /* 设置os_desc_group,可配置操作系统描述符 */
	config_group_init_type_name(&gi->os_desc_group, "os_desc",
			&os_desc_type);

	/* 初始化复合设备驱动-usb_composite_driver */
	gi->composite.bind = configfs_do_nothing;    // 实现为空
	gi->composite.unbind = configfs_do_nothing;  // 实现为空
	gi->composite.suspend = NULL;
	gi->composite.resume = NULL;
	gi->composite.max_speed = USB_SPEED_SUPER;   // 支持USB3.0

	/* 初始化复合设备-usb_composite_dev */
	composite_init_dev(&gi->cdev);
	/* 设置复合设备描述符 */
	gi->cdev.desc.bLength = USB_DT_DEVICE_SIZE;
	gi->cdev.desc.bDescriptorType = USB_DT_DEVICE;
	gi->cdev.desc.bcdDevice = cpu_to_le16(get_default_bcdDevice());

	/* 设置configfs的usb_gadget_driver */
	gi->composite.gadget_driver = configfs_driver_template;
	gi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL);
	gi->composite.name = gi->composite.gadget_driver.function;

	/* 设置config_group */
	config_group_init_type_name(&gi->group, name,
				&gadget_root_type);
	/* 向configfs系统返回&gi->group */
	return &gi->group;
}
/* usb gadget configfs定义的usb_gadget_driver */
static const struct usb_gadget_driver configfs_driver_template = {
	.bind           = configfs_composite_bind,
	.unbind         = configfs_composite_unbind,
#ifdef CONFIG_USB_CONFIGFS_UEVENT
	.setup          = android_setup,
	.reset          = android_disconnect,
	.disconnect     = android_disconnect,
#else
	.setup          = composite_setup,
	.reset          = composite_disconnect,
	.disconnect     = composite_disconnect,
#endif
	.suspend	    = composite_suspend,
	.resume		    = composite_resume,

	.max_speed	    = USB_SPEED_SUPER,
	.driver = {
		.owner      = THIS_MODULE,
		.name		= "configfs-gadget",
	},
};

gadget_root_type

 下面是usb gadget configfs定义的几种config_item_type和configfs_group_operations。当在/sys/kernel/config/usb_gadget/目录下实例化一个新的gadget实例(g1)时,首先调用gadget_root_type,在g1目录下生成bDeviceClass、bDeviceSubClass、bDeviceProtocol、bMaxPacketSize0、idVendor、idProduct、bcdDevice、bcdUSB、UDC属性文件,使用者可以在用户空间进行配置;接着调用functions_type,在g1目录下生成functions目录,绑定function驱动后,会在该目录下导出function驱动的属性文件,供使用者修改;然后调用config_desc_type,在g1目录下生成configs目录;随后调用gadget_strings_strings_type,在g1目录下生成strings目录,包含了使用字符串表示的英语ID,开发商、产品和序列号等信息。最后调用os_desc_type,在g1目录下生成os_desc目录,包含了操作系统信息,一般不需要设置。

创建设备

这个g1可以理解成一个设备描述符

[drivers/usb/gadget/configfs.c]
static struct configfs_attribute *gadget_root_attrs[] = {
	&gadget_dev_desc_attr_bDeviceClass,
	&gadget_dev_desc_attr_bDeviceSubClass,
	&gadget_dev_desc_attr_bDeviceProtocol,
	&gadget_dev_desc_attr_bMaxPacketSize0,
	&gadget_dev_desc_attr_idVendor,
	&gadget_dev_desc_attr_idProduct,
	&gadget_dev_desc_attr_bcdDevice,
	&gadget_dev_desc_attr_bcdUSB,
	&gadget_dev_desc_attr_UDC,
	NULL,
};
static struct config_item_type gadget_root_type = {
	.ct_item_ops	= &gadget_root_item_ops,
	.ct_attrs	= gadget_root_attrs,
	.ct_owner	= THIS_MODULE,
};


static struct configfs_group_operations functions_ops = {
	.make_group     = &function_make,
	.drop_item      = &function_drop,
};
static struct config_item_type functions_type = {
	.ct_group_ops   = &functions_ops,
	.ct_owner       = THIS_MODULE,
};


static struct configfs_group_operations config_desc_ops = {
	.make_group     = &config_desc_make,
	.drop_item      = &config_desc_drop,
};
static struct config_item_type config_desc_type = {
	.ct_group_ops   = &config_desc_ops,
	.ct_owner       = THIS_MODULE,
};


/* 定义gadget_strings_strings_type的宏 */
USB_CONFIG_STRINGS_LANG(gadget_strings, gadget_info);
[include/linux/usb/gadget_configfs.h]
#define USB_CONFIG_STRINGS_LANG(struct_in, struct_member)	\
	......                                                   \
static struct configfs_group_operations struct_in##_strings_ops = {	\
	.make_group     = &struct_in##_strings_make,			\
	.drop_item      = &struct_in##_strings_drop,			\
};									\
									\
static struct config_item_type struct_in##_strings_type = {		\
	.ct_group_ops   = &struct_in##_strings_ops,			\
	.ct_owner       = THIS_MODULE,					\
}


static struct configfs_item_operations os_desc_ops = {
	.release        = os_desc_attr_release,
	.allow_link		= os_desc_link,
	.drop_link		= os_desc_unlink,
};
static struct config_item_type os_desc_type = {
	.ct_item_ops	= &os_desc_ops,
	.ct_attrs	= os_desc_attrs,
	.ct_owner	= THIS_MODULE,
};

创建配置

这个可以理解成一个配置描述符

mkdir -m 0770 /sys/kernel/config/usb_gadget/g1/configs/c.1
    config_desc_make
        kzalloc // 分配一个config_usb_cfg1结构体,该结构体包含了usb_configuration结构体,保存了该USB设备的配置信息
        usb_add_config_only //函数将该配置挂到usb_composite_dev的configs链表

创建function实例

这个可以理解成一个接口描述符,对应一个功能驱动,里面会定义端点的使用情况;通过:DECLARE_USB_FUNCTION_INIT(uac2, afunc_alloc_inst, afunc_alloc)定义一个实例

mkdir /sys/kernel/config/usb_gadget/g1/functions/uac2.0
    function_make
        usb_get_function_instance 
            try_get_usb_function_instance
                strcmp(name, fd->name) //遍历func_list链表,根据名称进行匹配。用户空间输入的是uac2,则匹配uac2驱动。其定义在f_uac2.c文件中
                fd->alloc_inst() //匹配成功回调function驱动定义的alloc_inst函数,对于uac2,则回调afunc_alloc_inst函数
        list_add_tail(&fi->cfs_list, &gi->available_func); //将获取到的uac2的usb_function_instance挂到gadget_info的available_func链表。

 获取function

相当于将这个接口描述符作为配置描述符的一员

ln -s /sys/kernel/config/usb_gadget/g1/functions/uac2.0 /sys/kernel/config/usb_gadget/g1/configs/c.1
    config_usb_cfg_link
        usb_get_function
            fd->alloc_func() //对于uac2,则回调afunc_alloc函数
        list_add_tail(&f->list, &cfg->func_list); //将获取到的uac2的usb_function挂到config_usb_cfg的func_list链表

驱动绑定

echo fe800000.dwc3 > /sys/kernel/config/usb_gadget/g1/UDC
    gadget_dev_desc_UDC_store
        usb_gadget_probe_driver(&gi->composite.gadget_driver);
            strcmp(driver->udc_name, dev_name(&udc->dev)); //找到对应的USB设备控制器,则保存绑定的usb_gadget_driver,即configfs_driver_template。
            udc_bind_to_driver(udc, driver);
                driver->bind(udc->gadget, driver) //configfs_driver_template.bind ==> configfs_composite_bind
                    usb_add_function //afunc_bind
                        list_add_tail(&function->list, &config->functions);
                        function->bind(config, function) //对于uac2,则回调afunc_bind函数
                usb_gadget_udc_start(udc) //使能USB设备控制器
                usb_udc_connect_control(udc); //连接USB主机控制器,这样USB主机就能识别并枚举USB设备
        schedule_work(&gi->work); //android_work 上报事件给用户层

实例 :根据不同系统,使能usb gadget的不同config

事件的处理

上报事件给上层,包括连接,断连,配置三种事件给上层做处理

static void android_work(struct work_struct *data)
{
	struct gadget_info *gi = container_of(data, struct gadget_info, work);
	struct usb_composite_dev *cdev = &gi->cdev;
	char *disconnected[2] = { "USB_STATE=DISCONNECTED", NULL };
	char *connected[2]    = { "USB_STATE=CONNECTED", NULL };
	char *configured[2]   = { "USB_STATE=CONFIGURED", NULL };
	/* 0-connected 1-configured 2-disconnected*/
	bool status[3] = { false, false, false };
	unsigned long flags;
	bool uevent_sent = false;

	spin_lock_irqsave(&cdev->lock, flags);
	if (cdev->config)
		status[1] = true;

	if (gi->connected != gi->sw_connected) {
		if (gi->connected)
			status[0] = true;
		else
			status[2] = true;
		gi->sw_connected = gi->connected;
	}
	spin_unlock_irqrestore(&cdev->lock, flags);

	if (status[0]) {
		kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, connected);
		pr_info("%s: sent uevent %s\n", __func__, connected[0]);
		uevent_sent = true;
	}

	if (status[1]) {
		kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, configured);
		pr_info("%s: sent uevent %s\n", __func__, configured[0]);
		uevent_sent = true;
	}

	if (status[2]) {
		spin_lock_irqsave(&cdev->lock, flags);
		usb_enter_config = false;
		kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, disconnected);
		pr_info("%s: sent uevent %s\n", __func__, disconnected[0]);
		uevent_sent = true;
		spin_unlock_irqrestore(&cdev->lock, flags);
	}

	if (!uevent_sent) {
		pr_info("%s: did not send uevent (%d %d %p)\n", __func__,
			gi->connected, gi->sw_connected, cdev->config);
	}
}

 添加对连接的系统的判断

在android_setup调用composite_setup对setup令牌包做处理的时候,分析usb_ctrlrequest里的标准请求里的各参数来区分当前的系统,由于不同系统,下发的请求里面的参数会稍有差异,抓住这个差异,就能区分

enum OS_TYPE{
	UNKNOW_OS=0, WINDOWS_OS, MAC_OS
};
static int android_setup(struct usb_gadget *gadget,
			const struct usb_ctrlrequest *c)
{
	struct usb_composite_dev *cdev = get_gadget_data(gadget);
	unsigned long flags;
	struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev);
	int value = -EOPNOTSUPP;
	struct usb_function_instance *fi;
	/* Perform the following operations in non factory mode */
	if(gi->cdev.desc.idProduct != 0x9057){
		static struct delayed_work delay_work_rndis;
		static struct delayed_work delay_work_ecm;
		static enum OS_TYPE last_os = UNKNOW_OS;
		static enum OS_TYPE curt_os = UNKNOW_OS;
		if(last_os == UNKNOW_OS){
			INIT_DELAYED_WORK(&delay_work_rndis, switch_usb_func_2_rndis);
			INIT_DELAYED_WORK(&delay_work_ecm, switch_usb_func_2_ecm);
		}
		// printk("[HI] android_setup: requesttype = %02x, request = %02x, wValue = %04x, wIndex = %04x, wLen = %04x\n", 
		// 		c->bRequestType, c->bRequest, c->wValue, c->wIndex, c->wLength);
		if(usb_enter_config == false){
			if((c->wValue == 0x0300 || c->wValue == 0x0302) && delay_work_not_process == true){
				printk("[HI] android_setup: requesttype = %02x, request = %02x, wValue = %04x, wIndex = %04x, wLen = %04x\n", 
				c->bRequestType, c->bRequest, c->wValue, c->wIndex, c->wLength);
				/* judge os type */
				if(0xff == c->wLength)
					curt_os = WINDOWS_OS;	
				else
					curt_os = MAC_OS;
				/* If the system type is the same as last time, no action is required*/
				if(curt_os == last_os)
					printk("[HI] android_setup: Same as the previous operating system");
				else{
					printk("[HI] android_setup: The system type is different from last time");
					last_os = curt_os;
					if(last_os == WINDOWS_OS) {
						/* To avoid process errors, it is necessary to allow the following config steps to be completed before executing the work */
						delay_work_not_process = false;
						schedule_delayed_work(&delay_work_rndis, msecs_to_jiffies(5000));
					}	
					else if(last_os == MAC_OS){
						delay_work_not_process = false;
						schedule_delayed_work(&delay_work_ecm, msecs_to_jiffies(5000));
					}
				}
			}
		}
	}

	spin_lock_irqsave(&cdev->lock, flags);
	if (!gi->connected) {
		gi->connected = 1;
		schedule_work(&gi->work);
	}
	spin_unlock_irqrestore(&cdev->lock, flags);
	list_for_each_entry(fi, &gi->available_func, cfs_list) {
		if (fi != NULL && fi->f != NULL && fi->f->setup != NULL) {
			value = fi->f->setup(fi->f, c);
			if (value >= 0)
				break;
		}
	}

#ifdef CONFIG_USB_CONFIGFS_F_ACC
	if (value < 0)
		value = acc_ctrlrequest(cdev, c);
#endif

	if (value < 0)
		value = composite_setup(gadget, c);

	spin_lock_irqsave(&cdev->lock, flags);
	if (c->bRequest == USB_REQ_SET_CONFIGURATION &&
						cdev->config) {
		usb_enter_config = true;
		printk("[HI] android_setup: USB_REQ_SET_CONFIGURATION ");
		schedule_work(&gi->work);
	}
	spin_unlock_irqrestore(&cdev->lock, flags);

	return value;
}

 完成切换

直接在驱动里用configfs里的接口也是可以的,偷懒的话,可以直接使用call_usermodehelper来调用用户层的脚本来配置

static bool delay_work_not_process = true;
static void switch_usb_func_2_rndis(struct work_struct *work)
{
	int ret = -1;
    char path[] = "/sbin/usb/compositions/RNDIS"; 
    char *argv[] = {path,NULL};
    char *envp[] = {"HOME=/","PATH=/sbin:/bin:/usr/sbin:/usr/bin",NULL};
    ret = call_usermodehelper(path, argv, envp, UMH_WAIT_PROC);  
	printk("[HI] call_rndis ret = %d.", ret);
	/* Avoid entering this function repeatedly */
	ssleep(5);
	delay_work_not_process = true;
}

static void switch_usb_func_2_ecm(struct work_struct *work)
{
	int ret = -1;
    char path[] = "/sbin/usb/compositions/ECM"; 
    char *argv[] = {path,NULL};
    char *envp[] = {"HOME=/","PATH=/sbin:/bin:/usr/sbin:/usr/bin",NULL};
    ret = call_usermodehelper(path, argv, envp, UMH_WAIT_PROC);  
	printk("[HI] call_ecm ret = %d.", ret);
	/* Avoid entering this function repeatedly */
	ssleep(5);
	delay_work_not_process = true;
}


 

你可能感兴趣的:(Linux内核之驱动,驱动开发)