usb gadget driver 之二 gadget设备层

Gadget设备层

       这一层是可选的,介于UDC驱动层和Gadget功能层之间。主要源码在composite.c和composite.h文件中,设备层其实和硬件无关,主要实现一些通用性的代码,减少gadget功能层的代码重复工作。Gadget设备层其中承上启下的作用,联系Gadget功能层和UDC驱动层。

       将composite源码独立出来,还为复合设备的实现提供了一个通用性的框架。复合设备是指在一个配置描述符中支持多个功能,或者支持多个配置的设备中,每个配置都有一个不同的功能。如一个设备同时支持网络和存储,一个设备同时支持键盘和鼠标功能等。

1、usb_function

struct usb_function {//描述一个配置的功能
	const char			*name;          //功能名称
	struct usb_gadget_strings	**strings;      //
	struct usb_descriptor_header	**descriptors;  //全速和低速的描述符表,用于bind中分配的接口描述符和string描述符
	struct usb_descriptor_header	**hs_descriptors;//高速描述符
	struct usb_configuration	*config;
	/* REVISIT:  bind() functions can be marked __init, which
	 * makes trouble for section mismatch analysis.  See if
	 * we can't restructure things to avoid mismatching.
	 * Related:  unbind() may kfree() but bind() won't...
	 */
	/* configuration management:  bind/unbind */
	int			(*bind)(struct usb_configuration *,struct usb_function *);
	void		(*unbind)(struct usb_configuration *,struct usb_function *);
	/* runtime state management */
	int			(*set_alt)(struct usb_function *,unsigned interface, unsigned alt);
	int			(*get_alt)(struct usb_function *,unsigned interface);
	void		(*disable)(struct usb_function *);
	int			(*setup)(struct usb_function *,const struct usb_ctrlrequest *);//接口相关的控制处理
	void		(*suspend)(struct usb_function *);
	void		(*resume)(struct usb_function *);

	/* private: */
	/* internals */
	struct list_head		list;
};
2、usb_configuration

struct usb_configuration {//表示一个gadget配置
	const char			*label;         //配置名称
	struct usb_gadget_strings	**strings;
	const struct usb_descriptor_header **descriptors;//功能描述符表

	/* REVISIT:  bind() functions can be marked __init, which
	 * makes trouble for section mismatch analysis.  See if
	 * we can't restructure things to avoid mismatching...
	 */
	/* configuration management:  bind/unbind */
	int			(*bind)(struct usb_configuration *);
	void		(*unbind)(struct usb_configuration *);
	int			(*setup)(struct usb_configuration *,const struct usb_ctrlrequest *);
//处理驱动框架不能处理的配置控制请求
	/* fields in the config descriptor */
	u8			bConfigurationValue;
	u8			iConfiguration;
	u8			bmAttributes;
	u8			bMaxPower;
	struct usb_composite_dev	*cdev;
	/* private: */
	/* internals */
	struct list_head	list;
	struct list_head	functions;
	u8			next_interface_id;
	unsigned		highspeed:1;
	unsigned		fullspeed:1;
	struct usb_function	*interface[MAX_CONFIG_INTERFACES];
};
3、usb_composite_driver

struct usb_composite_driver {
	const char				*name;   //驱动名称
	const struct usb_device_descriptor	*dev;
	struct usb_gadget_strings		**strings;

	/* REVISIT:  bind() functions can be marked __init, which
	 * makes trouble for section mismatch analysis.  See if
	 * we can't restructure things to avoid mismatching...
	 */
	int			(*bind)(struct usb_composite_dev *);
	int			(*unbind)(struct usb_composite_dev *);
	/* global suspend hooks */
	void		(*suspend)(struct usb_composite_dev *);
	void		(*resume)(struct usb_composite_dev *);
};
4、usb_composite_dev

