您的位置:首页 > 运维架构 > Linux

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

2016-03-22 16:28 627 查看


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)

{

           return usb_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;

                          }

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

                   return enable_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;

                    }

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux 嵌入式 usb