aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/gadget
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/gadget')
-rw-r--r--drivers/usb/gadget/configfs.c13
-rw-r--r--drivers/usb/gadget/function/f_fs.c9
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c20
-rw-r--r--drivers/usb/gadget/function/f_midi.c4
-rw-r--r--drivers/usb/gadget/function/f_ncm.c79
-rw-r--r--drivers/usb/gadget/function/f_tcm.c4
-rw-r--r--drivers/usb/gadget/function/f_uac1.c83
-rw-r--r--drivers/usb/gadget/function/f_uac2.c6
-rw-r--r--drivers/usb/gadget/function/f_uvc.c74
-rw-r--r--drivers/usb/gadget/function/f_uvc.h2
-rw-r--r--drivers/usb/gadget/function/u_ether.c2
-rw-r--r--drivers/usb/gadget/function/u_ncm.h2
-rw-r--r--drivers/usb/gadget/function/u_uvc.h6
-rw-r--r--drivers/usb/gadget/function/uvc.h14
-rw-r--r--drivers/usb/gadget/function/uvc_configfs.c2
-rw-r--r--drivers/usb/gadget/function/uvc_v4l2.c16
-rw-r--r--drivers/usb/gadget/function/uvc_video.c445
-rw-r--r--drivers/usb/gadget/function/uvc_video.h3
-rw-r--r--drivers/usb/gadget/legacy/webcam.c333
-rw-r--r--drivers/usb/gadget/udc/at91_udc.c13
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.c3
-rw-r--r--drivers/usb/gadget/udc/cdns2/cdns2-debug.h138
-rw-r--r--drivers/usb/gadget/udc/fsl_udc_core.c15
-rw-r--r--drivers/usb/gadget/udc/gr_udc.c13
-rw-r--r--drivers/usb/gadget/udc/lpc32xx_udc.c13
-rw-r--r--drivers/usb/gadget/udc/max3420_udc.c2
-rw-r--r--drivers/usb/gadget/udc/mv_udc_core.c2
-rw-r--r--drivers/usb/gadget/udc/omap_udc.c3
-rw-r--r--drivers/usb/gadget/udc/pch_udc.c1
-rw-r--r--drivers/usb/gadget/udc/pxa25x_udc.c12
30 files changed, 976 insertions, 356 deletions
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index 4c639e9ddedc..ce3cfa1f36f5 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -606,10 +606,11 @@ static struct config_group *function_make(
char *instance_name;
int ret;
- ret = snprintf(buf, MAX_NAME_LEN, "%s", name);
- if (ret >= MAX_NAME_LEN)
+ if (strlen(name) >= MAX_NAME_LEN)
return ERR_PTR(-ENAMETOOLONG);
+ scnprintf(buf, MAX_NAME_LEN, "%s", name);
+
func_name = buf;
instance_name = strchr(func_name, '.');
if (!instance_name) {
@@ -701,10 +702,12 @@ static struct config_group *config_desc_make(
int ret;
gi = container_of(group, struct gadget_info, configs_group);
- ret = snprintf(buf, MAX_NAME_LEN, "%s", name);
- if (ret >= MAX_NAME_LEN)
+
+ if (strlen(name) >= MAX_NAME_LEN)
return ERR_PTR(-ENAMETOOLONG);
+ scnprintf(buf, MAX_NAME_LEN, "%s", name);
+
num_str = strchr(buf, '.');
if (!num_str) {
pr_err("Unable to locate . in name.bConfigurationValue\n");
@@ -812,7 +815,7 @@ static ssize_t gadget_string_s_show(struct config_item *item, char *page)
struct gadget_string *string = to_gadget_string(item);
int ret;
- ret = snprintf(page, sizeof(string->string), "%s\n", string->string);
+ ret = sysfs_emit(page, "%s\n", string->string);
return ret;
}
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index efe3e3b85769..6bff6cb93789 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -831,7 +831,7 @@ static void ffs_user_copy_worker(struct work_struct *work)
io_data->kiocb->ki_complete(io_data->kiocb, ret);
if (io_data->ffs->ffs_eventfd && !kiocb_has_eventfd)
- eventfd_signal(io_data->ffs->ffs_eventfd, 1);
+ eventfd_signal(io_data->ffs->ffs_eventfd);
if (io_data->read)
kfree(io_data->to_free);
@@ -2738,7 +2738,7 @@ static void __ffs_event_add(struct ffs_data *ffs,
ffs->ev.types[ffs->ev.count++] = type;
wake_up_locked(&ffs->ev.waitq);
if (ffs->ffs_eventfd)
- eventfd_signal(ffs->ffs_eventfd, 1);
+ eventfd_signal(ffs->ffs_eventfd);
}
static void ffs_event_add(struct ffs_data *ffs,
@@ -2931,9 +2931,8 @@ static int __ffs_func_bind_do_os_desc(enum ffs_os_desc_type type,
t = &func->function.os_desc_table[desc->bFirstInterfaceNumber];
t->if_id = func->interfaces_nums[desc->bFirstInterfaceNumber];
- memcpy(t->os_desc->ext_compat_id, &desc->CompatibleID,
- ARRAY_SIZE(desc->CompatibleID) +
- ARRAY_SIZE(desc->SubCompatibleID));
+ memcpy(t->os_desc->ext_compat_id, &desc->IDs,
+ sizeof_field(struct usb_ext_compat_desc, IDs));
length = sizeof(*desc);
}
break;
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index 722a3ab2b337..c265a1f62fc1 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -545,21 +545,37 @@ static int start_transfer(struct fsg_dev *fsg, struct usb_ep *ep,
static bool start_in_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
{
+ int rc;
+
if (!fsg_is_set(common))
return false;
bh->state = BUF_STATE_SENDING;
- if (start_transfer(common->fsg, common->fsg->bulk_in, bh->inreq))
+ rc = start_transfer(common->fsg, common->fsg->bulk_in, bh->inreq);
+ if (rc) {
bh->state = BUF_STATE_EMPTY;
+ if (rc == -ESHUTDOWN) {
+ common->running = 0;
+ return false;
+ }
+ }
return true;
}
static bool start_out_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
{
+ int rc;
+
if (!fsg_is_set(common))
return false;
bh->state = BUF_STATE_RECEIVING;
- if (start_transfer(common->fsg, common->fsg->bulk_out, bh->outreq))
+ rc = start_transfer(common->fsg, common->fsg->bulk_out, bh->outreq);
+ if (rc) {
bh->state = BUF_STATE_FULL;
+ if (rc == -ESHUTDOWN) {
+ common->running = 0;
+ return false;
+ }
+ }
return true;
}
diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index 5335845d697b..20c6fbd94f32 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -1177,11 +1177,11 @@ F_MIDI_OPT(out_ports, true, MAX_PORTS);
static ssize_t f_midi_opts_id_show(struct config_item *item, char *page)
{
struct f_midi_opts *opts = to_f_midi_opts(item);
- int result;
+ ssize_t result;
mutex_lock(&opts->lock);
if (opts->id) {
- result = strlcpy(page, opts->id, PAGE_SIZE);
+ result = strscpy(page, opts->id, PAGE_SIZE);
} else {
page[0] = 0;
result = 0;
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index cc0ed29a4adc..28f4e6552e84 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -103,6 +103,16 @@ static inline struct f_ncm *func_to_ncm(struct usb_function *f)
/* Delay for the transmit to wait before sending an unfilled NTB frame. */
#define TX_TIMEOUT_NSECS 300000
+/*
+ * Although max mtu as dictated by u_ether is 15412 bytes, setting
+ * max_segment_size to 15426 would not be efficient. If user chooses segment
+ * size to be (>= 8192), then we can't aggregate more than one buffer in each
+ * NTB (assuming each packet coming from network layer is >= 8192 bytes) as ep
+ * maxpacket limit is 16384. So let max_segment_size be limited to 8000 to allow
+ * at least 2 packets to be aggregated reducing wastage of NTB buffer space
+ */
+#define MAX_DATAGRAM_SIZE 8000
+
#define FORMATS_SUPPORTED (USB_CDC_NCM_NTB16_SUPPORTED | \
USB_CDC_NCM_NTB32_SUPPORTED)
@@ -179,7 +189,6 @@ static struct usb_cdc_ether_desc ecm_desc = {
/* this descriptor actually adds value, surprise! */
/* .iMACAddress = DYNAMIC */
.bmEthernetStatistics = cpu_to_le32(0), /* no statistics */
- .wMaxSegmentSize = cpu_to_le16(ETH_FRAME_LEN),
.wNumberMCFilters = cpu_to_le16(0),
.bNumberPowerFilters = 0,
};
@@ -1166,11 +1175,15 @@ static int ncm_unwrap_ntb(struct gether *port,
struct sk_buff *skb2;
int ret = -EINVAL;
unsigned ntb_max = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize);
- unsigned frame_max = le16_to_cpu(ecm_desc.wMaxSegmentSize);
+ unsigned frame_max;
const struct ndp_parser_opts *opts = ncm->parser_opts;
unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0;
int dgram_counter;
int to_process = skb->len;
+ struct f_ncm_opts *ncm_opts;
+
+ ncm_opts = container_of(port->func.fi, struct f_ncm_opts, func_inst);
+ frame_max = ncm_opts->max_segment_size;
parse_ntb:
tmp = (__le16 *)ntb_ptr;
@@ -1325,7 +1338,15 @@ parse_ntb:
"Parsed NTB with %d frames\n", dgram_counter);
to_process -= block_len;
- if (to_process != 0) {
+
+ /*
+ * Windows NCM driver avoids USB ZLPs by adding a 1-byte
+ * zero pad as needed.
+ */
+ if (to_process == 1 &&
+ (*(unsigned char *)(ntb_ptr + block_len) == 0x00)) {
+ to_process--;
+ } else if ((to_process > 0) && (block_len != 0)) {
ntb_ptr = (unsigned char *)(ntb_ptr + block_len);
goto parse_ntb;
}
@@ -1430,8 +1451,10 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
mutex_lock(&ncm_opts->lock);
gether_set_gadget(ncm_opts->net, cdev->gadget);
- if (!ncm_opts->bound)
+ if (!ncm_opts->bound) {
+ ncm_opts->net->mtu = (ncm_opts->max_segment_size - ETH_HLEN);
status = gether_register_netdev(ncm_opts->net);
+ }
mutex_unlock(&ncm_opts->lock);
if (status)
@@ -1474,6 +1497,8 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
ncm_data_intf.bInterfaceNumber = status;
ncm_union_desc.bSlaveInterface0 = status;
+ ecm_desc.wMaxSegmentSize = cpu_to_le16(ncm_opts->max_segment_size);
+
status = -ENODEV;
/* allocate instance-specific endpoints */
@@ -1576,11 +1601,56 @@ USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ncm);
/* f_ncm_opts_ifname */
USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ncm);
+static ssize_t ncm_opts_max_segment_size_show(struct config_item *item,
+ char *page)
+{
+ struct f_ncm_opts *opts = to_f_ncm_opts(item);
+ u16 segment_size;
+
+ mutex_lock(&opts->lock);
+ segment_size = opts->max_segment_size;
+ mutex_unlock(&opts->lock);
+
+ return sysfs_emit(page, "%u\n", segment_size);
+}
+
+static ssize_t ncm_opts_max_segment_size_store(struct config_item *item,
+ const char *page, size_t len)
+{
+ struct f_ncm_opts *opts = to_f_ncm_opts(item);
+ u16 segment_size;
+ int ret;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ret = kstrtou16(page, 0, &segment_size);
+ if (ret)
+ goto out;
+
+ if (segment_size > MAX_DATAGRAM_SIZE) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ opts->max_segment_size = segment_size;
+ ret = len;
+out:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+CONFIGFS_ATTR(ncm_opts_, max_segment_size);
+
static struct configfs_attribute *ncm_attrs[] = {
&ncm_opts_attr_dev_addr,
&ncm_opts_attr_host_addr,
&ncm_opts_attr_qmult,
&ncm_opts_attr_ifname,
+ &ncm_opts_attr_max_segment_size,
NULL,
};
@@ -1623,6 +1693,7 @@ static struct usb_function_instance *ncm_alloc_inst(void)
kfree(opts);
return ERR_CAST(net);
}
+ opts->max_segment_size = ETH_FRAME_LEN;
INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop);
descs[0] = &opts->ncm_os_desc;
diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c
index ff33f31bcdf6..37befd6db001 100644
--- a/drivers/usb/gadget/function/f_tcm.c
+++ b/drivers/usb/gadget/function/f_tcm.c
@@ -1504,8 +1504,8 @@ static ssize_t tcm_usbg_tpg_nexus_show(struct config_item *item, char *page)
ret = -ENODEV;
goto out;
}
- ret = snprintf(page, PAGE_SIZE, "%s\n",
- tv_nexus->tvn_se_sess->se_node_acl->initiatorname);
+ ret = sysfs_emit(page, "%s\n",
+ tv_nexus->tvn_se_sess->se_node_acl->initiatorname);
out:
mutex_unlock(&tpg->tpg_mutex);
return ret;
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index 6f0e1d803dc2..7de74a3dd392 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -292,6 +292,77 @@ static struct usb_descriptor_header *f_audio_desc[] = {
NULL,
};
+/* Standard ISO OUT Endpoint Descriptor */
+static struct usb_endpoint_descriptor ss_as_out_ep_desc = {
+ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_SYNC_ADAPTIVE
+ | USB_ENDPOINT_XFER_ISOC,
+ .wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE),
+ .bInterval = 4,
+};
+
+static struct usb_ss_ep_comp_descriptor ss_as_out_ep_desc_comp = {
+ .bLength = sizeof(ss_as_out_ep_desc_comp),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ .bMaxBurst = 0,
+ .bmAttributes = 0,
+ /* wBytesPerInterval = DYNAMIC */
+};
+
+/* Standard ISO OUT Endpoint Descriptor */
+static struct usb_endpoint_descriptor ss_as_in_ep_desc = {
+ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_SYNC_ASYNC
+ | USB_ENDPOINT_XFER_ISOC,
+ .wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE),
+ .bInterval = 4,
+};
+
+static struct usb_ss_ep_comp_descriptor ss_as_in_ep_desc_comp = {
+ .bLength = sizeof(ss_as_in_ep_desc_comp),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ .bMaxBurst = 0,
+ .bmAttributes = 0,
+ /* wBytesPerInterval = DYNAMIC */
+};
+
+static struct usb_descriptor_header *f_audio_ss_desc[] = {
+ (struct usb_descriptor_header *)&ac_interface_desc,
+ (struct usb_descriptor_header *)&ac_header_desc,
+
+ (struct usb_descriptor_header *)&usb_out_it_desc,
+ (struct usb_descriptor_header *)&io_out_ot_desc,
+ (struct usb_descriptor_header *)&io_in_it_desc,
+ (struct usb_descriptor_header *)&usb_in_ot_desc,
+
+ (struct usb_descriptor_header *)&as_out_interface_alt_0_desc,
+ (struct usb_descriptor_header *)&as_out_interface_alt_1_desc,
+ (struct usb_descriptor_header *)&as_out_header_desc,
+
+ (struct usb_descriptor_header *)&as_out_type_i_desc,
+
+ //(struct usb_descriptor_header *)&as_out_ep_desc,
+ (struct usb_descriptor_header *)&ss_as_out_ep_desc,
+ (struct usb_descriptor_header *)&ss_as_out_ep_desc_comp,
+ (struct usb_descriptor_header *)&as_iso_out_desc,
+
+ (struct usb_descriptor_header *)&as_in_interface_alt_0_desc,
+ (struct usb_descriptor_header *)&as_in_interface_alt_1_desc,
+ (struct usb_descriptor_header *)&as_in_header_desc,
+
+ (struct usb_descriptor_header *)&as_in_type_i_desc,
+
+ //(struct usb_descriptor_header *)&as_in_ep_desc,
+ (struct usb_descriptor_header *)&ss_as_in_ep_desc,
+ (struct usb_descriptor_header *)&ss_as_in_ep_desc_comp,
+ (struct usb_descriptor_header *)&as_iso_in_desc,
+ NULL,
+};
+
enum {
STR_AC_IF,
STR_USB_OUT_IT,
@@ -1352,6 +1423,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc);
if (!ep)
goto err_free_fu;
+ ss_as_out_ep_desc.bEndpointAddress = as_out_ep_desc.bEndpointAddress;
audio->out_ep = ep;
audio->out_ep->desc = &as_out_ep_desc;
}
@@ -1360,6 +1432,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
ep = usb_ep_autoconfig(cdev->gadget, &as_in_ep_desc);
if (!ep)
goto err_free_fu;
+ ss_as_in_ep_desc.bEndpointAddress = as_in_ep_desc.bEndpointAddress;
audio->in_ep = ep;
audio->in_ep->desc = &as_in_ep_desc;
}
@@ -1367,8 +1440,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
setup_descriptor(audio_opts);
/* copy descriptors, and track endpoint copies */
- status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL,
- NULL);
+ status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, f_audio_ss_desc,
+ f_audio_ss_desc);
if (status)
goto err_free_fu;
@@ -1561,7 +1634,7 @@ static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \
int result; \
\
mutex_lock(&opts->lock); \
- result = snprintf(page, sizeof(opts->name), "%s", opts->name); \
+ result = scnprintf(page, sizeof(opts->name), "%s", opts->name); \
mutex_unlock(&opts->lock); \
\
return result; \
@@ -1579,7 +1652,7 @@ static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \
goto end; \
} \
\
- ret = snprintf(opts->name, min(sizeof(opts->name), len), \
+ ret = scnprintf(opts->name, min(sizeof(opts->name), len), \
"%s", page); \
\
end: \
@@ -1685,7 +1758,7 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
opts->req_number = UAC1_DEF_REQ_NUM;
- snprintf(opts->function_name, sizeof(opts->function_name), "AC Interface");
+ scnprintf(opts->function_name, sizeof(opts->function_name), "AC Interface");
return &opts->func_inst;
}
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index f9a0f07a7476..383f6854cfec 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -2045,7 +2045,7 @@ static ssize_t f_uac2_opts_##name##_show(struct config_item *item, \
int result; \
\
mutex_lock(&opts->lock); \
- result = snprintf(page, sizeof(opts->name), "%s", opts->name); \
+ result = scnprintf(page, sizeof(opts->name), "%s", opts->name); \
mutex_unlock(&opts->lock); \
\
return result; \
@@ -2063,7 +2063,7 @@ static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \
goto end; \
} \
\
- ret = snprintf(opts->name, min(sizeof(opts->name), len), \
+ ret = scnprintf(opts->name, min(sizeof(opts->name), len), \
"%s", page); \
\
end: \
@@ -2187,7 +2187,7 @@ static struct usb_function_instance *afunc_alloc_inst(void)
opts->req_number = UAC2_DEF_REQ_NUM;
opts->fb_max = FBACK_FAST_MAX;
- snprintf(opts->function_name, sizeof(opts->function_name), "Source/Sink");
+ scnprintf(opts->function_name, sizeof(opts->function_name), "Source/Sink");
opts->p_terminal_type = UAC2_DEF_P_TERM_TYPE;
opts->c_terminal_type = UAC2_DEF_C_TERM_TYPE;
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index 786379f1b7b7..929666805bd2 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -263,10 +263,13 @@ uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
return 0;
}
-void uvc_function_setup_continue(struct uvc_device *uvc)
+void uvc_function_setup_continue(struct uvc_device *uvc, int disable_ep)
{
struct usb_composite_dev *cdev = uvc->func.config->cdev;
+ if (disable_ep && uvc->video.ep)
+ usb_ep_disable(uvc->video.ep);
+
usb_composite_setup_continue(cdev);
}
@@ -337,15 +340,11 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
if (uvc->state != UVC_STATE_STREAMING)
return 0;
- if (uvc->video.ep)
- usb_ep_disable(uvc->video.ep);
-
memset(&v4l2_event, 0, sizeof(v4l2_event));
v4l2_event.type = UVC_EVENT_STREAMOFF;
v4l2_event_queue(&uvc->vdev, &v4l2_event);
- uvc->state = UVC_STATE_CONNECTED;
- return 0;
+ return USB_GADGET_DELAYED_STATUS;
case 1:
if (uvc->state != UVC_STATE_CONNECTED)
@@ -722,13 +721,29 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
}
uvc->enable_interrupt_ep = opts->enable_interrupt_ep;
- ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep);
+ /*
+ * gadget_is_{super|dual}speed() API check UDC controller capitblity. It should pass down
+ * highest speed endpoint descriptor to UDC controller. So UDC controller driver can reserve
+ * enough resource at check_config(), especially mult and maxburst. So UDC driver (such as
+ * cdns3) can know need at least (mult + 1) * (maxburst + 1) * wMaxPacketSize internal
+ * memory for this uvc functions. This is the only straightforward method to resolve the UDC
+ * resource allocation issue in the current gadget framework.
+ */
+ if (gadget_is_superspeed(c->cdev->gadget))
+ ep = usb_ep_autoconfig_ss(cdev->gadget, &uvc_ss_streaming_ep,
+ &uvc_ss_streaming_comp);
+ else if (gadget_is_dualspeed(cdev->gadget))
+ ep = usb_ep_autoconfig(cdev->gadget, &uvc_hs_streaming_ep);
+ else
+ ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep);
+
if (!ep) {
uvcg_info(f, "Unable to allocate streaming EP\n");
goto error;
}
uvc->video.ep = ep;
+ uvc_fs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address;
@@ -960,7 +975,8 @@ static void uvc_free(struct usb_function *f)
struct uvc_device *uvc = to_uvc(f);
struct f_uvc_opts *opts = container_of(f->fi, struct f_uvc_opts,
func_inst);
- config_item_put(&uvc->header->item);
+ if (!opts->header)
+ config_item_put(&uvc->header->item);
--opts->refcnt;
kfree(uvc);
}
@@ -1052,25 +1068,29 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
uvc->desc.hs_streaming = opts->hs_streaming;
uvc->desc.ss_streaming = opts->ss_streaming;
- streaming = config_group_find_item(&opts->func_inst.group, "streaming");
- if (!streaming)
- goto err_config;
-
- header = config_group_find_item(to_config_group(streaming), "header");
- config_item_put(streaming);
- if (!header)
- goto err_config;
-
- h = config_group_find_item(to_config_group(header), "h");
- config_item_put(header);
- if (!h)
- goto err_config;
-
- uvc->header = to_uvcg_streaming_header(h);
- if (!uvc->header->linked) {
- mutex_unlock(&opts->lock);
- kfree(uvc);
- return ERR_PTR(-EBUSY);
+ if (opts->header) {
+ uvc->header = opts->header;
+ } else {
+ streaming = config_group_find_item(&opts->func_inst.group, "streaming");
+ if (!streaming)
+ goto err_config;
+
+ header = config_group_find_item(to_config_group(streaming), "header");
+ config_item_put(streaming);
+ if (!header)
+ goto err_config;
+
+ h = config_group_find_item(to_config_group(header), "h");
+ config_item_put(header);
+ if (!h)
+ goto err_config;
+
+ uvc->header = to_uvcg_streaming_header(h);
+ if (!uvc->header->linked) {
+ mutex_unlock(&opts->lock);
+ kfree(uvc);
+ return ERR_PTR(-EBUSY);
+ }
}
uvc->desc.extension_units = &opts->extension_units;
diff --git a/drivers/usb/gadget/function/f_uvc.h b/drivers/usb/gadget/function/f_uvc.h
index 1db972d4beeb..083aef0c65c6 100644
--- a/drivers/usb/gadget/function/f_uvc.h
+++ b/drivers/usb/gadget/function/f_uvc.h
@@ -11,7 +11,7 @@
struct uvc_device;
-void uvc_function_setup_continue(struct uvc_device *uvc);
+void uvc_function_setup_continue(struct uvc_device *uvc, int disable_ep);
void uvc_function_connect(struct uvc_device *uvc);
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index 9d1c40c152d8..3c5a6f6ac341 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -1163,6 +1163,8 @@ struct net_device *gether_connect(struct gether *link)
if (netif_running(dev->net))
eth_start(dev, GFP_ATOMIC);
+ netif_device_attach(dev->net);
+
/* on error, disable any endpoints */
} else {
(void) usb_ep_disable(link->out_ep);
diff --git a/drivers/usb/gadget/function/u_ncm.h b/drivers/usb/gadget/function/u_ncm.h
index 5408854d8407..49ec095cdb4b 100644
--- a/drivers/usb/gadget/function/u_ncm.h
+++ b/drivers/usb/gadget/function/u_ncm.h
@@ -31,6 +31,8 @@ struct f_ncm_opts {
*/
struct mutex lock;
int refcnt;
+
+ u16 max_segment_size;
};
#endif /* U_NCM_H */
diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h
index 1ce58f61253c..3ac392cbb779 100644
--- a/drivers/usb/gadget/function/u_uvc.h
+++ b/drivers/usb/gadget/function/u_uvc.h
@@ -98,6 +98,12 @@ struct f_uvc_opts {
*/
struct mutex lock;
int refcnt;
+
+ /*
+ * Only for legacy gadget. Shall be NULL for configfs-composed gadgets,
+ * which is guaranteed by alloc_inst implementation of f_uvc doing kzalloc.
+ */
+ struct uvcg_streaming_header *header;
};
#endif /* U_UVC_H */
diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h
index 6751de8b63ad..cb35687b11e7 100644
--- a/drivers/usb/gadget/function/uvc.h
+++ b/drivers/usb/gadget/function/uvc.h
@@ -81,6 +81,7 @@ struct uvc_request {
struct sg_table sgt;
u8 header[UVCG_REQUEST_HEADER_LEN];
struct uvc_buffer *last_buf;
+ struct list_head list;
};
struct uvc_video {
@@ -101,9 +102,18 @@ struct uvc_video {
unsigned int uvc_num_requests;
/* Requests */
+ bool is_enabled; /* tracks whether video stream is enabled */
unsigned int req_size;
- struct uvc_request *ureq;
+ struct list_head ureqs; /* all uvc_requests allocated by uvc_video */
+
+ /* USB requests that the video pump thread can encode into */
struct list_head req_free;
+
+ /*
+ * USB requests video pump thread has already encoded into. These are
+ * ready to be queued to the endpoint.
+ */
+ struct list_head req_ready;
spinlock_t req_lock;
unsigned int req_int_count;
@@ -177,7 +187,7 @@ struct uvc_file_handle {
* Functions
*/
-extern void uvc_function_setup_continue(struct uvc_device *uvc);
+extern void uvc_function_setup_continue(struct uvc_device *uvc, int disable_ep);
extern void uvc_function_connect(struct uvc_device *uvc);
extern void uvc_function_disconnect(struct uvc_device *uvc);
diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c
index 9bf0e985acfa..7e704b2bcfd1 100644
--- a/drivers/usb/gadget/function/uvc_configfs.c
+++ b/drivers/usb/gadget/function/uvc_configfs.c
@@ -3414,7 +3414,7 @@ static ssize_t f_uvc_opts_string_##cname##_show(struct config_item *item,\
int result; \
\
mutex_lock(&opts->lock); \
- result = snprintf(page, sizeof(opts->aname), "%s", opts->aname);\
+ result = scnprintf(page, sizeof(opts->aname), "%s", opts->aname);\
mutex_unlock(&opts->lock); \
\
return result; \
diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c
index 3f0a9795c0d4..c7e5fa4f29e0 100644
--- a/drivers/usb/gadget/function/uvc_v4l2.c
+++ b/drivers/usb/gadget/function/uvc_v4l2.c
@@ -443,7 +443,7 @@ uvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
return -EINVAL;
/* Enable UVC video. */
- ret = uvcg_video_enable(video, 1);
+ ret = uvcg_video_enable(video);
if (ret < 0)
return ret;
@@ -451,7 +451,7 @@ uvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
* Complete the alternate setting selection setup phase now that
* userspace is ready to provide video frames.
*/
- uvc_function_setup_continue(uvc);
+ uvc_function_setup_continue(uvc, 0);
uvc->state = UVC_STATE_STREAMING;
return 0;
@@ -463,11 +463,18 @@ uvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
struct uvc_video *video = &uvc->video;
+ int ret = 0;
if (type != video->queue.queue.type)
return -EINVAL;
- return uvcg_video_enable(video, 0);
+ ret = uvcg_video_disable(video);
+ if (ret < 0)
+ return ret;
+
+ uvc->state = UVC_STATE_CONNECTED;
+ uvc_function_setup_continue(uvc, 1);
+ return 0;
}
static int
@@ -500,7 +507,7 @@ uvc_v4l2_subscribe_event(struct v4l2_fh *fh,
static void uvc_v4l2_disable(struct uvc_device *uvc)
{
uvc_function_disconnect(uvc);
- uvcg_video_enable(&uvc->video, 0);
+ uvcg_video_disable(&uvc->video);
uvcg_free_buffers(&uvc->video.queue);
uvc->func_connected = false;
wake_up_interruptible(&uvc->func_connected_queue);
@@ -647,4 +654,3 @@ const struct v4l2_file_operations uvc_v4l2_fops = {
.get_unmapped_area = uvcg_v4l2_get_unmapped_area,
#endif
};
-
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index 91af3b1ef0d4..dd3241fc6939 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -227,6 +227,28 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video,
* Request handling
*/
+/*
+ * Callers must take care to hold req_lock when this function may be called
+ * from multiple threads. For example, when frames are streaming to the host.
+ */
+static void
+uvc_video_free_request(struct uvc_request *ureq, struct usb_ep *ep)
+{
+ sg_free_table(&ureq->sgt);
+ if (ureq->req && ep) {
+ usb_ep_free_request(ep, ureq->req);
+ ureq->req = NULL;
+ }
+
+ kfree(ureq->req_buffer);
+ ureq->req_buffer = NULL;
+
+ if (!list_empty(&ureq->list))
+ list_del_init(&ureq->list);
+
+ kfree(ureq);
+}
+
static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req)
{
int ret;
@@ -247,14 +269,127 @@ static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req)
return ret;
}
+/* This function must be called with video->req_lock held. */
+static int uvcg_video_usb_req_queue(struct uvc_video *video,
+ struct usb_request *req, bool queue_to_ep)
+{
+ bool is_bulk = video->max_payload_size;
+ struct list_head *list = NULL;
+
+ if (!video->is_enabled)
+ return -ENODEV;
+
+ if (queue_to_ep) {
+ struct uvc_request *ureq = req->context;
+ /*
+ * With USB3 handling more requests at a higher speed, we can't
+ * afford to generate an interrupt for every request. Decide to
+ * interrupt:
+ *
+ * - When no more requests are available in the free queue, as
+ * this may be our last chance to refill the endpoint's
+ * request queue.
+ *
+ * - When this is request is the last request for the video
+ * buffer, as we want to start sending the next video buffer
+ * ASAP in case it doesn't get started already in the next
+ * iteration of this loop.
+ *
+ * - Four times over the length of the requests queue (as
+ * indicated by video->uvc_num_requests), as a trade-off
+ * between latency and interrupt load.
+ */
+ if (list_empty(&video->req_free) || ureq->last_buf ||
+ !(video->req_int_count %
+ DIV_ROUND_UP(video->uvc_num_requests, 4))) {
+ video->req_int_count = 0;
+ req->no_interrupt = 0;
+ } else {
+ req->no_interrupt = 1;
+ }
+ video->req_int_count++;
+ return uvcg_video_ep_queue(video, req);
+ }
+ /*
+ * If we're not queuing to the ep, for isoc we're queuing
+ * to the req_ready list, otherwise req_free.
+ */
+ list = is_bulk ? &video->req_free : &video->req_ready;
+ list_add_tail(&req->list, list);
+ return 0;
+}
+
+/*
+ * Must only be called from uvcg_video_enable - since after that we only want to
+ * queue requests to the endpoint from the uvc_video_complete complete handler.
+ * This function is needed in order to 'kick start' the flow of requests from
+ * gadget driver to the usb controller.
+ */
+static void uvc_video_ep_queue_initial_requests(struct uvc_video *video)
+{
+ struct usb_request *req = NULL;
+ unsigned long flags = 0;
+ unsigned int count = 0;
+ int ret = 0;
+
+ /*
+ * We only queue half of the free list since we still want to have
+ * some free usb_requests in the free list for the video_pump async_wq
+ * thread to encode uvc buffers into. Otherwise we could get into a
+ * situation where the free list does not have any usb requests to
+ * encode into - we always end up queueing 0 length requests to the
+ * end point.
+ */
+ unsigned int half_list_size = video->uvc_num_requests / 2;
+
+ spin_lock_irqsave(&video->req_lock, flags);
+ /*
+ * Take these requests off the free list and queue them all to the
+ * endpoint. Since we queue 0 length requests with the req_lock held,
+ * there isn't any 'data' race involved here with the complete handler.
+ */
+ while (count < half_list_size) {
+ req = list_first_entry(&video->req_free, struct usb_request,
+ list);
+ list_del(&req->list);
+ req->length = 0;
+ ret = uvcg_video_ep_queue(video, req);
+ if (ret < 0) {
+ uvcg_queue_cancel(&video->queue, 0);
+ break;
+ }
+ count++;
+ }
+ spin_unlock_irqrestore(&video->req_lock, flags);
+}
+
static void
uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
{
struct uvc_request *ureq = req->context;
struct uvc_video *video = ureq->video;
struct uvc_video_queue *queue = &video->queue;
- struct uvc_device *uvc = video->uvc;
+ struct uvc_buffer *last_buf;
unsigned long flags;
+ bool is_bulk = video->max_payload_size;
+ int ret = 0;
+
+ spin_lock_irqsave(&video->req_lock, flags);
+ if (!video->is_enabled) {
+ /*
+ * When is_enabled is false, uvcg_video_disable() ensures
+ * that in-flight uvc_buffers are returned, so we can
+ * safely call free_request without worrying about
+ * last_buf.
+ */
+ uvc_video_free_request(ureq, ep);
+ spin_unlock_irqrestore(&video->req_lock, flags);
+ return;
+ }
+
+ last_buf = ureq->last_buf;
+ ureq->last_buf = NULL;
+ spin_unlock_irqrestore(&video->req_lock, flags);
switch (req->status) {
case 0:
@@ -277,44 +412,85 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
uvcg_queue_cancel(queue, 0);
}
- if (ureq->last_buf) {
- uvcg_complete_buffer(&video->queue, ureq->last_buf);
- ureq->last_buf = NULL;
+ if (last_buf) {
+ spin_lock_irqsave(&queue->irqlock, flags);
+ uvcg_complete_buffer(queue, last_buf);
+ spin_unlock_irqrestore(&queue->irqlock, flags);
}
spin_lock_irqsave(&video->req_lock, flags);
- list_add_tail(&req->list, &video->req_free);
+ /*
+ * Video stream might have been disabled while we were
+ * processing the current usb_request. So make sure
+ * we're still streaming before queueing the usb_request
+ * back to req_free
+ */
+ if (video->is_enabled) {
+ /*
+ * Here we check whether any request is available in the ready
+ * list. If it is, queue it to the ep and add the current
+ * usb_request to the req_free list - for video_pump to fill in.
+ * Otherwise, just use the current usb_request to queue a 0
+ * length request to the ep. Since we always add to the req_free
+ * list if we dequeue from the ready list, there will never
+ * be a situation where the req_free list is completely out of
+ * requests and cannot recover.
+ */
+ struct usb_request *to_queue = req;
+
+ to_queue->length = 0;
+ if (!list_empty(&video->req_ready)) {
+ to_queue = list_first_entry(&video->req_ready,
+ struct usb_request, list);
+ list_del(&to_queue->list);
+ list_add_tail(&req->list, &video->req_free);
+ /*
+ * Queue work to the wq as well since it is possible that a
+ * buffer may not have been completely encoded with the set of
+ * in-flight usb requests for whih the complete callbacks are
+ * firing.
+ * In that case, if we do not queue work to the worker thread,
+ * the buffer will never be marked as complete - and therefore
+ * not be returned to userpsace. As a result,
+ * dequeue -> queue -> dequeue flow of uvc buffers will not
+ * happen.
+ */
+ queue_work(video->async_wq, &video->pump);
+ }
+ /*
+ * Queue to the endpoint. The actual queueing to ep will
+ * only happen on one thread - the async_wq for bulk endpoints
+ * and this thread for isoc endpoints.
+ */
+ ret = uvcg_video_usb_req_queue(video, to_queue, !is_bulk);
+ if (ret < 0) {
+ /*
+ * Endpoint error, but the stream is still enabled.
+ * Put request back in req_free for it to be cleaned
+ * up later.
+ */
+ list_add_tail(&to_queue->list, &video->req_free);
+ }
+ } else {
+ uvc_video_free_request(ureq, ep);
+ ret = 0;
+ }
spin_unlock_irqrestore(&video->req_lock, flags);
-
- if (uvc->state == UVC_STATE_STREAMING)
- queue_work(video->async_wq, &video->pump);
+ if (ret < 0)
+ uvcg_queue_cancel(queue, 0);
}
static int
uvc_video_free_requests(struct uvc_video *video)
{
- unsigned int i;
-
- if (video->ureq) {
- for (i = 0; i < video->uvc_num_requests; ++i) {
- sg_free_table(&video->ureq[i].sgt);
+ struct uvc_request *ureq, *temp;
- if (video->ureq[i].req) {
- usb_ep_free_request(video->ep, video->ureq[i].req);
- video->ureq[i].req = NULL;
- }
-
- if (video->ureq[i].req_buffer) {
- kfree(video->ureq[i].req_buffer);
- video->ureq[i].req_buffer = NULL;
- }
- }
-
- kfree(video->ureq);
- video->ureq = NULL;
- }
+ list_for_each_entry_safe(ureq, temp, &video->ureqs, list)
+ uvc_video_free_request(ureq, video->ep);
+ INIT_LIST_HEAD(&video->ureqs);
INIT_LIST_HEAD(&video->req_free);
+ INIT_LIST_HEAD(&video->req_ready);
video->req_size = 0;
return 0;
}
@@ -322,6 +498,7 @@ uvc_video_free_requests(struct uvc_video *video)
static int
uvc_video_alloc_requests(struct uvc_video *video)
{
+ struct uvc_request *ureq;
unsigned int req_size;
unsigned int i;
int ret = -ENOMEM;
@@ -332,29 +509,33 @@ uvc_video_alloc_requests(struct uvc_video *video)
* max_t(unsigned int, video->ep->maxburst, 1)
* (video->ep->mult);
- video->ureq = kcalloc(video->uvc_num_requests, sizeof(struct uvc_request), GFP_KERNEL);
- if (video->ureq == NULL)
- return -ENOMEM;
+ for (i = 0; i < video->uvc_num_requests; i++) {
+ ureq = kzalloc(sizeof(struct uvc_request), GFP_KERNEL);
+ if (ureq == NULL)
+ goto error;
+
+ INIT_LIST_HEAD(&ureq->list);
- for (i = 0; i < video->uvc_num_requests; ++i) {
- video->ureq[i].req_buffer = kmalloc(req_size, GFP_KERNEL);
- if (video->ureq[i].req_buffer == NULL)
+ list_add_tail(&ureq->list, &video->ureqs);
+
+ ureq->req_buffer = kmalloc(req_size, GFP_KERNEL);
+ if (ureq->req_buffer == NULL)
goto error;
- video->ureq[i].req = usb_ep_alloc_request(video->ep, GFP_KERNEL);
- if (video->ureq[i].req == NULL)
+ ureq->req = usb_ep_alloc_request(video->ep, GFP_KERNEL);
+ if (ureq->req == NULL)
goto error;
- video->ureq[i].req->buf = video->ureq[i].req_buffer;
- video->ureq[i].req->length = 0;
- video->ureq[i].req->complete = uvc_video_complete;
- video->ureq[i].req->context = &video->ureq[i];
- video->ureq[i].video = video;
- video->ureq[i].last_buf = NULL;
+ ureq->req->buf = ureq->req_buffer;
+ ureq->req->length = 0;
+ ureq->req->complete = uvc_video_complete;
+ ureq->req->context = ureq;
+ ureq->video = video;
+ ureq->last_buf = NULL;
- list_add_tail(&video->ureq[i].req->list, &video->req_free);
+ list_add_tail(&ureq->req->list, &video->req_free);
/* req_size/PAGE_SIZE + 1 for overruns and + 1 for header */
- sg_alloc_table(&video->ureq[i].sgt,
+ sg_alloc_table(&ureq->sgt,
DIV_ROUND_UP(req_size - UVCG_REQUEST_HEADER_LEN,
PAGE_SIZE) + 2, GFP_KERNEL);
}
@@ -387,16 +568,18 @@ static void uvcg_video_pump(struct work_struct *work)
struct usb_request *req = NULL;
struct uvc_buffer *buf;
unsigned long flags;
- bool buf_done;
- int ret;
+ int ret = 0;
+
+ while (true) {
+ if (!video->ep->enabled)
+ return;
- while (video->ep->enabled) {
/*
- * Retrieve the first available USB request, protected by the
- * request lock.
+ * Check is_enabled and retrieve the first available USB
+ * request, protected by the request lock.
*/
spin_lock_irqsave(&video->req_lock, flags);
- if (list_empty(&video->req_free)) {
+ if (!video->is_enabled || list_empty(&video->req_free)) {
spin_unlock_irqrestore(&video->req_lock, flags);
return;
}
@@ -414,15 +597,6 @@ static void uvcg_video_pump(struct work_struct *work)
if (buf != NULL) {
video->encode(req, video, buf);
- buf_done = buf->state == UVC_BUF_STATE_DONE;
- } else if (!(queue->flags & UVC_QUEUE_DISCONNECTED) && !is_bulk) {
- /*
- * No video buffer available; the queue is still connected and
- * we're transferring over ISOC. Queue a 0 length request to
- * prevent missed ISOC transfers.
- */
- req->length = 0;
- buf_done = false;
} else {
/*
* Either the queue has been disconnected or no video buffer
@@ -433,83 +607,139 @@ static void uvcg_video_pump(struct work_struct *work)
break;
}
- /*
- * With USB3 handling more requests at a higher speed, we can't
- * afford to generate an interrupt for every request. Decide to
- * interrupt:
- *
- * - When no more requests are available in the free queue, as
- * this may be our last chance to refill the endpoint's
- * request queue.
- *
- * - When this is request is the last request for the video
- * buffer, as we want to start sending the next video buffer
- * ASAP in case it doesn't get started already in the next
- * iteration of this loop.
- *
- * - Four times over the length of the requests queue (as
- * indicated by video->uvc_num_requests), as a trade-off
- * between latency and interrupt load.
- */
- if (list_empty(&video->req_free) || buf_done ||
- !(video->req_int_count %
- DIV_ROUND_UP(video->uvc_num_requests, 4))) {
- video->req_int_count = 0;
- req->no_interrupt = 0;
- } else {
- req->no_interrupt = 1;
- }
-
- /* Queue the USB request */
- ret = uvcg_video_ep_queue(video, req);
spin_unlock_irqrestore(&queue->irqlock, flags);
+ spin_lock_irqsave(&video->req_lock, flags);
+ /* For bulk end points we queue from the worker thread
+ * since we would preferably not want to wait on requests
+ * to be ready, in the uvcg_video_complete() handler.
+ * For isoc endpoints we add the request to the ready list
+ * and only queue it to the endpoint from the complete handler.
+ */
+ ret = uvcg_video_usb_req_queue(video, req, is_bulk);
+ spin_unlock_irqrestore(&video->req_lock, flags);
+
if (ret < 0) {
uvcg_queue_cancel(queue, 0);
break;
}
- /* Endpoint now owns the request */
+ /* The request is owned by the endpoint / ready list. */
req = NULL;
- video->req_int_count++;
}
if (!req)
return;
spin_lock_irqsave(&video->req_lock, flags);
- list_add_tail(&req->list, &video->req_free);
+ if (video->is_enabled)
+ list_add_tail(&req->list, &video->req_free);
+ else
+ uvc_video_free_request(req->context, video->ep);
spin_unlock_irqrestore(&video->req_lock, flags);
- return;
}
/*
- * Enable or disable the video stream.
+ * Disable the video stream
*/
-int uvcg_video_enable(struct uvc_video *video, int enable)
+int
+uvcg_video_disable(struct uvc_video *video)
{
- unsigned int i;
- int ret;
+ unsigned long flags;
+ struct list_head inflight_bufs;
+ struct usb_request *req, *temp;
+ struct uvc_buffer *buf, *btemp;
+ struct uvc_request *ureq, *utemp;
if (video->ep == NULL) {
uvcg_info(&video->uvc->func,
- "Video enable failed, device is uninitialized.\n");
+ "Video disable failed, device is uninitialized.\n");
return -ENODEV;
}
- if (!enable) {
- cancel_work_sync(&video->pump);
- uvcg_queue_cancel(&video->queue, 0);
+ INIT_LIST_HEAD(&inflight_bufs);
+ spin_lock_irqsave(&video->req_lock, flags);
+ video->is_enabled = false;
+
+ /*
+ * Remove any in-flight buffers from the uvc_requests
+ * because we want to return them before cancelling the
+ * queue. This ensures that we aren't stuck waiting for
+ * all complete callbacks to come through before disabling
+ * vb2 queue.
+ */
+ list_for_each_entry(ureq, &video->ureqs, list) {
+ if (ureq->last_buf) {
+ list_add_tail(&ureq->last_buf->queue, &inflight_bufs);
+ ureq->last_buf = NULL;
+ }
+ }
+ spin_unlock_irqrestore(&video->req_lock, flags);
- for (i = 0; i < video->uvc_num_requests; ++i)
- if (video->ureq && video->ureq[i].req)
- usb_ep_dequeue(video->ep, video->ureq[i].req);
+ cancel_work_sync(&video->pump);
+ uvcg_queue_cancel(&video->queue, 0);
+
+ spin_lock_irqsave(&video->req_lock, flags);
+ /*
+ * Remove all uvc_requests from ureqs with list_del_init
+ * This lets uvc_video_free_request correctly identify
+ * if the uvc_request is attached to a list or not when freeing
+ * memory.
+ */
+ list_for_each_entry_safe(ureq, utemp, &video->ureqs, list)
+ list_del_init(&ureq->list);
+
+ list_for_each_entry_safe(req, temp, &video->req_free, list) {
+ list_del(&req->list);
+ uvc_video_free_request(req->context, video->ep);
+ }
- uvc_video_free_requests(video);
- uvcg_queue_enable(&video->queue, 0);
- return 0;
+ list_for_each_entry_safe(req, temp, &video->req_ready, list) {
+ list_del(&req->list);
+ uvc_video_free_request(req->context, video->ep);
}
+ INIT_LIST_HEAD(&video->ureqs);
+ INIT_LIST_HEAD(&video->req_free);
+ INIT_LIST_HEAD(&video->req_ready);
+ video->req_size = 0;
+ spin_unlock_irqrestore(&video->req_lock, flags);
+
+ /*
+ * Return all the video buffers before disabling the queue.
+ */
+ spin_lock_irqsave(&video->queue.irqlock, flags);
+ list_for_each_entry_safe(buf, btemp, &inflight_bufs, queue) {
+ list_del(&buf->queue);
+ uvcg_complete_buffer(&video->queue, buf);
+ }
+ spin_unlock_irqrestore(&video->queue.irqlock, flags);
+
+ uvcg_queue_enable(&video->queue, 0);
+ return 0;
+}
+
+/*
+ * Enable the video stream.
+ */
+int uvcg_video_enable(struct uvc_video *video)
+{
+ int ret;
+
+ if (video->ep == NULL) {
+ uvcg_info(&video->uvc->func,
+ "Video enable failed, device is uninitialized.\n");
+ return -ENODEV;
+ }
+
+ /*
+ * Safe to access request related fields without req_lock because
+ * this is the only thread currently active, and no other
+ * request handling thread will become active until this function
+ * returns.
+ */
+ video->is_enabled = true;
+
if ((ret = uvcg_queue_enable(&video->queue, 1)) < 0)
return ret;
@@ -525,7 +755,7 @@ int uvcg_video_enable(struct uvc_video *video, int enable)
video->req_int_count = 0;
- queue_work(video->async_wq, &video->pump);
+ uvc_video_ep_queue_initial_requests(video);
return ret;
}
@@ -535,7 +765,10 @@ int uvcg_video_enable(struct uvc_video *video, int enable)
*/
int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc)
{
+ video->is_enabled = false;
+ INIT_LIST_HEAD(&video->ureqs);
INIT_LIST_HEAD(&video->req_free);
+ INIT_LIST_HEAD(&video->req_ready);
spin_lock_init(&video->req_lock);
INIT_WORK(&video->pump, uvcg_video_pump);
diff --git a/drivers/usb/gadget/function/uvc_video.h b/drivers/usb/gadget/function/uvc_video.h
index 03adeefa343b..8ef6259741f1 100644
--- a/drivers/usb/gadget/function/uvc_video.h
+++ b/drivers/usb/gadget/function/uvc_video.h
@@ -14,7 +14,8 @@
struct uvc_video;
-int uvcg_video_enable(struct uvc_video *video, int enable);
+int uvcg_video_enable(struct uvc_video *video);
+int uvcg_video_disable(struct uvc_video *video);
int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc);
diff --git a/drivers/usb/gadget/legacy/webcam.c b/drivers/usb/gadget/legacy/webcam.c
index c06dd1af7a0c..c395438d3978 100644
--- a/drivers/usb/gadget/legacy/webcam.c
+++ b/drivers/usb/gadget/legacy/webcam.c
@@ -12,6 +12,7 @@
#include <linux/usb/video.h>
#include "u_uvc.h"
+#include "uvc_configfs.h"
USB_GADGET_COMPOSITE_OPTIONS();
@@ -84,8 +85,6 @@ static struct usb_device_descriptor webcam_device_descriptor = {
.bNumConfigurations = 0, /* dynamic */
};
-DECLARE_UVC_HEADER_DESCRIPTOR(1);
-
static const struct UVC_HEADER_DESCRIPTOR(1) uvc_control_header = {
.bLength = UVC_DT_HEADER_SIZE(1),
.bDescriptorType = USB_DT_CS_INTERFACE,
@@ -158,43 +157,112 @@ static const struct UVC_INPUT_HEADER_DESCRIPTOR(1, 2) uvc_input_header = {
.bmaControls[1][0] = 4,
};
-static const struct uvc_format_uncompressed uvc_format_yuv = {
- .bLength = UVC_DT_FORMAT_UNCOMPRESSED_SIZE,
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubType = UVC_VS_FORMAT_UNCOMPRESSED,
- .bFormatIndex = 1,
- .bNumFrameDescriptors = 2,
- .guidFormat =
- { 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00,
- 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71},
- .bBitsPerPixel = 16,
- .bDefaultFrameIndex = 1,
- .bAspectRatioX = 0,
- .bAspectRatioY = 0,
- .bmInterlaceFlags = 0,
- .bCopyProtect = 0,
+static const struct uvcg_color_matching uvcg_color_matching = {
+ .desc = {
+ .bLength = UVC_DT_COLOR_MATCHING_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = UVC_VS_COLORFORMAT,
+ .bColorPrimaries = 1,
+ .bTransferCharacteristics = 1,
+ .bMatrixCoefficients = 4,
+ },
+};
+
+static struct uvcg_uncompressed uvcg_format_yuv = {
+ .fmt = {
+ .type = UVCG_UNCOMPRESSED,
+ /* add to .frames and fill .num_frames at runtime */
+ .color_matching = (struct uvcg_color_matching *)&uvcg_color_matching,
+ },
+ .desc = {
+ .bLength = UVC_DT_FORMAT_UNCOMPRESSED_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = UVC_VS_FORMAT_UNCOMPRESSED,
+ .bFormatIndex = 1,
+ .bNumFrameDescriptors = 2,
+ .guidFormat = {
+ 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
+ },
+ .bBitsPerPixel = 16,
+ .bDefaultFrameIndex = 1,
+ .bAspectRatioX = 0,
+ .bAspectRatioY = 0,
+ .bmInterlaceFlags = 0,
+ .bCopyProtect = 0,
+ },
+};
+
+static struct uvcg_format_ptr uvcg_format_ptr_yuv = {
+ .fmt = &uvcg_format_yuv.fmt,
};
DECLARE_UVC_FRAME_UNCOMPRESSED(1);
DECLARE_UVC_FRAME_UNCOMPRESSED(3);
+#define UVCG_WIDTH_360P 640
+#define UVCG_HEIGHT_360P 360
+#define UVCG_MIN_BITRATE_360P 18432000
+#define UVCG_MAX_BITRATE_360P 55296000
+#define UVCG_MAX_VIDEO_FB_SZ_360P 460800
+#define UVCG_FRM_INTERV_0_360P 666666
+#define UVCG_FRM_INTERV_1_360P 1000000
+#define UVCG_FRM_INTERV_2_360P 5000000
+#define UVCG_DEFAULT_FRM_INTERV_360P UVCG_FRM_INTERV_0_360P
+
static const struct UVC_FRAME_UNCOMPRESSED(3) uvc_frame_yuv_360p = {
.bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(3),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED,
.bFrameIndex = 1,
.bmCapabilities = 0,
- .wWidth = cpu_to_le16(640),
- .wHeight = cpu_to_le16(360),
- .dwMinBitRate = cpu_to_le32(18432000),
- .dwMaxBitRate = cpu_to_le32(55296000),
- .dwMaxVideoFrameBufferSize = cpu_to_le32(460800),
- .dwDefaultFrameInterval = cpu_to_le32(666666),
+ .wWidth = cpu_to_le16(UVCG_WIDTH_360P),
+ .wHeight = cpu_to_le16(UVCG_HEIGHT_360P),
+ .dwMinBitRate = cpu_to_le32(UVCG_MIN_BITRATE_360P),
+ .dwMaxBitRate = cpu_to_le32(UVCG_MAX_BITRATE_360P),
+ .dwMaxVideoFrameBufferSize = cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_360P),
+ .dwDefaultFrameInterval = cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_360P),
.bFrameIntervalType = 3,
- .dwFrameInterval[0] = cpu_to_le32(666666),
- .dwFrameInterval[1] = cpu_to_le32(1000000),
- .dwFrameInterval[2] = cpu_to_le32(5000000),
+ .dwFrameInterval[0] = cpu_to_le32(UVCG_FRM_INTERV_0_360P),
+ .dwFrameInterval[1] = cpu_to_le32(UVCG_FRM_INTERV_1_360P),
+ .dwFrameInterval[2] = cpu_to_le32(UVCG_FRM_INTERV_2_360P),
+};
+
+static u32 uvcg_frame_yuv_360p_dw_frame_interval[] = {
+ [0] = UVCG_FRM_INTERV_0_360P,
+ [1] = UVCG_FRM_INTERV_1_360P,
+ [2] = UVCG_FRM_INTERV_2_360P,
+};
+
+static const struct uvcg_frame uvcg_frame_yuv_360p = {
+ .fmt_type = UVCG_UNCOMPRESSED,
+ .frame = {
+ .b_length = UVC_DT_FRAME_UNCOMPRESSED_SIZE(3),
+ .b_descriptor_type = USB_DT_CS_INTERFACE,
+ .b_descriptor_subtype = UVC_VS_FRAME_UNCOMPRESSED,
+ .b_frame_index = 1,
+ .bm_capabilities = 0,
+ .w_width = UVCG_WIDTH_360P,
+ .w_height = UVCG_HEIGHT_360P,
+ .dw_min_bit_rate = UVCG_MIN_BITRATE_360P,
+ .dw_max_bit_rate = UVCG_MAX_BITRATE_360P,
+ .dw_max_video_frame_buffer_size = UVCG_MAX_VIDEO_FB_SZ_360P,
+ .dw_default_frame_interval = UVCG_DEFAULT_FRM_INTERV_360P,
+ .b_frame_interval_type = 3,
+ },
+ .dw_frame_interval = uvcg_frame_yuv_360p_dw_frame_interval,
+};
+
+static struct uvcg_frame_ptr uvcg_frame_ptr_yuv_360p = {
+ .frm = (struct uvcg_frame *)&uvcg_frame_yuv_360p,
};
+#define UVCG_WIDTH_720P 1280
+#define UVCG_HEIGHT_720P 720
+#define UVCG_MIN_BITRATE_720P 29491200
+#define UVCG_MAX_BITRATE_720P 29491200
+#define UVCG_MAX_VIDEO_FB_SZ_720P 1843200
+#define UVCG_FRM_INTERV_0_720P 5000000
+#define UVCG_DEFAULT_FRM_INTERV_720P UVCG_FRM_INTERV_0_720P
static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_720p = {
.bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1),
@@ -202,28 +270,66 @@ static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_720p = {
.bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED,
.bFrameIndex = 2,
.bmCapabilities = 0,
- .wWidth = cpu_to_le16(1280),
- .wHeight = cpu_to_le16(720),
- .dwMinBitRate = cpu_to_le32(29491200),
- .dwMaxBitRate = cpu_to_le32(29491200),
- .dwMaxVideoFrameBufferSize = cpu_to_le32(1843200),
- .dwDefaultFrameInterval = cpu_to_le32(5000000),
+ .wWidth = cpu_to_le16(UVCG_WIDTH_720P),
+ .wHeight = cpu_to_le16(UVCG_HEIGHT_720P),
+ .dwMinBitRate = cpu_to_le32(UVCG_MIN_BITRATE_720P),
+ .dwMaxBitRate = cpu_to_le32(UVCG_MAX_BITRATE_720P),
+ .dwMaxVideoFrameBufferSize = cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_720P),
+ .dwDefaultFrameInterval = cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_720P),
.bFrameIntervalType = 1,
- .dwFrameInterval[0] = cpu_to_le32(5000000),
+ .dwFrameInterval[0] = cpu_to_le32(UVCG_FRM_INTERV_0_720P),
};
-static const struct uvc_format_mjpeg uvc_format_mjpg = {
- .bLength = UVC_DT_FORMAT_MJPEG_SIZE,
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubType = UVC_VS_FORMAT_MJPEG,
- .bFormatIndex = 2,
- .bNumFrameDescriptors = 2,
- .bmFlags = 0,
- .bDefaultFrameIndex = 1,
- .bAspectRatioX = 0,
- .bAspectRatioY = 0,
- .bmInterlaceFlags = 0,
- .bCopyProtect = 0,
+static u32 uvcg_frame_yuv_720p_dw_frame_interval[] = {
+ [0] = UVCG_FRM_INTERV_0_720P,
+};
+
+static const struct uvcg_frame uvcg_frame_yuv_720p = {
+ .fmt_type = UVCG_UNCOMPRESSED,
+ .frame = {
+ .b_length = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1),
+ .b_descriptor_type = USB_DT_CS_INTERFACE,
+ .b_descriptor_subtype = UVC_VS_FRAME_UNCOMPRESSED,
+ .b_frame_index = 2,
+ .bm_capabilities = 0,
+ .w_width = UVCG_WIDTH_720P,
+ .w_height = UVCG_HEIGHT_720P,
+ .dw_min_bit_rate = UVCG_MIN_BITRATE_720P,
+ .dw_max_bit_rate = UVCG_MAX_BITRATE_720P,
+ .dw_max_video_frame_buffer_size = UVCG_MAX_VIDEO_FB_SZ_720P,
+ .dw_default_frame_interval = UVCG_DEFAULT_FRM_INTERV_720P,
+ .b_frame_interval_type = 1,
+ },
+ .dw_frame_interval = uvcg_frame_yuv_720p_dw_frame_interval,
+};
+
+static struct uvcg_frame_ptr uvcg_frame_ptr_yuv_720p = {
+ .frm = (struct uvcg_frame *)&uvcg_frame_yuv_720p,
+};
+
+static struct uvcg_mjpeg uvcg_format_mjpeg = {
+ .fmt = {
+ .type = UVCG_MJPEG,
+ /* add to .frames and fill .num_frames at runtime */
+ .color_matching = (struct uvcg_color_matching *)&uvcg_color_matching,
+ },
+ .desc = {
+ .bLength = UVC_DT_FORMAT_MJPEG_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = UVC_VS_FORMAT_MJPEG,
+ .bFormatIndex = 2,
+ .bNumFrameDescriptors = 2,
+ .bmFlags = 0,
+ .bDefaultFrameIndex = 1,
+ .bAspectRatioX = 0,
+ .bAspectRatioY = 0,
+ .bmInterlaceFlags = 0,
+ .bCopyProtect = 0,
+ },
+};
+
+static struct uvcg_format_ptr uvcg_format_ptr_mjpeg = {
+ .fmt = &uvcg_format_mjpeg.fmt,
};
DECLARE_UVC_FRAME_MJPEG(1);
@@ -235,16 +341,45 @@ static const struct UVC_FRAME_MJPEG(3) uvc_frame_mjpg_360p = {
.bDescriptorSubType = UVC_VS_FRAME_MJPEG,
.bFrameIndex = 1,
.bmCapabilities = 0,
- .wWidth = cpu_to_le16(640),
- .wHeight = cpu_to_le16(360),
- .dwMinBitRate = cpu_to_le32(18432000),
- .dwMaxBitRate = cpu_to_le32(55296000),
- .dwMaxVideoFrameBufferSize = cpu_to_le32(460800),
- .dwDefaultFrameInterval = cpu_to_le32(666666),
+ .wWidth = cpu_to_le16(UVCG_WIDTH_360P),
+ .wHeight = cpu_to_le16(UVCG_HEIGHT_360P),
+ .dwMinBitRate = cpu_to_le32(UVCG_MIN_BITRATE_360P),
+ .dwMaxBitRate = cpu_to_le32(UVCG_MAX_BITRATE_360P),
+ .dwMaxVideoFrameBufferSize = cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_360P),
+ .dwDefaultFrameInterval = cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_360P),
.bFrameIntervalType = 3,
- .dwFrameInterval[0] = cpu_to_le32(666666),
- .dwFrameInterval[1] = cpu_to_le32(1000000),
- .dwFrameInterval[2] = cpu_to_le32(5000000),
+ .dwFrameInterval[0] = cpu_to_le32(UVCG_FRM_INTERV_0_360P),
+ .dwFrameInterval[1] = cpu_to_le32(UVCG_FRM_INTERV_1_360P),
+ .dwFrameInterval[2] = cpu_to_le32(UVCG_FRM_INTERV_2_360P),
+};
+
+static u32 uvcg_frame_mjpeg_360p_dw_frame_interval[] = {
+ [0] = UVCG_FRM_INTERV_0_360P,
+ [1] = UVCG_FRM_INTERV_1_360P,
+ [2] = UVCG_FRM_INTERV_2_360P,
+};
+
+static const struct uvcg_frame uvcg_frame_mjpeg_360p = {
+ .fmt_type = UVCG_MJPEG,
+ .frame = {
+ .b_length = UVC_DT_FRAME_MJPEG_SIZE(3),
+ .b_descriptor_type = USB_DT_CS_INTERFACE,
+ .b_descriptor_subtype = UVC_VS_FRAME_MJPEG,
+ .b_frame_index = 1,
+ .bm_capabilities = 0,
+ .w_width = UVCG_WIDTH_360P,
+ .w_height = UVCG_HEIGHT_360P,
+ .dw_min_bit_rate = UVCG_MIN_BITRATE_360P,
+ .dw_max_bit_rate = UVCG_MAX_BITRATE_360P,
+ .dw_max_video_frame_buffer_size = UVCG_MAX_VIDEO_FB_SZ_360P,
+ .dw_default_frame_interval = UVCG_DEFAULT_FRM_INTERV_360P,
+ .b_frame_interval_type = 3,
+ },
+ .dw_frame_interval = uvcg_frame_mjpeg_360p_dw_frame_interval,
+};
+
+static struct uvcg_frame_ptr uvcg_frame_ptr_mjpeg_360p = {
+ .frm = (struct uvcg_frame *)&uvcg_frame_mjpeg_360p,
};
static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_720p = {
@@ -253,23 +388,44 @@ static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_720p = {
.bDescriptorSubType = UVC_VS_FRAME_MJPEG,
.bFrameIndex = 2,
.bmCapabilities = 0,
- .wWidth = cpu_to_le16(1280),
- .wHeight = cpu_to_le16(720),
- .dwMinBitRate = cpu_to_le32(29491200),
- .dwMaxBitRate = cpu_to_le32(29491200),
- .dwMaxVideoFrameBufferSize = cpu_to_le32(1843200),
- .dwDefaultFrameInterval = cpu_to_le32(5000000),
+ .wWidth = cpu_to_le16(UVCG_WIDTH_720P),
+ .wHeight = cpu_to_le16(UVCG_HEIGHT_720P),
+ .dwMinBitRate = cpu_to_le32(UVCG_MIN_BITRATE_720P),
+ .dwMaxBitRate = cpu_to_le32(UVCG_MAX_BITRATE_720P),
+ .dwMaxVideoFrameBufferSize = cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_720P),
+ .dwDefaultFrameInterval = cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_720P),
.bFrameIntervalType = 1,
- .dwFrameInterval[0] = cpu_to_le32(5000000),
+ .dwFrameInterval[0] = cpu_to_le32(UVCG_FRM_INTERV_0_720P),
};
-static const struct uvc_color_matching_descriptor uvc_color_matching = {
- .bLength = UVC_DT_COLOR_MATCHING_SIZE,
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubType = UVC_VS_COLORFORMAT,
- .bColorPrimaries = 1,
- .bTransferCharacteristics = 1,
- .bMatrixCoefficients = 4,
+static u32 uvcg_frame_mjpeg_720p_dw_frame_interval[] = {
+ [0] = UVCG_FRM_INTERV_0_720P,
+};
+
+static const struct uvcg_frame uvcg_frame_mjpeg_720p = {
+ .fmt_type = UVCG_MJPEG,
+ .frame = {
+ .b_length = UVC_DT_FRAME_MJPEG_SIZE(1),
+ .b_descriptor_type = USB_DT_CS_INTERFACE,
+ .b_descriptor_subtype = UVC_VS_FRAME_MJPEG,
+ .b_frame_index = 2,
+ .bm_capabilities = 0,
+ .w_width = UVCG_WIDTH_720P,
+ .w_height = UVCG_HEIGHT_720P,
+ .dw_min_bit_rate = UVCG_MIN_BITRATE_720P,
+ .dw_max_bit_rate = UVCG_MAX_BITRATE_720P,
+ .dw_max_video_frame_buffer_size = UVCG_MAX_VIDEO_FB_SZ_720P,
+ .dw_default_frame_interval = UVCG_DEFAULT_FRM_INTERV_720P,
+ .b_frame_interval_type = 1,
+ },
+ .dw_frame_interval = uvcg_frame_mjpeg_720p_dw_frame_interval,
+};
+
+static struct uvcg_frame_ptr uvcg_frame_ptr_mjpeg_720p = {
+ .frm = (struct uvcg_frame *)&uvcg_frame_mjpeg_720p,
+};
+
+static struct uvcg_streaming_header uvcg_streaming_header = {
};
static const struct uvc_descriptor_header * const uvc_fs_control_cls[] = {
@@ -290,40 +446,40 @@ static const struct uvc_descriptor_header * const uvc_ss_control_cls[] = {
static const struct uvc_descriptor_header * const uvc_fs_streaming_cls[] = {
(const struct uvc_descriptor_header *) &uvc_input_header,
- (const struct uvc_descriptor_header *) &uvc_format_yuv,
+ (const struct uvc_descriptor_header *) &uvcg_format_yuv.desc,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
- (const struct uvc_descriptor_header *) &uvc_color_matching,
- (const struct uvc_descriptor_header *) &uvc_format_mjpg,
+ (const struct uvc_descriptor_header *) &uvcg_color_matching.desc,
+ (const struct uvc_descriptor_header *) &uvcg_format_mjpeg.desc,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
- (const struct uvc_descriptor_header *) &uvc_color_matching,
+ (const struct uvc_descriptor_header *) &uvcg_color_matching.desc,
NULL,
};
static const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = {
(const struct uvc_descriptor_header *) &uvc_input_header,
- (const struct uvc_descriptor_header *) &uvc_format_yuv,
+ (const struct uvc_descriptor_header *) &uvcg_format_yuv.desc,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
- (const struct uvc_descriptor_header *) &uvc_color_matching,
- (const struct uvc_descriptor_header *) &uvc_format_mjpg,
+ (const struct uvc_descriptor_header *) &uvcg_color_matching.desc,
+ (const struct uvc_descriptor_header *) &uvcg_format_mjpeg.desc,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
- (const struct uvc_descriptor_header *) &uvc_color_matching,
+ (const struct uvc_descriptor_header *) &uvcg_color_matching.desc,
NULL,
};
static const struct uvc_descriptor_header * const uvc_ss_streaming_cls[] = {
(const struct uvc_descriptor_header *) &uvc_input_header,
- (const struct uvc_descriptor_header *) &uvc_format_yuv,
+ (const struct uvc_descriptor_header *) &uvcg_format_yuv.desc,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
- (const struct uvc_descriptor_header *) &uvc_color_matching,
- (const struct uvc_descriptor_header *) &uvc_format_mjpg,
+ (const struct uvc_descriptor_header *) &uvcg_color_matching.desc,
+ (const struct uvc_descriptor_header *) &uvcg_format_mjpeg.desc,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
- (const struct uvc_descriptor_header *) &uvc_color_matching,
+ (const struct uvc_descriptor_header *) &uvcg_color_matching.desc,
NULL,
};
@@ -387,6 +543,23 @@ webcam_bind(struct usb_composite_dev *cdev)
uvc_opts->hs_streaming = uvc_hs_streaming_cls;
uvc_opts->ss_streaming = uvc_ss_streaming_cls;
+ INIT_LIST_HEAD(&uvcg_format_yuv.fmt.frames);
+ list_add_tail(&uvcg_frame_ptr_yuv_360p.entry, &uvcg_format_yuv.fmt.frames);
+ list_add_tail(&uvcg_frame_ptr_yuv_720p.entry, &uvcg_format_yuv.fmt.frames);
+ uvcg_format_yuv.fmt.num_frames = 2;
+
+ INIT_LIST_HEAD(&uvcg_format_mjpeg.fmt.frames);
+ list_add_tail(&uvcg_frame_ptr_mjpeg_360p.entry, &uvcg_format_mjpeg.fmt.frames);
+ list_add_tail(&uvcg_frame_ptr_mjpeg_720p.entry, &uvcg_format_mjpeg.fmt.frames);
+ uvcg_format_mjpeg.fmt.num_frames = 2;
+
+ INIT_LIST_HEAD(&uvcg_streaming_header.formats);
+ list_add_tail(&uvcg_format_ptr_yuv.entry, &uvcg_streaming_header.formats);
+ list_add_tail(&uvcg_format_ptr_mjpeg.entry, &uvcg_streaming_header.formats);
+ uvcg_streaming_header.num_fmt = 2;
+
+ uvc_opts->header = &uvcg_streaming_header;
+
/* Allocate string descriptor numbers ... note that string contents
* can be overridden by the composite_dev glue.
*/
diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c
index 30ea4a9d5301..e3bf17a98b38 100644
--- a/drivers/usb/gadget/udc/at91_udc.c
+++ b/drivers/usb/gadget/udc/at91_udc.c
@@ -1924,7 +1924,7 @@ err_unprepare_fclk:
return retval;
}
-static int at91udc_remove(struct platform_device *pdev)
+static void at91udc_remove(struct platform_device *pdev)
{
struct at91_udc *udc = platform_get_drvdata(pdev);
unsigned long flags;
@@ -1932,8 +1932,11 @@ static int at91udc_remove(struct platform_device *pdev)
DBG("remove\n");
usb_del_gadget_udc(&udc->gadget);
- if (udc->driver)
- return -EBUSY;
+ if (udc->driver) {
+ dev_err(&pdev->dev,
+ "Driver still in use but removing anyhow\n");
+ return;
+ }
spin_lock_irqsave(&udc->lock, flags);
pullup(udc, 0);
@@ -1943,8 +1946,6 @@ static int at91udc_remove(struct platform_device *pdev)
remove_debug_file(udc);
clk_unprepare(udc->fclk);
clk_unprepare(udc->iclk);
-
- return 0;
}
#ifdef CONFIG_PM
@@ -2001,7 +2002,7 @@ static int at91udc_resume(struct platform_device *pdev)
static struct platform_driver at91_udc_driver = {
.probe = at91udc_probe,
- .remove = at91udc_remove,
+ .remove_new = at91udc_remove,
.shutdown = at91udc_shutdown,
.suspend = at91udc_suspend,
.resume = at91udc_resume,
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c
index 02b1bef5e22e..b76885d78e8a 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.c
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.c
@@ -94,7 +94,7 @@ static ssize_t queue_dbg_read(struct file *file, char __user *buf,
inode_lock(file_inode(file));
list_for_each_entry_safe(req, tmp_req, queue, queue) {
- len = snprintf(tmpbuf, sizeof(tmpbuf),
+ len = scnprintf(tmpbuf, sizeof(tmpbuf),
"%8p %08x %c%c%c %5d %c%c%c\n",
req->req.buf, req->req.length,
req->req.no_interrupt ? 'i' : 'I',
@@ -104,7 +104,6 @@ static ssize_t queue_dbg_read(struct file *file, char __user *buf,
req->submitted ? 'F' : 'f',
req->using_dma ? 'D' : 'd',
req->last_transaction ? 'L' : 'l');
- len = min(len, sizeof(tmpbuf));
if (len > nbytes)
break;
diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-debug.h b/drivers/usb/gadget/udc/cdns2/cdns2-debug.h
index be9ae0d28a40..f5f330004190 100644
--- a/drivers/usb/gadget/udc/cdns2/cdns2-debug.h
+++ b/drivers/usb/gadget/udc/cdns2/cdns2-debug.h
@@ -16,34 +16,34 @@ static inline const char *cdns2_decode_usb_irq(char *str, size_t size,
{
int ret;
- ret = snprintf(str, size, "usbirq: 0x%02x - ", usb_irq);
+ ret = scnprintf(str, size, "usbirq: 0x%02x - ", usb_irq);
if (usb_irq & USBIRQ_SOF)
- ret += snprintf(str + ret, size - ret, "SOF ");
+ ret += scnprintf(str + ret, size - ret, "SOF ");
if (usb_irq & USBIRQ_SUTOK)
- ret += snprintf(str + ret, size - ret, "SUTOK ");
+ ret += scnprintf(str + ret, size - ret, "SUTOK ");
if (usb_irq & USBIRQ_SUDAV)
- ret += snprintf(str + ret, size - ret, "SETUP ");
+ ret += scnprintf(str + ret, size - ret, "SETUP ");
if (usb_irq & USBIRQ_SUSPEND)
- ret += snprintf(str + ret, size - ret, "Suspend ");
+ ret += scnprintf(str + ret, size - ret, "Suspend ");
if (usb_irq & USBIRQ_URESET)
- ret += snprintf(str + ret, size - ret, "Reset ");
+ ret += scnprintf(str + ret, size - ret, "Reset ");
if (usb_irq & USBIRQ_HSPEED)
- ret += snprintf(str + ret, size - ret, "HS ");
+ ret += scnprintf(str + ret, size - ret, "HS ");
if (usb_irq & USBIRQ_LPM)
- ret += snprintf(str + ret, size - ret, "LPM ");
+ ret += scnprintf(str + ret, size - ret, "LPM ");
- ret += snprintf(str + ret, size - ret, ", EXT: 0x%02x - ", ext_irq);
+ ret += scnprintf(str + ret, size - ret, ", EXT: 0x%02x - ", ext_irq);
if (ext_irq & EXTIRQ_WAKEUP)
- ret += snprintf(str + ret, size - ret, "Wakeup ");
+ ret += scnprintf(str + ret, size - ret, "Wakeup ");
if (ext_irq & EXTIRQ_VBUSFAULT_FALL)
- ret += snprintf(str + ret, size - ret, "VBUS_FALL ");
+ ret += scnprintf(str + ret, size - ret, "VBUS_FALL ");
if (ext_irq & EXTIRQ_VBUSFAULT_RISE)
- ret += snprintf(str + ret, size - ret, "VBUS_RISE ");
+ ret += scnprintf(str + ret, size - ret, "VBUS_RISE ");
- if (ret >= size)
- pr_info("CDNS2: buffer overflowed.\n");
+ if (ret == size - 1)
+ pr_info("CDNS2: buffer may be truncated.\n");
return str;
}
@@ -54,28 +54,28 @@ static inline const char *cdns2_decode_dma_irq(char *str, size_t size,
{
int ret;
- ret = snprintf(str, size, "ISTS: %08x, %s: %08x ",
- ep_ists, ep_name, ep_sts);
+ ret = scnprintf(str, size, "ISTS: %08x, %s: %08x ",
+ ep_ists, ep_name, ep_sts);
if (ep_sts & DMA_EP_STS_IOC)
- ret += snprintf(str + ret, size - ret, "IOC ");
+ ret += scnprintf(str + ret, size - ret, "IOC ");
if (ep_sts & DMA_EP_STS_ISP)
- ret += snprintf(str + ret, size - ret, "ISP ");
+ ret += scnprintf(str + ret, size - ret, "ISP ");
if (ep_sts & DMA_EP_STS_DESCMIS)
- ret += snprintf(str + ret, size - ret, "DESCMIS ");
+ ret += scnprintf(str + ret, size - ret, "DESCMIS ");
if (ep_sts & DMA_EP_STS_TRBERR)
- ret += snprintf(str + ret, size - ret, "TRBERR ");
+ ret += scnprintf(str + ret, size - ret, "TRBERR ");
if (ep_sts & DMA_EP_STS_OUTSMM)
- ret += snprintf(str + ret, size - ret, "OUTSMM ");
+ ret += scnprintf(str + ret, size - ret, "OUTSMM ");
if (ep_sts & DMA_EP_STS_ISOERR)
- ret += snprintf(str + ret, size - ret, "ISOERR ");
+ ret += scnprintf(str + ret, size - ret, "ISOERR ");
if (ep_sts & DMA_EP_STS_DBUSY)
- ret += snprintf(str + ret, size - ret, "DBUSY ");
+ ret += scnprintf(str + ret, size - ret, "DBUSY ");
if (DMA_EP_STS_CCS(ep_sts))
- ret += snprintf(str + ret, size - ret, "CCS ");
+ ret += scnprintf(str + ret, size - ret, "CCS ");
- if (ret >= size)
- pr_info("CDNS2: buffer overflowed.\n");
+ if (ret == size - 1)
+ pr_info("CDNS2: buffer may be truncated.\n");
return str;
}
@@ -105,43 +105,43 @@ static inline const char *cdns2_raw_ring(struct cdns2_endpoint *pep,
int ret;
int i;
- ret = snprintf(str, size, "\n\t\tTR for %s:", pep->name);
+ ret = scnprintf(str, size, "\n\t\tTR for %s:", pep->name);
trb = &trbs[ring->dequeue];
dma = cdns2_trb_virt_to_dma(pep, trb);
- ret += snprintf(str + ret, size - ret,
- "\n\t\tRing deq index: %d, trb: V=%p, P=0x%pad\n",
- ring->dequeue, trb, &dma);
+ ret += scnprintf(str + ret, size - ret,
+ "\n\t\tRing deq index: %d, trb: V=%p, P=0x%pad\n",
+ ring->dequeue, trb, &dma);
trb = &trbs[ring->enqueue];
dma = cdns2_trb_virt_to_dma(pep, trb);
- ret += snprintf(str + ret, size - ret,
- "\t\tRing enq index: %d, trb: V=%p, P=0x%pad\n",
- ring->enqueue, trb, &dma);
+ ret += scnprintf(str + ret, size - ret,
+ "\t\tRing enq index: %d, trb: V=%p, P=0x%pad\n",
+ ring->enqueue, trb, &dma);
- ret += snprintf(str + ret, size - ret,
- "\t\tfree trbs: %d, CCS=%d, PCS=%d\n",
- ring->free_trbs, ring->ccs, ring->pcs);
+ ret += scnprintf(str + ret, size - ret,
+ "\t\tfree trbs: %d, CCS=%d, PCS=%d\n",
+ ring->free_trbs, ring->ccs, ring->pcs);
if (TRBS_PER_SEGMENT > 40) {
- ret += snprintf(str + ret, size - ret,
- "\t\tTransfer ring %d too big\n", TRBS_PER_SEGMENT);
+ ret += scnprintf(str + ret, size - ret,
+ "\t\tTransfer ring %d too big\n", TRBS_PER_SEGMENT);
return str;
}
dma = ring->dma;
for (i = 0; i < TRBS_PER_SEGMENT; ++i) {
trb = &trbs[i];
- ret += snprintf(str + ret, size - ret,
- "\t\t@%pad %08x %08x %08x\n", &dma,
- le32_to_cpu(trb->buffer),
- le32_to_cpu(trb->length),
- le32_to_cpu(trb->control));
+ ret += scnprintf(str + ret, size - ret,
+ "\t\t@%pad %08x %08x %08x\n", &dma,
+ le32_to_cpu(trb->buffer),
+ le32_to_cpu(trb->length),
+ le32_to_cpu(trb->control));
dma += sizeof(*trb);
}
- if (ret >= size)
- pr_info("CDNS2: buffer overflowed.\n");
+ if (ret == size - 1)
+ pr_info("CDNS2: buffer may be truncated.\n");
return str;
}
@@ -166,36 +166,36 @@ static inline const char *cdns2_decode_trb(char *str, size_t size, u32 flags,
switch (type) {
case TRB_LINK:
- ret = snprintf(str, size,
- "LINK %08x type '%s' flags %c:%c:%c%c:%c",
- buffer, cdns2_trb_type_string(type),
- flags & TRB_CYCLE ? 'C' : 'c',
- flags & TRB_TOGGLE ? 'T' : 't',
- flags & TRB_CHAIN ? 'C' : 'c',
- flags & TRB_CHAIN ? 'H' : 'h',
- flags & TRB_IOC ? 'I' : 'i');
+ ret = scnprintf(str, size,
+ "LINK %08x type '%s' flags %c:%c:%c%c:%c",
+ buffer, cdns2_trb_type_string(type),
+ flags & TRB_CYCLE ? 'C' : 'c',
+ flags & TRB_TOGGLE ? 'T' : 't',
+ flags & TRB_CHAIN ? 'C' : 'c',
+ flags & TRB_CHAIN ? 'H' : 'h',
+ flags & TRB_IOC ? 'I' : 'i');
break;
case TRB_NORMAL:
- ret = snprintf(str, size,
- "type: '%s', Buffer: %08x, length: %ld, burst len: %ld, "
- "flags %c:%c:%c%c:%c",
- cdns2_trb_type_string(type),
- buffer, TRB_LEN(length),
- TRB_FIELD_TO_BURST(length),
- flags & TRB_CYCLE ? 'C' : 'c',
- flags & TRB_ISP ? 'I' : 'i',
- flags & TRB_CHAIN ? 'C' : 'c',
- flags & TRB_CHAIN ? 'H' : 'h',
- flags & TRB_IOC ? 'I' : 'i');
+ ret = scnprintf(str, size,
+ "type: '%s', Buffer: %08x, length: %ld, burst len: %ld, "
+ "flags %c:%c:%c%c:%c",
+ cdns2_trb_type_string(type),
+ buffer, TRB_LEN(length),
+ TRB_FIELD_TO_BURST(length),
+ flags & TRB_CYCLE ? 'C' : 'c',
+ flags & TRB_ISP ? 'I' : 'i',
+ flags & TRB_CHAIN ? 'C' : 'c',
+ flags & TRB_CHAIN ? 'H' : 'h',
+ flags & TRB_IOC ? 'I' : 'i');
break;
default:
- ret = snprintf(str, size, "type '%s' -> raw %08x %08x %08x",
- cdns2_trb_type_string(type),
- buffer, length, flags);
+ ret = scnprintf(str, size, "type '%s' -> raw %08x %08x %08x",
+ cdns2_trb_type_string(type),
+ buffer, length, flags);
}
- if (ret >= size)
- pr_info("CDNS2: buffer overflowed.\n");
+ if (ret == size - 1)
+ pr_info("CDNS2: buffer may be truncated.\n");
return str;
}
diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c
index 2693a10eb0c7..e8042c158f6d 100644
--- a/drivers/usb/gadget/udc/fsl_udc_core.c
+++ b/drivers/usb/gadget/udc/fsl_udc_core.c
@@ -1360,7 +1360,7 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value,
udc->ep0_dir = USB_DIR_IN;
/* Borrow the per device status_req */
req = udc->status_req;
- /* Fill in the reqest structure */
+ /* Fill in the request structure */
*((u16 *) req->req.buf) = cpu_to_le16(tmp);
req->ep = ep;
@@ -2532,15 +2532,18 @@ err_kfree:
/* Driver removal function
* Free resources and finish pending transactions
*/
-static int fsl_udc_remove(struct platform_device *pdev)
+static void fsl_udc_remove(struct platform_device *pdev)
{
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev);
DECLARE_COMPLETION_ONSTACK(done);
- if (!udc_controller)
- return -ENODEV;
+ if (!udc_controller) {
+ dev_err(&pdev->dev,
+ "Driver still in use but removing anyhow\n");
+ return;
+ }
udc_controller->done = &done;
usb_del_gadget_udc(&udc_controller->gadget);
@@ -2568,8 +2571,6 @@ static int fsl_udc_remove(struct platform_device *pdev)
*/
if (pdata->exit)
pdata->exit(pdev);
-
- return 0;
}
/*-----------------------------------------------------------------
@@ -2667,7 +2668,7 @@ static const struct platform_device_id fsl_udc_devtype[] = {
MODULE_DEVICE_TABLE(platform, fsl_udc_devtype);
static struct platform_driver udc_driver = {
.probe = fsl_udc_probe,
- .remove = fsl_udc_remove,
+ .remove_new = fsl_udc_remove,
.id_table = fsl_udc_devtype,
/* these suspend and resume are not usb suspend and resume */
.suspend = fsl_udc_suspend,
diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c
index c6dfa7cccc11..fb901be5dac1 100644
--- a/drivers/usb/gadget/udc/gr_udc.c
+++ b/drivers/usb/gadget/udc/gr_udc.c
@@ -2089,15 +2089,18 @@ static void gr_ep_remove(struct gr_udc *dev, int num, int is_in)
ep->tailbuf, ep->tailbuf_paddr);
}
-static int gr_remove(struct platform_device *pdev)
+static void gr_remove(struct platform_device *pdev)
{
struct gr_udc *dev = platform_get_drvdata(pdev);
int i;
if (dev->added)
usb_del_gadget_udc(&dev->gadget); /* Shuts everything down */
- if (dev->driver)
- return -EBUSY;
+ if (dev->driver) {
+ dev_err(&pdev->dev,
+ "Driver still in use but removing anyhow\n");
+ return;
+ }
gr_dfs_delete(dev);
dma_pool_destroy(dev->desc_pool);
@@ -2110,8 +2113,6 @@ static int gr_remove(struct platform_device *pdev)
gr_ep_remove(dev, i, 0);
for (i = 0; i < dev->nepi; i++)
gr_ep_remove(dev, i, 1);
-
- return 0;
}
static int gr_request_irq(struct gr_udc *dev, int irq)
{
@@ -2248,7 +2249,7 @@ static struct platform_driver gr_driver = {
.of_match_table = gr_match,
},
.probe = gr_probe,
- .remove = gr_remove,
+ .remove_new = gr_remove,
};
module_platform_driver(gr_driver);
diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c
index a917cc9a32ab..d5f29f8fe481 100644
--- a/drivers/usb/gadget/udc/lpc32xx_udc.c
+++ b/drivers/usb/gadget/udc/lpc32xx_udc.c
@@ -3174,13 +3174,16 @@ i2c_fail:
return retval;
}
-static int lpc32xx_udc_remove(struct platform_device *pdev)
+static void lpc32xx_udc_remove(struct platform_device *pdev)
{
struct lpc32xx_udc *udc = platform_get_drvdata(pdev);
usb_del_gadget_udc(&udc->gadget);
- if (udc->driver)
- return -EBUSY;
+ if (udc->driver) {
+ dev_err(&pdev->dev,
+ "Driver still in use but removing anyhow\n");
+ return;
+ }
udc_clk_set(udc, 1);
udc_disable(udc);
@@ -3194,8 +3197,6 @@ static int lpc32xx_udc_remove(struct platform_device *pdev)
udc->udca_v_base, udc->udca_p_base);
clk_disable_unprepare(udc->usb_slv_clk);
-
- return 0;
}
#ifdef CONFIG_PM
@@ -3255,7 +3256,7 @@ MODULE_DEVICE_TABLE(of, lpc32xx_udc_of_match);
static struct platform_driver lpc32xx_udc_driver = {
.probe = lpc32xx_udc_probe,
- .remove = lpc32xx_udc_remove,
+ .remove_new = lpc32xx_udc_remove,
.shutdown = lpc32xx_udc_shutdown,
.suspend = lpc32xx_udc_suspend,
.resume = lpc32xx_udc_resume,
diff --git a/drivers/usb/gadget/udc/max3420_udc.c b/drivers/usb/gadget/udc/max3420_udc.c
index 2d57786d3db7..89e8cf2a2a7d 100644
--- a/drivers/usb/gadget/udc/max3420_udc.c
+++ b/drivers/usb/gadget/udc/max3420_udc.c
@@ -1201,7 +1201,7 @@ static int max3420_probe(struct spi_device *spi)
int err, irq;
u8 reg[8];
- if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) {
+ if (spi->master->flags & SPI_CONTROLLER_HALF_DUPLEX) {
dev_err(&spi->dev, "UDC needs full duplex to work\n");
return -EINVAL;
}
diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c
index d888dcda2bc8..78308b64955d 100644
--- a/drivers/usb/gadget/udc/mv_udc_core.c
+++ b/drivers/usb/gadget/udc/mv_udc_core.c
@@ -1451,7 +1451,7 @@ udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty)
req = udc->status_req;
- /* fill in the reqest structure */
+ /* fill in the request structure */
if (empty == false) {
*((u16 *) req->req.buf) = cpu_to_le16(status);
req->req.length = 2;
diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c
index 10c5d7f726a1..f90eeecf27de 100644
--- a/drivers/usb/gadget/udc/omap_udc.c
+++ b/drivers/usb/gadget/udc/omap_udc.c
@@ -2036,7 +2036,8 @@ static irqreturn_t omap_udc_iso_irq(int irq, void *_dev)
static inline int machine_without_vbus_sense(void)
{
- return machine_is_omap_osk() || machine_is_sx1();
+ return machine_is_omap_osk() || machine_is_omap_palmte() ||
+ machine_is_sx1();
}
static int omap_udc_start(struct usb_gadget *g,
diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c
index 4f8617210d85..169f72665739 100644
--- a/drivers/usb/gadget/udc/pch_udc.c
+++ b/drivers/usb/gadget/udc/pch_udc.c
@@ -274,7 +274,6 @@ struct pch_udc_cfg_data {
* @td_data: for data request
* @dev: reference to device struct
* @offset_addr: offset address of ep register
- * @desc: for this ep
* @queue: queue for requests
* @num: endpoint number
* @in: endpoint is IN
diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c
index df0551ecc810..1ac26cb49ecf 100644
--- a/drivers/usb/gadget/udc/pxa25x_udc.c
+++ b/drivers/usb/gadget/udc/pxa25x_udc.c
@@ -2397,12 +2397,15 @@ static void pxa25x_udc_shutdown(struct platform_device *_dev)
pullup_off();
}
-static int pxa25x_udc_remove(struct platform_device *pdev)
+static void pxa25x_udc_remove(struct platform_device *pdev)
{
struct pxa25x_udc *dev = platform_get_drvdata(pdev);
- if (dev->driver)
- return -EBUSY;
+ if (dev->driver) {
+ dev_err(&pdev->dev,
+ "Driver still in use but removing anyhow\n");
+ return;
+ }
usb_del_gadget_udc(&dev->gadget);
dev->pullup = 0;
@@ -2414,7 +2417,6 @@ static int pxa25x_udc_remove(struct platform_device *pdev)
dev->transceiver = NULL;
the_controller = NULL;
- return 0;
}
/*-------------------------------------------------------------------------*/
@@ -2472,7 +2474,7 @@ static int pxa25x_udc_resume(struct platform_device *dev)
static struct platform_driver udc_driver = {
.shutdown = pxa25x_udc_shutdown,
.probe = pxa25x_udc_probe,
- .remove = pxa25x_udc_remove,
+ .remove_new = pxa25x_udc_remove,
.suspend = pxa25x_udc_suspend,
.resume = pxa25x_udc_resume,
.driver = {