struct usb_composite_dev {//表示一个composite设备
	struct usb_gadget		*gadget;//关联的gadget
	struct usb_request		*req;   //用于控制响应,提前分配
	unsigned			bufsiz; //req中提前分配的buffer长度
	struct usb_configuration	*config;
	/* private: */
	/* internals */
	struct usb_device_descriptor	desc;
	struct list_head		configs;
	struct usb_composite_driver	*driver;
	u8				next_string_id;
	/* the gadget driver won't enable the data pullup
	 * while the deactivation count is nonzero.
	 */
	unsigned			deactivations;
	/* protects at least deactivation count */
	spinlock_t			lock;
};


重要函数

static int __init composite_bind(struct usb_gadget *gadget)
{
	struct usb_composite_dev	*cdev;
	int				status = -ENOMEM;

	cdev = kzalloc(sizeof *cdev, GFP_KERNEL);
	if (!cdev)
		return status;

	spin_lock_init(&cdev->lock);
	cdev->gadget = gadget;         //将udc gadget与composite设备建立联系
	set_gadget_data(gadget, cdev);
	INIT_LIST_HEAD(&cdev->configs);

	/* preallocate control response and buffer */
	cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
	if (!cdev->req)
		goto fail;
	cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL);
	if (!cdev->req->buf)
		goto fail;
	cdev->req->complete = composite_setup_complete;
	gadget->ep0->driver_data = cdev;

	cdev->bufsiz = USB_BUFSIZ;
	cdev->driver = composite;

	usb_gadget_set_selfpowered(gadget);

	/* interface and string IDs start at zero via kzalloc.
	 * we force endpoints to start unassigned; few controller
	 * drivers will zero ep->driver_data.
	 */
	usb_ep_autoconfig_reset(cdev->gadget);//将所有端点的driver_data成员清零

	/* composite gadget needs to assign strings for whole device (like
	 * serial number), register function drivers, potentially update
	 * power state and consumption, etc
	 */
	status = composite->bind(cdev);
	if (status < 0)
		goto fail;
//设置composite设备的设备描述符
	cdev->desc = *composite->dev;
	cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
//设置composite设备的设备描述符信息
	/* standardized runtime overrides for device ID data */
	if (idVendor)
		cdev->desc.idVendor = cpu_to_le16(idVendor);
	if (idProduct)
		cdev->desc.idProduct = cpu_to_le16(idProduct);
	if (bcdDevice)
		cdev->desc.bcdDevice = cpu_to_le16(bcdDevice);
//设置字符串描述符
	/* strings can't be assigned before bind() allocates the
	 * releavnt identifiers
	 */
	if (cdev->desc.iManufacturer && iManufacturer)
		string_override(composite->strings, cdev->desc.iManufacturer, iManufacturer);
	if (cdev->desc.iProduct && iProduct)
		string_override(composite->strings, cdev->desc.iProduct, iProduct);
	if (cdev->desc.iSerialNumber && iSerialNumber)
		string_override(composite->strings, cdev->desc.iSerialNumber, iSerialNumber);

	INFO(cdev, "%s ready\n", composite->name);
	return 0;

