第16章 USB主机、设备与Gadget驱动之USB UDC与Gadget驱动(二)

16.4.2 Chipidea(公司) USB UDC(USB设备控制器)驱动

    Chipidea USB UDC驱动的主体代码路径:drivers/usb/chipidea/udc.c,代码清单16.33列出其初始化流程部分,并定义了usb_ep_ops、usb_gadget_ops,在最终进行usb_add_gadget_udc()之前填充好了UDC的端点列表。

代码清单16.33 Chipidea USB UDC驱动实例

/**
 * Endpoint-specific part of the API to the USB controller hardware
 * Check "usb_gadget.h" for details
 */
static const struct usb_ep_ops usb_ep_ops = {
.enable        = ep_enable,
.disable       = ep_disable,
.alloc_request = ep_alloc_request,
.free_request  = ep_free_request,
.queue        = ep_queue,
.dequeue       = ep_dequeue,
.set_halt      = ep_set_halt,
.set_wedge     = ep_set_wedge,
.fifo_flush    = ep_fifo_flush,

};

/**
 * Device operations part of the API to the USB controller hardware,
 * which don't involve endpoints (or i/o)
 * Check  "usb_gadget.h" for details
 */
static const struct usb_gadget_ops usb_gadget_ops = {
.vbus_session = ci_udc_vbus_session,
.wakeup = ci_udc_wakeup,
.pullup = ci_udc_pullup,
.vbus_draw = ci_udc_vbus_draw,
.udc_start = ci_udc_start,
.udc_stop = ci_udc_stop,

};

static int init_eps(struct ci_hdrc *ci)
{
int retval = 0, i, j;

for (i = 0; i < ci->hw_ep_max/2; i++)
for (j = RX; j <= TX; j++) {
int k = i + j * ci->hw_ep_max/2;
struct ci_hw_ep *hwep = &ci->ci_hw_ep[k];

scnprintf(hwep->name, sizeof(hwep->name), "ep%i%s", i, (j == TX)  ? "in" : "out");
hwep->ci          = ci;
hwep->lock         = &ci->lock;
hwep->td_pool      = ci->td_pool;
hwep->ep.name      = hwep->name;
hwep->ep.ops       = &usb_ep_ops;
/*
* for ep0: maxP defined in desc, for other
* eps, maxP is set by epautoconfig() called
* by gadget layer
*/
usb_ep_set_maxpacket_limit(&hwep->ep, (unsigned short)~0);
INIT_LIST_HEAD(&hwep->qh.queue);
hwep->qh.ptr = dma_pool_alloc(ci->qh_pool, GFP_KERNEL, &hwep->qh.dma);
if (hwep->qh.ptr == NULL)
retval = -ENOMEM;
else
memset(hwep->qh.ptr, 0, sizeof(*hwep->qh.ptr));

/*
* set up shorthands for ep0 out and in endpoints,
* don't add to gadget's ep_list
*/
if (i == 0) {
if (j == RX)
ci->ep0out = hwep;
else
ci->ep0in = hwep;

usb_ep_set_maxpacket_limit(&hwep->ep, CTRL_PAYLOAD_MAX);
continue;
}
list_add_tail(&hwep->ep.ep_list, &ci->gadget.ep_list); 
}
return retval;
}


/**
 * udc_start: initialize gadget role
 * @ci: chipidea controller
 */
static int udc_start(struct ci_hdrc *ci)
{
struct device *dev = ci->dev;
int retval = 0;

spin_lock_init(&ci->lock);

ci->gadget.ops          = &usb_gadget_ops;
ci->gadget.speed        = USB_SPEED_UNKNOWN;
ci->gadget.max_speed    = USB_SPEED_HIGH;
ci->gadget.is_otg       = ci->is_otg ? 1 : 0;
ci->gadget.name         = ci->platdata->name;

INIT_LIST_HEAD(&ci->gadget.ep_list);

/* alloc resources */
ci->qh_pool = dma_pool_create("ci_hw_qh", dev,  sizeof(struct ci_hw_qh),
       64, CI_HDRC_PAGE_SIZE);
if (ci->qh_pool == NULL)
return -ENOMEM;

ci->td_pool = dma_pool_create("ci_hw_td", dev, sizeof(struct ci_hw_td),
       64, CI_HDRC_PAGE_SIZE);
if (ci->td_pool == NULL) {
retval = -ENOMEM;
goto free_qh_pool;
}

retval = init_eps(ci);
if (retval)
goto free_pools;

ci->gadget.ep0 = &ci->ep0in->ep;

retval = usb_add_gadget_udc(dev, &ci->gadget);
if (retval)
goto destroy_eps;

pm_runtime_no_callbacks(&ci->gadget.dev);
pm_runtime_enable(&ci->gadget.dev);

return retval;

destroy_eps:
destroy_eps(ci);
free_pools:
dma_pool_destroy(ci->td_pool);
free_qh_pool:
dma_pool_destroy(ci->qh_pool);
return retval;

}

16.4.3 Loopback Function驱动

drivers/usb/gadget/function/f_loopback.c实现一个最简单的Loopback驱动,主要完成的工作如下:

