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

Gadget设备层
       这一层是可选的,介于UDC驱动层和Gadget功能层之间。主要源码在composite.c和composite.h文件中,设备层其实和硬件无关,主要实现一些通用性的代码,减少gadget功能层的代码重复工作。Gadget设备层其中承上启下的作用,联系Gadget功能层和UDC驱动层。
       将composite源码独立出来,还为复合设备的实现提供了一个通用性的框架。复合设备是指在一个配置描述符中支持多个功能,或者支持多个配置的设备中,每个配置都有一个不同的功能。如一个设备同时支持网络和存储,一个设备同时支持键盘和鼠标功能等。

Gadget设备层的主要数据结构:

1. Function

struct usb_function { //描述一个配置的一个功能

 const char *name; //功能名称

 struct usb_gadget_strings **strings; //string数组,通过bind中分配的id访问

 struct usb_descriptor_header **descriptors; //全速和低速的描述符表,用于bind中分配的接口描述符和string描述符

 struct usb_descriptor_header **hs_descriptors; //高速描述符表

         struct usb_configuration *config;//调用usb_add_function()函数赋值,是这个功能关联的配置描述符

         /* REVISIT: bind() functions can be marked __init, which

          * makes trouble for section mismatch analysis. See if

          * we can't restructure things to avoid mismatching.

          * Related: unbind() may kfree() but bind() won't...

          */

         /* configuration management: bind/unbind */

 /*在Gadget注册时,分配资源*/

 int (*bind)(struct usb_configuration *, struct usb_function *);

 /*unbind的逆操作*/

 void (*unbind)(struct usb_configuration *, struct usb_function *);

/* runtime state management */

/*重新配置altsetting,*/

int (*set_alt)(struct usb_function *,unsigned interface, unsigned alt);

 /*获取当前altsetting*/

 int (*get_alt)(struct usb_function *, unsigned interface);

/*关闭功能*/

 void (*disable)(struct usb_function *);

/*接口相关的控制处理*/

 int (*setup)(struct usb_function *, const struct usb_ctrlrequest *);

 /*电源管理相关的挂起和恢复功能*/

 void (*suspend)(struct usb_function *);

 void (*resume)(struct usb_function *);

/* private: */

 /* internals */

 struct list_head list;

DECLARE_BITMAP(endpoints, 32);

};

2. Config

struct usb_configuration { //表示一个Gadget配置

       const char *label; //配置名称

struct usb_gadget_strings **strings; //字符串表

const struct usb_descriptor_header **descriptors; //功能描述符表

/* REVISIT: bind() functions can be marked __init, which

* makes trouble for section mismatch analysis. See if

* we can't restructure things to avoid mismatching...

*/

/* configuration management: bind/unbind */

/*在usb_add_config函数中调用,分配资源等*/

int (*bind)(struct usb_configuration *);

void (*unbind)(struct usb_configuration *);

/*处理驱动框架不能处理的配置控制请求*/

               int (*setup)(struct usb_configuration *, const struct usb_ctrlrequest *);

/* fields in the config descriptor */

/*用来赋值配置描述符*/

u8 bConfigurationValue;

u8 iConfiguration;

u8 bmAttributes;

u8 bMaxPower;

/*和composite设备关联,在usb_add_config函数中设置*/

struct usb_composite_dev *cdev;

/* private: */

/* internals */

struct list_head list;

struct list_head functions; //功能链表

u8 next_interface_id;

unsigned highspeed:1;

unsigned fullspeed:1;

struct usb_function *interface[MAX_CONFIG_INTERFACES];

};

3. Driver

struct usb_composite_driver {

const char *name; //驱动名称

const struct usb_device_descriptor *dev; //设备描述符

struct usb_gadget_strings **strings; //字符串表

/* REVISIT: bind() functions can be marked __init, which

* makes trouble for section mismatch analysis. See if

* we can't restructure things to avoid mismatching...

*/

int (*bind)(struct usb_composite_dev *);

int (*unbind)(struct usb_composite_dev *);

/* global suspend hooks */

void (*suspend)(struct usb_composite_dev *);

void (*resume)(struct usb_composite_dev *);

};

4. Dev

struct usb_composite_dev { //表示一个composite设备

struct usb_gadget *gadget; //关联的gadget

struct usb_request *req; //用于控制响应,提前分配的

unsigned bufsiz; //req中提前分配的buffer长度

struct usb_configuration *config; //当前配置

       /* private: */

/* internals */

struct usb_device_descriptor desc;

struct list_head configs; //配置链表

struct usb_composite_driver *driver;

u8 next_string_id;