fail:
	composite_unbind(gadget);
	return status;
}
Composite_bind函数主要完成UDC驱动层gadget设备和设备层composite设备关系的建立,分配控制端点0的请求数据结构,设置设备描述符,并调用功能层的bind函数分配功能层所需要的资源等工作。
static void /* __init_or_exit */
composite_unbind(struct usb_gadget *gadget)
{
	struct usb_composite_dev	*cdev = get_gadget_data(gadget);

	/* composite_disconnect() must already have been called
	 * by the underlying peripheral controller driver!
	 * so there's no i/o concurrency that could affect the
	 * state protected by cdev->lock.
	 */
	WARN_ON(cdev->config);

	while (!list_empty(&cdev->configs)) {//遍历设备的配置链表
		struct usb_configuration	 *c;

		c = list_first_entry(&cdev->configs,struct usb_configuration, list);
		while (!list_empty(&c->functions)) {//遍历这个配置的所有功能
			struct usb_function	*f;

			f = list_first_entry(&c->functions,struct usb_function, list);
			list_del(&f->list);
			if (f->unbind) {
				DBG(cdev, "unbind function '%s'/%p\n",f->name, f);
				f->unbind(c, f);
				/* may free memory for "f" */
			}
		}
		list_del(&c->list);
		if (c->unbind) {
			DBG(cdev, "unbind config '%s'/%p\n", c->label, c);
			c->unbind(c);
			/* may free memory for "c" */
		}
	}
	if (composite->unbind)
		composite->unbind(cdev);

	if (cdev->req) {
		kfree(cdev->req->buf);
		usb_ep_free_request(gadget->ep0, cdev->req);
	}
	kfree(cdev);
	set_gadget_data(gadget, NULL);
	composite = NULL;
}
在配置中增加一个功能,每个配置初始化后都必须至少有一个功能。
int __init usb_add_function(struct usb_configuration *config,struct usb_function *function)
{
	int	value = -EINVAL;

	DBG(config->cdev, "adding '%s'/%p to config '%s'/%p\n", function->name, function, config->label, config);

	if (!function->set_alt || !function->disable)
		goto done;
//设置功能所属配置,并将功能加入配置的链表中
	function->config = config;
	list_add_tail(&function->list, &config->functions);

	/* REVISIT *require* function->bind? */
	if (function->bind) {
		value = function->bind(config, function);//分配这个功能所需要的资源
		if (value < 0) {
			list_del(&function->list);
			function->config = NULL;
		}
	} else
		value = 0;

	/* We allow configurations that don't work at both speeds.
	 * If we run into a lowspeed Linux system, treat it the same
	 * as full speed ... it's the function drivers that will need
	 * to avoid bulk and ISO transfers.
	 */
	if (!config->fullspeed && function->descriptors)
		config->fullspeed = true;
	if (!config->highspeed && function->hs_descriptors)
		config->highspeed = true;

done:
	if (value)
		DBG(config->cdev, "adding '%s'/%p --> %d\n", function->name, function, value);
	return value;
}
为设备增加一个配置,这个实现原理和在配置下增加一个功能类似
int __init usb_add_config(struct usb_composite_dev *cdev, struct usb_configuration *config)
{
	int				status = -EINVAL;
	struct usb_configuration	*c;

	DBG(cdev, "adding config #%u '%s'/%p\n", config->bConfigurationValue, config->label, config);

	if (!config->bConfigurationValue || !config->bind)
		goto done;

	/* Prevent duplicate configuration identifiers */
	list_for_each_entry(c, &cdev->configs, list) {
		if (c->bConfigurationValue == config->bConfigurationValue) {//若配置已经加入了,则不需要在加
			status = -EBUSY;
			goto done;
		}
	}

	config->cdev = cdev;
	list_add_tail(&config->list, &cdev->configs);
//一个USB设备可以有多个配置
//这句是将配置加入设备的配置链表中

	INIT_LIST_HEAD(&config->functions);
//初始化配置的functions链表,functions链表要链接struct usb_function类型的数据结构,这个数据结构也很重要,其实他代表一个USB接口 
	config->next_interface_id = 0;

	status = config->bind(config);//完成这个配置的资源分配
	//这里函数调用的是sourcesink_bind_config,
	//初始化一个struct usb_function结构,并且将其加入到配置的functions链表
	if (status < 0) {
		list_del(&config->list);
		config->cdev = NULL;
	} else {
		unsigned	i;

		DBG(cdev, "cfg %d/%p speeds:%s%s\n",
			config->bConfigurationValue, config,
			config->highspeed ? " high" : "",
			config->fullspeed ? (gadget_is_dualspeed(cdev->gadget) ? " full" : " full/low")
			: "");

		for (i = 0; i < MAX_CONFIG_INTERFACES; i++) {
			//最大接口数,遍历配置的所有接口,打印信息
			struct usb_function	*f = config->interface[i];

			if (!f)
				continue;
			DBG(cdev, "  interface %d = %s/%p\n", i, f->name, f);
		}
	}

	/* set_alt(), or next config->bind(), sets up
	 * ep->driver_data as needed.
	 */
	usb_ep_autoconfig_reset(cdev->gadget);
//这个函数的主要作用就是将cdev->gadget的所有端点的driver_data清空  

done:
	if (status)
		DBG(cdev, "added config '%s'/%u --> %d\n", config->label, config->bConfigurationValue, status);
	return status;
}