1、实现usb_function实例及其中成员函数bind()、set_alt()、disable()、free_func()等成员函数

2、准备USB外设的配置描述符、接口描述符usb_interface_descriptor、端点描述符usb_endpoint_descriptor等

3、发起usb_request、处理usb_request的完成并回环

代码清单16.34是抽取drivers/usb/gadget/function/f_loopback.c文件中一个Function驱动主体结构的实例代码

代码清单16.34 Loopback USB Gadget Function驱动实例

static struct usb_interface_descriptor loopback_intf = {
.bLength = sizeof loopback_intf,
.bDescriptorType = USB_DT_INTERFACE,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
/* .iInterface = DYNAMIC */
};

static struct usb_endpoint_descriptor fs_loop_source_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};

static struct usb_descriptor_header *fs_loopback_descs[] = {
(struct usb_descriptor_header *) &loopback_intf,
(struct usb_descriptor_header *) &fs_loop_sink_desc,
(struct usb_descriptor_header *) &fs_loop_source_desc,
NULL,
};

/* function-specific strings: */

static struct usb_string strings_loopback[] = {
[0].s = "loop input to output",
{  } /* end of list */
};

static struct usb_gadget_strings stringtab_loop = {
.language = 0x0409, /* en-us */
.strings = strings_loopback,
};

static struct usb_gadget_strings *loopback_strings[] = {
&stringtab_loop,
NULL,
};

static int loopback_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_loopback *loop = func_to_loop(f);
int id;
int ret;

/* allocate interface ID(s) */
id = usb_interface_id(c, f);
if (id < 0)
return id;
loopback_intf.bInterfaceNumber = id;
id = usb_string_id(cdev);
if (id < 0)
return id;
strings_loopback[0].id = id;
loopback_intf.iInterface = id;
/* allocate endpoints */
loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_source_desc);
if (!loop->in_ep) {
autoconf_fail:
ERROR(cdev, "%s: can't autoconfigure on %s\n",
f->name, cdev->gadget->name);
return -ENODEV;
}
loop->in_ep->driver_data = cdev; /* claim */
loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_sink_desc);
if (!loop->out_ep)
goto autoconf_fail;
loop->out_ep->driver_data = cdev; /* claim */

/* support high speed hardware */
hs_loop_source_desc.bEndpointAddress =
fs_loop_source_desc.bEndpointAddress;
hs_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress;

/* support super speed hardware */
ss_loop_source_desc.bEndpointAddress =
fs_loop_source_desc.bEndpointAddress;
ss_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress;

ret = usb_assign_descriptors(f, fs_loopback_descs, hs_loopback_descs,
ss_loopback_descs);
if (ret)
return ret;

DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
    (gadget_is_superspeed(c->cdev->gadget) ? "super" :
     (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")),
f->name, loop->in_ep->name, loop->out_ep->name);
return 0;
}

static void lb_free_func(struct usb_function *f)
{
struct f_lb_opts *opts;

opts = container_of(f->fi, struct f_lb_opts, func_inst);
mutex_lock(&opts->lock);
opts->refcnt--; // 临界区
mutex_unlock(&opts->lock);
usb_free_all_descriptors(f);
kfree(func_to_loop(f));
}

static struct usb_function *loopback_alloc(struct usb_function_instance *fi)
{
struct f_loopback *loop;
struct f_lb_opts *lb_opts;

loop = kzalloc(sizeof *loop, GFP_KERNEL);
if (!loop)
return ERR_PTR(-ENOMEM);

lb_opts = container_of(fi, struct f_lb_opts, func_inst);
mutex_lock(&lb_opts->lock);
lb_opts->refcnt++;
mutex_unlock(&lb_opts->lock);
buflen = lb_opts->bulk_buflen;
qlen = lb_opts->qlen;
if (!qlen)

qlen = 32;

loop->function.name = "loopback";
loop->function.bind = loopback_bind; // 绑定函数指针
loop->function.set_alt = loopback_set_alt;
loop->function.disable = loopback_disable;
loop->function.strings = loopback_strings;
loop->function.free_func = lb_free_func;

return &loop->function;
}

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) {
req->zero = (req->actual < req->length);
req->length = req->actual;
}

/* queue the buffer for some later OUT packet */
req->length = buflen;
status = usb_ep_queue(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;
}
}

static int enable_endpoint(struct usb_composite_dev *cdev, struct f_loopback *loop,
struct usb_ep *ep)
{
struct usb_request *req;
unsigned i;
int result;

/*
* one endpoint writes data back IN to the host while another endpoint
* just reads OUT packets
*/
result = config_ep_by_speed(cdev->gadget, &(loop->function), ep);
if (result)
goto fail0;
result = usb_ep_enable(ep);
if (result < 0)
goto fail0;
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 = lb_alloc_ep_req(ep, 0);
if (!req)
goto fail1;

req->complete = loopback_complete;
result = usb_ep_queue(ep, req, GFP_ATOMIC);
if (result) {
ERROR(cdev, "%s queue req --> %d\n",
ep->name, result);
goto fail1;
}
}

return 0;

fail1:
usb_ep_disable(ep);
fail0:
return result;
}




本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部