700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 第16章 USB主机 设备与Gadget驱动之USB UDC与Gadget驱动(二)

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

时间:2020-10-12 09:47:35

相关推荐

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

16.4.2Chipidea(公司) 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.33Chipidea 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.3Loopback 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.34Loopback 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;

}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。