设置一个配置

static int set_config(struct usb_composite_dev *cdev,
		const struct usb_ctrlrequest *ctrl, unsigned number)
{
	struct usb_gadget	*gadget = cdev->gadget;
	struct usb_configuration *c = NULL;
	int			result = -EINVAL;
	unsigned		power = gadget_is_otg(gadget) ? 8 : 100;
	int			tmp;

	if (cdev->config)
		reset_config(cdev);//清除设备的配置值

	if (number) {//根据配置值找到对应的配置
		list_for_each_entry(c, &cdev->configs, list) {
			if (c->bConfigurationValue == number) {
				result = 0;
				break;
			}
		}
		if (result < 0)
			goto done;
	} else
		result = 0;

	INFO(cdev, "%s speed config #%d: %s\n",
		({ char *speed;
		switch (gadget->speed) {
		case USB_SPEED_LOW:	speed = "low"; break;
		case USB_SPEED_FULL:	speed = "full"; break;
		case USB_SPEED_HIGH:	speed = "high"; break;
		default:		speed = "?"; break;
		} ; speed; }), number, c ? c->label : "unconfigured");

	if (!c)
		goto done;

	cdev->config = c;

	/* Initialize all interfaces by setting them to altsetting zero. */
	for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) {
		struct usb_function	*f = c->interface[tmp];

		if (!f)
			break;

		result = f->set_alt(f, tmp, 0);
		if (result < 0) {
			DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n",
					tmp, f->name, f, result);

			reset_config(cdev);
			goto done;
		}
	}

	/* when we return, be sure our power usage is valid */
	power = c->bMaxPower ? (2 * c->bMaxPower) : CONFIG_USB_GADGET_VBUS_DRAW;
done:
	usb_gadget_vbus_draw(gadget, power);
	return result;
}
这个setup函数完成了ep0所需要处理的而底层不能处理的功能,由于这一个层实现了这个函数,所以功能层就不需要关注于USB协议的这些事务性处理,而重点放在需要实现的功能上