  /* the gadget driver won't enable the data pullup

* while the deactivation count is nonzero.

*/

unsigned deactivations;

/* protects at least deactivation count */

spinlock_t lock;

};

其中重要的函数:

1.

static int __init composite_bind(struct usb_gadget *gadget)

{

          struct usb_composite_dev *cdev;

           int status = -ENOMEM;

           cdev = kzalloc(sizeof *cdev, GFP_KERNEL); //分配一个composite设备对象

           if (!cdev)

                         return status;

            spin_lock_init(&cdev->lock);

            /*将udc gadget和composite设备建立关系*/

             cdev->gadget = gadget;

            set_gadget_data(gadget, cdev);

             INIT_LIST_HEAD(&cdev->configs); //初始化配置链表

               /* preallocate control response and buffer */

              /*分配控制端点的请求数据结构和缓存区*/

             cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);

             if (!cdev->req)

                                      goto fail;

             cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL); //分配控制请求的缓冲区

            if (!cdev->req->buf)

                                      goto fail;

             cdev->req->complete = composite_setup_complete; //端的0的完成函数

             gadget->ep0->driver_data = cdev;

              cdev->bufsiz = USB_BUFSIZ;

               cdev->driver = composite;

           /*设置gadget的供电模式*/

            usb_gadget_set_selfpowered(gadget);

          /* interface and string IDs start at zero via kzalloc.

           * we force endpoints to start unassigned; few controller

           * drivers will zero ep->driver_data.

           */

           /*将所有端点的driver_data成员清零*/

            usb_ep_autoconfig_reset(cdev->gadget);

         /* composite gadget needs to assign strings for whole device (like

           * serial number), register function drivers, potentially update

           * power state and consumption, etc

          */

         /*调用功能层提供的bind函数,分配资源*/

         status = composite->bind(cdev);

         if (status < 0)

                          goto fail;

          /*设置composite设备的设备描述符*/

          cdev->desc = *composite->dev;

           cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket;

          /* standardized runtime overrides for device ID data */

          /*设置composite设备的设备描述符信息*/

         if (idVendor)

                        cdev->desc.idVendor = cpu_to_le16(idVendor);

         if (idProduct)

                       cdev->desc.idProduct = cpu_to_le16(idProduct);

        if (bcdDevice)

                       cdev->desc.bcdDevice = cpu_to_le16(bcdDevice);

         /* strings can't be assigned before bind() allocates the

          * releavnt identifiers

          */

          /*设置字符串描述符*/

        if (cdev->desc.iManufacturer && iManufacturer)

                          string_override(composite->strings, cdev->desc.iManufacturer, iManufacturer);

       if (cdev->desc.iProduct && iProduct)

                         string_override(composite->strings, cdev->desc.iProduct, iProduct);

      if (cdev->desc.iSerialNumber && iSerialNumber)

                          string_override(composite->strings, cdev->desc.iSerialNumber, iSerialNumber);

         INFO(cdev, "%s ready\n", composite->name);

          return 0;

fail:

               composite_unbind(gadget);

              return status;

}

Composite_bind函数主要完成UDC驱动层gadget设备和设备层composite设备关系的建立,分配控制端点0的请求数据结构,设置设备描述符,并调用功能层的bind函数分配功能层所需要的资源等工作。

2.

static void /* __init_or_exit */ composite_unbind(struct usb_gadget *gadget)

{

 struct usb_composite_dev *cdev = get_gadget_data(gadget);

/* composite_disconnect() must already have been called

 * by the underlying peripheral controller driver!

 * so there's no i/o concurrency that could affect the

 * state protected by cdev->lock.

 */

WARN_ON(cdev->config);

while ( !list_empty(&cdev->configs)) { //遍历设备的配置链表

struct usb_configuration *c;

c = list_first_entry(&cdev->configs, struct usb_configuration, list);

 while (!list_empty(&c->functions)) { //遍历这个配置下的所有功能

struct usb_function *f;

                f = list_first_entry(&c->functions, struct usb_function, list);

                list_del(&f->list);

                if (f->unbind) { //调用功能的unbind函数

              DBG(cdev, "unbind function '%s'/%p\n", f->name, f);

                       f->unbind(c, f);

                       /* may free memory for "f" */

              }

     }

list_del(&c->list);

                                        if (c->unbind) { //调用配置的unbind函数

                                                            DBG(cdev, "unbind config '%s'/%p\n", c->label, c);

                                                                      c->unbind(c);

                                                            /* may free memory for "c" */

                                          }

                   }

                  if (composite->unbind) //调用功能驱动的unbind函数

                                  composite->unbind(cdev);

                 if (cdev->req) { //释放分配的控制请求的缓存区

                                 kfree(cdev->req->buf);

                                usb_ep_free_request(gadget->ep0, cdev->req);

                 }

                kfree(cdev); //释放composite设备

                set_gadget_data(gadget, NULL);

                composite = NULL;

}

