Gadget equipment Layer
This layer is optional, between the UDC drive layer and the gadget functional layer. The main source in the composite.c and composite.h files, the device layer in fact, and hardware-independent, mainly to achieve some common code, reduce the gadget function layer code duplication of work. Gadget equipment layer where the link between the role of the gadget functional layer and the UDC drive layer.
The composite source code is independent, but also provides a universal framework for the realization of composite equipment. A composite device is a device that supports multiple functions in one configuration descriptor, or that supports multiple configurations, each with a different function. If a device supports both network and storage, a device supports both keyboard and mouse functions.
1, Usb_function
struct Usb_function {//Describes a configured function for const char *name; function name struct usb_gadget_strings **strings; struct Usb_descriptor_header **descriptors; Full-speed and low-speed descriptor list for interface descriptors and string descriptors assigned in bind, struct Usb_descriptor_header **hs_descriptors;//high-speed descriptor struct USB_
Configuration *config; /* Revisit:bind () functions can be marked __init, which * makes trouble for sections mismatch analysis.
See if * We can ' t restructure things to avoid mismatching. * Related:unbind () may kfree () but bind () won ' t ... */* Configuration Management:bind/unbind */int (*bind) (str
UCT usb_configuration *,struct usb_function *);
void (*unbind) (struct usb_configuration *,struct usb_function *);
/* Runtime state Management */INT (*set_alt) (struct usb_function *,unsigned interface, unsigned alt);
Int (*get_alt) (struct usb_function *,unsigned interface);
void (*disable) (struct usb_function *); Int (*setup) (struct usb_function *,const struct usb_ctrlrequest *);//interface-related control processing void(*suspend)
(struct usb_function *);
void (*resume) (struct usb_function *);
/* Private: *//* Internals */struct list_head list; };
2, Usb_configuration
struct usb_configuration {//Represents a gadget configuration const char *label;
Configuration name struct usb_gadget_strings **strings; const struct Usb_descriptor_header **descriptors;//feature Descriptor/* REVISIT:BIND () functions can be marked __init, which * Makes trouble for sections mismatch analysis. See if * We can ' t restructure things to avoid mismatching ... */* Configuration Management:bind/unbind */INT (
*bind) (struct usb_configuration *);
void (*unbind) (struct usb_configuration *);
Int (*setup) (struct usb_configuration *,const struct usb_ctrlrequest *);
Handle configuration control requests that the drive framework cannot handle */* fields in the config descriptor */U8 Bconfigurationvalue;
U8 IConfiguration;
U8 Bmattributes;
U8 Bmaxpower;
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, Usb_composite_driver
struct Usb_composite_driver {
const char *name; Drive name
const struct Usb_device_descriptor *dev;
struct usb_gadget_strings **strings;
/* Revisit: bind () functions can is marked __init, which
* makes trouble for sections 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, Usb_composite_dev
struct Usb_composite_dev {//Represents a composite device
struct usb_gadget *gadget;//associated gadget
struct usb_request *req; Used to control the response, allocate unsigned bufsiz in advance, //req buffer length allocated in advance,
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;
};
Important functions
static int __init composite_bind (struct usb_gadget *gadget) {struct Usb_composite_dev *cdev;
int status =-ENOMEM;
Cdev = kzalloc (sizeof *cdev, gfp_kernel);
if (!cdev) return status;
Spin_lock_init (&cdev->lock); Cdev->gadget = gadget;
Establish a link between UDC gadget and composite equipment 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;
Gadget->ep0->driver_data = Cdev;
Cdev->bufsiz = Usb_bufsiz;
Cdev->driver = composite;
Usb_gadget_set_selfpowered (gadget);
/* Interface and string IDs start at zero via Kzalloc. * We force endpoints to start unassigned;
Few controller * drivers would zero ep->driver_data. */Usb_ep_autoconfig_reset (CDEV->gadget);//Driver_data members of all endpoints are cleared 0/composite gadget needs to assign strings for whole device (like * serial numb ER), register function drivers, potentially update * power state and consumption, etc * * status = Composite->bind (
CDEV);
if (Status < 0) goto fail;
Set device descriptor for composite device CDEV->DESC = *composite->dev;
CDEV->DESC.BMAXPACKETSIZE0 = gadget->ep0->maxpacket; Set device descriptor information for composite devices/* Standardized runtime overrides for device ID data * * 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); Set string Descriptor/* strings can ' t be assigned before bind () allocates the * releavnt identifiers */if (Cdev->desc.iman Ufacturer && 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; }
The Composite_bind function mainly completes the establishment of the relationship between the gadget device and the device layer Composite device in the UDC driver layer, assigns the request data structure of the control endpoint 0, sets the device descriptor, and invokes the function layer's bind function to allocate the resources needed by the functional layer.
static void/* __init_or_exit */composite_unbind (struct usb_gadget *gadget) {struct Usb_composite_dev *cdev = Get_gadge
T_data (gadget);
/* Composite_disconnect () must already has 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)) {//Traverse device Configuration list struct usb_configuration *c;
c = list_first_entry (&cdev->configs,struct usb_configuration, list);
while (!list_empty (&c->functions)) {//Traverse all functions of this configuration struct usb_function *f;
f = list_first_entry (&c->functions,struct usb_function, list);
List_del (&f->list);
if (f->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) {DBG (Cdev, "unbind config '%s '/%p\n", C->label, C); C->unBind (c);
/* May free memory for "C" */}} if (Composite->unbind) composite->unbind (Cdev);
if (cdev->req) {kfree (CDEV->REQ->BUF);
Usb_ep_free_request (Gadget->ep0, cdev->req);
} kfree (Cdev);
Set_gadget_data (gadget, NULL);
composite = NULL;
}
Add a feature to the configuration, and each configuration must have at least one function after initialization.
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;
Set the function to which the configuration belongs, and add the function to the list of configurations function->config = config;
List_add_tail (&function->list, &config->functions); /* Revisit *require* function->bind? */if (function->bind) {value = function->bind (config, function);//The resource required to allocate this function if (value < 0) {List_de
L (&function->list);
Function->config = NULL;
}} else value = 0;
/* We allow configurations The that don ' t work at both speeds.
* If we run into a lowspeed Linux system, treat it's the same * as full speed ... it's the function drivers that'll 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-to-%d\n", function->name, function, value);
return value;
}
Add a configuration to the device, which is similar to adding a function under configuration
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->bconf
Igurationvalue = = Config->bconfigurationvalue) {//If the configuration has been added, you do not need to add status =-ebusy;
Goto done;
}} Config->cdev = Cdev;
List_add_tail (&config->list, &cdev->configs);
A USB device can have multiple configurations//This sentence is configured to join the device in the configuration list of Init_list_head (&config->functions); Initialize the configuration of the functions linked list, functions linked list to link the struct usb_function type data structure, which is also very important, in fact, he represents a USB interface Config->next_interface_
id = 0; Status = Config->bind (config);//complete resource allocation for this configuration//This function calls the Sourcesink_bind_config,//Initializes a struct usb_function structure, and add it to the configured functions list 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++) {//maximum number of interfaces, traversing all interfaces of the configuration, printing information struct usb_function *f = Config->interfac
E[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.
*/Usb_ep_autoconfig_reset (cdev->gadget); The main function of this function is to empty the driver_data of all cdev->gadget endpoints done:if (status) DBG (Cdev, "added config '%s '/%u--%d\n", con
Fig->label, Config->bconfigurationvalue, status);
return status; }
Set up a configuration
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);//Clear the configuration value of the device if (number) {/////To find the corresponding configuration based on the configured value 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 '; 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];
if (!f) break;
result = F->set_alt (f, TMP, 0);
if (Result < 0) {DBG (Cdev, "interface%d (%s/%p) ALT 0--and%d\n", TMP, F->name, F, result);
Reset_config (Cdev);
Goto done; }}/* When we return, being 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; }
This setup function accomplishes what EP0 needs to deal with and the underlying cannot handle, because this layer implements this function, so the functional layer does not need to focus on these transactional processing of the USB protocol, but focus on the functions that need to be implemented.
static int Composite_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) {struct Usb_composite_dev *cde
v = get_gadget_data (gadget);
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; /* Partial re-init of the response message;
The function or the * gadget might need to intercept e.g. a control-out completion * If 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->b
RequestType! = 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: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.%0
2x v%04x i%04x l%d\n ", Ctrl->brequesttype, Ctrl->brequest, W_value, W_index, w_length);
/* functions always handle their interfaces ... punt other * recipients (endpoint, 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 wor
K * in config 0, etc. */if ((Ctrl->brequesttype & usb_recip_mask) = = Usb_recip_interface) {f = cdev->config->interface[intf]
; if (f && f->setup) value = F->setup (f, ctrl);
else F = NULL;
} if (Value < 0 &&!f) {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);
The main function of the Usb_ep_queue function is to write the data in the Req into the FIFO if (value < 0) {DBG (Cdev, "ep_queue-to-%d\n", value);
Req->status = 0;
Composite_setup_complete (GADGET->EP0, req);
}} Done:/* device either stalls (value < 0) or reports Success */return value; }
Transferred from: http://blog.chinaunix.net/uid-14518381-id-3940391.html Visualfan's Chinaunix Blog