700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > USB设备驱动程序-USB Gadget Driver(四)

USB设备驱动程序-USB Gadget Driver(四)

时间:2021-06-24 12:07:57

相关推荐

USB设备驱动程序-USB Gadget Driver(四)

FROM:/uid-14518381-id-3942425.html

Gadget 功能层

Gadget功能层完成USB设备的具体功能,其表现的形式各不相同,如键盘、鼠标、存储和网卡等等。功能层不仅涉及到Gadget驱动相关的内容,还涉及到其功能相关的内核子系统。如存储还涉及到内核存储子系统,网卡还涉及到网络驱动子系统。因此,Gadget功能的代码非常复杂。这里以zero.c为例,这个模块只是简单地将接收的数据回显回去。

一、数据结构

首先需要实现usb_composite_driver函数集:

static struct usb_composite_driver zero_driver = {

.name = "zero",

.dev = &device_desc,

.strings = dev_strings,

.bind = zero_bind,

.unbind = zero_unbind,

.suspend = zero_suspend,

.resume = zero_resume,

};

二、主要函数

这个模块的实现就是这么简单:

static int __init init(void)

{

returnusb_composite_register(&zero_driver);

}

module_init(init);

static void __exit cleanup(void)

{

usb_composite_unregister(&zero_driver);

}

Bind函数是功能层需要实现与设备层关联的重要函数:

static int __init zero_bind(struct usb_composite_dev *cdev)

{

int gcnum;

struct usb_gadget *gadget = cdev->gadget; //Gadget设备

int id;

/* Allocate string descriptor numbers ... note that string

* contents can be overridden by the composite_dev glue.

*/

/*分配字符串描述符的id,并赋值给设备描述符中字符串索引*/

id = usb_string_id(cdev);

strings_dev[STRING_MANUFACTURER_IDX].id = id;

device_desc.iManufacturer = id;

id = usb_string_id(cdev); i

strings_dev[STRING_PRODUCT_IDX].id = id;

device_desc.iProduct = id;

id = usb_string_id(cdev);

strings_dev[STRING_SERIAL_IDX].id = id;

device_desc.iSerialNumber = id;

/*设置挂起后,设备自动恢复的定时器*/

setup_timer(&autoresume_timer, zero_autoresume, (unsigned long) cdev);

/*核心代码,实现功能*/

if (loopdefault) {

loopback_add(cdev, autoresume != 0);//数据简单回显功能

if (!gadget_is_sh(gadget))

sourcesink_add(cdev, autoresume != 0);

}

else

{

sourcesink_add(cdev, autoresume != 0);

if (!gadget_is_sh(gadget))

loopback_add(cdev, autoresume != 0);

}

/*初始化设备描述符*/

gcnum = usb_gadget_controller_number(gadget);

if (gcnum >= 0)

device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);

else {

device_desc.bcdDevice = cpu_to_le16(0x9999);

}

return 0;

}

/*增加数据简单回显功能*/

int __init loopback_add(struct usb_composite_dev *cdev, bool autoresume)

{

int id;

/*获取字符串描述符id索引*/

id = usb_string_id(cdev);

strings_loopback[0].id = id;

loopback_intf.iInterface = id;

loopback_driver.iConfiguration = id;

/* support autoresume for remote wakeup testing */

if (autoresume)

sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;

/* support OTG systems */

if (gadget_is_otg(cdev->gadget)) {

loopback_driver.descriptors = otg_desc;

loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;

}

returnusb_add_config(cdev, &loopback_driver);//增加一个配置

}

/*loopback配置*/

static struct usb_configuration loopback_driver = {

.label = "loopback",

.strings = loopback_strings,

.bind = loopback_bind_config,

.bConfigurationValue = 2,

.bmAttributes = USB_CONFIG_ATT_SELFPOWER,

/* .iConfiguration = DYNAMIC */

};

将增加配置的usb_add_config函数中会调用其bind函数,即loopback_bind_config函数,来分配这个配置所需要的资源。

struct f_loopback {

struct usb_function function;

struct usb_ep *in_ep;

struct usb_ep *out_ep;

};

static int __init loopback_bind_config(struct usb_configuration *c)

{

struct f_loopback *loop;

int status;

loop = kzalloc(sizeof *loop, GFP_KERNEL); //分配一个loop结构

if (!loop)

return -ENOMEM;

/*初始化一个功能*/

loop->function.name = "loopback";

loop->function.descriptors = fs_loopback_descs;

loop->function.bind = loopback_bind;

loop->function.unbind = loopback_unbind;

loop->function.set_alt = loopback_set_alt;

loop->function.disable = loopback_disable;

status =usb_add_function(c, &loop->function); //加入这个功能

if (status)

kfree(loop);

return status;

}

在usb_add_function函数中,又会调用这个功能的bind函数,即loopback_bind函数:

static int __init 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;

/* allocate interface ID(s) */

id = usb_interface_id(c, f); //分配一个接口id

if (id < 0)

return id;

loopback_intf.bInterfaceNumber = 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 */

if (gadget_is_dualspeed(c->cdev->gadget)) {

hs_loop_source_desc.bEndpointAddress = fs_loop_source_desc.bEndpointAddress;

hs_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress;

f->hs_descriptors = hs_loopback_descs;

}

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

return 0;

}

功能的实现

Loopback_set_alt函数将在设备层的setup函数中被调用,控制通信设置接口。

static int loopback_set_alt(struct usb_function *f, unsigned intf, unsigned alt)

{

struct f_loopback *loop = func_to_loop(f);

struct usb_composite_dev *cdev = f->config->cdev;

/* we know alt is zero */

if (loop->in_ep->driver_data)

disable_loopback(loop);

returnenable_loopback(cdev, loop); //开启功能

}

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);

if (result < 0) {

fail0:

ep = loop->in_ep;

usb_ep_disable(ep);

ep->driver_data = NULL;

return result;

}

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.

*/

/*qlen=32,分配32个请求,将这个请求放入输出端点队列,等待接收数据*/

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;

/* "should never get here" */

ERROR(cdev, "can't loop %s to %s: %d\n", ep->name, loop->in_ep->name,status);

}

/* 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;

}

}

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