Unbind函数完成bind函数中分配的资源,并遍历所有配置和各个配置下的功能,调用其对应得unbind函数来释放资源。

其他重要函数:

在配置中增加一个功能,每个配置初始化后都必须至少有一个功能。

int __init usb_add_function(struct usb_configuration *config, struct usb_function *function)

{

                 int value = -EINVAL;

                  DBG(config->cdev, "adding '%s'/%p to config '%s'/%p\n",function->name, function,config->label, config);

                  if (!function->set_alt || !function->disable)

                                           goto done;

                  /*设置功能所属配置,并将功能加入配置的链表中*/

                   function->config = config;

                   list_add_tail(&function->list, &config->functions);

                   /* REVISIT *require* function->bind? */

                   if (function->bind) { //调用功能的bind函数

                                                  value = function->bind(config, function); //分配这个功能所需要的资源

                                                   if (value < 0) {

                                                                         list_del(&function->list);

                                                                         function->config = NULL;

                                                   }

                    }

                    else

                                     value = 0;

                   /* We allow configurations that don't work at both speeds.

                     * If we run into a lowspeed Linux system, treat it the same

                     * as full speed ... it's the function drivers that will need

                     * to avoid bulk and ISO transfers.

                      */

                   if (!config->fullspeed && function->descriptors)

                                                   config->fullspeed = true;

                    if (!config->highspeed && function->hs_descriptors)

                                                 config->highspeed = true;

         done:

                   if (value)

                                DBG(config->cdev, "adding '%s'/%p --> %d\n", function->name, function, value);

                  return value;

}

/*为设备增加一个配置,这个实现原理和在配置下增加一个功能类似*/

int __init usb_add_config(struct usb_composite_dev *cdev, struct usb_configuration *config)

{

                 int status = -EINVAL;

                  struct usb_configuration *c;

                  DBG(cdev, "adding config #%u '%s'/%p\n", config->bConfigurationValue, config->label, config);

                  if (!config->bConfigurationValue || !config->bind)

                                            goto done;

                     /* Prevent duplicate configuration identifiers */

                         list_for_each_entry(c, &cdev->configs, list) { //若配置已经加入了,则不需要再加

                                                 if (c->bConfigurationValue == config->bConfigurationValue) {

                                                                                    status = -EBUSY;

                                                                                     goto done;

                                                 }

                            }

                        /*将这个配置加入composite设备*/

                         config->cdev = cdev;

                         list_add_tail(&config->list, &cdev->configs);

                         /*初始化这个配置的功能链表头*/

                         INIT_LIST_HEAD(&config->functions);

                         config->next_interface_id = 0;

                          status = config->bind(config); //完成这个配置的资源分配

                           if (status < 0) {

                                                       list_del(&config->list);

                                                      config->cdev = NULL;

                            }

                            else

                            {

                                                      unsigned i;

                                                      DBG(cdev, "cfg %d/%p speeds:%s%s\n", config->bConfigurationValue, config, config->highspeed ? " high" : "",

                                                              config->fullspeed? (gadget_is_dualspeed(cdev->gadget)? " full": " full/low"): "");

                                                      for (i = 0; i < MAX_CONFIG_INTERFACES; i++) { //查看这个配置下有哪有功能

                                                                                     struct usb_function *f = config->interface[i];

                                                                                     if (!f)

                                                                                             continue;

                                                                                     DBG(cdev, " interface %d = %s/%p\n",i, f->name, f);

                                                         }

                            }

                          /* set_alt(), or next config->bind(), sets up

                            * ep->driver_data as needed.

                          */

                           //将gadget设备的所有端点的driver_data清零

                            usb_ep_autoconfig_reset(cdev->gadget);

              done:

                           if (status)

                                           DBG(cdev, "added config '%s'/%u --> %d\n", config->label, config->bConfigurationValue, status);

                           return status;

}

 

/*设置一个配置*/

static int set_config(struct usb_composite_dev *cdev, const struct usb_ctrlrequest *ctrl, unsigned number)