static int composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
{
	struct usb_composite_dev	*cdev = get_gadget_data(gadget);
	struct usb_request		*req = cdev->req;
	int				value = -EOPNOTSUPP;
	u16				w_index = le16_to_cpu(ctrl->wIndex);
	u8				intf = w_index & 0xFF;
	u16				w_value = le16_to_cpu(ctrl->wValue);
	u16				w_length = le16_to_cpu(ctrl->wLength);
	struct usb_function		*f = NULL;

	/* partial re-init of the response message; the function or the
	 * gadget might need to intercept e.g. a control-OUT completion
	 * when we delegate to it.
	 */
	req->zero = 0;
	req->complete = composite_setup_complete;
	req->length = USB_BUFSIZ;
	gadget->ep0->driver_data = cdev;

	switch (ctrl->bRequest) {

	/* we handle all standard USB descriptors */
	case USB_REQ_GET_DESCRIPTOR:
		if (ctrl->bRequestType != USB_DIR_IN)
			goto unknown;
		switch (w_value >> 8) {

		case USB_DT_DEVICE:
			cdev->desc.bNumConfigurations = count_configs(cdev, USB_DT_DEVICE);
			value = min(w_length, (u16) sizeof cdev->desc);
			memcpy(req->buf, &cdev->desc, value);
			break;
		case USB_DT_DEVICE_QUALIFIER:
			if (!gadget_is_dualspeed(gadget))
				break;
			device_qual(cdev);
			value = min_t(int, w_length, sizeof(struct usb_qualifier_descriptor));
			break;
		case USB_DT_OTHER_SPEED_CONFIG:
			if (!gadget_is_dualspeed(gadget))
				break;
			/* FALLTHROUGH */
		case USB_DT_CONFIG:
			value = config_desc(cdev, w_value);
			if (value >= 0)
				value = min(w_length, (u16) value);
			break;
		case USB_DT_STRING:
			value = get_string(cdev, req->buf, w_index, w_value & 0xff);
			if (value >= 0)
				value = min(w_length, (u16) value);
			break;
		}
		break;

	/* any number of configs can work */
	case USB_REQ_SET_CONFIGURATION:
		if (ctrl->bRequestType != 0)
			goto unknown;
		if (gadget_is_otg(gadget)) {
			if (gadget->a_hnp_support)
				DBG(cdev, "HNP available\n");
			else if (gadget->a_alt_hnp_support)
				DBG(cdev, "HNP on another port\n");
			else
				VDBG(cdev, "HNP inactive\n");
		}
		spin_lock(&cdev->lock);
		value = set_config(cdev, ctrl, w_value);
		spin_unlock(&cdev->lock);
		break;
	case USB_REQ_GET_CONFIGURATION:
		if (ctrl->bRequestType != USB_DIR_IN)
			goto unknown;
		if (cdev->config)
			*(u8 *)req->buf = cdev->config->bConfigurationValue;
		else
			*(u8 *)req->buf = 0;
		value = min(w_length, (u16) 1);
		break;

	/* function drivers must handle get/set altsetting; if there's
	 * no get() method, we know only altsetting zero works.
	 */
	case USB_REQ_SET_INTERFACE:
		if (ctrl->bRequestType != USB_RECIP_INTERFACE)
			goto unknown;
		if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES)
			break;
		f = cdev->config->interface[intf];
		if (!f)
			break;
		if (w_value && !f->set_alt)
			break;
		value = f->set_alt(f, w_index, w_value);
		break;
	case USB_REQ_GET_INTERFACE:
		if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
			goto unknown;
		if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES)
			break;
		f = cdev->config->interface[intf];
		if (!f)
			break;
		/* lots of interfaces only need altsetting zero... */
		value = f->get_alt ? f->get_alt(f, w_index) : 0;
		if (value < 0)
			break;
		*((u8 *)req->buf) = value;
		value = min(w_length, (u16) 1);
		break;
	default:
unknown:
		VDBG(cdev,
			"non-core control req%02x.%02x v%04x i%04x l%d\n",
			ctrl->bRequestType, ctrl->bRequest,
			w_value, w_index, w_length);

		/* functions always handle their interfaces ... punt other
		 * recipients (endpoint, other, WUSB, ...) to the current
		 * configuration code.
		 *
		 * REVISIT it could make sense to let the composite device
		 * take such requests too, if that's ever needed:  to work
		 * in config 0, etc.
		 */
		if ((ctrl->bRequestType & USB_RECIP_MASK) == USB_RECIP_INTERFACE) {
			f = cdev->config->interface[intf];
			if (f && f->setup)
				value = f->setup(f, ctrl);
			else
				f = NULL;
		}
		if (value < 0 && !f) {
			struct usb_configuration	*c;

			c = cdev->config;
			if (c && c->setup)
				value = c->setup(c, ctrl);
		}

		goto done;
	}

	/* respond with data transfer before status phase? */
	if (value >= 0) {
		req->length = value;
		req->zero = value < w_length;
		value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
//usb_ep_queue函数主要作用就是将req中的数据写入到FIFO中
		if (value < 0) {
			DBG(cdev, "ep_queue --> %d\n", value);
			req->status = 0;
			composite_setup_complete(gadget->ep0, req);
		}
	}

done:
	/* device either stalls (value < 0) or reports success */
	return value;
}

转自:http://blog.chinaunix.net/uid-14518381-id-3940391.html  visualfan的chinaunix博客

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值