现在的位置: 首页 > 综合 > 正文

mini2440 usb device controller 驱动的分析–gadget设备(四)—class driver

2013年12月04日 ⁄ 综合 ⁄ 共 2936字 ⁄ 字号 评论关闭

这节分析loopback具体功能是怎么实现的。

loopback功能的入口在这里。

首先,在枚举的时候,有这样一段:

switch  (ctrl->bRequest) {
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;

在set interface阶段,会调用f->set_alt函数。对应于loopback的function,调用的就是loopback_set_alt,在这个函数中会调用enable_loopback。也就是说loopback是在set interface 时候使能的。

static int
enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop)
{
	int					result = 0;
	const struct usb_endpoint_descriptor	*src, *sink;
	struct usb_ep				*ep;
	struct usb_request			*req;
	unsigned				i;

	src = ep_choose(cdev->gadget,
			&hs_loop_source_desc, &fs_loop_source_desc);
	sink = ep_choose(cdev->gadget,
			&hs_loop_sink_desc, &fs_loop_sink_desc);

	/* one endpoint writes data back IN to the host */
	ep = loop->in_ep;
	result = usb_ep_enable(ep, src);
	if (result < 0)
		return result;
	ep->driver_data = loop;

	/* one endpoint just reads OUT packets */
	ep = loop->out_ep;
	result = usb_ep_enable(ep, sink);
	ep->driver_data = loop;

	/* allocate a bunch of read buffers and queue them all at once.
	 * we buffer at most 'qlen' transfers; fewer if any need more
	 * than 'buflen' bytes each.
	 */
	for (i = 0; i < qlen && result == 0; i++) {
		req = alloc_ep_req(ep);
		if (req) {
			req->complete = loopback_complete;
			result = usb_ep_queue(ep, req, GFP_ATOMIC);
			if (result)
				ERROR(cdev, "%s queue req --> %d\n",
						ep->name, result);
		} else {
			usb_ep_disable(ep);
			ep->driver_data = NULL;
			result = -ENOMEM;
			goto fail0;
		}
	}

	DBG(cdev, "%s enabled\n", loop->function.name);
	return result;
}                                                                                                                             
static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
{
	struct f_loopback	*loop = ep->driver_data;
	struct usb_composite_dev *cdev = loop->function.config->cdev;
	int			status = req->status;

	switch (status) {

	case 0:				/* normal completion? */
		if (ep == loop->out_ep) {
			/* loop this OUT packet back IN to the host */
			req->zero = (req->actual < req->length);
			req->length = req->actual;
			status = usb_ep_queue(loop->in_ep, req, GFP_ATOMIC);
			if (status == 0)
				return;

		}

		/* queue the buffer for some later OUT packet */
		req->length = buflen;
		status = usb_ep_queue(loop->out_ep, req, GFP_ATOMIC);
		if (status == 0)
			return;

		/* "should never get here" */
		/* FALLTHROUGH */

	default:
		ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name,
				status, req->actual, req->length);
		/* FALLTHROUGH */

	/* NOTE:  since this driver doesn't maintain an explicit record
	 * of requests it submitted (just maintains qlen count), we
	 * rely on the hardware driver to clean up on disconnect or
	 * endpoint disable.
	 */
	case -ECONNABORTED:		/* hardware forced ep reset */
	case -ECONNRESET:		/* request dequeued */
	case -ESHUTDOWN:		/* disconnect from host */
		free_ep_req(ep, req);
		return;
	}
}

 

在enable_loopback函数中,使能了in_ep和out_ep。并且调用alloc_ep_req(ep)。调用这个的功能相当于是在out_ep上面添加了qlen个读请求。当host端发送消息时,device就会收到消息,并调用回调函数loopback_complete。按照我们的逻辑,既然这个device的功能是收到信息,并将数据在返回给host,那么返回信息的功能就应该在回调函数中进行实现。下面我们分析回调函数。

我们重点关注这个complete函数的前半部分。这个函数if(ep == loop->out_ep),也就是收到了数据,那么就usb_ep_queue(loop->in_ep...),这个就相当于是发送数据,req的数据部分没有发生改动,通过这里就将数据返回给了host。

另外,这个回调函数也负责处理 发送完毕的情况, 因此, if(ep == loop->in_ep),那么就需要 在添加一个读请求到 out_ep,用于继续从host端进行接收。这里就是上面这部分函数的逻辑。
 

至此,device部分的实现就分析完了。我们接下来分析host部分的实现。

抱歉!评论已关闭.