{

                   struct usb_gadget *gadget = cdev->gadget;

                   struct usb_configuration *c = NULL;

                    int result = -EINVAL;

                    unsigned power = gadget_is_otg(gadget) ? 8 : 100;

                     int tmp;

                   if (cdev->config)

                                  reset_config(cdev); //清除设备目前使用的配置

                  if (number) { //根据配置值找到对应的配置

                                       list_for_each_entry(c, &cdev->configs, list) {

                                                                          if (c->bConfigurationValue == number) {

                                                                                                        result = 0;

                                                                                                        break;

                                                                          }

                                      }

                                     if (result < 0) //没有找到这个配置

                                                      goto done;

                 }

                else

                          result = 0;

              INFO(cdev, "%s speed config #%d: %s\n", ({ char *speed;

                           switch (gadget->speed) {

                                              case USB_SPEED_LOW: speed = "low"; break;

                                             case USB_SPEED_FULL: speed = "full"; break;

                                            case USB_SPEED_HIGH: speed = "high"; break;

                                             default: speed = "?"; break;

                           } ; speed; }), number, c ? c->label : "unconfigured");

          if (!c)

                      goto done;

         cdev->config = c; //设置为设备使用的配置

        /* Initialize all interfaces by setting them to altsetting zero. */

           for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) {

                                struct usb_function *f = c->interface[tmp];

                                struct usb_descriptor_header **descriptors;

                                if (!f)

                                                           break;

                                 /*

                                           * Record which endpoints are used by the function. This is used

                                          * to dispatch control requests targeted at that endpoint to the

                                          * function's setup callback instead of the current

                                          * configuration's setup callback.

                                 */

                                if (gadget->speed == USB_SPEED_HIGH)

                                                      descriptors = f->hs_descriptors;

                                else

                                                      descriptors = f->descriptors;

                               for (; *descriptors; ++descriptors) { //遍历功能的所有端点描述符

                                                     struct usb_endpoint_descriptor *ep;

                                                    int addr;

                                                    if ((*descriptors)->bDescriptorType != USB_DT_ENDPOINT)

                                                                                      continue;

                                                     ep = (struct usb_endpoint_descriptor *)*descriptors;

                                                     addr = ((ep->bEndpointAddress & 0x80) >> 3) | (ep->bEndpointAddress & 0x0f);

                                                   set_bit(addr, f->endpoints); //这个功能采用的端点号

                                  }

                                  result = f->set_alt(f, tmp, 0); //设置功能的默认值

                                 if (result < 0) {

                                                    DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n", tmp, f->name, f, result);

                                                    reset_config(cdev);

                                                     goto done;

                                   }

                 }

               /* when we return, be sure our power usage is valid */

                        power = c->bMaxPower ? (2 * c->bMaxPower) : CONFIG_USB_GADGET_VBUS_DRAW;

    done:

             usb_gadget_vbus_draw(gadget, power);

              return result;

}

/*这个setup函数完成了ep0所需要处理的而底层不能处理的功能,由于这一个层实现了这个函数,所以功能层就不需要关注于USB协议的这些事务性处理,而重点放在需要实现的功能上*/

static int composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)

