usb: gadget: add SuperSpeed support to the Gadget Framework
SuperSpeed USB has defined a new descriptor, called the Binary Device Object Store (BOS) Descriptor. It has also changed a bit the definition of SET_FEATURE and GET_STATUS requests to add USB3-specific details. This patch implements both changes to the Composite Gadget Framework. [ balbi@ti.com : slight changes to commit log fixed a compile error on ARM ] Signed-off-by: Tatyana Brokhman <tlinder@codeaurora.org> Signed-off-by: Felipe Balbi <balbi@ti.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
35a0e0bf6f
commit
bdb64d7272
@ -666,6 +666,12 @@ config USB_GADGET_DUALSPEED
|
|||||||
bool
|
bool
|
||||||
depends on USB_GADGET
|
depends on USB_GADGET
|
||||||
|
|
||||||
|
# Selected by UDC drivers that support super-speed opperation
|
||||||
|
config USB_GADGET_SUPERSPEED
|
||||||
|
bool
|
||||||
|
depends on USB_GADGET
|
||||||
|
depends on USB_GADGET_DUALSPEED
|
||||||
|
|
||||||
#
|
#
|
||||||
# USB Gadget Drivers
|
# USB Gadget Drivers
|
||||||
#
|
#
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
#include <linux/utsname.h>
|
#include <linux/utsname.h>
|
||||||
|
|
||||||
#include <linux/usb/composite.h>
|
#include <linux/usb/composite.h>
|
||||||
|
#include <asm/unaligned.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The code in this file is utility code, used to build a gadget driver
|
* The code in this file is utility code, used to build a gadget driver
|
||||||
@ -128,6 +128,9 @@ int config_ep_by_speed(struct usb_gadget *g,
|
|||||||
struct usb_endpoint_descriptor *chosen_desc = NULL;
|
struct usb_endpoint_descriptor *chosen_desc = NULL;
|
||||||
struct usb_descriptor_header **speed_desc = NULL;
|
struct usb_descriptor_header **speed_desc = NULL;
|
||||||
|
|
||||||
|
struct usb_ss_ep_comp_descriptor *comp_desc = NULL;
|
||||||
|
int want_comp_desc = 0;
|
||||||
|
|
||||||
struct usb_descriptor_header **d_spd; /* cursor for speed desc */
|
struct usb_descriptor_header **d_spd; /* cursor for speed desc */
|
||||||
|
|
||||||
if (!g || !f || !_ep)
|
if (!g || !f || !_ep)
|
||||||
@ -135,6 +138,13 @@ int config_ep_by_speed(struct usb_gadget *g,
|
|||||||
|
|
||||||
/* select desired speed */
|
/* select desired speed */
|
||||||
switch (g->speed) {
|
switch (g->speed) {
|
||||||
|
case USB_SPEED_SUPER:
|
||||||
|
if (gadget_is_superspeed(g)) {
|
||||||
|
speed_desc = f->ss_descriptors;
|
||||||
|
want_comp_desc = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* else: Fall trough */
|
||||||
case USB_SPEED_HIGH:
|
case USB_SPEED_HIGH:
|
||||||
if (gadget_is_dualspeed(g)) {
|
if (gadget_is_dualspeed(g)) {
|
||||||
speed_desc = f->hs_descriptors;
|
speed_desc = f->hs_descriptors;
|
||||||
@ -156,7 +166,36 @@ int config_ep_by_speed(struct usb_gadget *g,
|
|||||||
/* commit results */
|
/* commit results */
|
||||||
_ep->maxpacket = le16_to_cpu(chosen_desc->wMaxPacketSize);
|
_ep->maxpacket = le16_to_cpu(chosen_desc->wMaxPacketSize);
|
||||||
_ep->desc = chosen_desc;
|
_ep->desc = chosen_desc;
|
||||||
|
_ep->comp_desc = NULL;
|
||||||
|
_ep->maxburst = 0;
|
||||||
|
_ep->mult = 0;
|
||||||
|
if (!want_comp_desc)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Companion descriptor should follow EP descriptor
|
||||||
|
* USB 3.0 spec, #9.6.7
|
||||||
|
*/
|
||||||
|
comp_desc = (struct usb_ss_ep_comp_descriptor *)*(++d_spd);
|
||||||
|
if (!comp_desc ||
|
||||||
|
(comp_desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP))
|
||||||
|
return -EIO;
|
||||||
|
_ep->comp_desc = comp_desc;
|
||||||
|
if (g->speed == USB_SPEED_SUPER) {
|
||||||
|
switch (usb_endpoint_type(_ep->desc)) {
|
||||||
|
case USB_ENDPOINT_XFER_BULK:
|
||||||
|
case USB_ENDPOINT_XFER_INT:
|
||||||
|
_ep->maxburst = comp_desc->bMaxBurst;
|
||||||
|
break;
|
||||||
|
case USB_ENDPOINT_XFER_ISOC:
|
||||||
|
/* mult: bits 1:0 of bmAttributes */
|
||||||
|
_ep->mult = comp_desc->bmAttributes & 0x3;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Do nothing for control endpoints */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,6 +247,8 @@ int usb_add_function(struct usb_configuration *config,
|
|||||||
config->fullspeed = true;
|
config->fullspeed = true;
|
||||||
if (!config->highspeed && function->hs_descriptors)
|
if (!config->highspeed && function->hs_descriptors)
|
||||||
config->highspeed = true;
|
config->highspeed = true;
|
||||||
|
if (!config->superspeed && function->ss_descriptors)
|
||||||
|
config->superspeed = true;
|
||||||
|
|
||||||
done:
|
done:
|
||||||
if (value)
|
if (value)
|
||||||
@ -351,10 +392,17 @@ static int config_buf(struct usb_configuration *config,
|
|||||||
list_for_each_entry(f, &config->functions, list) {
|
list_for_each_entry(f, &config->functions, list) {
|
||||||
struct usb_descriptor_header **descriptors;
|
struct usb_descriptor_header **descriptors;
|
||||||
|
|
||||||
if (speed == USB_SPEED_HIGH)
|
switch (speed) {
|
||||||
|
case USB_SPEED_SUPER:
|
||||||
|
descriptors = f->ss_descriptors;
|
||||||
|
break;
|
||||||
|
case USB_SPEED_HIGH:
|
||||||
descriptors = f->hs_descriptors;
|
descriptors = f->hs_descriptors;
|
||||||
else
|
break;
|
||||||
|
default:
|
||||||
descriptors = f->descriptors;
|
descriptors = f->descriptors;
|
||||||
|
}
|
||||||
|
|
||||||
if (!descriptors)
|
if (!descriptors)
|
||||||
continue;
|
continue;
|
||||||
status = usb_descriptor_fillbuf(next, len,
|
status = usb_descriptor_fillbuf(next, len,
|
||||||
@ -377,9 +425,10 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
|
|||||||
u8 type = w_value >> 8;
|
u8 type = w_value >> 8;
|
||||||
enum usb_device_speed speed = USB_SPEED_UNKNOWN;
|
enum usb_device_speed speed = USB_SPEED_UNKNOWN;
|
||||||
|
|
||||||
if (gadget_is_dualspeed(gadget)) {
|
if (gadget->speed == USB_SPEED_SUPER)
|
||||||
int hs = 0;
|
speed = gadget->speed;
|
||||||
|
else if (gadget_is_dualspeed(gadget)) {
|
||||||
|
int hs = 0;
|
||||||
if (gadget->speed == USB_SPEED_HIGH)
|
if (gadget->speed == USB_SPEED_HIGH)
|
||||||
hs = 1;
|
hs = 1;
|
||||||
if (type == USB_DT_OTHER_SPEED_CONFIG)
|
if (type == USB_DT_OTHER_SPEED_CONFIG)
|
||||||
@ -393,13 +442,20 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
|
|||||||
w_value &= 0xff;
|
w_value &= 0xff;
|
||||||
list_for_each_entry(c, &cdev->configs, list) {
|
list_for_each_entry(c, &cdev->configs, list) {
|
||||||
/* ignore configs that won't work at this speed */
|
/* ignore configs that won't work at this speed */
|
||||||
if (speed == USB_SPEED_HIGH) {
|
switch (speed) {
|
||||||
|
case USB_SPEED_SUPER:
|
||||||
|
if (!c->superspeed)
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
case USB_SPEED_HIGH:
|
||||||
if (!c->highspeed)
|
if (!c->highspeed)
|
||||||
continue;
|
continue;
|
||||||
} else {
|
break;
|
||||||
|
default:
|
||||||
if (!c->fullspeed)
|
if (!c->fullspeed)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (w_value == 0)
|
if (w_value == 0)
|
||||||
return config_buf(c, speed, cdev->req->buf, type);
|
return config_buf(c, speed, cdev->req->buf, type);
|
||||||
w_value--;
|
w_value--;
|
||||||
@ -413,16 +469,22 @@ static int count_configs(struct usb_composite_dev *cdev, unsigned type)
|
|||||||
struct usb_configuration *c;
|
struct usb_configuration *c;
|
||||||
unsigned count = 0;
|
unsigned count = 0;
|
||||||
int hs = 0;
|
int hs = 0;
|
||||||
|
int ss = 0;
|
||||||
|
|
||||||
if (gadget_is_dualspeed(gadget)) {
|
if (gadget_is_dualspeed(gadget)) {
|
||||||
if (gadget->speed == USB_SPEED_HIGH)
|
if (gadget->speed == USB_SPEED_HIGH)
|
||||||
hs = 1;
|
hs = 1;
|
||||||
|
if (gadget->speed == USB_SPEED_SUPER)
|
||||||
|
ss = 1;
|
||||||
if (type == USB_DT_DEVICE_QUALIFIER)
|
if (type == USB_DT_DEVICE_QUALIFIER)
|
||||||
hs = !hs;
|
hs = !hs;
|
||||||
}
|
}
|
||||||
list_for_each_entry(c, &cdev->configs, list) {
|
list_for_each_entry(c, &cdev->configs, list) {
|
||||||
/* ignore configs that won't work at this speed */
|
/* ignore configs that won't work at this speed */
|
||||||
if (hs) {
|
if (ss) {
|
||||||
|
if (!c->superspeed)
|
||||||
|
continue;
|
||||||
|
} else if (hs) {
|
||||||
if (!c->highspeed)
|
if (!c->highspeed)
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
@ -434,6 +496,71 @@ static int count_configs(struct usb_composite_dev *cdev, unsigned type)
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bos_desc() - prepares the BOS descriptor.
|
||||||
|
* @cdev: pointer to usb_composite device to generate the bos
|
||||||
|
* descriptor for
|
||||||
|
*
|
||||||
|
* This function generates the BOS (Binary Device Object)
|
||||||
|
* descriptor and its device capabilities descriptors. The BOS
|
||||||
|
* descriptor should be supported by a SuperSpeed device.
|
||||||
|
*/
|
||||||
|
static int bos_desc(struct usb_composite_dev *cdev)
|
||||||
|
{
|
||||||
|
struct usb_ext_cap_descriptor *usb_ext;
|
||||||
|
struct usb_ss_cap_descriptor *ss_cap;
|
||||||
|
struct usb_dcd_config_params dcd_config_params;
|
||||||
|
struct usb_bos_descriptor *bos = cdev->req->buf;
|
||||||
|
|
||||||
|
bos->bLength = USB_DT_BOS_SIZE;
|
||||||
|
bos->bDescriptorType = USB_DT_BOS;
|
||||||
|
|
||||||
|
bos->wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE);
|
||||||
|
bos->bNumDeviceCaps = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A SuperSpeed device shall include the USB2.0 extension descriptor
|
||||||
|
* and shall support LPM when operating in USB2.0 HS mode.
|
||||||
|
*/
|
||||||
|
usb_ext = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
|
||||||
|
bos->bNumDeviceCaps++;
|
||||||
|
le16_add_cpu(&bos->wTotalLength, USB_DT_USB_EXT_CAP_SIZE);
|
||||||
|
usb_ext->bLength = USB_DT_USB_EXT_CAP_SIZE;
|
||||||
|
usb_ext->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
|
||||||
|
usb_ext->bDevCapabilityType = USB_CAP_TYPE_EXT;
|
||||||
|
usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The Superspeed USB Capability descriptor shall be implemented by all
|
||||||
|
* SuperSpeed devices.
|
||||||
|
*/
|
||||||
|
ss_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
|
||||||
|
bos->bNumDeviceCaps++;
|
||||||
|
le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SS_CAP_SIZE);
|
||||||
|
ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE;
|
||||||
|
ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
|
||||||
|
ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE;
|
||||||
|
ss_cap->bmAttributes = 0; /* LTM is not supported yet */
|
||||||
|
ss_cap->wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION |
|
||||||
|
USB_FULL_SPEED_OPERATION |
|
||||||
|
USB_HIGH_SPEED_OPERATION |
|
||||||
|
USB_5GBPS_OPERATION);
|
||||||
|
ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;
|
||||||
|
|
||||||
|
/* Get Controller configuration */
|
||||||
|
if (cdev->gadget->ops->get_config_params)
|
||||||
|
cdev->gadget->ops->get_config_params(&dcd_config_params);
|
||||||
|
else {
|
||||||
|
dcd_config_params.bU1devExitLat = USB_DEFULT_U1_DEV_EXIT_LAT;
|
||||||
|
dcd_config_params.bU2DevExitLat =
|
||||||
|
cpu_to_le16(USB_DEFULT_U2_DEV_EXIT_LAT);
|
||||||
|
}
|
||||||
|
ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat;
|
||||||
|
ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat;
|
||||||
|
|
||||||
|
return le16_to_cpu(bos->wTotalLength);
|
||||||
|
}
|
||||||
|
|
||||||
static void device_qual(struct usb_composite_dev *cdev)
|
static void device_qual(struct usb_composite_dev *cdev)
|
||||||
{
|
{
|
||||||
struct usb_qualifier_descriptor *qual = cdev->req->buf;
|
struct usb_qualifier_descriptor *qual = cdev->req->buf;
|
||||||
@ -477,20 +604,27 @@ static int set_config(struct usb_composite_dev *cdev,
|
|||||||
unsigned power = gadget_is_otg(gadget) ? 8 : 100;
|
unsigned power = gadget_is_otg(gadget) ? 8 : 100;
|
||||||
int tmp;
|
int tmp;
|
||||||
|
|
||||||
if (cdev->config)
|
|
||||||
reset_config(cdev);
|
|
||||||
|
|
||||||
if (number) {
|
if (number) {
|
||||||
list_for_each_entry(c, &cdev->configs, list) {
|
list_for_each_entry(c, &cdev->configs, list) {
|
||||||
if (c->bConfigurationValue == number) {
|
if (c->bConfigurationValue == number) {
|
||||||
|
/*
|
||||||
|
* We disable the FDs of the previous
|
||||||
|
* configuration only if the new configuration
|
||||||
|
* is a valid one
|
||||||
|
*/
|
||||||
|
if (cdev->config)
|
||||||
|
reset_config(cdev);
|
||||||
result = 0;
|
result = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
goto done;
|
goto done;
|
||||||
} else
|
} else { /* Zero configuration value - need to reset the config */
|
||||||
|
if (cdev->config)
|
||||||
|
reset_config(cdev);
|
||||||
result = 0;
|
result = 0;
|
||||||
|
}
|
||||||
|
|
||||||
INFO(cdev, "%s speed config #%d: %s\n",
|
INFO(cdev, "%s speed config #%d: %s\n",
|
||||||
({ char *speed;
|
({ char *speed;
|
||||||
@ -504,6 +638,9 @@ static int set_config(struct usb_composite_dev *cdev,
|
|||||||
case USB_SPEED_HIGH:
|
case USB_SPEED_HIGH:
|
||||||
speed = "high";
|
speed = "high";
|
||||||
break;
|
break;
|
||||||
|
case USB_SPEED_SUPER:
|
||||||
|
speed = "super";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
speed = "?";
|
speed = "?";
|
||||||
break;
|
break;
|
||||||
@ -528,10 +665,16 @@ static int set_config(struct usb_composite_dev *cdev,
|
|||||||
* function's setup callback instead of the current
|
* function's setup callback instead of the current
|
||||||
* configuration's setup callback.
|
* configuration's setup callback.
|
||||||
*/
|
*/
|
||||||
if (gadget->speed == USB_SPEED_HIGH)
|
switch (gadget->speed) {
|
||||||
|
case USB_SPEED_SUPER:
|
||||||
|
descriptors = f->ss_descriptors;
|
||||||
|
break;
|
||||||
|
case USB_SPEED_HIGH:
|
||||||
descriptors = f->hs_descriptors;
|
descriptors = f->hs_descriptors;
|
||||||
else
|
break;
|
||||||
|
default:
|
||||||
descriptors = f->descriptors;
|
descriptors = f->descriptors;
|
||||||
|
}
|
||||||
|
|
||||||
for (; *descriptors; ++descriptors) {
|
for (; *descriptors; ++descriptors) {
|
||||||
struct usb_endpoint_descriptor *ep;
|
struct usb_endpoint_descriptor *ep;
|
||||||
@ -624,8 +767,9 @@ int usb_add_config(struct usb_composite_dev *cdev,
|
|||||||
} else {
|
} else {
|
||||||
unsigned i;
|
unsigned i;
|
||||||
|
|
||||||
DBG(cdev, "cfg %d/%p speeds:%s%s\n",
|
DBG(cdev, "cfg %d/%p speeds:%s%s%s\n",
|
||||||
config->bConfigurationValue, config,
|
config->bConfigurationValue, config,
|
||||||
|
config->superspeed ? " super" : "",
|
||||||
config->highspeed ? " high" : "",
|
config->highspeed ? " high" : "",
|
||||||
config->fullspeed
|
config->fullspeed
|
||||||
? (gadget_is_dualspeed(cdev->gadget)
|
? (gadget_is_dualspeed(cdev->gadget)
|
||||||
@ -904,6 +1048,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
|||||||
struct usb_composite_dev *cdev = get_gadget_data(gadget);
|
struct usb_composite_dev *cdev = get_gadget_data(gadget);
|
||||||
struct usb_request *req = cdev->req;
|
struct usb_request *req = cdev->req;
|
||||||
int value = -EOPNOTSUPP;
|
int value = -EOPNOTSUPP;
|
||||||
|
int status = 0;
|
||||||
u16 w_index = le16_to_cpu(ctrl->wIndex);
|
u16 w_index = le16_to_cpu(ctrl->wIndex);
|
||||||
u8 intf = w_index & 0xFF;
|
u8 intf = w_index & 0xFF;
|
||||||
u16 w_value = le16_to_cpu(ctrl->wValue);
|
u16 w_value = le16_to_cpu(ctrl->wValue);
|
||||||
@ -931,18 +1076,29 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
|||||||
case USB_DT_DEVICE:
|
case USB_DT_DEVICE:
|
||||||
cdev->desc.bNumConfigurations =
|
cdev->desc.bNumConfigurations =
|
||||||
count_configs(cdev, USB_DT_DEVICE);
|
count_configs(cdev, USB_DT_DEVICE);
|
||||||
|
cdev->desc.bMaxPacketSize0 =
|
||||||
|
cdev->gadget->ep0->maxpacket;
|
||||||
|
if (gadget_is_superspeed(gadget)) {
|
||||||
|
if (gadget->speed >= USB_SPEED_SUPER)
|
||||||
|
cdev->desc.bcdUSB = cpu_to_le16(0x0300);
|
||||||
|
else
|
||||||
|
cdev->desc.bcdUSB = cpu_to_le16(0x0210);
|
||||||
|
}
|
||||||
|
|
||||||
value = min(w_length, (u16) sizeof cdev->desc);
|
value = min(w_length, (u16) sizeof cdev->desc);
|
||||||
memcpy(req->buf, &cdev->desc, value);
|
memcpy(req->buf, &cdev->desc, value);
|
||||||
break;
|
break;
|
||||||
case USB_DT_DEVICE_QUALIFIER:
|
case USB_DT_DEVICE_QUALIFIER:
|
||||||
if (!gadget_is_dualspeed(gadget))
|
if (!gadget_is_dualspeed(gadget) ||
|
||||||
|
gadget->speed >= USB_SPEED_SUPER)
|
||||||
break;
|
break;
|
||||||
device_qual(cdev);
|
device_qual(cdev);
|
||||||
value = min_t(int, w_length,
|
value = min_t(int, w_length,
|
||||||
sizeof(struct usb_qualifier_descriptor));
|
sizeof(struct usb_qualifier_descriptor));
|
||||||
break;
|
break;
|
||||||
case USB_DT_OTHER_SPEED_CONFIG:
|
case USB_DT_OTHER_SPEED_CONFIG:
|
||||||
if (!gadget_is_dualspeed(gadget))
|
if (!gadget_is_dualspeed(gadget) ||
|
||||||
|
gadget->speed >= USB_SPEED_SUPER)
|
||||||
break;
|
break;
|
||||||
/* FALLTHROUGH */
|
/* FALLTHROUGH */
|
||||||
case USB_DT_CONFIG:
|
case USB_DT_CONFIG:
|
||||||
@ -956,6 +1112,12 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
|||||||
if (value >= 0)
|
if (value >= 0)
|
||||||
value = min(w_length, (u16) value);
|
value = min(w_length, (u16) value);
|
||||||
break;
|
break;
|
||||||
|
case USB_DT_BOS:
|
||||||
|
if (gadget_is_superspeed(gadget)) {
|
||||||
|
value = bos_desc(cdev);
|
||||||
|
value = min(w_length, (u16) value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1023,6 +1185,61 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
|||||||
*((u8 *)req->buf) = value;
|
*((u8 *)req->buf) = value;
|
||||||
value = min(w_length, (u16) 1);
|
value = min(w_length, (u16) 1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* USB 3.0 additions:
|
||||||
|
* Function driver should handle get_status request. If such cb
|
||||||
|
* wasn't supplied we respond with default value = 0
|
||||||
|
* Note: function driver should supply such cb only for the first
|
||||||
|
* interface of the function
|
||||||
|
*/
|
||||||
|
case USB_REQ_GET_STATUS:
|
||||||
|
if (!gadget_is_superspeed(gadget))
|
||||||
|
goto unknown;
|
||||||
|
if (ctrl->bRequestType != (USB_DIR_IN | USB_RECIP_INTERFACE))
|
||||||
|
goto unknown;
|
||||||
|
value = 2; /* This is the length of the get_status reply */
|
||||||
|
put_unaligned_le16(0, req->buf);
|
||||||
|
if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
|
||||||
|
break;
|
||||||
|
f = cdev->config->interface[intf];
|
||||||
|
if (!f)
|
||||||
|
break;
|
||||||
|
status = f->get_status ? f->get_status(f) : 0;
|
||||||
|
if (status < 0)
|
||||||
|
break;
|
||||||
|
put_unaligned_le16(status & 0x0000ffff, req->buf);
|
||||||
|
break;
|
||||||
|
/*
|
||||||
|
* Function drivers should handle SetFeature/ClearFeature
|
||||||
|
* (FUNCTION_SUSPEND) request. function_suspend cb should be supplied
|
||||||
|
* only for the first interface of the function
|
||||||
|
*/
|
||||||
|
case USB_REQ_CLEAR_FEATURE:
|
||||||
|
case USB_REQ_SET_FEATURE:
|
||||||
|
if (!gadget_is_superspeed(gadget))
|
||||||
|
goto unknown;
|
||||||
|
if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_INTERFACE))
|
||||||
|
goto unknown;
|
||||||
|
switch (w_value) {
|
||||||
|
case USB_INTRF_FUNC_SUSPEND:
|
||||||
|
if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
|
||||||
|
break;
|
||||||
|
f = cdev->config->interface[intf];
|
||||||
|
if (!f)
|
||||||
|
break;
|
||||||
|
value = 0;
|
||||||
|
if (f->func_suspend)
|
||||||
|
value = f->func_suspend(f, w_index >> 8);
|
||||||
|
if (value < 0) {
|
||||||
|
ERROR(cdev,
|
||||||
|
"func_suspend() returned error %d\n",
|
||||||
|
value);
|
||||||
|
value = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
unknown:
|
unknown:
|
||||||
VDBG(cdev,
|
VDBG(cdev,
|
||||||
@ -1340,7 +1557,11 @@ composite_resume(struct usb_gadget *gadget)
|
|||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
static struct usb_gadget_driver composite_driver = {
|
static struct usb_gadget_driver composite_driver = {
|
||||||
|
#ifdef CONFIG_USB_GADGET_SUPERSPEED
|
||||||
|
.speed = USB_SPEED_SUPER,
|
||||||
|
#else
|
||||||
.speed = USB_SPEED_HIGH,
|
.speed = USB_SPEED_HIGH,
|
||||||
|
#endif
|
||||||
|
|
||||||
.unbind = composite_unbind,
|
.unbind = composite_unbind,
|
||||||
|
|
||||||
|
@ -161,13 +161,13 @@ ep_matches (
|
|||||||
max = 0x7ff & le16_to_cpu(desc->wMaxPacketSize);
|
max = 0x7ff & le16_to_cpu(desc->wMaxPacketSize);
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case USB_ENDPOINT_XFER_INT:
|
case USB_ENDPOINT_XFER_INT:
|
||||||
/* INT: limit 64 bytes full speed, 1024 high speed */
|
/* INT: limit 64 bytes full speed, 1024 high/super speed */
|
||||||
if (!gadget->is_dualspeed && max > 64)
|
if (!gadget->is_dualspeed && max > 64)
|
||||||
return 0;
|
return 0;
|
||||||
/* FALLTHROUGH */
|
/* FALLTHROUGH */
|
||||||
|
|
||||||
case USB_ENDPOINT_XFER_ISOC:
|
case USB_ENDPOINT_XFER_ISOC:
|
||||||
/* ISO: limit 1023 bytes full speed, 1024 high speed */
|
/* ISO: limit 1023 bytes full speed, 1024 high/super speed */
|
||||||
if (ep->maxpacket < max)
|
if (ep->maxpacket < max)
|
||||||
return 0;
|
return 0;
|
||||||
if (!gadget->is_dualspeed && max > 1023)
|
if (!gadget->is_dualspeed && max > 1023)
|
||||||
@ -202,7 +202,7 @@ ep_matches (
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* report (variable) full speed bulk maxpacket */
|
/* report (variable) full speed bulk maxpacket */
|
||||||
if (USB_ENDPOINT_XFER_BULK == type) {
|
if ((USB_ENDPOINT_XFER_BULK == type) && !ep_comp) {
|
||||||
int size = ep->maxpacket;
|
int size = ep->maxpacket;
|
||||||
|
|
||||||
/* min() doesn't work on bitfields with gcc-3.5 */
|
/* min() doesn't work on bitfields with gcc-3.5 */
|
||||||
|
@ -59,6 +59,10 @@ struct usb_configuration;
|
|||||||
* @hs_descriptors: Table of high speed descriptors, using interface and
|
* @hs_descriptors: Table of high speed descriptors, using interface and
|
||||||
* string identifiers assigned during @bind(). If this pointer is null,
|
* string identifiers assigned during @bind(). If this pointer is null,
|
||||||
* the function will not be available at high speed.
|
* the function will not be available at high speed.
|
||||||
|
* @ss_descriptors: Table of super speed descriptors, using interface and
|
||||||
|
* string identifiers assigned during @bind(). If this
|
||||||
|
* pointer is null after initiation, the function will not
|
||||||
|
* be available at super speed.
|
||||||
* @config: assigned when @usb_add_function() is called; this is the
|
* @config: assigned when @usb_add_function() is called; this is the
|
||||||
* configuration with which this function is associated.
|
* configuration with which this function is associated.
|
||||||
* @bind: Before the gadget can register, all of its functions bind() to the
|
* @bind: Before the gadget can register, all of its functions bind() to the
|
||||||
@ -77,6 +81,10 @@ struct usb_configuration;
|
|||||||
* @setup: Used for interface-specific control requests.
|
* @setup: Used for interface-specific control requests.
|
||||||
* @suspend: Notifies functions when the host stops sending USB traffic.
|
* @suspend: Notifies functions when the host stops sending USB traffic.
|
||||||
* @resume: Notifies functions when the host restarts USB traffic.
|
* @resume: Notifies functions when the host restarts USB traffic.
|
||||||
|
* @get_status: Returns function status as a reply to
|
||||||
|
* GetStatus() request when the recepient is Interface.
|
||||||
|
* @func_suspend: callback to be called when
|
||||||
|
* SetFeature(FUNCTION_SUSPEND) is reseived
|
||||||
*
|
*
|
||||||
* A single USB function uses one or more interfaces, and should in most
|
* A single USB function uses one or more interfaces, and should in most
|
||||||
* cases support operation at both full and high speeds. Each function is
|
* cases support operation at both full and high speeds. Each function is
|
||||||
@ -106,6 +114,7 @@ struct usb_function {
|
|||||||
struct usb_gadget_strings **strings;
|
struct usb_gadget_strings **strings;
|
||||||
struct usb_descriptor_header **descriptors;
|
struct usb_descriptor_header **descriptors;
|
||||||
struct usb_descriptor_header **hs_descriptors;
|
struct usb_descriptor_header **hs_descriptors;
|
||||||
|
struct usb_descriptor_header **ss_descriptors;
|
||||||
|
|
||||||
struct usb_configuration *config;
|
struct usb_configuration *config;
|
||||||
|
|
||||||
@ -132,6 +141,10 @@ struct usb_function {
|
|||||||
void (*suspend)(struct usb_function *);
|
void (*suspend)(struct usb_function *);
|
||||||
void (*resume)(struct usb_function *);
|
void (*resume)(struct usb_function *);
|
||||||
|
|
||||||
|
/* USB 3.0 additions */
|
||||||
|
int (*get_status)(struct usb_function *);
|
||||||
|
int (*func_suspend)(struct usb_function *,
|
||||||
|
u8 suspend_opt);
|
||||||
/* private: */
|
/* private: */
|
||||||
/* internals */
|
/* internals */
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
@ -219,6 +232,7 @@ struct usb_configuration {
|
|||||||
struct list_head list;
|
struct list_head list;
|
||||||
struct list_head functions;
|
struct list_head functions;
|
||||||
u8 next_interface_id;
|
u8 next_interface_id;
|
||||||
|
unsigned superspeed:1;
|
||||||
unsigned highspeed:1;
|
unsigned highspeed:1;
|
||||||
unsigned fullspeed:1;
|
unsigned fullspeed:1;
|
||||||
struct usb_function *interface[MAX_CONFIG_INTERFACES];
|
struct usb_function *interface[MAX_CONFIG_INTERFACES];
|
||||||
|
@ -136,6 +136,8 @@ struct usb_ep_ops {
|
|||||||
* the endpoint descriptor used to configure the endpoint.
|
* the endpoint descriptor used to configure the endpoint.
|
||||||
* @max_streams: The maximum number of streams supported
|
* @max_streams: The maximum number of streams supported
|
||||||
* by this EP (0 - 16, actual number is 2^n)
|
* by this EP (0 - 16, actual number is 2^n)
|
||||||
|
* @mult: multiplier, 'mult' value for SS Isoc EPs
|
||||||
|
* @maxburst: the maximum number of bursts supported by this EP (for usb3)
|
||||||
* @driver_data:for use by the gadget driver.
|
* @driver_data:for use by the gadget driver.
|
||||||
* @address: used to identify the endpoint when finding descriptor that
|
* @address: used to identify the endpoint when finding descriptor that
|
||||||
* matches connection speed
|
* matches connection speed
|
||||||
@ -156,6 +158,8 @@ struct usb_ep {
|
|||||||
struct list_head ep_list;
|
struct list_head ep_list;
|
||||||
unsigned maxpacket:16;
|
unsigned maxpacket:16;
|
||||||
unsigned max_streams:16;
|
unsigned max_streams:16;
|
||||||
|
unsigned mult:2;
|
||||||
|
unsigned maxburst:4;
|
||||||
u8 address;
|
u8 address;
|
||||||
const struct usb_endpoint_descriptor *desc;
|
const struct usb_endpoint_descriptor *desc;
|
||||||
const struct usb_ss_ep_comp_descriptor *comp_desc;
|
const struct usb_ss_ep_comp_descriptor *comp_desc;
|
||||||
@ -426,6 +430,14 @@ static inline void usb_ep_fifo_flush(struct usb_ep *ep)
|
|||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
struct usb_dcd_config_params {
|
||||||
|
__u8 bU1devExitLat; /* U1 Device exit Latency */
|
||||||
|
#define USB_DEFULT_U1_DEV_EXIT_LAT 0x01 /* Less then 1 microsec */
|
||||||
|
__le16 bU2DevExitLat; /* U2 Device exit Latency */
|
||||||
|
#define USB_DEFULT_U2_DEV_EXIT_LAT 0x1F4 /* Less then 500 microsec */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct usb_gadget;
|
struct usb_gadget;
|
||||||
struct usb_gadget_driver;
|
struct usb_gadget_driver;
|
||||||
|
|
||||||
@ -441,6 +453,7 @@ struct usb_gadget_ops {
|
|||||||
int (*pullup) (struct usb_gadget *, int is_on);
|
int (*pullup) (struct usb_gadget *, int is_on);
|
||||||
int (*ioctl)(struct usb_gadget *,
|
int (*ioctl)(struct usb_gadget *,
|
||||||
unsigned code, unsigned long param);
|
unsigned code, unsigned long param);
|
||||||
|
void (*get_config_params)(struct usb_dcd_config_params *);
|
||||||
int (*start)(struct usb_gadget_driver *,
|
int (*start)(struct usb_gadget_driver *,
|
||||||
int (*bind)(struct usb_gadget *));
|
int (*bind)(struct usb_gadget *));
|
||||||
int (*stop)(struct usb_gadget_driver *);
|
int (*stop)(struct usb_gadget_driver *);
|
||||||
@ -534,6 +547,24 @@ static inline int gadget_is_dualspeed(struct usb_gadget *g)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gadget_is_superspeed() - return true if the hardware handles
|
||||||
|
* supperspeed
|
||||||
|
* @g: controller that might support supper speed
|
||||||
|
*/
|
||||||
|
static inline int gadget_is_superspeed(struct usb_gadget *g)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_USB_GADGET_SUPERSPEED
|
||||||
|
/*
|
||||||
|
* runtime test would check "g->is_superspeed" ... that might be
|
||||||
|
* useful to work around hardware bugs, but is mostly pointless
|
||||||
|
*/
|
||||||
|
return 1;
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gadget_is_otg - return true iff the hardware is OTG-ready
|
* gadget_is_otg - return true iff the hardware is OTG-ready
|
||||||
* @g: controller that might have a Mini-AB connector
|
* @g: controller that might have a Mini-AB connector
|
||||||
|
Loading…
Reference in New Issue
Block a user