{

                  struct usb_composite_dev *cdev = get_gadget_data(gadget); //由gadget获取composite设备

                  struct usb_request *req = cdev->req; //控制请求

                  int value = -EOPNOTSUPP;

                   u16 w_index = le16_to_cpu(ctrl->wIndex);

                    u8 intf = w_index & 0xFF;

                    u16 w_value = le16_to_cpu(ctrl->wValue);

                   u16 w_length = le16_to_cpu(ctrl->wLength);

                 struct usb_function *f = NULL;

                 u8 endp;

               /* partial re-init of the response message; the function or the

                   * gadget might need to intercept e.g. a control-OUT completion

                * when we delegate to it.

                */

              //初始化响应请求信息

                req->zero = 0;

                req->complete = composite_setup_complete;

               req->length = USB_BUFSIZ;

               gadget->ep0->driver_data = cdev;

               switch (ctrl->bRequest) { //根据请求类型处理

                                                 /* we handle all standard USB descriptors */

                                               case USB_REQ_GET_DESCRIPTOR: //返回描述符

                                                                        if (ctrl->bRequestType != USB_DIR_IN)

                                                                                                      goto unknown;

                                                                       switch (w_value >> 8) {

                                                                                                   case USB_DT_DEVICE: //返回设备描述符

                                                                                                                         cdev->desc.bNumConfigurations =  count_configs(cdev, USB_DT_DEVICE);

                                                                                                                          value = min(w_length, (u16) sizeof cdev->desc);

                                                                                                                          memcpy(req->buf, &cdev->desc, value);

                                                                                                                          break;

                                                                                                     case USB_DT_DEVICE_QUALIFIER: //返回qualifier描述符

                                                                                                                          if (!gadget_is_dualspeed(gadget))

                                                                                                                                                           break;

                                                                                                                           device_qual(cdev);

                                                                                                                          value = min_t(int, w_length, sizeof(struct usb_qualifier_descriptor));

                                                                                                                          break;

                                                                                                    case USB_DT_OTHER_SPEED_CONFIG:

                                                                                                                           if (!gadget_is_dualspeed(gadget))

                                                                                                                                                               break;

                                                                                                                            /* FALLTHROUGH */

                                                                                                    case USB_DT_CONFIG: //返回配置描述符

                                                                                                                           value = config_desc(cdev, w_value);

                                                                                                                           if (value >= 0)

                                                                                                                                                  value = min(w_length, (u16) value);

                                                                                                                            break;

                                                                                                      case USB_DT_STRING: //返回字符串描述符

                                                                                                                             value = get_string(cdev, req->buf, w_index, w_value & 0xff);

                                                                                                                             if (value >= 0)

                                                                                                                                                      value = min(w_length, (u16) value);

                                                                                                                             break;

                                                                                       }

                                                                             break;

                                                                             /* any number of configs can work */

                                       case USB_REQ_SET_CONFIGURATION: //设置配置

                                                                            if (ctrl->bRequestType != 0)

                                                                                                      goto unknown;

                                                                           if (gadget_is_otg(gadget)) {

                                                                                                    if (gadget->a_hnp_support)

                                                                                                                          DBG(cdev, "HNP available\n");

                                                                                                     else if (gadget->a_alt_hnp_support)

                                                                                                                          DBG(cdev, "HNP on another port\n");

                                                                                                    else

                                                                                                                         VDBG(cdev, "HNP inactive\n");

                                                                               }

                                                                            spin_lock(&cdev->lock);

                                                                             value = set_config(cdev, ctrl, w_value);

                                                                             spin_unlock(&cdev->lock);

                                                                             break;

                                   case USB_REQ_GET_CONFIGURATION: //返回当前配置

                                                                          if (ctrl->bRequestType != USB_DIR_IN)

                                                                                                                  goto unknown;

                                                                         if (cdev->config)

                                                                                                                *(u8 *)req->buf = cdev->config->bConfigurationValue;

                                                                         else

                                                                                                                   *(u8 *)req->buf = 0;

                                                                          value = min(w_length, (u16) 1);

                                                                           break;

                            /* function drivers must handle get/set altsetting; if there's

                             * no get() method, we know only altsetting zero works.

                             */

                           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;

                                 case USB_REQ_GET_INTERFACE: //返回接口设置

                                                                   if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))

                                                                                                    goto unknown;

                                                                  if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES)

                                                                                                    break;

                                                                    f = cdev->config->interface[intf];

                                                                  if (!f)

                                                                                                break;

                                                                  /* lots of interfaces only need altsetting zero... */

                                                                   value = f->get_alt ? f->get_alt(f, w_index) : 0;

                                                                   if (value < 0)

                                                                                                    break;

                                                                  *((u8 *)req->buf) = value;

                                                                  value = min(w_length, (u16) 1);

                                                                   break;

                                default:

   unknown:

              VDBG(cdev, "non-core control req%02x.%02x v%04x i%04x l%d\n",ctrl->bRequestType, ctrl->bRequest,w_value, w_index, w_length);

              /* functions always handle their interfaces and endpoints...

                * punt other recipients (other, WUSB, ...) to the current

                * configuration code.

                *

                * REVISIT it could make sense to let the composite device

                * take such requests too, if that's ever needed: to work

                * in config 0, etc.

              */

          switch (ctrl->bRequestType & USB_RECIP_MASK) {

                       case USB_RECIP_INTERFACE:

                                       f = cdev->config->interface[intf];

                                       break;

                      case USB_RECIP_ENDPOINT:

                                      endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f);

                                       list_for_each_entry(f, &cdev->config->functions, list) {

                                                    if (test_bit(endp, f->endpoints))

                                                                                     break;

                                         }

                                       if (&f->list == &cdev->config->functions)

                                                                     f = NULL;

                                     break;

       }

       if (f && f->setup)

                       value = f->setup(f, ctrl);

     else {

                        struct usb_configuration *c;

                         c = cdev->config;

                         if (c && c->setup)

                                       value = c->setup(c, ctrl);

       }

     goto done;

}

   /* respond with data transfer before status phase? */

    if (value >= 0) {

                           req->length = value;

                              req->zero = value < w_length;

                          value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);

                        if (value < 0) {

                                        DBG(cdev, "ep_queue --> %d\n", value);

                                         req->status = 0;

                                       composite_setup_complete(gadget->ep0, req);

                      }

         }

done:

           /* device either stalls (value < 0) or reports success */

             return value;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值