aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/gadget
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2015-04-14 08:51:33 -0700
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2015-04-14 08:51:33 -0700
commit85a3685852d9ac7d92be9d824533c915a4597fa4 (patch)
treeb7c542e2061cf96c9f7ad500fa12567f9ff0b39f /drivers/usb/gadget
parentInput: alps - non interleaved V2 dualpoint has separate stick button bits (diff)
parentInput: atkbd - document "no new force-release quirks" policy (diff)
downloadlinux-dev-85a3685852d9ac7d92be9d824533c915a4597fa4.tar.xz
linux-dev-85a3685852d9ac7d92be9d824533c915a4597fa4.zip
Merge branch 'next' into for-linus
Prepare first round of input updates for 4.1 merge window.
Diffstat (limited to 'drivers/usb/gadget')
-rw-r--r--drivers/usb/gadget/Kconfig45
-rw-r--r--drivers/usb/gadget/composite.c2
-rw-r--r--drivers/usb/gadget/configfs.c2
-rw-r--r--drivers/usb/gadget/function/Makefile2
-rw-r--r--drivers/usb/gadget/function/f_fs.c323
-rw-r--r--drivers/usb/gadget/function/f_hid.c4
-rw-r--r--drivers/usb/gadget/function/f_loopback.c3
-rw-r--r--drivers/usb/gadget/function/f_phonet.c5
-rw-r--r--drivers/usb/gadget/function/f_sourcesink.c523
-rw-r--r--drivers/usb/gadget/function/f_uac1.c16
-rw-r--r--drivers/usb/gadget/function/f_uac2.c34
-rw-r--r--drivers/usb/gadget/function/f_uvc.c136
-rw-r--r--drivers/usb/gadget/function/g_zero.h13
-rw-r--r--drivers/usb/gadget/function/u_ether.c4
-rw-r--r--drivers/usb/gadget/function/u_fs.h25
-rw-r--r--drivers/usb/gadget/function/u_uac1.c3
-rw-r--r--drivers/usb/gadget/function/u_uac1.h1
-rw-r--r--drivers/usb/gadget/function/u_uvc.h52
-rw-r--r--drivers/usb/gadget/function/uvc_configfs.c2468
-rw-r--r--drivers/usb/gadget/function/uvc_configfs.h22
-rw-r--r--drivers/usb/gadget/function/uvc_v4l2.c1
-rw-r--r--drivers/usb/gadget/function/uvc_video.c1
-rw-r--r--drivers/usb/gadget/legacy/Kconfig2
-rw-r--r--drivers/usb/gadget/legacy/g_ffs.c6
-rw-r--r--drivers/usb/gadget/legacy/inode.c466
-rw-r--r--drivers/usb/gadget/legacy/tcm_usb_gadget.c5
-rw-r--r--drivers/usb/gadget/legacy/zero.c21
-rw-r--r--drivers/usb/gadget/udc/Kconfig5
-rw-r--r--drivers/usb/gadget/udc/at91_udc.c533
-rw-r--r--drivers/usb/gadget/udc/at91_udc.h10
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.c162
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.h9
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_core.c1
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_ep.c10
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_udc.c1
-rw-r--r--drivers/usb/gadget/udc/dummy_hcd.c9
-rw-r--r--drivers/usb/gadget/udc/fsl_qe_udc.c2
-rw-r--r--drivers/usb/gadget/udc/fsl_udc_core.c5
-rw-r--r--drivers/usb/gadget/udc/lpc32xx_udc.c12
-rw-r--r--drivers/usb/gadget/udc/mv_udc_core.c3
-rw-r--r--drivers/usb/gadget/udc/net2272.c7
-rw-r--r--drivers/usb/gadget/udc/net2272.h1
-rw-r--r--drivers/usb/gadget/udc/net2280.c533
-rw-r--r--drivers/usb/gadget/udc/net2280.h24
-rw-r--r--drivers/usb/gadget/udc/omap_udc.c1
-rw-r--r--drivers/usb/gadget/udc/pch_udc.c1
-rw-r--r--drivers/usb/gadget/udc/pxa25x_udc.c2
-rw-r--r--drivers/usb/gadget/udc/pxa27x_udc.c2
-rw-r--r--drivers/usb/gadget/udc/r8a66597-udc.c1
-rw-r--r--drivers/usb/gadget/udc/s3c2410_udc.c21
-rw-r--r--drivers/usb/gadget/udc/udc-core.c2
-rw-r--r--drivers/usb/gadget/udc/udc-xilinx.c4
52 files changed, 3731 insertions, 1815 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 747ef53bda14..b454d05be583 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -45,7 +45,7 @@ menuconfig USB_GADGET
if USB_GADGET
config USB_GADGET_DEBUG
- boolean "Debugging messages (DEVELOPMENT)"
+ bool "Debugging messages (DEVELOPMENT)"
depends on DEBUG_KERNEL
help
Many controller and gadget drivers will print some debugging
@@ -73,7 +73,7 @@ config USB_GADGET_VERBOSE
production build.
config USB_GADGET_DEBUG_FILES
- boolean "Debugging information files (DEVELOPMENT)"
+ bool "Debugging information files (DEVELOPMENT)"
depends on PROC_FS
help
Some of the drivers in the "gadget" framework can expose
@@ -84,7 +84,7 @@ config USB_GADGET_DEBUG_FILES
here. If in doubt, or to conserve kernel memory, say "N".
config USB_GADGET_DEBUG_FS
- boolean "Debugging information files in debugfs (DEVELOPMENT)"
+ bool "Debugging information files in debugfs (DEVELOPMENT)"
depends on DEBUG_FS
help
Some of the drivers in the "gadget" framework can expose
@@ -230,7 +230,7 @@ config USB_CONFIGFS
For more information see Documentation/usb/gadget_configfs.txt.
config USB_CONFIGFS_SERIAL
- boolean "Generic serial bulk in/out"
+ bool "Generic serial bulk in/out"
depends on USB_CONFIGFS
depends on TTY
select USB_U_SERIAL
@@ -239,7 +239,7 @@ config USB_CONFIGFS_SERIAL
The function talks to the Linux-USB generic serial driver.
config USB_CONFIGFS_ACM
- boolean "Abstract Control Model (CDC ACM)"
+ bool "Abstract Control Model (CDC ACM)"
depends on USB_CONFIGFS
depends on TTY
select USB_U_SERIAL
@@ -249,7 +249,7 @@ config USB_CONFIGFS_ACM
MS-Windows hosts or with the Linux-USB "cdc-acm" driver.
config USB_CONFIGFS_OBEX
- boolean "Object Exchange Model (CDC OBEX)"
+ bool "Object Exchange Model (CDC OBEX)"
depends on USB_CONFIGFS
depends on TTY
select USB_U_SERIAL
@@ -259,7 +259,7 @@ config USB_CONFIGFS_OBEX
since the kernel itself doesn't implement the OBEX protocol.
config USB_CONFIGFS_NCM
- boolean "Network Control Model (CDC NCM)"
+ bool "Network Control Model (CDC NCM)"
depends on USB_CONFIGFS
depends on NET
select USB_U_ETHER
@@ -270,7 +270,7 @@ config USB_CONFIGFS_NCM
different alignment possibilities.
config USB_CONFIGFS_ECM
- boolean "Ethernet Control Model (CDC ECM)"
+ bool "Ethernet Control Model (CDC ECM)"
depends on USB_CONFIGFS
depends on NET
select USB_U_ETHER
@@ -282,7 +282,7 @@ config USB_CONFIGFS_ECM
supported by firmware for smart network devices.
config USB_CONFIGFS_ECM_SUBSET
- boolean "Ethernet Control Model (CDC ECM) subset"
+ bool "Ethernet Control Model (CDC ECM) subset"
depends on USB_CONFIGFS
depends on NET
select USB_U_ETHER
@@ -323,7 +323,7 @@ config USB_CONFIGFS_EEM
the host is the same (a usbX device), so the differences are minimal.
config USB_CONFIGFS_PHONET
- boolean "Phonet protocol"
+ bool "Phonet protocol"
depends on USB_CONFIGFS
depends on NET
depends on PHONET
@@ -333,7 +333,7 @@ config USB_CONFIGFS_PHONET
The Phonet protocol implementation for USB device.
config USB_CONFIGFS_MASS_STORAGE
- boolean "Mass storage"
+ bool "Mass storage"
depends on USB_CONFIGFS
depends on BLOCK
select USB_F_MASS_STORAGE
@@ -344,7 +344,7 @@ config USB_CONFIGFS_MASS_STORAGE
specified as a module parameter or sysfs option.
config USB_CONFIGFS_F_LB_SS
- boolean "Loopback and sourcesink function (for testing)"
+ bool "Loopback and sourcesink function (for testing)"
depends on USB_CONFIGFS
select USB_F_SS_LB
help
@@ -357,7 +357,7 @@ config USB_CONFIGFS_F_LB_SS
and its driver through a basic set of functional tests.
config USB_CONFIGFS_F_FS
- boolean "Function filesystem (FunctionFS)"
+ bool "Function filesystem (FunctionFS)"
depends on USB_CONFIGFS
select USB_F_FS
help
@@ -369,7 +369,7 @@ config USB_CONFIGFS_F_FS
mass storage) and other are implemented in user space.
config USB_CONFIGFS_F_UAC1
- boolean "Audio Class 1.0"
+ bool "Audio Class 1.0"
depends on USB_CONFIGFS
depends on SND
select USB_LIBCOMPOSITE
@@ -382,7 +382,7 @@ config USB_CONFIGFS_F_UAC1
on the device.
config USB_CONFIGFS_F_UAC2
- boolean "Audio Class 2.0"
+ bool "Audio Class 2.0"
depends on USB_CONFIGFS
depends on SND
select USB_LIBCOMPOSITE
@@ -400,7 +400,7 @@ config USB_CONFIGFS_F_UAC2
wants as audio data to the USB Host.
config USB_CONFIGFS_F_MIDI
- boolean "MIDI function"
+ bool "MIDI function"
depends on USB_CONFIGFS
depends on SND
select USB_LIBCOMPOSITE
@@ -414,7 +414,7 @@ config USB_CONFIGFS_F_MIDI
ALSA's aconnect utility etc.
config USB_CONFIGFS_F_HID
- boolean "HID function"
+ bool "HID function"
depends on USB_CONFIGFS
select USB_F_HID
help
@@ -423,6 +423,17 @@ config USB_CONFIGFS_F_HID
For more information, see Documentation/usb/gadget_hid.txt.
+config USB_CONFIGFS_F_UVC
+ bool "USB Webcam function"
+ depends on USB_CONFIGFS
+ depends on VIDEO_DEV
+ select VIDEOBUF2_VMALLOC
+ select USB_F_UVC
+ help
+ The Webcam function acts as a composite USB Audio and Video Class
+ device. It provides a userspace API to process UVC control requests
+ and stream video data to the host.
+
source "drivers/usb/gadget/legacy/Kconfig"
endchoice
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 617835348569..13adfd1a3f54 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -1655,7 +1655,7 @@ unknown:
* OS descriptors handling
*/
if (cdev->use_os_string && cdev->os_desc_config &&
- (ctrl->bRequest & USB_TYPE_VENDOR) &&
+ (ctrl->bRequestType & USB_TYPE_VENDOR) &&
ctrl->bRequest == cdev->b_vendor_code) {
struct usb_request *req;
struct usb_configuration *os_desc_cfg;
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index 75648145dc1b..c42765b3a060 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -1161,7 +1161,6 @@ static ssize_t interf_grp_compatible_id_store(struct usb_os_desc *desc,
if (desc->opts_mutex)
mutex_lock(desc->opts_mutex);
memcpy(desc->ext_compat_id, page, l);
- desc->ext_compat_id[l] = '\0';
if (desc->opts_mutex)
mutex_unlock(desc->opts_mutex);
@@ -1192,7 +1191,6 @@ static ssize_t interf_grp_sub_compatible_id_store(struct usb_os_desc *desc,
if (desc->opts_mutex)
mutex_lock(desc->opts_mutex);
memcpy(desc->ext_compat_id + 8, page, l);
- desc->ext_compat_id[l + 8] = '\0';
if (desc->opts_mutex)
mutex_unlock(desc->opts_mutex);
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
index dd68091d92f0..f71b1aaa0edf 100644
--- a/drivers/usb/gadget/function/Makefile
+++ b/drivers/usb/gadget/function/Makefile
@@ -36,7 +36,7 @@ usb_f_uac1-y := f_uac1.o u_uac1.o
obj-$(CONFIG_USB_F_UAC1) += usb_f_uac1.o
usb_f_uac2-y := f_uac2.o
obj-$(CONFIG_USB_F_UAC2) += usb_f_uac2.o
-usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o
+usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_configfs.o
obj-$(CONFIG_USB_F_UVC) += usb_f_uvc.o
usb_f_midi-y := f_midi.o
obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 63314ede7ba6..175c9956cbe3 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -31,6 +31,7 @@
#include <linux/aio.h>
#include <linux/mmu_context.h>
#include <linux/poll.h>
+#include <linux/eventfd.h>
#include "u_fs.h"
#include "u_f.h"
@@ -143,16 +144,17 @@ struct ffs_io_data {
bool read;
struct kiocb *kiocb;
- const struct iovec *iovec;
- unsigned long nr_segs;
- char __user *buf;
- size_t len;
+ struct iov_iter data;
+ const void *to_free;
+ char *buf;
struct mm_struct *mm;
struct work_struct work;
struct usb_ep *ep;
struct usb_request *req;
+
+ struct ffs_data *ffs;
};
struct ffs_desc_helper {
@@ -390,17 +392,20 @@ done_spin:
return ret;
}
+/* Called with ffs->ev.waitq.lock and ffs->mutex held, both released on exit. */
static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
size_t n)
{
/*
- * We are holding ffs->ev.waitq.lock and ffs->mutex and we need
- * to release them.
+ * n cannot be bigger than ffs->ev.count, which cannot be bigger than
+ * size of ffs->ev.types array (which is four) so that's how much space
+ * we reserve.
*/
- struct usb_functionfs_event events[n];
+ struct usb_functionfs_event events[ARRAY_SIZE(ffs->ev.types)];
+ const size_t size = n * sizeof *events;
unsigned i = 0;
- memset(events, 0, sizeof events);
+ memset(events, 0, size);
do {
events[i].type = ffs->ev.types[i];
@@ -410,19 +415,15 @@ static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
}
} while (++i < n);
- if (n < ffs->ev.count) {
- ffs->ev.count -= n;
+ ffs->ev.count -= n;
+ if (ffs->ev.count)
memmove(ffs->ev.types, ffs->ev.types + n,
ffs->ev.count * sizeof *ffs->ev.types);
- } else {
- ffs->ev.count = 0;
- }
spin_unlock_irq(&ffs->ev.waitq.lock);
mutex_unlock(&ffs->mutex);
- return unlikely(__copy_to_user(buf, events, sizeof events))
- ? -EFAULT : sizeof events;
+ return unlikely(__copy_to_user(buf, events, size)) ? -EFAULT : size;
}
static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
@@ -606,6 +607,8 @@ static unsigned int ffs_ep0_poll(struct file *file, poll_table *wait)
}
case FFS_CLOSING:
break;
+ case FFS_DEACTIVATED:
+ break;
}
mutex_unlock(&ffs->mutex);
@@ -645,39 +648,23 @@ static void ffs_user_copy_worker(struct work_struct *work)
io_data->req->actual;
if (io_data->read && ret > 0) {
- int i;
- size_t pos = 0;
-
- /*
- * Since req->length may be bigger than io_data->len (after
- * being rounded up to maxpacketsize), we may end up with more
- * data then user space has space for.
- */
- ret = min_t(int, ret, io_data->len);
-
use_mm(io_data->mm);
- for (i = 0; i < io_data->nr_segs; i++) {
- size_t len = min_t(size_t, ret - pos,
- io_data->iovec[i].iov_len);
- if (!len)
- break;
- if (unlikely(copy_to_user(io_data->iovec[i].iov_base,
- &io_data->buf[pos], len))) {
- ret = -EFAULT;
- break;
- }
- pos += len;
- }
+ ret = copy_to_iter(io_data->buf, ret, &io_data->data);
+ if (iov_iter_count(&io_data->data))
+ ret = -EFAULT;
unuse_mm(io_data->mm);
}
aio_complete(io_data->kiocb, ret, ret);
+ if (io_data->ffs->ffs_eventfd && !io_data->kiocb->ki_eventfd)
+ eventfd_signal(io_data->ffs->ffs_eventfd, 1);
+
usb_ep_free_request(io_data->ep, io_data->req);
io_data->kiocb->private = NULL;
if (io_data->read)
- kfree(io_data->iovec);
+ kfree(io_data->to_free);
kfree(io_data->buf);
kfree(io_data);
}
@@ -736,6 +723,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
* before the waiting completes, so do not assign to 'gadget' earlier
*/
struct usb_gadget *gadget = epfile->ffs->gadget;
+ size_t copied;
spin_lock_irq(&epfile->ffs->eps_lock);
/* In the meantime, endpoint got disabled or changed. */
@@ -743,34 +731,21 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
spin_unlock_irq(&epfile->ffs->eps_lock);
return -ESHUTDOWN;
}
+ data_len = iov_iter_count(&io_data->data);
/*
* Controller may require buffer size to be aligned to
* maxpacketsize of an out endpoint.
*/
- data_len = io_data->read ?
- usb_ep_align_maybe(gadget, ep->ep, io_data->len) :
- io_data->len;
+ if (io_data->read)
+ data_len = usb_ep_align_maybe(gadget, ep->ep, data_len);
spin_unlock_irq(&epfile->ffs->eps_lock);
data = kmalloc(data_len, GFP_KERNEL);
if (unlikely(!data))
return -ENOMEM;
- if (io_data->aio && !io_data->read) {
- int i;
- size_t pos = 0;
- for (i = 0; i < io_data->nr_segs; i++) {
- if (unlikely(copy_from_user(&data[pos],
- io_data->iovec[i].iov_base,
- io_data->iovec[i].iov_len))) {
- ret = -EFAULT;
- goto error;
- }
- pos += io_data->iovec[i].iov_len;
- }
- } else {
- if (!io_data->read &&
- unlikely(__copy_from_user(data, io_data->buf,
- io_data->len))) {
+ if (!io_data->read) {
+ copied = copy_from_iter(data, data_len, &io_data->data);
+ if (copied != data_len) {
ret = -EFAULT;
goto error;
}
@@ -826,6 +801,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
io_data->buf = data;
io_data->ep = ep->ep;
io_data->req = req;
+ io_data->ffs = epfile->ffs;
req->context = io_data;
req->complete = ffs_epfile_async_io_complete;
@@ -868,10 +844,8 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
*/
ret = ep->status;
if (io_data->read && ret > 0) {
- ret = min_t(size_t, ret, io_data->len);
-
- if (unlikely(copy_to_user(io_data->buf,
- data, ret)))
+ ret = copy_to_iter(data, ret, &io_data->data);
+ if (unlikely(iov_iter_count(&io_data->data)))
ret = -EFAULT;
}
}
@@ -890,37 +864,6 @@ error:
return ret;
}
-static ssize_t
-ffs_epfile_write(struct file *file, const char __user *buf, size_t len,
- loff_t *ptr)
-{
- struct ffs_io_data io_data;
-
- ENTER();
-
- io_data.aio = false;
- io_data.read = false;
- io_data.buf = (char * __user)buf;
- io_data.len = len;
-
- return ffs_epfile_io(file, &io_data);
-}
-
-static ssize_t
-ffs_epfile_read(struct file *file, char __user *buf, size_t len, loff_t *ptr)
-{
- struct ffs_io_data io_data;
-
- ENTER();
-
- io_data.aio = false;
- io_data.read = true;
- io_data.buf = buf;
- io_data.len = len;
-
- return ffs_epfile_io(file, &io_data);
-}
-
static int
ffs_epfile_open(struct inode *inode, struct file *file)
{
@@ -957,67 +900,86 @@ static int ffs_aio_cancel(struct kiocb *kiocb)
return value;
}
-static ssize_t ffs_epfile_aio_write(struct kiocb *kiocb,
- const struct iovec *iovec,
- unsigned long nr_segs, loff_t loff)
+static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from)
{
- struct ffs_io_data *io_data;
+ struct ffs_io_data io_data, *p = &io_data;
+ ssize_t res;
ENTER();
- io_data = kmalloc(sizeof(*io_data), GFP_KERNEL);
- if (unlikely(!io_data))
- return -ENOMEM;
+ if (!is_sync_kiocb(kiocb)) {
+ p = kmalloc(sizeof(io_data), GFP_KERNEL);
+ if (unlikely(!p))
+ return -ENOMEM;
+ p->aio = true;
+ } else {
+ p->aio = false;
+ }
- io_data->aio = true;
- io_data->read = false;
- io_data->kiocb = kiocb;
- io_data->iovec = iovec;
- io_data->nr_segs = nr_segs;
- io_data->len = kiocb->ki_nbytes;
- io_data->mm = current->mm;
+ p->read = false;
+ p->kiocb = kiocb;
+ p->data = *from;
+ p->mm = current->mm;
- kiocb->private = io_data;
+ kiocb->private = p;
kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
- return ffs_epfile_io(kiocb->ki_filp, io_data);
+ res = ffs_epfile_io(kiocb->ki_filp, p);
+ if (res == -EIOCBQUEUED)
+ return res;
+ if (p->aio)
+ kfree(p);
+ else
+ *from = p->data;
+ return res;
}
-static ssize_t ffs_epfile_aio_read(struct kiocb *kiocb,
- const struct iovec *iovec,
- unsigned long nr_segs, loff_t loff)
+static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to)
{
- struct ffs_io_data *io_data;
- struct iovec *iovec_copy;
+ struct ffs_io_data io_data, *p = &io_data;
+ ssize_t res;
ENTER();
- iovec_copy = kmalloc_array(nr_segs, sizeof(*iovec_copy), GFP_KERNEL);
- if (unlikely(!iovec_copy))
- return -ENOMEM;
-
- memcpy(iovec_copy, iovec, sizeof(struct iovec)*nr_segs);
-
- io_data = kmalloc(sizeof(*io_data), GFP_KERNEL);
- if (unlikely(!io_data)) {
- kfree(iovec_copy);
- return -ENOMEM;
+ if (!is_sync_kiocb(kiocb)) {
+ p = kmalloc(sizeof(io_data), GFP_KERNEL);
+ if (unlikely(!p))
+ return -ENOMEM;
+ p->aio = true;
+ } else {
+ p->aio = false;
}
- io_data->aio = true;
- io_data->read = true;
- io_data->kiocb = kiocb;
- io_data->iovec = iovec_copy;
- io_data->nr_segs = nr_segs;
- io_data->len = kiocb->ki_nbytes;
- io_data->mm = current->mm;
+ p->read = true;
+ p->kiocb = kiocb;
+ if (p->aio) {
+ p->to_free = dup_iter(&p->data, to, GFP_KERNEL);
+ if (!p->to_free) {
+ kfree(p);
+ return -ENOMEM;
+ }
+ } else {
+ p->data = *to;
+ p->to_free = NULL;
+ }
+ p->mm = current->mm;
- kiocb->private = io_data;
+ kiocb->private = p;
kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
- return ffs_epfile_io(kiocb->ki_filp, io_data);
+ res = ffs_epfile_io(kiocb->ki_filp, p);
+ if (res == -EIOCBQUEUED)
+ return res;
+
+ if (p->aio) {
+ kfree(p->to_free);
+ kfree(p);
+ } else {
+ *to = p->data;
+ }
+ return res;
}
static int
@@ -1097,10 +1059,10 @@ static const struct file_operations ffs_epfile_operations = {
.llseek = no_llseek,
.open = ffs_epfile_open,
- .write = ffs_epfile_write,
- .read = ffs_epfile_read,
- .aio_write = ffs_epfile_aio_write,
- .aio_read = ffs_epfile_aio_read,
+ .write = new_sync_write,
+ .read = new_sync_read,
+ .write_iter = ffs_epfile_write_iter,
+ .read_iter = ffs_epfile_read_iter,
.release = ffs_epfile_release,
.unlocked_ioctl = ffs_epfile_ioctl,
};
@@ -1180,6 +1142,7 @@ struct ffs_sb_fill_data {
struct ffs_file_perms perms;
umode_t root_mode;
const char *dev_name;
+ bool no_disconnect;
struct ffs_data *ffs_data;
};
@@ -1250,6 +1213,12 @@ static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
/* Interpret option */
switch (eq - opts) {
+ case 13:
+ if (!memcmp(opts, "no_disconnect", 13))
+ data->no_disconnect = !!value;
+ else
+ goto invalid;
+ break;
case 5:
if (!memcmp(opts, "rmode", 5))
data->root_mode = (value & 0555) | S_IFDIR;
@@ -1314,6 +1283,7 @@ ffs_fs_mount(struct file_system_type *t, int flags,
.gid = GLOBAL_ROOT_GID,
},
.root_mode = S_IFDIR | 0500,
+ .no_disconnect = false,
};
struct dentry *rv;
int ret;
@@ -1330,6 +1300,7 @@ ffs_fs_mount(struct file_system_type *t, int flags,
if (unlikely(!ffs))
return ERR_PTR(-ENOMEM);
ffs->file_perms = data.perms;
+ ffs->no_disconnect = data.no_disconnect;
ffs->dev_name = kstrdup(dev_name, GFP_KERNEL);
if (unlikely(!ffs->dev_name)) {
@@ -1361,6 +1332,7 @@ ffs_fs_kill_sb(struct super_block *sb)
kill_litter_super(sb);
if (sb->s_fs_info) {
ffs_release_dev(sb->s_fs_info);
+ ffs_data_closed(sb->s_fs_info);
ffs_data_put(sb->s_fs_info);
}
}
@@ -1417,7 +1389,11 @@ static void ffs_data_opened(struct ffs_data *ffs)
ENTER();
atomic_inc(&ffs->ref);
- atomic_inc(&ffs->opened);
+ if (atomic_add_return(1, &ffs->opened) == 1 &&
+ ffs->state == FFS_DEACTIVATED) {
+ ffs->state = FFS_CLOSING;
+ ffs_data_reset(ffs);
+ }
}
static void ffs_data_put(struct ffs_data *ffs)
@@ -1439,6 +1415,21 @@ static void ffs_data_closed(struct ffs_data *ffs)
ENTER();
if (atomic_dec_and_test(&ffs->opened)) {
+ if (ffs->no_disconnect) {
+ ffs->state = FFS_DEACTIVATED;
+ if (ffs->epfiles) {
+ ffs_epfiles_destroy(ffs->epfiles,
+ ffs->eps_count);
+ ffs->epfiles = NULL;
+ }
+ if (ffs->setup_state == FFS_SETUP_PENDING)
+ __ffs_ep0_stall(ffs);
+ } else {
+ ffs->state = FFS_CLOSING;
+ ffs_data_reset(ffs);
+ }
+ }
+ if (atomic_read(&ffs->opened) < 0) {
ffs->state = FFS_CLOSING;
ffs_data_reset(ffs);
}
@@ -1480,6 +1471,9 @@ static void ffs_data_clear(struct ffs_data *ffs)
if (ffs->epfiles)
ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count);
+ if (ffs->ffs_eventfd)
+ eventfd_ctx_put(ffs->ffs_eventfd);
+
kfree(ffs->raw_descs_data);
kfree(ffs->raw_strings);
kfree(ffs->stringtabs);
@@ -1581,10 +1575,10 @@ static int ffs_epfiles_create(struct ffs_data *ffs)
mutex_init(&epfile->mutex);
init_waitqueue_head(&epfile->wait);
if (ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR)
- sprintf(epfiles->name, "ep%02x", ffs->eps_addrmap[i]);
+ sprintf(epfile->name, "ep%02x", ffs->eps_addrmap[i]);
else
- sprintf(epfiles->name, "ep%u", i);
- epfile->dentry = ffs_sb_create_file(ffs->sb, epfiles->name,
+ sprintf(epfile->name, "ep%u", i);
+ epfile->dentry = ffs_sb_create_file(ffs->sb, epfile->name,
epfile,
&ffs_epfile_operations);
if (unlikely(!epfile->dentry)) {
@@ -1616,7 +1610,6 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
kfree(epfiles);
}
-
static void ffs_func_eps_disable(struct ffs_function *func)
{
struct ffs_ep *ep = func->eps;
@@ -1629,10 +1622,12 @@ static void ffs_func_eps_disable(struct ffs_function *func)
/* pending requests get nuked */
if (likely(ep->ep))
usb_ep_disable(ep->ep);
- epfile->ep = NULL;
-
++ep;
- ++epfile;
+
+ if (epfile) {
+ epfile->ep = NULL;
+ ++epfile;
+ }
} while (--count);
spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
}
@@ -2138,7 +2133,8 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
FUNCTIONFS_HAS_HS_DESC |
FUNCTIONFS_HAS_SS_DESC |
FUNCTIONFS_HAS_MS_OS_DESC |
- FUNCTIONFS_VIRTUAL_ADDR)) {
+ FUNCTIONFS_VIRTUAL_ADDR |
+ FUNCTIONFS_EVENTFD)) {
ret = -ENOSYS;
goto error;
}
@@ -2149,6 +2145,20 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
goto error;
}
+ if (flags & FUNCTIONFS_EVENTFD) {
+ if (len < 4)
+ goto error;
+ ffs->ffs_eventfd =
+ eventfd_ctx_fdget((int)get_unaligned_le32(data));
+ if (IS_ERR(ffs->ffs_eventfd)) {
+ ret = PTR_ERR(ffs->ffs_eventfd);
+ ffs->ffs_eventfd = NULL;
+ goto error;
+ }
+ data += 4;
+ len -= 4;
+ }
+
/* Read fs_count, hs_count and ss_count (if present) */
for (i = 0; i < 3; ++i) {
if (!(flags & (1 << i))) {
@@ -2377,6 +2387,13 @@ static void __ffs_event_add(struct ffs_data *ffs,
if (ffs->setup_state == FFS_SETUP_PENDING)
ffs->setup_state = FFS_SETUP_CANCELLED;
+ /*
+ * Logic of this function guarantees that there are at most four pending
+ * evens on ffs->ev.types queue. This is important because the queue
+ * has space for four elements only and __ffs_ep0_read_events function
+ * depends on that limit as well. If more event types are added, those
+ * limits have to be revisited or guaranteed to still hold.
+ */
switch (type) {
case FUNCTIONFS_RESUME:
rem_type2 = FUNCTIONFS_SUSPEND;
@@ -2416,6 +2433,8 @@ static void __ffs_event_add(struct ffs_data *ffs,
pr_vdebug("adding event %d\n", type);
ffs->ev.types[ffs->ev.count++] = type;
wake_up_locked(&ffs->ev.waitq);
+ if (ffs->ffs_eventfd)
+ eventfd_signal(ffs->ffs_eventfd, 1);
}
static void ffs_event_add(struct ffs_data *ffs,
@@ -2888,6 +2907,13 @@ static int ffs_func_bind(struct usb_configuration *c,
/* Other USB function hooks *************************************************/
+static void ffs_reset_work(struct work_struct *work)
+{
+ struct ffs_data *ffs = container_of(work,
+ struct ffs_data, reset_work);
+ ffs_data_reset(ffs);
+}
+
static int ffs_func_set_alt(struct usb_function *f,
unsigned interface, unsigned alt)
{
@@ -2904,6 +2930,13 @@ static int ffs_func_set_alt(struct usb_function *f,
if (ffs->func)
ffs_func_eps_disable(ffs->func);
+ if (ffs->state == FFS_DEACTIVATED) {
+ ffs->state = FFS_CLOSING;
+ INIT_WORK(&ffs->reset_work, ffs_reset_work);
+ schedule_work(&ffs->reset_work);
+ return -ENODEV;
+ }
+
if (ffs->state != FFS_ACTIVE)
return -ENODEV;
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index a1bc3e3a0b09..a2612fb79eff 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -569,7 +569,7 @@ fail:
return status;
}
-const struct file_operations f_hidg_fops = {
+static const struct file_operations f_hidg_fops = {
.owner = THIS_MODULE,
.open = f_hidg_open,
.release = f_hidg_release,
@@ -759,7 +759,7 @@ static struct f_hid_opts_attribute f_hid_opts_##name = \
F_HID_OPT(subclass, 8, 255);
F_HID_OPT(protocol, 8, 255);
-F_HID_OPT(report_length, 16, 65536);
+F_HID_OPT(report_length, 16, 65535);
static ssize_t f_hid_opts_report_desc_show(struct f_hid_opts *opts, char *page)
{
diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c
index 298b46112b1a..39f49f1ad22f 100644
--- a/drivers/usb/gadget/function/f_loopback.c
+++ b/drivers/usb/gadget/function/f_loopback.c
@@ -289,8 +289,7 @@ static void disable_loopback(struct f_loopback *loop)
struct usb_composite_dev *cdev;
cdev = loop->function.config->cdev;
- disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL, NULL,
- NULL);
+ disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL);
VDBG(cdev, "%s disabled\n", loop->function.name);
}
diff --git a/drivers/usb/gadget/function/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c
index c89e96cfa3e4..c0c3ef272714 100644
--- a/drivers/usb/gadget/function/f_phonet.c
+++ b/drivers/usb/gadget/function/f_phonet.c
@@ -417,7 +417,10 @@ static int pn_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
return -EINVAL;
spin_lock(&port->lock);
- __pn_reset(f);
+
+ if (fp->in_ep->driver_data)
+ __pn_reset(f);
+
if (alt == 1) {
int i;
diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c
index 80be25b32cd7..3a5ae9900b1e 100644
--- a/drivers/usb/gadget/function/f_sourcesink.c
+++ b/drivers/usb/gadget/function/f_sourcesink.c
@@ -23,15 +23,6 @@
#include "gadget_chips.h"
#include "u_f.h"
-#define USB_MS_TO_SS_INTERVAL(x) USB_MS_TO_HS_INTERVAL(x)
-
-enum eptype {
- EP_CONTROL = 0,
- EP_BULK,
- EP_ISOC,
- EP_INTERRUPT,
-};
-
/*
* SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral
* controller drivers.
@@ -64,8 +55,6 @@ struct f_sourcesink {
struct usb_ep *out_ep;
struct usb_ep *iso_in_ep;
struct usb_ep *iso_out_ep;
- struct usb_ep *int_in_ep;
- struct usb_ep *int_out_ep;
int cur_alt;
};
@@ -79,10 +68,6 @@ static unsigned isoc_interval;
static unsigned isoc_maxpacket;
static unsigned isoc_mult;
static unsigned isoc_maxburst;
-static unsigned int_interval; /* In ms */
-static unsigned int_maxpacket;
-static unsigned int_mult;
-static unsigned int_maxburst;
static unsigned buflen;
/*-------------------------------------------------------------------------*/
@@ -107,16 +92,6 @@ static struct usb_interface_descriptor source_sink_intf_alt1 = {
/* .iInterface = DYNAMIC */
};
-static struct usb_interface_descriptor source_sink_intf_alt2 = {
- .bLength = USB_DT_INTERFACE_SIZE,
- .bDescriptorType = USB_DT_INTERFACE,
-
- .bAlternateSetting = 2,
- .bNumEndpoints = 2,
- .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
- /* .iInterface = DYNAMIC */
-};
-
/* full speed support: */
static struct usb_endpoint_descriptor fs_source_desc = {
@@ -155,26 +130,6 @@ static struct usb_endpoint_descriptor fs_iso_sink_desc = {
.bInterval = 4,
};
-static struct usb_endpoint_descriptor fs_int_source_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bEndpointAddress = USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = cpu_to_le16(64),
- .bInterval = GZERO_INT_INTERVAL,
-};
-
-static struct usb_endpoint_descriptor fs_int_sink_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bEndpointAddress = USB_DIR_OUT,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = cpu_to_le16(64),
- .bInterval = GZERO_INT_INTERVAL,
-};
-
static struct usb_descriptor_header *fs_source_sink_descs[] = {
(struct usb_descriptor_header *) &source_sink_intf_alt0,
(struct usb_descriptor_header *) &fs_sink_desc,
@@ -185,10 +140,6 @@ static struct usb_descriptor_header *fs_source_sink_descs[] = {
(struct usb_descriptor_header *) &fs_source_desc,
(struct usb_descriptor_header *) &fs_iso_sink_desc,
(struct usb_descriptor_header *) &fs_iso_source_desc,
- (struct usb_descriptor_header *) &source_sink_intf_alt2,
-#define FS_ALT_IFC_2_OFFSET 8
- (struct usb_descriptor_header *) &fs_int_sink_desc,
- (struct usb_descriptor_header *) &fs_int_source_desc,
NULL,
};
@@ -228,24 +179,6 @@ static struct usb_endpoint_descriptor hs_iso_sink_desc = {
.bInterval = 4,
};
-static struct usb_endpoint_descriptor hs_int_source_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = cpu_to_le16(1024),
- .bInterval = USB_MS_TO_HS_INTERVAL(GZERO_INT_INTERVAL),
-};
-
-static struct usb_endpoint_descriptor hs_int_sink_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = cpu_to_le16(1024),
- .bInterval = USB_MS_TO_HS_INTERVAL(GZERO_INT_INTERVAL),
-};
-
static struct usb_descriptor_header *hs_source_sink_descs[] = {
(struct usb_descriptor_header *) &source_sink_intf_alt0,
(struct usb_descriptor_header *) &hs_source_desc,
@@ -256,10 +189,6 @@ static struct usb_descriptor_header *hs_source_sink_descs[] = {
(struct usb_descriptor_header *) &hs_sink_desc,
(struct usb_descriptor_header *) &hs_iso_source_desc,
(struct usb_descriptor_header *) &hs_iso_sink_desc,
- (struct usb_descriptor_header *) &source_sink_intf_alt2,
-#define HS_ALT_IFC_2_OFFSET 8
- (struct usb_descriptor_header *) &hs_int_source_desc,
- (struct usb_descriptor_header *) &hs_int_sink_desc,
NULL,
};
@@ -335,42 +264,6 @@ static struct usb_ss_ep_comp_descriptor ss_iso_sink_comp_desc = {
.wBytesPerInterval = cpu_to_le16(1024),
};
-static struct usb_endpoint_descriptor ss_int_source_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = cpu_to_le16(1024),
- .bInterval = USB_MS_TO_SS_INTERVAL(GZERO_INT_INTERVAL),
-};
-
-struct usb_ss_ep_comp_descriptor ss_int_source_comp_desc = {
- .bLength = USB_DT_SS_EP_COMP_SIZE,
- .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
-
- .bMaxBurst = 0,
- .bmAttributes = 0,
- .wBytesPerInterval = cpu_to_le16(1024),
-};
-
-static struct usb_endpoint_descriptor ss_int_sink_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = cpu_to_le16(1024),
- .bInterval = USB_MS_TO_SS_INTERVAL(GZERO_INT_INTERVAL),
-};
-
-struct usb_ss_ep_comp_descriptor ss_int_sink_comp_desc = {
- .bLength = USB_DT_SS_EP_COMP_SIZE,
- .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
-
- .bMaxBurst = 0,
- .bmAttributes = 0,
- .wBytesPerInterval = cpu_to_le16(1024),
-};
-
static struct usb_descriptor_header *ss_source_sink_descs[] = {
(struct usb_descriptor_header *) &source_sink_intf_alt0,
(struct usb_descriptor_header *) &ss_source_desc,
@@ -387,12 +280,6 @@ static struct usb_descriptor_header *ss_source_sink_descs[] = {
(struct usb_descriptor_header *) &ss_iso_source_comp_desc,
(struct usb_descriptor_header *) &ss_iso_sink_desc,
(struct usb_descriptor_header *) &ss_iso_sink_comp_desc,
- (struct usb_descriptor_header *) &source_sink_intf_alt2,
-#define SS_ALT_IFC_2_OFFSET 14
- (struct usb_descriptor_header *) &ss_int_source_desc,
- (struct usb_descriptor_header *) &ss_int_source_comp_desc,
- (struct usb_descriptor_header *) &ss_int_sink_desc,
- (struct usb_descriptor_header *) &ss_int_sink_comp_desc,
NULL,
};
@@ -414,21 +301,6 @@ static struct usb_gadget_strings *sourcesink_strings[] = {
};
/*-------------------------------------------------------------------------*/
-static const char *get_ep_string(enum eptype ep_type)
-{
- switch (ep_type) {
- case EP_ISOC:
- return "ISOC-";
- case EP_INTERRUPT:
- return "INTERRUPT-";
- case EP_CONTROL:
- return "CTRL-";
- case EP_BULK:
- return "BULK-";
- default:
- return "UNKNOWN-";
- }
-}
static inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len)
{
@@ -456,8 +328,7 @@ static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep)
void disable_endpoints(struct usb_composite_dev *cdev,
struct usb_ep *in, struct usb_ep *out,
- struct usb_ep *iso_in, struct usb_ep *iso_out,
- struct usb_ep *int_in, struct usb_ep *int_out)
+ struct usb_ep *iso_in, struct usb_ep *iso_out)
{
disable_ep(cdev, in);
disable_ep(cdev, out);
@@ -465,10 +336,6 @@ void disable_endpoints(struct usb_composite_dev *cdev,
disable_ep(cdev, iso_in);
if (iso_out)
disable_ep(cdev, iso_out);
- if (int_in)
- disable_ep(cdev, int_in);
- if (int_out)
- disable_ep(cdev, int_out);
}
static int
@@ -485,7 +352,6 @@ sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
return id;
source_sink_intf_alt0.bInterfaceNumber = id;
source_sink_intf_alt1.bInterfaceNumber = id;
- source_sink_intf_alt2.bInterfaceNumber = id;
/* allocate bulk endpoints */
ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
@@ -546,55 +412,14 @@ no_iso:
if (isoc_maxpacket > 1024)
isoc_maxpacket = 1024;
- /* sanity check the interrupt module parameters */
- if (int_interval < 1)
- int_interval = 1;
- if (int_interval > 4096)
- int_interval = 4096;
- if (int_mult > 2)
- int_mult = 2;
- if (int_maxburst > 15)
- int_maxburst = 15;
-
- /* fill in the FS interrupt descriptors from the module parameters */
- fs_int_source_desc.wMaxPacketSize = int_maxpacket > 64 ?
- 64 : int_maxpacket;
- fs_int_source_desc.bInterval = int_interval > 255 ?
- 255 : int_interval;
- fs_int_sink_desc.wMaxPacketSize = int_maxpacket > 64 ?
- 64 : int_maxpacket;
- fs_int_sink_desc.bInterval = int_interval > 255 ?
- 255 : int_interval;
-
- /* allocate int endpoints */
- ss->int_in_ep = usb_ep_autoconfig(cdev->gadget, &fs_int_source_desc);
- if (!ss->int_in_ep)
- goto no_int;
- ss->int_in_ep->driver_data = cdev; /* claim */
-
- ss->int_out_ep = usb_ep_autoconfig(cdev->gadget, &fs_int_sink_desc);
- if (ss->int_out_ep) {
- ss->int_out_ep->driver_data = cdev; /* claim */
- } else {
- ss->int_in_ep->driver_data = NULL;
- ss->int_in_ep = NULL;
-no_int:
- fs_source_sink_descs[FS_ALT_IFC_2_OFFSET] = NULL;
- hs_source_sink_descs[HS_ALT_IFC_2_OFFSET] = NULL;
- ss_source_sink_descs[SS_ALT_IFC_2_OFFSET] = NULL;
- }
-
- if (int_maxpacket > 1024)
- int_maxpacket = 1024;
-
/* support high speed hardware */
hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress;
hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress;
/*
- * Fill in the HS isoc and interrupt descriptors from the module
- * parameters. We assume that the user knows what they are doing and
- * won't give parameters that their UDC doesn't support.
+ * Fill in the HS isoc descriptors from the module parameters.
+ * We assume that the user knows what they are doing and won't
+ * give parameters that their UDC doesn't support.
*/
hs_iso_source_desc.wMaxPacketSize = isoc_maxpacket;
hs_iso_source_desc.wMaxPacketSize |= isoc_mult << 11;
@@ -607,17 +432,6 @@ no_int:
hs_iso_sink_desc.bInterval = isoc_interval;
hs_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress;
- hs_int_source_desc.wMaxPacketSize = int_maxpacket;
- hs_int_source_desc.wMaxPacketSize |= int_mult << 11;
- hs_int_source_desc.bInterval = USB_MS_TO_HS_INTERVAL(int_interval);
- hs_int_source_desc.bEndpointAddress =
- fs_int_source_desc.bEndpointAddress;
-
- hs_int_sink_desc.wMaxPacketSize = int_maxpacket;
- hs_int_sink_desc.wMaxPacketSize |= int_mult << 11;
- hs_int_sink_desc.bInterval = USB_MS_TO_HS_INTERVAL(int_interval);
- hs_int_sink_desc.bEndpointAddress = fs_int_sink_desc.bEndpointAddress;
-
/* support super speed hardware */
ss_source_desc.bEndpointAddress =
fs_source_desc.bEndpointAddress;
@@ -625,9 +439,9 @@ no_int:
fs_sink_desc.bEndpointAddress;
/*
- * Fill in the SS isoc and interrupt descriptors from the module
- * parameters. We assume that the user knows what they are doing and
- * won't give parameters that their UDC doesn't support.
+ * Fill in the SS isoc descriptors from the module parameters.
+ * We assume that the user knows what they are doing and won't
+ * give parameters that their UDC doesn't support.
*/
ss_iso_source_desc.wMaxPacketSize = isoc_maxpacket;
ss_iso_source_desc.bInterval = isoc_interval;
@@ -646,37 +460,17 @@ no_int:
isoc_maxpacket * (isoc_mult + 1) * (isoc_maxburst + 1);
ss_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress;
- ss_int_source_desc.wMaxPacketSize = int_maxpacket;
- ss_int_source_desc.bInterval = USB_MS_TO_SS_INTERVAL(int_interval);
- ss_int_source_comp_desc.bmAttributes = int_mult;
- ss_int_source_comp_desc.bMaxBurst = int_maxburst;
- ss_int_source_comp_desc.wBytesPerInterval =
- int_maxpacket * (int_mult + 1) * (int_maxburst + 1);
- ss_int_source_desc.bEndpointAddress =
- fs_int_source_desc.bEndpointAddress;
-
- ss_int_sink_desc.wMaxPacketSize = int_maxpacket;
- ss_int_sink_desc.bInterval = USB_MS_TO_SS_INTERVAL(int_interval);
- ss_int_sink_comp_desc.bmAttributes = int_mult;
- ss_int_sink_comp_desc.bMaxBurst = int_maxburst;
- ss_int_sink_comp_desc.wBytesPerInterval =
- int_maxpacket * (int_mult + 1) * (int_maxburst + 1);
- ss_int_sink_desc.bEndpointAddress = fs_int_sink_desc.bEndpointAddress;
-
ret = usb_assign_descriptors(f, fs_source_sink_descs,
hs_source_sink_descs, ss_source_sink_descs);
if (ret)
return ret;
- DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s, "
- "INT-IN/%s, INT-OUT/%s\n",
+ DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s\n",
(gadget_is_superspeed(c->cdev->gadget) ? "super" :
(gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")),
f->name, ss->in_ep->name, ss->out_ep->name,
ss->iso_in_ep ? ss->iso_in_ep->name : "<none>",
- ss->iso_out_ep ? ss->iso_out_ep->name : "<none>",
- ss->int_in_ep ? ss->int_in_ep->name : "<none>",
- ss->int_out_ep ? ss->int_out_ep->name : "<none>");
+ ss->iso_out_ep ? ss->iso_out_ep->name : "<none>");
return 0;
}
@@ -807,15 +601,14 @@ static void source_sink_complete(struct usb_ep *ep, struct usb_request *req)
}
static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
- enum eptype ep_type, int speed)
+ bool is_iso, int speed)
{
struct usb_ep *ep;
struct usb_request *req;
int i, size, status;
for (i = 0; i < 8; i++) {
- switch (ep_type) {
- case EP_ISOC:
+ if (is_iso) {
switch (speed) {
case USB_SPEED_SUPER:
size = isoc_maxpacket * (isoc_mult + 1) *
@@ -831,28 +624,9 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
}
ep = is_in ? ss->iso_in_ep : ss->iso_out_ep;
req = ss_alloc_ep_req(ep, size);
- break;
- case EP_INTERRUPT:
- switch (speed) {
- case USB_SPEED_SUPER:
- size = int_maxpacket * (int_mult + 1) *
- (int_maxburst + 1);
- break;
- case USB_SPEED_HIGH:
- size = int_maxpacket * (int_mult + 1);
- break;
- default:
- size = int_maxpacket > 1023 ?
- 1023 : int_maxpacket;
- break;
- }
- ep = is_in ? ss->int_in_ep : ss->int_out_ep;
- req = ss_alloc_ep_req(ep, size);
- break;
- default:
+ } else {
ep = is_in ? ss->in_ep : ss->out_ep;
req = ss_alloc_ep_req(ep, 0);
- break;
}
if (!req)
@@ -870,12 +644,12 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
cdev = ss->function.config->cdev;
ERROR(cdev, "start %s%s %s --> %d\n",
- get_ep_string(ep_type), is_in ? "IN" : "OUT",
- ep->name, status);
+ is_iso ? "ISO-" : "", is_in ? "IN" : "OUT",
+ ep->name, status);
free_ep_req(ep, req);
}
- if (!(ep_type == EP_ISOC))
+ if (!is_iso)
break;
}
@@ -888,7 +662,7 @@ static void disable_source_sink(struct f_sourcesink *ss)
cdev = ss->function.config->cdev;
disable_endpoints(cdev, ss->in_ep, ss->out_ep, ss->iso_in_ep,
- ss->iso_out_ep, ss->int_in_ep, ss->int_out_ep);
+ ss->iso_out_ep);
VDBG(cdev, "%s disabled\n", ss->function.name);
}
@@ -900,62 +674,6 @@ enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss,
int speed = cdev->gadget->speed;
struct usb_ep *ep;
- if (alt == 2) {
- /* Configure for periodic interrupt endpoint */
- ep = ss->int_in_ep;
- if (ep) {
- result = config_ep_by_speed(cdev->gadget,
- &(ss->function), ep);
- if (result)
- return result;
-
- result = usb_ep_enable(ep);
- if (result < 0)
- return result;
-
- ep->driver_data = ss;
- result = source_sink_start_ep(ss, true, EP_INTERRUPT,
- speed);
- if (result < 0) {
-fail1:
- ep = ss->int_in_ep;
- if (ep) {
- usb_ep_disable(ep);
- ep->driver_data = NULL;
- }
- return result;
- }
- }
-
- /*
- * one interrupt endpoint reads (sinks) anything OUT (from the
- * host)
- */
- ep = ss->int_out_ep;
- if (ep) {
- result = config_ep_by_speed(cdev->gadget,
- &(ss->function), ep);
- if (result)
- goto fail1;
-
- result = usb_ep_enable(ep);
- if (result < 0)
- goto fail1;
-
- ep->driver_data = ss;
- result = source_sink_start_ep(ss, false, EP_INTERRUPT,
- speed);
- if (result < 0) {
- ep = ss->int_out_ep;
- usb_ep_disable(ep);
- ep->driver_data = NULL;
- goto fail1;
- }
- }
-
- goto out;
- }
-
/* one bulk endpoint writes (sources) zeroes IN (to the host) */
ep = ss->in_ep;
result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
@@ -966,7 +684,7 @@ fail1:
return result;
ep->driver_data = ss;
- result = source_sink_start_ep(ss, true, EP_BULK, speed);
+ result = source_sink_start_ep(ss, true, false, speed);
if (result < 0) {
fail:
ep = ss->in_ep;
@@ -985,7 +703,7 @@ fail:
goto fail;
ep->driver_data = ss;
- result = source_sink_start_ep(ss, false, EP_BULK, speed);
+ result = source_sink_start_ep(ss, false, false, speed);
if (result < 0) {
fail2:
ep = ss->out_ep;
@@ -1008,7 +726,7 @@ fail2:
goto fail2;
ep->driver_data = ss;
- result = source_sink_start_ep(ss, true, EP_ISOC, speed);
+ result = source_sink_start_ep(ss, true, true, speed);
if (result < 0) {
fail3:
ep = ss->iso_in_ep;
@@ -1031,14 +749,13 @@ fail3:
goto fail3;
ep->driver_data = ss;
- result = source_sink_start_ep(ss, false, EP_ISOC, speed);
+ result = source_sink_start_ep(ss, false, true, speed);
if (result < 0) {
usb_ep_disable(ep);
ep->driver_data = NULL;
goto fail3;
}
}
-
out:
ss->cur_alt = alt;
@@ -1054,8 +771,6 @@ static int sourcesink_set_alt(struct usb_function *f,
if (ss->in_ep->driver_data)
disable_source_sink(ss);
- else if (alt == 2 && ss->int_in_ep->driver_data)
- disable_source_sink(ss);
return enable_source_sink(cdev, ss, alt);
}
@@ -1168,10 +883,6 @@ static struct usb_function *source_sink_alloc_func(
isoc_maxpacket = ss_opts->isoc_maxpacket;
isoc_mult = ss_opts->isoc_mult;
isoc_maxburst = ss_opts->isoc_maxburst;
- int_interval = ss_opts->int_interval;
- int_maxpacket = ss_opts->int_maxpacket;
- int_mult = ss_opts->int_mult;
- int_maxburst = ss_opts->int_maxburst;
buflen = ss_opts->bulk_buflen;
ss->function.name = "source/sink";
@@ -1214,7 +925,7 @@ static ssize_t f_ss_opts_pattern_show(struct f_ss_opts *opts, char *page)
int result;
mutex_lock(&opts->lock);
- result = sprintf(page, "%d", opts->pattern);
+ result = sprintf(page, "%u", opts->pattern);
mutex_unlock(&opts->lock);
return result;
@@ -1258,7 +969,7 @@ static ssize_t f_ss_opts_isoc_interval_show(struct f_ss_opts *opts, char *page)
int result;
mutex_lock(&opts->lock);
- result = sprintf(page, "%d", opts->isoc_interval);
+ result = sprintf(page, "%u", opts->isoc_interval);
mutex_unlock(&opts->lock);
return result;
@@ -1302,7 +1013,7 @@ static ssize_t f_ss_opts_isoc_maxpacket_show(struct f_ss_opts *opts, char *page)
int result;
mutex_lock(&opts->lock);
- result = sprintf(page, "%d", opts->isoc_maxpacket);
+ result = sprintf(page, "%u", opts->isoc_maxpacket);
mutex_unlock(&opts->lock);
return result;
@@ -1346,7 +1057,7 @@ static ssize_t f_ss_opts_isoc_mult_show(struct f_ss_opts *opts, char *page)
int result;
mutex_lock(&opts->lock);
- result = sprintf(page, "%d", opts->isoc_mult);
+ result = sprintf(page, "%u", opts->isoc_mult);
mutex_unlock(&opts->lock);
return result;
@@ -1390,7 +1101,7 @@ static ssize_t f_ss_opts_isoc_maxburst_show(struct f_ss_opts *opts, char *page)
int result;
mutex_lock(&opts->lock);
- result = sprintf(page, "%d", opts->isoc_maxburst);
+ result = sprintf(page, "%u", opts->isoc_maxburst);
mutex_unlock(&opts->lock);
return result;
@@ -1434,7 +1145,7 @@ static ssize_t f_ss_opts_bulk_buflen_show(struct f_ss_opts *opts, char *page)
int result;
mutex_lock(&opts->lock);
- result = sprintf(page, "%d", opts->bulk_buflen);
+ result = sprintf(page, "%u", opts->bulk_buflen);
mutex_unlock(&opts->lock);
return result;
@@ -1468,182 +1179,6 @@ static struct f_ss_opts_attribute f_ss_opts_bulk_buflen =
f_ss_opts_bulk_buflen_show,
f_ss_opts_bulk_buflen_store);
-static ssize_t f_ss_opts_int_interval_show(struct f_ss_opts *opts, char *page)
-{
- int result;
-
- mutex_lock(&opts->lock);
- result = sprintf(page, "%d", opts->int_interval);
- mutex_unlock(&opts->lock);
-
- return result;
-}
-
-static ssize_t f_ss_opts_int_interval_store(struct f_ss_opts *opts,
- const char *page, size_t len)
-{
- int ret;
- u32 num;
-
- mutex_lock(&opts->lock);
- if (opts->refcnt) {
- ret = -EBUSY;
- goto end;
- }
-
- ret = kstrtou32(page, 0, &num);
- if (ret)
- goto end;
-
- if (num > 4096) {
- ret = -EINVAL;
- goto end;
- }
-
- opts->int_interval = num;
- ret = len;
-end:
- mutex_unlock(&opts->lock);
- return ret;
-}
-
-static struct f_ss_opts_attribute f_ss_opts_int_interval =
- __CONFIGFS_ATTR(int_interval, S_IRUGO | S_IWUSR,
- f_ss_opts_int_interval_show,
- f_ss_opts_int_interval_store);
-
-static ssize_t f_ss_opts_int_maxpacket_show(struct f_ss_opts *opts, char *page)
-{
- int result;
-
- mutex_lock(&opts->lock);
- result = sprintf(page, "%d", opts->int_maxpacket);
- mutex_unlock(&opts->lock);
-
- return result;
-}
-
-static ssize_t f_ss_opts_int_maxpacket_store(struct f_ss_opts *opts,
- const char *page, size_t len)
-{
- int ret;
- u16 num;
-
- mutex_lock(&opts->lock);
- if (opts->refcnt) {
- ret = -EBUSY;
- goto end;
- }
-
- ret = kstrtou16(page, 0, &num);
- if (ret)
- goto end;
-
- if (num > 1024) {
- ret = -EINVAL;
- goto end;
- }
-
- opts->int_maxpacket = num;
- ret = len;
-end:
- mutex_unlock(&opts->lock);
- return ret;
-}
-
-static struct f_ss_opts_attribute f_ss_opts_int_maxpacket =
- __CONFIGFS_ATTR(int_maxpacket, S_IRUGO | S_IWUSR,
- f_ss_opts_int_maxpacket_show,
- f_ss_opts_int_maxpacket_store);
-
-static ssize_t f_ss_opts_int_mult_show(struct f_ss_opts *opts, char *page)
-{
- int result;
-
- mutex_lock(&opts->lock);
- result = sprintf(page, "%d", opts->int_mult);
- mutex_unlock(&opts->lock);
-
- return result;
-}
-
-static ssize_t f_ss_opts_int_mult_store(struct f_ss_opts *opts,
- const char *page, size_t len)
-{
- int ret;
- u8 num;
-
- mutex_lock(&opts->lock);
- if (opts->refcnt) {
- ret = -EBUSY;
- goto end;
- }
-
- ret = kstrtou8(page, 0, &num);
- if (ret)
- goto end;
-
- if (num > 2) {
- ret = -EINVAL;
- goto end;
- }
-
- opts->int_mult = num;
- ret = len;
-end:
- mutex_unlock(&opts->lock);
- return ret;
-}
-
-static struct f_ss_opts_attribute f_ss_opts_int_mult =
- __CONFIGFS_ATTR(int_mult, S_IRUGO | S_IWUSR,
- f_ss_opts_int_mult_show,
- f_ss_opts_int_mult_store);
-
-static ssize_t f_ss_opts_int_maxburst_show(struct f_ss_opts *opts, char *page)
-{
- int result;
-
- mutex_lock(&opts->lock);
- result = sprintf(page, "%d", opts->int_maxburst);
- mutex_unlock(&opts->lock);
-
- return result;
-}
-
-static ssize_t f_ss_opts_int_maxburst_store(struct f_ss_opts *opts,
- const char *page, size_t len)
-{
- int ret;
- u8 num;
-
- mutex_lock(&opts->lock);
- if (opts->refcnt) {
- ret = -EBUSY;
- goto end;
- }
-
- ret = kstrtou8(page, 0, &num);
- if (ret)
- goto end;
-
- if (num > 15) {
- ret = -EINVAL;
- goto end;
- }
-
- opts->int_maxburst = num;
- ret = len;
-end:
- mutex_unlock(&opts->lock);
- return ret;
-}
-
-static struct f_ss_opts_attribute f_ss_opts_int_maxburst =
- __CONFIGFS_ATTR(int_maxburst, S_IRUGO | S_IWUSR,
- f_ss_opts_int_maxburst_show,
- f_ss_opts_int_maxburst_store);
-
static struct configfs_attribute *ss_attrs[] = {
&f_ss_opts_pattern.attr,
&f_ss_opts_isoc_interval.attr,
@@ -1651,10 +1186,6 @@ static struct configfs_attribute *ss_attrs[] = {
&f_ss_opts_isoc_mult.attr,
&f_ss_opts_isoc_maxburst.attr,
&f_ss_opts_bulk_buflen.attr,
- &f_ss_opts_int_interval.attr,
- &f_ss_opts_int_maxpacket.attr,
- &f_ss_opts_int_mult.attr,
- &f_ss_opts_int_maxburst.attr,
NULL,
};
@@ -1684,8 +1215,6 @@ static struct usb_function_instance *source_sink_alloc_inst(void)
ss_opts->isoc_interval = GZERO_ISOC_INTERVAL;
ss_opts->isoc_maxpacket = GZERO_ISOC_MAXPACKET;
ss_opts->bulk_buflen = GZERO_BULK_BUFLEN;
- ss_opts->int_interval = GZERO_INT_INTERVAL;
- ss_opts->int_maxpacket = GZERO_INT_MAXPACKET;
config_group_init_type_name(&ss_opts->func_inst.group, "",
&ss_func_type);
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index e9715845f82e..9719abfb6145 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -31,7 +31,7 @@ static int generic_get_cmd(struct usb_audio_control *con, u8 cmd);
*/
#define F_AUDIO_AC_INTERFACE 0
#define F_AUDIO_AS_INTERFACE 1
-#define F_AUDIO_NUM_INTERFACES 2
+#define F_AUDIO_NUM_INTERFACES 1
/* B.3.1 Standard AC Interface Descriptor */
static struct usb_interface_descriptor ac_interface_desc = {
@@ -42,14 +42,18 @@ static struct usb_interface_descriptor ac_interface_desc = {
.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
};
-DECLARE_UAC_AC_HEADER_DESCRIPTOR(2);
+/*
+ * The number of AudioStreaming and MIDIStreaming interfaces
+ * in the Audio Interface Collection
+ */
+DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);
#define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES)
/* 1 input terminal, 1 output terminal and 1 feature unit */
#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH + UAC_DT_INPUT_TERMINAL_SIZE \
+ UAC_DT_OUTPUT_TERMINAL_SIZE + UAC_DT_FEATURE_UNIT_SIZE(0))
/* B.3.2 Class-Specific AC Interface Descriptor */
-static struct uac1_ac_header_descriptor_2 ac_header_desc = {
+static struct uac1_ac_header_descriptor_1 ac_header_desc = {
.bLength = UAC_DT_AC_HEADER_LENGTH,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_HEADER,
@@ -57,8 +61,8 @@ static struct uac1_ac_header_descriptor_2 ac_header_desc = {
.wTotalLength = __constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH),
.bInCollection = F_AUDIO_NUM_INTERFACES,
.baInterfaceNr = {
- [0] = F_AUDIO_AC_INTERFACE,
- [1] = F_AUDIO_AS_INTERFACE,
+ /* Interface number of the first AudioStream interface */
+ [0] = 1,
}
};
@@ -584,6 +588,7 @@ static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (intf == 1) {
if (alt == 1) {
+ config_ep_by_speed(cdev->gadget, f, out_ep);
usb_ep_enable(out_ep);
out_ep->driver_data = audio;
audio->copy_buf = f_audio_buffer_alloc(audio_buf_size);
@@ -669,7 +674,6 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f)
audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst);
audio->card.gadget = c->cdev->gadget;
- audio_opts->card = &audio->card;
/* set up ASLA audio devices */
if (!audio_opts->bound) {
status = gaudio_setup(&audio->card);
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 33e16658e5cf..6d3eb8b00a48 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -54,7 +54,7 @@
#define UNFLW_CTRL 8
#define OVFLW_CTRL 10
-const char *uac2_name = "snd_uac2";
+static const char *uac2_name = "snd_uac2";
struct uac2_req {
struct uac2_rtd_params *pp; /* parent param */
@@ -634,7 +634,7 @@ static struct usb_interface_descriptor std_ac_if_desc = {
};
/* Clock source for IN traffic */
-struct uac_clock_source_descriptor in_clk_src_desc = {
+static struct uac_clock_source_descriptor in_clk_src_desc = {
.bLength = sizeof in_clk_src_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
@@ -646,7 +646,7 @@ struct uac_clock_source_descriptor in_clk_src_desc = {
};
/* Clock source for OUT traffic */
-struct uac_clock_source_descriptor out_clk_src_desc = {
+static struct uac_clock_source_descriptor out_clk_src_desc = {
.bLength = sizeof out_clk_src_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
@@ -658,7 +658,7 @@ struct uac_clock_source_descriptor out_clk_src_desc = {
};
/* Input Terminal for USB_OUT */
-struct uac2_input_terminal_descriptor usb_out_it_desc = {
+static struct uac2_input_terminal_descriptor usb_out_it_desc = {
.bLength = sizeof usb_out_it_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
@@ -672,7 +672,7 @@ struct uac2_input_terminal_descriptor usb_out_it_desc = {
};
/* Input Terminal for I/O-In */
-struct uac2_input_terminal_descriptor io_in_it_desc = {
+static struct uac2_input_terminal_descriptor io_in_it_desc = {
.bLength = sizeof io_in_it_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
@@ -686,7 +686,7 @@ struct uac2_input_terminal_descriptor io_in_it_desc = {
};
/* Ouput Terminal for USB_IN */
-struct uac2_output_terminal_descriptor usb_in_ot_desc = {
+static struct uac2_output_terminal_descriptor usb_in_ot_desc = {
.bLength = sizeof usb_in_ot_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
@@ -700,7 +700,7 @@ struct uac2_output_terminal_descriptor usb_in_ot_desc = {
};
/* Ouput Terminal for I/O-Out */
-struct uac2_output_terminal_descriptor io_out_ot_desc = {
+static struct uac2_output_terminal_descriptor io_out_ot_desc = {
.bLength = sizeof io_out_ot_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
@@ -713,7 +713,7 @@ struct uac2_output_terminal_descriptor io_out_ot_desc = {
.bmControls = (CONTROL_RDWR << COPY_CTRL),
};
-struct uac2_ac_header_descriptor ac_hdr_desc = {
+static struct uac2_ac_header_descriptor ac_hdr_desc = {
.bLength = sizeof ac_hdr_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
@@ -751,7 +751,7 @@ static struct usb_interface_descriptor std_as_out_if1_desc = {
};
/* Audio Stream OUT Intface Desc */
-struct uac2_as_header_descriptor as_out_hdr_desc = {
+static struct uac2_as_header_descriptor as_out_hdr_desc = {
.bLength = sizeof as_out_hdr_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
@@ -764,7 +764,7 @@ struct uac2_as_header_descriptor as_out_hdr_desc = {
};
/* Audio USB_OUT Format */
-struct uac2_format_type_i_descriptor as_out_fmt1_desc = {
+static struct uac2_format_type_i_descriptor as_out_fmt1_desc = {
.bLength = sizeof as_out_fmt1_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_FORMAT_TYPE,
@@ -772,7 +772,7 @@ struct uac2_format_type_i_descriptor as_out_fmt1_desc = {
};
/* STD AS ISO OUT Endpoint */
-struct usb_endpoint_descriptor fs_epout_desc = {
+static struct usb_endpoint_descriptor fs_epout_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -782,7 +782,7 @@ struct usb_endpoint_descriptor fs_epout_desc = {
.bInterval = 1,
};
-struct usb_endpoint_descriptor hs_epout_desc = {
+static struct usb_endpoint_descriptor hs_epout_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -828,7 +828,7 @@ static struct usb_interface_descriptor std_as_in_if1_desc = {
};
/* Audio Stream IN Intface Desc */
-struct uac2_as_header_descriptor as_in_hdr_desc = {
+static struct uac2_as_header_descriptor as_in_hdr_desc = {
.bLength = sizeof as_in_hdr_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
@@ -841,7 +841,7 @@ struct uac2_as_header_descriptor as_in_hdr_desc = {
};
/* Audio USB_IN Format */
-struct uac2_format_type_i_descriptor as_in_fmt1_desc = {
+static struct uac2_format_type_i_descriptor as_in_fmt1_desc = {
.bLength = sizeof as_in_fmt1_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_FORMAT_TYPE,
@@ -849,7 +849,7 @@ struct uac2_format_type_i_descriptor as_in_fmt1_desc = {
};
/* STD AS ISO IN Endpoint */
-struct usb_endpoint_descriptor fs_epin_desc = {
+static struct usb_endpoint_descriptor fs_epin_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -859,7 +859,7 @@ struct usb_endpoint_descriptor fs_epin_desc = {
.bInterval = 1,
};
-struct usb_endpoint_descriptor hs_epin_desc = {
+static struct usb_endpoint_descriptor hs_epin_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -1563,7 +1563,7 @@ static void afunc_unbind(struct usb_configuration *c, struct usb_function *f)
agdev->out_ep->driver_data = NULL;
}
-struct usb_function *afunc_alloc(struct usb_function_instance *fi)
+static struct usb_function *afunc_alloc(struct usb_function_instance *fi)
{
struct audio_dev *agdev;
struct f_uac2_opts *opts;
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index 945b3bd2ca98..76891adfba7a 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -27,10 +27,11 @@
#include <media/v4l2-dev.h>
#include <media/v4l2-event.h>
+#include "u_uvc.h"
#include "uvc.h"
+#include "uvc_configfs.h"
#include "uvc_v4l2.h"
#include "uvc_video.h"
-#include "u_uvc.h"
unsigned int uvc_gadget_trace_param;
@@ -509,6 +510,9 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
break;
}
+ if (!uvc_control_desc || !uvc_streaming_cls)
+ return ERR_PTR(-ENODEV);
+
/* Descriptors layout
*
* uvc_iad
@@ -605,7 +609,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
INFO(cdev, "uvc_function_bind\n");
- opts = to_f_uvc_opts(f->fi);
+ opts = fi_to_f_uvc_opts(f->fi);
/* Sanity check the streaming endpoint module parameters.
*/
opts->streaming_interval = clamp(opts->streaming_interval, 1U, 16U);
@@ -700,10 +704,27 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
/* Copy descriptors */
f->fs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL);
- if (gadget_is_dualspeed(cdev->gadget))
+ if (IS_ERR(f->fs_descriptors)) {
+ ret = PTR_ERR(f->fs_descriptors);
+ f->fs_descriptors = NULL;
+ goto error;
+ }
+ if (gadget_is_dualspeed(cdev->gadget)) {
f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH);
- if (gadget_is_superspeed(c->cdev->gadget))
+ if (IS_ERR(f->hs_descriptors)) {
+ ret = PTR_ERR(f->hs_descriptors);
+ f->hs_descriptors = NULL;
+ goto error;
+ }
+ }
+ if (gadget_is_superspeed(c->cdev->gadget)) {
f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER);
+ if (IS_ERR(f->ss_descriptors)) {
+ ret = PTR_ERR(f->ss_descriptors);
+ f->ss_descriptors = NULL;
+ goto error;
+ }
+ }
/* Preallocate control endpoint request. */
uvc->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);
@@ -766,27 +787,106 @@ error:
static void uvc_free_inst(struct usb_function_instance *f)
{
- struct f_uvc_opts *opts = to_f_uvc_opts(f);
+ struct f_uvc_opts *opts = fi_to_f_uvc_opts(f);
+ mutex_destroy(&opts->lock);
kfree(opts);
}
static struct usb_function_instance *uvc_alloc_inst(void)
{
struct f_uvc_opts *opts;
+ struct uvc_camera_terminal_descriptor *cd;
+ struct uvc_processing_unit_descriptor *pd;
+ struct uvc_output_terminal_descriptor *od;
+ struct uvc_color_matching_descriptor *md;
+ struct uvc_descriptor_header **ctl_cls;
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return ERR_PTR(-ENOMEM);
opts->func_inst.free_func_inst = uvc_free_inst;
-
+ mutex_init(&opts->lock);
+
+ cd = &opts->uvc_camera_terminal;
+ cd->bLength = UVC_DT_CAMERA_TERMINAL_SIZE(3);
+ cd->bDescriptorType = USB_DT_CS_INTERFACE;
+ cd->bDescriptorSubType = UVC_VC_INPUT_TERMINAL;
+ cd->bTerminalID = 1;
+ cd->wTerminalType = cpu_to_le16(0x0201);
+ cd->bAssocTerminal = 0;
+ cd->iTerminal = 0;
+ cd->wObjectiveFocalLengthMin = cpu_to_le16(0);
+ cd->wObjectiveFocalLengthMax = cpu_to_le16(0);
+ cd->wOcularFocalLength = cpu_to_le16(0);
+ cd->bControlSize = 3;
+ cd->bmControls[0] = 2;
+ cd->bmControls[1] = 0;
+ cd->bmControls[2] = 0;
+
+ pd = &opts->uvc_processing;
+ pd->bLength = UVC_DT_PROCESSING_UNIT_SIZE(2);
+ pd->bDescriptorType = USB_DT_CS_INTERFACE;
+ pd->bDescriptorSubType = UVC_VC_PROCESSING_UNIT;
+ pd->bUnitID = 2;
+ pd->bSourceID = 1;
+ pd->wMaxMultiplier = cpu_to_le16(16*1024);
+ pd->bControlSize = 2;
+ pd->bmControls[0] = 1;
+ pd->bmControls[1] = 0;
+ pd->iProcessing = 0;
+
+ od = &opts->uvc_output_terminal;
+ od->bLength = UVC_DT_OUTPUT_TERMINAL_SIZE;
+ od->bDescriptorType = USB_DT_CS_INTERFACE;
+ od->bDescriptorSubType = UVC_VC_OUTPUT_TERMINAL;
+ od->bTerminalID = 3;
+ od->wTerminalType = cpu_to_le16(0x0101);
+ od->bAssocTerminal = 0;
+ od->bSourceID = 2;
+ od->iTerminal = 0;
+
+ md = &opts->uvc_color_matching;
+ md->bLength = UVC_DT_COLOR_MATCHING_SIZE;
+ md->bDescriptorType = USB_DT_CS_INTERFACE;
+ md->bDescriptorSubType = UVC_VS_COLORFORMAT;
+ md->bColorPrimaries = 1;
+ md->bTransferCharacteristics = 1;
+ md->bMatrixCoefficients = 4;
+
+ /* Prepare fs control class descriptors for configfs-based gadgets */
+ ctl_cls = opts->uvc_fs_control_cls;
+ ctl_cls[0] = NULL; /* assigned elsewhere by configfs */
+ ctl_cls[1] = (struct uvc_descriptor_header *)cd;
+ ctl_cls[2] = (struct uvc_descriptor_header *)pd;
+ ctl_cls[3] = (struct uvc_descriptor_header *)od;
+ ctl_cls[4] = NULL; /* NULL-terminate */
+ opts->fs_control =
+ (const struct uvc_descriptor_header * const *)ctl_cls;
+
+ /* Prepare hs control class descriptors for configfs-based gadgets */
+ ctl_cls = opts->uvc_ss_control_cls;
+ ctl_cls[0] = NULL; /* assigned elsewhere by configfs */
+ ctl_cls[1] = (struct uvc_descriptor_header *)cd;
+ ctl_cls[2] = (struct uvc_descriptor_header *)pd;
+ ctl_cls[3] = (struct uvc_descriptor_header *)od;
+ ctl_cls[4] = NULL; /* NULL-terminate */
+ opts->ss_control =
+ (const struct uvc_descriptor_header * const *)ctl_cls;
+
+ opts->streaming_interval = 1;
+ opts->streaming_maxpacket = 1024;
+
+ uvcg_attach_configfs(opts);
return &opts->func_inst;
}
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);
+ --opts->refcnt;
kfree(uvc);
}
@@ -812,19 +912,39 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
{
struct uvc_device *uvc;
struct f_uvc_opts *opts;
+ struct uvc_descriptor_header **strm_cls;
uvc = kzalloc(sizeof(*uvc), GFP_KERNEL);
if (uvc == NULL)
return ERR_PTR(-ENOMEM);
uvc->state = UVC_STATE_DISCONNECTED;
- opts = to_f_uvc_opts(fi);
+ opts = fi_to_f_uvc_opts(fi);
+
+ mutex_lock(&opts->lock);
+ if (opts->uvc_fs_streaming_cls) {
+ strm_cls = opts->uvc_fs_streaming_cls;
+ opts->fs_streaming =
+ (const struct uvc_descriptor_header * const *)strm_cls;
+ }
+ if (opts->uvc_hs_streaming_cls) {
+ strm_cls = opts->uvc_hs_streaming_cls;
+ opts->hs_streaming =
+ (const struct uvc_descriptor_header * const *)strm_cls;
+ }
+ if (opts->uvc_ss_streaming_cls) {
+ strm_cls = opts->uvc_ss_streaming_cls;
+ opts->ss_streaming =
+ (const struct uvc_descriptor_header * const *)strm_cls;
+ }
uvc->desc.fs_control = opts->fs_control;
uvc->desc.ss_control = opts->ss_control;
uvc->desc.fs_streaming = opts->fs_streaming;
uvc->desc.hs_streaming = opts->hs_streaming;
uvc->desc.ss_streaming = opts->ss_streaming;
+ ++opts->refcnt;
+ mutex_unlock(&opts->lock);
/* Register the function. */
uvc->func.name = "uvc";
diff --git a/drivers/usb/gadget/function/g_zero.h b/drivers/usb/gadget/function/g_zero.h
index 2ce28b9d97cc..15f180904f8a 100644
--- a/drivers/usb/gadget/function/g_zero.h
+++ b/drivers/usb/gadget/function/g_zero.h
@@ -10,8 +10,6 @@
#define GZERO_QLEN 32
#define GZERO_ISOC_INTERVAL 4
#define GZERO_ISOC_MAXPACKET 1024
-#define GZERO_INT_INTERVAL 1 /* Default interrupt interval = 1 ms */
-#define GZERO_INT_MAXPACKET 1024
struct usb_zero_options {
unsigned pattern;
@@ -19,10 +17,6 @@ struct usb_zero_options {
unsigned isoc_maxpacket;
unsigned isoc_mult;
unsigned isoc_maxburst;
- unsigned int_interval; /* In ms */
- unsigned int_maxpacket;
- unsigned int_mult;
- unsigned int_maxburst;
unsigned bulk_buflen;
unsigned qlen;
};
@@ -34,10 +28,6 @@ struct f_ss_opts {
unsigned isoc_maxpacket;
unsigned isoc_mult;
unsigned isoc_maxburst;
- unsigned int_interval; /* In ms */
- unsigned int_maxpacket;
- unsigned int_mult;
- unsigned int_maxburst;
unsigned bulk_buflen;
/*
@@ -72,7 +62,6 @@ int lb_modinit(void);
void free_ep_req(struct usb_ep *ep, struct usb_request *req);
void disable_endpoints(struct usb_composite_dev *cdev,
struct usb_ep *in, struct usb_ep *out,
- struct usb_ep *iso_in, struct usb_ep *iso_out,
- struct usb_ep *int_in, struct usb_ep *int_out);
+ struct usb_ep *iso_in, struct usb_ep *iso_out);
#endif /* __G_ZERO_H */
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index 6e6f87656e7b..f1fd777ef4ec 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -729,9 +729,7 @@ static int get_ether_addr_str(u8 dev_addr[ETH_ALEN], char *str, int len)
if (len < 18)
return -EINVAL;
- snprintf(str, len, "%02x:%02x:%02x:%02x:%02x:%02x",
- dev_addr[0], dev_addr[1], dev_addr[2],
- dev_addr[3], dev_addr[4], dev_addr[5]);
+ snprintf(str, len, "%pM", dev_addr);
return 18;
}
diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h
index cd128e31f808..60139854e0b1 100644
--- a/drivers/usb/gadget/function/u_fs.h
+++ b/drivers/usb/gadget/function/u_fs.h
@@ -19,6 +19,7 @@
#include <linux/usb/composite.h>
#include <linux/list.h>
#include <linux/mutex.h>
+#include <linux/workqueue.h>
#ifdef VERBOSE_DEBUG
#ifndef pr_vdebug
@@ -93,6 +94,26 @@ enum ffs_state {
FFS_ACTIVE,
/*
+ * Function is visible to host, but it's not functional. All
+ * setup requests are stalled and transfers on another endpoints
+ * are refused. All epfiles, except ep0, are deleted so there
+ * is no way to perform any operations on them.
+ *
+ * This state is set after closing all functionfs files, when
+ * mount parameter "no_disconnect=1" has been set. Function will
+ * remain in deactivated state until filesystem is umounted or
+ * ep0 is opened again. In the second case functionfs state will
+ * be reset, and it will be ready for descriptors and strings
+ * writing.
+ *
+ * This is useful only when functionfs is composed to gadget
+ * with another function which can perform some critical
+ * operations, and it's strongly desired to have this operations
+ * completed, even after functionfs files closure.
+ */
+ FFS_DEACTIVATED,
+
+ /*
* All endpoints have been closed. This state is also set if
* we encounter an unrecoverable error. The only
* unrecoverable error is situation when after reading strings
@@ -251,6 +272,10 @@ struct ffs_data {
kgid_t gid;
} file_perms;
+ struct eventfd_ctx *ffs_eventfd;
+ bool no_disconnect;
+ struct work_struct reset_work;
+
/*
* The endpoint files, filled by ffs_epfiles_create(),
* destroyed by ffs_epfiles_destroy().
diff --git a/drivers/usb/gadget/function/u_uac1.c b/drivers/usb/gadget/function/u_uac1.c
index 53842a1b947f..c78c84138a28 100644
--- a/drivers/usb/gadget/function/u_uac1.c
+++ b/drivers/usb/gadget/function/u_uac1.c
@@ -308,8 +308,7 @@ int gaudio_setup(struct gaudio *card)
*/
void gaudio_cleanup(struct gaudio *the_card)
{
- if (the_card) {
+ if (the_card)
gaudio_close_snd_dev(the_card);
- }
}
diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h
index f8b17fe82efe..fe386df6dd3e 100644
--- a/drivers/usb/gadget/function/u_uac1.h
+++ b/drivers/usb/gadget/function/u_uac1.h
@@ -70,7 +70,6 @@ struct f_uac1_opts {
unsigned fn_play_alloc:1;
unsigned fn_cap_alloc:1;
unsigned fn_cntl_alloc:1;
- struct gaudio *card;
struct mutex lock;
int refcnt;
};
diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h
index 2a8dfdff0332..4676b60a5063 100644
--- a/drivers/usb/gadget/function/u_uvc.h
+++ b/drivers/usb/gadget/function/u_uvc.h
@@ -17,8 +17,9 @@
#define U_UVC_H
#include <linux/usb/composite.h>
+#include <linux/usb/video.h>
-#define to_f_uvc_opts(f) container_of(f, struct f_uvc_opts, func_inst)
+#define fi_to_f_uvc_opts(f) container_of(f, struct f_uvc_opts, func_inst)
struct f_uvc_opts {
struct usb_function_instance func_inst;
@@ -26,11 +27,60 @@ struct f_uvc_opts {
unsigned int streaming_interval;
unsigned int streaming_maxpacket;
unsigned int streaming_maxburst;
+
+ /*
+ * Control descriptors array pointers for full-/high-speed and
+ * super-speed. They point by default to the uvc_fs_control_cls and
+ * uvc_ss_control_cls arrays respectively. Legacy gadgets must
+ * override them in their gadget bind callback.
+ */
const struct uvc_descriptor_header * const *fs_control;
const struct uvc_descriptor_header * const *ss_control;
+
+ /*
+ * Streaming descriptors array pointers for full-speed, high-speed and
+ * super-speed. They will point to the uvc_[fhs]s_streaming_cls arrays
+ * for configfs-based gadgets. Legacy gadgets must initialize them in
+ * their gadget bind callback.
+ */
const struct uvc_descriptor_header * const *fs_streaming;
const struct uvc_descriptor_header * const *hs_streaming;
const struct uvc_descriptor_header * const *ss_streaming;
+
+ /* Default control descriptors for configfs-based gadgets. */
+ struct uvc_camera_terminal_descriptor uvc_camera_terminal;
+ struct uvc_processing_unit_descriptor uvc_processing;
+ struct uvc_output_terminal_descriptor uvc_output_terminal;
+ struct uvc_color_matching_descriptor uvc_color_matching;
+
+ /*
+ * Control descriptors pointers arrays for full-/high-speed and
+ * super-speed. The first element is a configurable control header
+ * descriptor, the other elements point to the fixed default control
+ * descriptors. Used by configfs only, must not be touched by legacy
+ * gadgets.
+ */
+ struct uvc_descriptor_header *uvc_fs_control_cls[5];
+ struct uvc_descriptor_header *uvc_ss_control_cls[5];
+
+ /*
+ * Streaming descriptors for full-speed, high-speed and super-speed.
+ * Used by configfs only, must not be touched by legacy gadgets. The
+ * arrays are allocated at runtime as the number of descriptors isn't
+ * known in advance.
+ */
+ struct uvc_descriptor_header **uvc_fs_streaming_cls;
+ struct uvc_descriptor_header **uvc_hs_streaming_cls;
+ struct uvc_descriptor_header **uvc_ss_streaming_cls;
+
+ /*
+ * Read/write access to configfs attributes is handled by configfs.
+ *
+ * This lock protects the descriptors from concurrent access by
+ * read/write and symlink creation/removal.
+ */
+ struct mutex lock;
+ int refcnt;
};
void uvc_set_trace_param(unsigned int trace);
diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c
new file mode 100644
index 000000000000..3c0467bcb14f
--- /dev/null
+++ b/drivers/usb/gadget/function/uvc_configfs.c
@@ -0,0 +1,2468 @@
+/*
+ * uvc_configfs.c
+ *
+ * Configfs support for the uvc function.
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include "u_uvc.h"
+#include "uvc_configfs.h"
+
+#define UVCG_STREAMING_CONTROL_SIZE 1
+
+#define CONFIGFS_ATTR_OPS_RO(_item) \
+static ssize_t _item##_attr_show(struct config_item *item, \
+ struct configfs_attribute *attr, \
+ char *page) \
+{ \
+ struct _item *_item = to_##_item(item); \
+ struct _item##_attribute *_item##_attr = \
+ container_of(attr, struct _item##_attribute, attr); \
+ ssize_t ret = 0; \
+ \
+ if (_item##_attr->show) \
+ ret = _item##_attr->show(_item, page); \
+ return ret; \
+}
+
+static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item);
+
+/* control/header/<NAME> */
+DECLARE_UVC_HEADER_DESCRIPTOR(1);
+
+struct uvcg_control_header {
+ struct config_item item;
+ struct UVC_HEADER_DESCRIPTOR(1) desc;
+ unsigned linked;
+};
+
+static struct uvcg_control_header *to_uvcg_control_header(struct config_item *item)
+{
+ return container_of(item, struct uvcg_control_header, item);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_control_header);
+CONFIGFS_ATTR_OPS(uvcg_control_header);
+
+static struct configfs_item_operations uvcg_control_header_item_ops = {
+ .show_attribute = uvcg_control_header_attr_show,
+ .store_attribute = uvcg_control_header_attr_store,
+};
+
+#define UVCG_CTRL_HDR_ATTR(cname, aname, conv, str2u, uxx, vnoc, limit) \
+static ssize_t uvcg_control_header_##cname##_show( \
+ struct uvcg_control_header *ch, char *page) \
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;\
+ int result; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = ch->item.ci_parent->ci_parent->ci_parent; \
+ opts = to_f_uvc_opts(opts_item); \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%d\n", conv(ch->desc.aname)); \
+ mutex_unlock(&opts->lock); \
+ \
+ mutex_unlock(su_mutex); \
+ return result; \
+} \
+ \
+static ssize_t \
+uvcg_control_header_##cname##_store(struct uvcg_control_header *ch, \
+ const char *page, size_t len) \
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;\
+ int ret; \
+ uxx num; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = ch->item.ci_parent->ci_parent->ci_parent; \
+ opts = to_f_uvc_opts(opts_item); \
+ \
+ mutex_lock(&opts->lock); \
+ if (ch->linked || opts->refcnt) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ ret = str2u(page, 0, &num); \
+ if (ret) \
+ goto end; \
+ \
+ if (num > limit) { \
+ ret = -EINVAL; \
+ goto end; \
+ } \
+ ch->desc.aname = vnoc(num); \
+ ret = len; \
+end: \
+ mutex_unlock(&opts->lock); \
+ mutex_unlock(su_mutex); \
+ return ret; \
+} \
+ \
+static struct uvcg_control_header_attribute \
+ uvcg_control_header_##cname = \
+ __CONFIGFS_ATTR(aname, S_IRUGO | S_IWUSR, \
+ uvcg_control_header_##cname##_show, \
+ uvcg_control_header_##cname##_store)
+
+UVCG_CTRL_HDR_ATTR(bcd_uvc, bcdUVC, le16_to_cpu, kstrtou16, u16, cpu_to_le16,
+ 0xffff);
+
+UVCG_CTRL_HDR_ATTR(dw_clock_frequency, dwClockFrequency, le32_to_cpu, kstrtou32,
+ u32, cpu_to_le32, 0x7fffffff);
+
+#undef UVCG_CTRL_HDR_ATTR
+
+static struct configfs_attribute *uvcg_control_header_attrs[] = {
+ &uvcg_control_header_bcd_uvc.attr,
+ &uvcg_control_header_dw_clock_frequency.attr,
+ NULL,
+};
+
+static struct config_item_type uvcg_control_header_type = {
+ .ct_item_ops = &uvcg_control_header_item_ops,
+ .ct_attrs = uvcg_control_header_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_item *uvcg_control_header_make(struct config_group *group,
+ const char *name)
+{
+ struct uvcg_control_header *h;
+
+ h = kzalloc(sizeof(*h), GFP_KERNEL);
+ if (!h)
+ return ERR_PTR(-ENOMEM);
+
+ h->desc.bLength = UVC_DT_HEADER_SIZE(1);
+ h->desc.bDescriptorType = USB_DT_CS_INTERFACE;
+ h->desc.bDescriptorSubType = UVC_VC_HEADER;
+ h->desc.bcdUVC = cpu_to_le16(0x0100);
+ h->desc.dwClockFrequency = cpu_to_le32(48000000);
+
+ config_item_init_type_name(&h->item, name, &uvcg_control_header_type);
+
+ return &h->item;
+}
+
+static void uvcg_control_header_drop(struct config_group *group,
+ struct config_item *item)
+{
+ struct uvcg_control_header *h = to_uvcg_control_header(item);
+
+ kfree(h);
+}
+
+/* control/header */
+static struct uvcg_control_header_grp {
+ struct config_group group;
+} uvcg_control_header_grp;
+
+static struct configfs_group_operations uvcg_control_header_grp_ops = {
+ .make_item = uvcg_control_header_make,
+ .drop_item = uvcg_control_header_drop,
+};
+
+static struct config_item_type uvcg_control_header_grp_type = {
+ .ct_group_ops = &uvcg_control_header_grp_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+/* control/processing/default */
+static struct uvcg_default_processing {
+ struct config_group group;
+} uvcg_default_processing;
+
+static inline struct uvcg_default_processing
+*to_uvcg_default_processing(struct config_item *item)
+{
+ return container_of(to_config_group(item),
+ struct uvcg_default_processing, group);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_default_processing);
+CONFIGFS_ATTR_OPS_RO(uvcg_default_processing);
+
+static struct configfs_item_operations uvcg_default_processing_item_ops = {
+ .show_attribute = uvcg_default_processing_attr_show,
+};
+
+#define UVCG_DEFAULT_PROCESSING_ATTR(cname, aname, conv) \
+static ssize_t uvcg_default_processing_##cname##_show( \
+ struct uvcg_default_processing *dp, char *page) \
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &dp->group.cg_subsys->su_mutex; \
+ struct uvc_processing_unit_descriptor *pd; \
+ int result; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = dp->group.cg_item.ci_parent->ci_parent->ci_parent; \
+ opts = to_f_uvc_opts(opts_item); \
+ pd = &opts->uvc_processing; \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%d\n", conv(pd->aname)); \
+ mutex_unlock(&opts->lock); \
+ \
+ mutex_unlock(su_mutex); \
+ return result; \
+} \
+ \
+static struct uvcg_default_processing_attribute \
+ uvcg_default_processing_##cname = \
+ __CONFIGFS_ATTR_RO(aname, uvcg_default_processing_##cname##_show)
+
+#define identity_conv(x) (x)
+
+UVCG_DEFAULT_PROCESSING_ATTR(b_unit_id, bUnitID, identity_conv);
+UVCG_DEFAULT_PROCESSING_ATTR(b_source_id, bSourceID, identity_conv);
+UVCG_DEFAULT_PROCESSING_ATTR(w_max_multiplier, wMaxMultiplier, le16_to_cpu);
+UVCG_DEFAULT_PROCESSING_ATTR(i_processing, iProcessing, identity_conv);
+
+#undef identity_conv
+
+#undef UVCG_DEFAULT_PROCESSING_ATTR
+
+static ssize_t uvcg_default_processing_bm_controls_show(
+ struct uvcg_default_processing *dp, char *page)
+{
+ struct f_uvc_opts *opts;
+ struct config_item *opts_item;
+ struct mutex *su_mutex = &dp->group.cg_subsys->su_mutex;
+ struct uvc_processing_unit_descriptor *pd;
+ int result, i;
+ char *pg = page;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ opts_item = dp->group.cg_item.ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+ pd = &opts->uvc_processing;
+
+ mutex_lock(&opts->lock);
+ for (result = 0, i = 0; i < pd->bControlSize; ++i) {
+ result += sprintf(pg, "%d\n", pd->bmControls[i]);
+ pg = page + result;
+ }
+ mutex_unlock(&opts->lock);
+
+ mutex_unlock(su_mutex);
+
+ return result;
+}
+
+static struct uvcg_default_processing_attribute
+ uvcg_default_processing_bm_controls =
+ __CONFIGFS_ATTR_RO(bmControls,
+ uvcg_default_processing_bm_controls_show);
+
+static struct configfs_attribute *uvcg_default_processing_attrs[] = {
+ &uvcg_default_processing_b_unit_id.attr,
+ &uvcg_default_processing_b_source_id.attr,
+ &uvcg_default_processing_w_max_multiplier.attr,
+ &uvcg_default_processing_bm_controls.attr,
+ &uvcg_default_processing_i_processing.attr,
+ NULL,
+};
+
+static struct config_item_type uvcg_default_processing_type = {
+ .ct_item_ops = &uvcg_default_processing_item_ops,
+ .ct_attrs = uvcg_default_processing_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/* struct uvcg_processing {}; */
+
+static struct config_group *uvcg_processing_default_groups[] = {
+ &uvcg_default_processing.group,
+ NULL,
+};
+
+/* control/processing */
+static struct uvcg_processing_grp {
+ struct config_group group;
+} uvcg_processing_grp;
+
+static struct config_item_type uvcg_processing_grp_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+/* control/terminal/camera/default */
+static struct uvcg_default_camera {
+ struct config_group group;
+} uvcg_default_camera;
+
+static inline struct uvcg_default_camera
+*to_uvcg_default_camera(struct config_item *item)
+{
+ return container_of(to_config_group(item),
+ struct uvcg_default_camera, group);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_default_camera);
+CONFIGFS_ATTR_OPS_RO(uvcg_default_camera);
+
+static struct configfs_item_operations uvcg_default_camera_item_ops = {
+ .show_attribute = uvcg_default_camera_attr_show,
+};
+
+#define UVCG_DEFAULT_CAMERA_ATTR(cname, aname, conv) \
+static ssize_t uvcg_default_camera_##cname##_show( \
+ struct uvcg_default_camera *dc, char *page) \
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex; \
+ struct uvc_camera_terminal_descriptor *cd; \
+ int result; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent-> \
+ ci_parent; \
+ opts = to_f_uvc_opts(opts_item); \
+ cd = &opts->uvc_camera_terminal; \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%d\n", conv(cd->aname)); \
+ mutex_unlock(&opts->lock); \
+ \
+ mutex_unlock(su_mutex); \
+ \
+ return result; \
+} \
+ \
+static struct uvcg_default_camera_attribute \
+ uvcg_default_camera_##cname = \
+ __CONFIGFS_ATTR_RO(aname, uvcg_default_camera_##cname##_show)
+
+#define identity_conv(x) (x)
+
+UVCG_DEFAULT_CAMERA_ATTR(b_terminal_id, bTerminalID, identity_conv);
+UVCG_DEFAULT_CAMERA_ATTR(w_terminal_type, wTerminalType, le16_to_cpu);
+UVCG_DEFAULT_CAMERA_ATTR(b_assoc_terminal, bAssocTerminal, identity_conv);
+UVCG_DEFAULT_CAMERA_ATTR(i_terminal, iTerminal, identity_conv);
+UVCG_DEFAULT_CAMERA_ATTR(w_objective_focal_length_min, wObjectiveFocalLengthMin,
+ le16_to_cpu);
+UVCG_DEFAULT_CAMERA_ATTR(w_objective_focal_length_max, wObjectiveFocalLengthMax,
+ le16_to_cpu);
+UVCG_DEFAULT_CAMERA_ATTR(w_ocular_focal_length, wOcularFocalLength,
+ le16_to_cpu);
+
+#undef identity_conv
+
+#undef UVCG_DEFAULT_CAMERA_ATTR
+
+static ssize_t uvcg_default_camera_bm_controls_show(
+ struct uvcg_default_camera *dc, char *page)
+{
+ struct f_uvc_opts *opts;
+ struct config_item *opts_item;
+ struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex;
+ struct uvc_camera_terminal_descriptor *cd;
+ int result, i;
+ char *pg = page;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent->
+ ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+ cd = &opts->uvc_camera_terminal;
+
+ mutex_lock(&opts->lock);
+ for (result = 0, i = 0; i < cd->bControlSize; ++i) {
+ result += sprintf(pg, "%d\n", cd->bmControls[i]);
+ pg = page + result;
+ }
+ mutex_unlock(&opts->lock);
+
+ mutex_unlock(su_mutex);
+ return result;
+}
+
+static struct uvcg_default_camera_attribute
+ uvcg_default_camera_bm_controls =
+ __CONFIGFS_ATTR_RO(bmControls, uvcg_default_camera_bm_controls_show);
+
+static struct configfs_attribute *uvcg_default_camera_attrs[] = {
+ &uvcg_default_camera_b_terminal_id.attr,
+ &uvcg_default_camera_w_terminal_type.attr,
+ &uvcg_default_camera_b_assoc_terminal.attr,
+ &uvcg_default_camera_i_terminal.attr,
+ &uvcg_default_camera_w_objective_focal_length_min.attr,
+ &uvcg_default_camera_w_objective_focal_length_max.attr,
+ &uvcg_default_camera_w_ocular_focal_length.attr,
+ &uvcg_default_camera_bm_controls.attr,
+ NULL,
+};
+
+static struct config_item_type uvcg_default_camera_type = {
+ .ct_item_ops = &uvcg_default_camera_item_ops,
+ .ct_attrs = uvcg_default_camera_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/* struct uvcg_camera {}; */
+
+static struct config_group *uvcg_camera_default_groups[] = {
+ &uvcg_default_camera.group,
+ NULL,
+};
+
+/* control/terminal/camera */
+static struct uvcg_camera_grp {
+ struct config_group group;
+} uvcg_camera_grp;
+
+static struct config_item_type uvcg_camera_grp_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+/* control/terminal/output/default */
+static struct uvcg_default_output {
+ struct config_group group;
+} uvcg_default_output;
+
+static inline struct uvcg_default_output
+*to_uvcg_default_output(struct config_item *item)
+{
+ return container_of(to_config_group(item),
+ struct uvcg_default_output, group);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_default_output);
+CONFIGFS_ATTR_OPS_RO(uvcg_default_output);
+
+static struct configfs_item_operations uvcg_default_output_item_ops = {
+ .show_attribute = uvcg_default_output_attr_show,
+};
+
+#define UVCG_DEFAULT_OUTPUT_ATTR(cname, aname, conv) \
+static ssize_t uvcg_default_output_##cname##_show( \
+ struct uvcg_default_output *dout, char *page) \
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &dout->group.cg_subsys->su_mutex; \
+ struct uvc_output_terminal_descriptor *cd; \
+ int result; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = dout->group.cg_item.ci_parent->ci_parent-> \
+ ci_parent->ci_parent; \
+ opts = to_f_uvc_opts(opts_item); \
+ cd = &opts->uvc_output_terminal; \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%d\n", conv(cd->aname)); \
+ mutex_unlock(&opts->lock); \
+ \
+ mutex_unlock(su_mutex); \
+ \
+ return result; \
+} \
+ \
+static struct uvcg_default_output_attribute \
+ uvcg_default_output_##cname = \
+ __CONFIGFS_ATTR_RO(aname, uvcg_default_output_##cname##_show)
+
+#define identity_conv(x) (x)
+
+UVCG_DEFAULT_OUTPUT_ATTR(b_terminal_id, bTerminalID, identity_conv);
+UVCG_DEFAULT_OUTPUT_ATTR(w_terminal_type, wTerminalType, le16_to_cpu);
+UVCG_DEFAULT_OUTPUT_ATTR(b_assoc_terminal, bAssocTerminal, identity_conv);
+UVCG_DEFAULT_OUTPUT_ATTR(b_source_id, bSourceID, identity_conv);
+UVCG_DEFAULT_OUTPUT_ATTR(i_terminal, iTerminal, identity_conv);
+
+#undef identity_conv
+
+#undef UVCG_DEFAULT_OUTPUT_ATTR
+
+static struct configfs_attribute *uvcg_default_output_attrs[] = {
+ &uvcg_default_output_b_terminal_id.attr,
+ &uvcg_default_output_w_terminal_type.attr,
+ &uvcg_default_output_b_assoc_terminal.attr,
+ &uvcg_default_output_b_source_id.attr,
+ &uvcg_default_output_i_terminal.attr,
+ NULL,
+};
+
+static struct config_item_type uvcg_default_output_type = {
+ .ct_item_ops = &uvcg_default_output_item_ops,
+ .ct_attrs = uvcg_default_output_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/* struct uvcg_output {}; */
+
+static struct config_group *uvcg_output_default_groups[] = {
+ &uvcg_default_output.group,
+ NULL,
+};
+
+/* control/terminal/output */
+static struct uvcg_output_grp {
+ struct config_group group;
+} uvcg_output_grp;
+
+static struct config_item_type uvcg_output_grp_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *uvcg_terminal_default_groups[] = {
+ &uvcg_camera_grp.group,
+ &uvcg_output_grp.group,
+ NULL,
+};
+
+/* control/terminal */
+static struct uvcg_terminal_grp {
+ struct config_group group;
+} uvcg_terminal_grp;
+
+static struct config_item_type uvcg_terminal_grp_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+/* control/class/{fs} */
+static struct uvcg_control_class {
+ struct config_group group;
+} uvcg_control_class_fs, uvcg_control_class_ss;
+
+
+static inline struct uvc_descriptor_header
+**uvcg_get_ctl_class_arr(struct config_item *i, struct f_uvc_opts *o)
+{
+ struct uvcg_control_class *cl = container_of(to_config_group(i),
+ struct uvcg_control_class, group);
+
+ if (cl == &uvcg_control_class_fs)
+ return o->uvc_fs_control_cls;
+
+ if (cl == &uvcg_control_class_ss)
+ return o->uvc_ss_control_cls;
+
+ return NULL;
+}
+
+static int uvcg_control_class_allow_link(struct config_item *src,
+ struct config_item *target)
+{
+ struct config_item *control, *header;
+ struct f_uvc_opts *opts;
+ struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+ struct uvc_descriptor_header **class_array;
+ struct uvcg_control_header *target_hdr;
+ int ret = -EINVAL;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ control = src->ci_parent->ci_parent;
+ header = config_group_find_item(to_config_group(control), "header");
+ if (!header || target->ci_parent != header)
+ goto out;
+
+ opts = to_f_uvc_opts(control->ci_parent);
+
+ mutex_lock(&opts->lock);
+
+ class_array = uvcg_get_ctl_class_arr(src, opts);
+ if (!class_array)
+ goto unlock;
+ if (opts->refcnt || class_array[0]) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ target_hdr = to_uvcg_control_header(target);
+ ++target_hdr->linked;
+ class_array[0] = (struct uvc_descriptor_header *)&target_hdr->desc;
+ ret = 0;
+
+unlock:
+ mutex_unlock(&opts->lock);
+out:
+ mutex_unlock(su_mutex);
+ return ret;
+}
+
+static int uvcg_control_class_drop_link(struct config_item *src,
+ struct config_item *target)
+{
+ struct config_item *control, *header;
+ struct f_uvc_opts *opts;
+ struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+ struct uvc_descriptor_header **class_array;
+ struct uvcg_control_header *target_hdr;
+ int ret = -EINVAL;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ control = src->ci_parent->ci_parent;
+ header = config_group_find_item(to_config_group(control), "header");
+ if (!header || target->ci_parent != header)
+ goto out;
+
+ opts = to_f_uvc_opts(control->ci_parent);
+
+ mutex_lock(&opts->lock);
+
+ class_array = uvcg_get_ctl_class_arr(src, opts);
+ if (!class_array)
+ goto unlock;
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ target_hdr = to_uvcg_control_header(target);
+ --target_hdr->linked;
+ class_array[0] = NULL;
+ ret = 0;
+
+unlock:
+ mutex_unlock(&opts->lock);
+out:
+ mutex_unlock(su_mutex);
+ return ret;
+}
+
+static struct configfs_item_operations uvcg_control_class_item_ops = {
+ .allow_link = uvcg_control_class_allow_link,
+ .drop_link = uvcg_control_class_drop_link,
+};
+
+static struct config_item_type uvcg_control_class_type = {
+ .ct_item_ops = &uvcg_control_class_item_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *uvcg_control_class_default_groups[] = {
+ &uvcg_control_class_fs.group,
+ &uvcg_control_class_ss.group,
+ NULL,
+};
+
+/* control/class */
+static struct uvcg_control_class_grp {
+ struct config_group group;
+} uvcg_control_class_grp;
+
+static struct config_item_type uvcg_control_class_grp_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *uvcg_control_default_groups[] = {
+ &uvcg_control_header_grp.group,
+ &uvcg_processing_grp.group,
+ &uvcg_terminal_grp.group,
+ &uvcg_control_class_grp.group,
+ NULL,
+};
+
+/* control */
+static struct uvcg_control_grp {
+ struct config_group group;
+} uvcg_control_grp;
+
+static struct config_item_type uvcg_control_grp_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+/* streaming/uncompressed */
+static struct uvcg_uncompressed_grp {
+ struct config_group group;
+} uvcg_uncompressed_grp;
+
+/* streaming/mjpeg */
+static struct uvcg_mjpeg_grp {
+ struct config_group group;
+} uvcg_mjpeg_grp;
+
+static struct config_item *fmt_parent[] = {
+ &uvcg_uncompressed_grp.group.cg_item,
+ &uvcg_mjpeg_grp.group.cg_item,
+};
+
+enum uvcg_format_type {
+ UVCG_UNCOMPRESSED = 0,
+ UVCG_MJPEG,
+};
+
+struct uvcg_format {
+ struct config_group group;
+ enum uvcg_format_type type;
+ unsigned linked;
+ unsigned num_frames;
+ __u8 bmaControls[UVCG_STREAMING_CONTROL_SIZE];
+};
+
+static struct uvcg_format *to_uvcg_format(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct uvcg_format, group);
+}
+
+static ssize_t uvcg_format_bma_controls_show(struct uvcg_format *f, char *page)
+{
+ struct f_uvc_opts *opts;
+ struct config_item *opts_item;
+ struct mutex *su_mutex = &f->group.cg_subsys->su_mutex;
+ int result, i;
+ char *pg = page;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ opts_item = f->group.cg_item.ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+ result = sprintf(pg, "0x");
+ pg += result;
+ for (i = 0; i < UVCG_STREAMING_CONTROL_SIZE; ++i) {
+ result += sprintf(pg, "%x\n", f->bmaControls[i]);
+ pg = page + result;
+ }
+ mutex_unlock(&opts->lock);
+
+ mutex_unlock(su_mutex);
+ return result;
+}
+
+static ssize_t uvcg_format_bma_controls_store(struct uvcg_format *ch,
+ const char *page, size_t len)
+{
+ struct f_uvc_opts *opts;
+ struct config_item *opts_item;
+ struct mutex *su_mutex = &ch->group.cg_subsys->su_mutex;
+ int ret = -EINVAL;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ opts_item = ch->group.cg_item.ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+ if (ch->linked || opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ if (len < 4 || *page != '0' ||
+ (*(page + 1) != 'x' && *(page + 1) != 'X'))
+ goto end;
+ ret = hex2bin(ch->bmaControls, page + 2, 1);
+ if (ret < 0)
+ goto end;
+ ret = len;
+end:
+ mutex_unlock(&opts->lock);
+ mutex_unlock(su_mutex);
+ return ret;
+}
+
+struct uvcg_format_ptr {
+ struct uvcg_format *fmt;
+ struct list_head entry;
+};
+
+/* streaming/header/<NAME> */
+struct uvcg_streaming_header {
+ struct config_item item;
+ struct uvc_input_header_descriptor desc;
+ unsigned linked;
+ struct list_head formats;
+ unsigned num_fmt;
+};
+
+static struct uvcg_streaming_header *to_uvcg_streaming_header(struct config_item *item)
+{
+ return container_of(item, struct uvcg_streaming_header, item);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_streaming_header);
+CONFIGFS_ATTR_OPS(uvcg_streaming_header);
+
+static int uvcg_streaming_header_allow_link(struct config_item *src,
+ struct config_item *target)
+{
+ struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+ struct config_item *opts_item;
+ struct f_uvc_opts *opts;
+ struct uvcg_streaming_header *src_hdr;
+ struct uvcg_format *target_fmt = NULL;
+ struct uvcg_format_ptr *format_ptr;
+ int i, ret = -EINVAL;
+
+ src_hdr = to_uvcg_streaming_header(src);
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ opts_item = src->ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+
+ if (src_hdr->linked) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(fmt_parent); ++i)
+ if (target->ci_parent == fmt_parent[i])
+ break;
+ if (i == ARRAY_SIZE(fmt_parent))
+ goto out;
+
+ target_fmt = container_of(to_config_group(target), struct uvcg_format,
+ group);
+ if (!target_fmt)
+ goto out;
+
+ format_ptr = kzalloc(sizeof(*format_ptr), GFP_KERNEL);
+ if (!format_ptr) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ ret = 0;
+ format_ptr->fmt = target_fmt;
+ list_add_tail(&format_ptr->entry, &src_hdr->formats);
+ ++src_hdr->num_fmt;
+
+out:
+ mutex_unlock(&opts->lock);
+ mutex_unlock(su_mutex);
+ return ret;
+}
+
+static int uvcg_streaming_header_drop_link(struct config_item *src,
+ struct config_item *target)
+{
+ struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+ struct config_item *opts_item;
+ struct f_uvc_opts *opts;
+ struct uvcg_streaming_header *src_hdr;
+ struct uvcg_format *target_fmt = NULL;
+ struct uvcg_format_ptr *format_ptr, *tmp;
+ int ret = -EINVAL;
+
+ src_hdr = to_uvcg_streaming_header(src);
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ opts_item = src->ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+ target_fmt = container_of(to_config_group(target), struct uvcg_format,
+ group);
+ if (!target_fmt)
+ goto out;
+
+ list_for_each_entry_safe(format_ptr, tmp, &src_hdr->formats, entry)
+ if (format_ptr->fmt == target_fmt) {
+ list_del(&format_ptr->entry);
+ kfree(format_ptr);
+ --src_hdr->num_fmt;
+ break;
+ }
+
+out:
+ mutex_unlock(&opts->lock);
+ mutex_unlock(su_mutex);
+ return ret;
+
+}
+
+static struct configfs_item_operations uvcg_streaming_header_item_ops = {
+ .show_attribute = uvcg_streaming_header_attr_show,
+ .store_attribute = uvcg_streaming_header_attr_store,
+ .allow_link = uvcg_streaming_header_allow_link,
+ .drop_link = uvcg_streaming_header_drop_link,
+};
+
+#define UVCG_STREAMING_HEADER_ATTR(cname, aname, conv) \
+static ssize_t uvcg_streaming_header_##cname##_show( \
+ struct uvcg_streaming_header *sh, char *page) \
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &sh->item.ci_group->cg_subsys->su_mutex;\
+ int result; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = sh->item.ci_parent->ci_parent->ci_parent; \
+ opts = to_f_uvc_opts(opts_item); \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%d\n", conv(sh->desc.aname)); \
+ mutex_unlock(&opts->lock); \
+ \
+ mutex_unlock(su_mutex); \
+ return result; \
+} \
+ \
+static struct uvcg_streaming_header_attribute \
+ uvcg_streaming_header_##cname = \
+ __CONFIGFS_ATTR_RO(aname, uvcg_streaming_header_##cname##_show)
+
+#define identity_conv(x) (x)
+
+UVCG_STREAMING_HEADER_ATTR(bm_info, bmInfo, identity_conv);
+UVCG_STREAMING_HEADER_ATTR(b_terminal_link, bTerminalLink, identity_conv);
+UVCG_STREAMING_HEADER_ATTR(b_still_capture_method, bStillCaptureMethod,
+ identity_conv);
+UVCG_STREAMING_HEADER_ATTR(b_trigger_support, bTriggerSupport, identity_conv);
+UVCG_STREAMING_HEADER_ATTR(b_trigger_usage, bTriggerUsage, identity_conv);
+
+#undef identity_conv
+
+#undef UVCG_STREAMING_HEADER_ATTR
+
+static struct configfs_attribute *uvcg_streaming_header_attrs[] = {
+ &uvcg_streaming_header_bm_info.attr,
+ &uvcg_streaming_header_b_terminal_link.attr,
+ &uvcg_streaming_header_b_still_capture_method.attr,
+ &uvcg_streaming_header_b_trigger_support.attr,
+ &uvcg_streaming_header_b_trigger_usage.attr,
+ NULL,
+};
+
+static struct config_item_type uvcg_streaming_header_type = {
+ .ct_item_ops = &uvcg_streaming_header_item_ops,
+ .ct_attrs = uvcg_streaming_header_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_item
+*uvcg_streaming_header_make(struct config_group *group, const char *name)
+{
+ struct uvcg_streaming_header *h;
+
+ h = kzalloc(sizeof(*h), GFP_KERNEL);
+ if (!h)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&h->formats);
+ h->desc.bDescriptorType = USB_DT_CS_INTERFACE;
+ h->desc.bDescriptorSubType = UVC_VS_INPUT_HEADER;
+ h->desc.bTerminalLink = 3;
+ h->desc.bControlSize = UVCG_STREAMING_CONTROL_SIZE;
+
+ config_item_init_type_name(&h->item, name, &uvcg_streaming_header_type);
+
+ return &h->item;
+}
+
+static void uvcg_streaming_header_drop(struct config_group *group,
+ struct config_item *item)
+{
+ struct uvcg_streaming_header *h = to_uvcg_streaming_header(item);
+
+ kfree(h);
+}
+
+/* streaming/header */
+static struct uvcg_streaming_header_grp {
+ struct config_group group;
+} uvcg_streaming_header_grp;
+
+static struct configfs_group_operations uvcg_streaming_header_grp_ops = {
+ .make_item = uvcg_streaming_header_make,
+ .drop_item = uvcg_streaming_header_drop,
+};
+
+static struct config_item_type uvcg_streaming_header_grp_type = {
+ .ct_group_ops = &uvcg_streaming_header_grp_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+/* streaming/<mode>/<format>/<NAME> */
+struct uvcg_frame {
+ struct {
+ u8 b_length;
+ u8 b_descriptor_type;
+ u8 b_descriptor_subtype;
+ u8 b_frame_index;
+ u8 bm_capabilities;
+ u16 w_width;
+ u16 w_height;
+ u32 dw_min_bit_rate;
+ u32 dw_max_bit_rate;
+ u32 dw_max_video_frame_buffer_size;
+ u32 dw_default_frame_interval;
+ u8 b_frame_interval_type;
+ } __attribute__((packed)) frame;
+ u32 *dw_frame_interval;
+ enum uvcg_format_type fmt_type;
+ struct config_item item;
+};
+
+static struct uvcg_frame *to_uvcg_frame(struct config_item *item)
+{
+ return container_of(item, struct uvcg_frame, item);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_frame);
+CONFIGFS_ATTR_OPS(uvcg_frame);
+
+static struct configfs_item_operations uvcg_frame_item_ops = {
+ .show_attribute = uvcg_frame_attr_show,
+ .store_attribute = uvcg_frame_attr_store,
+};
+
+#define UVCG_FRAME_ATTR(cname, aname, to_cpu_endian, to_little_endian, bits) \
+static ssize_t uvcg_frame_##cname##_show(struct uvcg_frame *f, char *page)\
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &f->item.ci_group->cg_subsys->su_mutex;\
+ int result; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = f->item.ci_parent->ci_parent->ci_parent->ci_parent; \
+ opts = to_f_uvc_opts(opts_item); \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%d\n", to_cpu_endian(f->frame.cname)); \
+ mutex_unlock(&opts->lock); \
+ \
+ mutex_unlock(su_mutex); \
+ return result; \
+} \
+ \
+static ssize_t uvcg_frame_##cname##_store(struct uvcg_frame *f, \
+ const char *page, size_t len)\
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct uvcg_format *fmt; \
+ struct mutex *su_mutex = &f->item.ci_group->cg_subsys->su_mutex;\
+ int ret; \
+ u##bits num; \
+ \
+ ret = kstrtou##bits(page, 0, &num); \
+ if (ret) \
+ return ret; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = f->item.ci_parent->ci_parent->ci_parent->ci_parent; \
+ opts = to_f_uvc_opts(opts_item); \
+ fmt = to_uvcg_format(f->item.ci_parent); \
+ \
+ mutex_lock(&opts->lock); \
+ if (fmt->linked || opts->refcnt) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ f->frame.cname = to_little_endian(num); \
+ ret = len; \
+end: \
+ mutex_unlock(&opts->lock); \
+ mutex_unlock(su_mutex); \
+ return ret; \
+} \
+ \
+static struct uvcg_frame_attribute \
+ uvcg_frame_##cname = \
+ __CONFIGFS_ATTR(aname, S_IRUGO | S_IWUSR, \
+ uvcg_frame_##cname##_show, \
+ uvcg_frame_##cname##_store)
+
+#define noop_conversion(x) (x)
+
+UVCG_FRAME_ATTR(bm_capabilities, bmCapabilities, noop_conversion,
+ noop_conversion, 8);
+UVCG_FRAME_ATTR(w_width, wWidth, le16_to_cpu, cpu_to_le16, 16);
+UVCG_FRAME_ATTR(w_height, wHeight, le16_to_cpu, cpu_to_le16, 16);
+UVCG_FRAME_ATTR(dw_min_bit_rate, dwMinBitRate, le32_to_cpu, cpu_to_le32, 32);
+UVCG_FRAME_ATTR(dw_max_bit_rate, dwMaxBitRate, le32_to_cpu, cpu_to_le32, 32);
+UVCG_FRAME_ATTR(dw_max_video_frame_buffer_size, dwMaxVideoFrameBufferSize,
+ le32_to_cpu, cpu_to_le32, 32);
+UVCG_FRAME_ATTR(dw_default_frame_interval, dwDefaultFrameInterval,
+ le32_to_cpu, cpu_to_le32, 32);
+
+#undef noop_conversion
+
+#undef UVCG_FRAME_ATTR
+
+static ssize_t uvcg_frame_dw_frame_interval_show(struct uvcg_frame *frm,
+ char *page)
+{
+ struct f_uvc_opts *opts;
+ struct config_item *opts_item;
+ struct mutex *su_mutex = &frm->item.ci_group->cg_subsys->su_mutex;
+ int result, i;
+ char *pg = page;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ opts_item = frm->item.ci_parent->ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+ for (result = 0, i = 0; i < frm->frame.b_frame_interval_type; ++i) {
+ result += sprintf(pg, "%d\n",
+ le32_to_cpu(frm->dw_frame_interval[i]));
+ pg = page + result;
+ }
+ mutex_unlock(&opts->lock);
+
+ mutex_unlock(su_mutex);
+ return result;
+}
+
+static inline int __uvcg_count_frm_intrv(char *buf, void *priv)
+{
+ ++*((int *)priv);
+ return 0;
+}
+
+static inline int __uvcg_fill_frm_intrv(char *buf, void *priv)
+{
+ u32 num, **interv;
+ int ret;
+
+ ret = kstrtou32(buf, 0, &num);
+ if (ret)
+ return ret;
+
+ interv = priv;
+ **interv = cpu_to_le32(num);
+ ++*interv;
+
+ return 0;
+}
+
+static int __uvcg_iter_frm_intrv(const char *page, size_t len,
+ int (*fun)(char *, void *), void *priv)
+{
+ /* sign, base 2 representation, newline, terminator */
+ char buf[1 + sizeof(u32) * 8 + 1 + 1];
+ const char *pg = page;
+ int i, ret;
+
+ if (!fun)
+ return -EINVAL;
+
+ while (pg - page < len) {
+ i = 0;
+ while (i < sizeof(buf) && (pg - page < len) &&
+ *pg != '\0' && *pg != '\n')
+ buf[i++] = *pg++;
+ if (i == sizeof(buf))
+ return -EINVAL;
+ while ((pg - page < len) && (*pg == '\0' || *pg == '\n'))
+ ++pg;
+ buf[i] = '\0';
+ ret = fun(buf, priv);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static ssize_t uvcg_frame_dw_frame_interval_store(struct uvcg_frame *ch,
+ const char *page, size_t len)
+{
+ struct f_uvc_opts *opts;
+ struct config_item *opts_item;
+ struct uvcg_format *fmt;
+ struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;
+ int ret = 0, n = 0;
+ u32 *frm_intrv, *tmp;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ opts_item = ch->item.ci_parent->ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+ fmt = to_uvcg_format(ch->item.ci_parent);
+
+ mutex_lock(&opts->lock);
+ if (fmt->linked || opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ ret = __uvcg_iter_frm_intrv(page, len, __uvcg_count_frm_intrv, &n);
+ if (ret)
+ goto end;
+
+ tmp = frm_intrv = kcalloc(n, sizeof(u32), GFP_KERNEL);
+ if (!frm_intrv) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ ret = __uvcg_iter_frm_intrv(page, len, __uvcg_fill_frm_intrv, &tmp);
+ if (ret) {
+ kfree(frm_intrv);
+ goto end;
+ }
+
+ kfree(ch->dw_frame_interval);
+ ch->dw_frame_interval = frm_intrv;
+ ch->frame.b_frame_interval_type = n;
+ ret = len;
+
+end:
+ mutex_unlock(&opts->lock);
+ mutex_unlock(su_mutex);
+ return ret;
+}
+
+static struct uvcg_frame_attribute
+ uvcg_frame_dw_frame_interval =
+ __CONFIGFS_ATTR(dwFrameInterval, S_IRUGO | S_IWUSR,
+ uvcg_frame_dw_frame_interval_show,
+ uvcg_frame_dw_frame_interval_store);
+
+static struct configfs_attribute *uvcg_frame_attrs[] = {
+ &uvcg_frame_bm_capabilities.attr,
+ &uvcg_frame_w_width.attr,
+ &uvcg_frame_w_height.attr,
+ &uvcg_frame_dw_min_bit_rate.attr,
+ &uvcg_frame_dw_max_bit_rate.attr,
+ &uvcg_frame_dw_max_video_frame_buffer_size.attr,
+ &uvcg_frame_dw_default_frame_interval.attr,
+ &uvcg_frame_dw_frame_interval.attr,
+ NULL,
+};
+
+static struct config_item_type uvcg_frame_type = {
+ .ct_item_ops = &uvcg_frame_item_ops,
+ .ct_attrs = uvcg_frame_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_item *uvcg_frame_make(struct config_group *group,
+ const char *name)
+{
+ struct uvcg_frame *h;
+ struct uvcg_format *fmt;
+ struct f_uvc_opts *opts;
+ struct config_item *opts_item;
+
+ h = kzalloc(sizeof(*h), GFP_KERNEL);
+ if (!h)
+ return ERR_PTR(-ENOMEM);
+
+ h->frame.b_descriptor_type = USB_DT_CS_INTERFACE;
+ h->frame.b_frame_index = 1;
+ h->frame.w_width = cpu_to_le16(640);
+ h->frame.w_height = cpu_to_le16(360);
+ h->frame.dw_min_bit_rate = cpu_to_le32(18432000);
+ h->frame.dw_max_bit_rate = cpu_to_le32(55296000);
+ h->frame.dw_max_video_frame_buffer_size = cpu_to_le32(460800);
+ h->frame.dw_default_frame_interval = cpu_to_le32(666666);
+
+ opts_item = group->cg_item.ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+ fmt = to_uvcg_format(&group->cg_item);
+ if (fmt->type == UVCG_UNCOMPRESSED) {
+ h->frame.b_descriptor_subtype = UVC_VS_FRAME_UNCOMPRESSED;
+ h->fmt_type = UVCG_UNCOMPRESSED;
+ } else if (fmt->type == UVCG_MJPEG) {
+ h->frame.b_descriptor_subtype = UVC_VS_FRAME_MJPEG;
+ h->fmt_type = UVCG_MJPEG;
+ } else {
+ mutex_unlock(&opts->lock);
+ kfree(h);
+ return ERR_PTR(-EINVAL);
+ }
+ ++fmt->num_frames;
+ mutex_unlock(&opts->lock);
+
+ config_item_init_type_name(&h->item, name, &uvcg_frame_type);
+
+ return &h->item;
+}
+
+static void uvcg_frame_drop(struct config_group *group, struct config_item *item)
+{
+ struct uvcg_frame *h = to_uvcg_frame(item);
+ struct uvcg_format *fmt;
+ struct f_uvc_opts *opts;
+ struct config_item *opts_item;
+
+ opts_item = group->cg_item.ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+ fmt = to_uvcg_format(&group->cg_item);
+ --fmt->num_frames;
+ kfree(h);
+ mutex_unlock(&opts->lock);
+}
+
+/* streaming/uncompressed/<NAME> */
+struct uvcg_uncompressed {
+ struct uvcg_format fmt;
+ struct uvc_format_uncompressed desc;
+};
+
+static struct uvcg_uncompressed *to_uvcg_uncompressed(struct config_item *item)
+{
+ return container_of(
+ container_of(to_config_group(item), struct uvcg_format, group),
+ struct uvcg_uncompressed, fmt);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_uncompressed);
+CONFIGFS_ATTR_OPS(uvcg_uncompressed);
+
+static struct configfs_item_operations uvcg_uncompressed_item_ops = {
+ .show_attribute = uvcg_uncompressed_attr_show,
+ .store_attribute = uvcg_uncompressed_attr_store,
+};
+
+static struct configfs_group_operations uvcg_uncompressed_group_ops = {
+ .make_item = uvcg_frame_make,
+ .drop_item = uvcg_frame_drop,
+};
+
+static ssize_t uvcg_uncompressed_guid_format_show(struct uvcg_uncompressed *ch,
+ char *page)
+{
+ struct f_uvc_opts *opts;
+ struct config_item *opts_item;
+ struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+ memcpy(page, ch->desc.guidFormat, sizeof(ch->desc.guidFormat));
+ mutex_unlock(&opts->lock);
+
+ mutex_unlock(su_mutex);
+
+ return sizeof(ch->desc.guidFormat);
+}
+
+static ssize_t uvcg_uncompressed_guid_format_store(struct uvcg_uncompressed *ch,
+ const char *page, size_t len)
+{
+ struct f_uvc_opts *opts;
+ struct config_item *opts_item;
+ struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex;
+ int ret;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+ if (ch->fmt.linked || opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ memcpy(ch->desc.guidFormat, page,
+ min(sizeof(ch->desc.guidFormat), len));
+ ret = sizeof(ch->desc.guidFormat);
+
+end:
+ mutex_unlock(&opts->lock);
+ mutex_unlock(su_mutex);
+ return ret;
+}
+
+static struct uvcg_uncompressed_attribute uvcg_uncompressed_guid_format =
+ __CONFIGFS_ATTR(guidFormat, S_IRUGO | S_IWUSR,
+ uvcg_uncompressed_guid_format_show,
+ uvcg_uncompressed_guid_format_store);
+
+
+#define UVCG_UNCOMPRESSED_ATTR_RO(cname, aname, conv) \
+static ssize_t uvcg_uncompressed_##cname##_show( \
+ struct uvcg_uncompressed *u, char *page) \
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \
+ int result; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+ opts = to_f_uvc_opts(opts_item); \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%d\n", conv(u->desc.aname)); \
+ mutex_unlock(&opts->lock); \
+ \
+ mutex_unlock(su_mutex); \
+ return result; \
+} \
+ \
+static struct uvcg_uncompressed_attribute \
+ uvcg_uncompressed_##cname = \
+ __CONFIGFS_ATTR_RO(aname, uvcg_uncompressed_##cname##_show)
+
+#define UVCG_UNCOMPRESSED_ATTR(cname, aname, conv) \
+static ssize_t uvcg_uncompressed_##cname##_show( \
+ struct uvcg_uncompressed *u, char *page) \
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \
+ int result; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+ opts = to_f_uvc_opts(opts_item); \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%d\n", conv(u->desc.aname)); \
+ mutex_unlock(&opts->lock); \
+ \
+ mutex_unlock(su_mutex); \
+ return result; \
+} \
+ \
+static ssize_t \
+uvcg_uncompressed_##cname##_store(struct uvcg_uncompressed *u, \
+ const char *page, size_t len) \
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \
+ int ret; \
+ u8 num; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+ opts = to_f_uvc_opts(opts_item); \
+ \
+ mutex_lock(&opts->lock); \
+ if (u->fmt.linked || opts->refcnt) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ ret = kstrtou8(page, 0, &num); \
+ if (ret) \
+ goto end; \
+ \
+ if (num > 255) { \
+ ret = -EINVAL; \
+ goto end; \
+ } \
+ u->desc.aname = num; \
+ ret = len; \
+end: \
+ mutex_unlock(&opts->lock); \
+ mutex_unlock(su_mutex); \
+ return ret; \
+} \
+ \
+static struct uvcg_uncompressed_attribute \
+ uvcg_uncompressed_##cname = \
+ __CONFIGFS_ATTR(aname, S_IRUGO | S_IWUSR, \
+ uvcg_uncompressed_##cname##_show, \
+ uvcg_uncompressed_##cname##_store)
+
+#define identity_conv(x) (x)
+
+UVCG_UNCOMPRESSED_ATTR(b_bits_per_pixel, bBitsPerPixel, identity_conv);
+UVCG_UNCOMPRESSED_ATTR(b_default_frame_index, bDefaultFrameIndex,
+ identity_conv);
+UVCG_UNCOMPRESSED_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, identity_conv);
+UVCG_UNCOMPRESSED_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, identity_conv);
+UVCG_UNCOMPRESSED_ATTR_RO(bm_interface_flags, bmInterfaceFlags, identity_conv);
+
+#undef identity_conv
+
+#undef UVCG_UNCOMPRESSED_ATTR
+#undef UVCG_UNCOMPRESSED_ATTR_RO
+
+static inline ssize_t
+uvcg_uncompressed_bma_controls_show(struct uvcg_uncompressed *unc, char *page)
+{
+ return uvcg_format_bma_controls_show(&unc->fmt, page);
+}
+
+static inline ssize_t
+uvcg_uncompressed_bma_controls_store(struct uvcg_uncompressed *ch,
+ const char *page, size_t len)
+{
+ return uvcg_format_bma_controls_store(&ch->fmt, page, len);
+}
+
+static struct uvcg_uncompressed_attribute uvcg_uncompressed_bma_controls =
+ __CONFIGFS_ATTR(bmaControls, S_IRUGO | S_IWUSR,
+ uvcg_uncompressed_bma_controls_show,
+ uvcg_uncompressed_bma_controls_store);
+
+static struct configfs_attribute *uvcg_uncompressed_attrs[] = {
+ &uvcg_uncompressed_guid_format.attr,
+ &uvcg_uncompressed_b_bits_per_pixel.attr,
+ &uvcg_uncompressed_b_default_frame_index.attr,
+ &uvcg_uncompressed_b_aspect_ratio_x.attr,
+ &uvcg_uncompressed_b_aspect_ratio_y.attr,
+ &uvcg_uncompressed_bm_interface_flags.attr,
+ &uvcg_uncompressed_bma_controls.attr,
+ NULL,
+};
+
+static struct config_item_type uvcg_uncompressed_type = {
+ .ct_item_ops = &uvcg_uncompressed_item_ops,
+ .ct_group_ops = &uvcg_uncompressed_group_ops,
+ .ct_attrs = uvcg_uncompressed_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *uvcg_uncompressed_make(struct config_group *group,
+ const char *name)
+{
+ static char guid[] = {
+ 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
+ };
+ struct uvcg_uncompressed *h;
+
+ h = kzalloc(sizeof(*h), GFP_KERNEL);
+ if (!h)
+ return ERR_PTR(-ENOMEM);
+
+ h->desc.bLength = UVC_DT_FORMAT_UNCOMPRESSED_SIZE;
+ h->desc.bDescriptorType = USB_DT_CS_INTERFACE;
+ h->desc.bDescriptorSubType = UVC_VS_FORMAT_UNCOMPRESSED;
+ memcpy(h->desc.guidFormat, guid, sizeof(guid));
+ h->desc.bBitsPerPixel = 16;
+ h->desc.bDefaultFrameIndex = 1;
+ h->desc.bAspectRatioX = 0;
+ h->desc.bAspectRatioY = 0;
+ h->desc.bmInterfaceFlags = 0;
+ h->desc.bCopyProtect = 0;
+
+ h->fmt.type = UVCG_UNCOMPRESSED;
+ config_group_init_type_name(&h->fmt.group, name,
+ &uvcg_uncompressed_type);
+
+ return &h->fmt.group;
+}
+
+static void uvcg_uncompressed_drop(struct config_group *group,
+ struct config_item *item)
+{
+ struct uvcg_uncompressed *h = to_uvcg_uncompressed(item);
+
+ kfree(h);
+}
+
+static struct configfs_group_operations uvcg_uncompressed_grp_ops = {
+ .make_group = uvcg_uncompressed_make,
+ .drop_item = uvcg_uncompressed_drop,
+};
+
+static struct config_item_type uvcg_uncompressed_grp_type = {
+ .ct_group_ops = &uvcg_uncompressed_grp_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+/* streaming/mjpeg/<NAME> */
+struct uvcg_mjpeg {
+ struct uvcg_format fmt;
+ struct uvc_format_mjpeg desc;
+};
+
+static struct uvcg_mjpeg *to_uvcg_mjpeg(struct config_item *item)
+{
+ return container_of(
+ container_of(to_config_group(item), struct uvcg_format, group),
+ struct uvcg_mjpeg, fmt);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_mjpeg);
+CONFIGFS_ATTR_OPS(uvcg_mjpeg);
+
+static struct configfs_item_operations uvcg_mjpeg_item_ops = {
+ .show_attribute = uvcg_mjpeg_attr_show,
+ .store_attribute = uvcg_mjpeg_attr_store,
+};
+
+static struct configfs_group_operations uvcg_mjpeg_group_ops = {
+ .make_item = uvcg_frame_make,
+ .drop_item = uvcg_frame_drop,
+};
+
+#define UVCG_MJPEG_ATTR_RO(cname, aname, conv) \
+static ssize_t uvcg_mjpeg_##cname##_show(struct uvcg_mjpeg *u, char *page)\
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \
+ int result; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+ opts = to_f_uvc_opts(opts_item); \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%d\n", conv(u->desc.aname)); \
+ mutex_unlock(&opts->lock); \
+ \
+ mutex_unlock(su_mutex); \
+ return result; \
+} \
+ \
+static struct uvcg_mjpeg_attribute \
+ uvcg_mjpeg_##cname = \
+ __CONFIGFS_ATTR_RO(aname, uvcg_mjpeg_##cname##_show)
+
+#define UVCG_MJPEG_ATTR(cname, aname, conv) \
+static ssize_t uvcg_mjpeg_##cname##_show(struct uvcg_mjpeg *u, char *page)\
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \
+ int result; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+ opts = to_f_uvc_opts(opts_item); \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%d\n", conv(u->desc.aname)); \
+ mutex_unlock(&opts->lock); \
+ \
+ mutex_unlock(su_mutex); \
+ return result; \
+} \
+ \
+static ssize_t \
+uvcg_mjpeg_##cname##_store(struct uvcg_mjpeg *u, \
+ const char *page, size_t len) \
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \
+ int ret; \
+ u8 num; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+ opts = to_f_uvc_opts(opts_item); \
+ \
+ mutex_lock(&opts->lock); \
+ if (u->fmt.linked || opts->refcnt) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ ret = kstrtou8(page, 0, &num); \
+ if (ret) \
+ goto end; \
+ \
+ if (num > 255) { \
+ ret = -EINVAL; \
+ goto end; \
+ } \
+ u->desc.aname = num; \
+ ret = len; \
+end: \
+ mutex_unlock(&opts->lock); \
+ mutex_unlock(su_mutex); \
+ return ret; \
+} \
+ \
+static struct uvcg_mjpeg_attribute \
+ uvcg_mjpeg_##cname = \
+ __CONFIGFS_ATTR(aname, S_IRUGO | S_IWUSR, \
+ uvcg_mjpeg_##cname##_show, \
+ uvcg_mjpeg_##cname##_store)
+
+#define identity_conv(x) (x)
+
+UVCG_MJPEG_ATTR(b_default_frame_index, bDefaultFrameIndex,
+ identity_conv);
+UVCG_MJPEG_ATTR_RO(bm_flags, bmFlags, identity_conv);
+UVCG_MJPEG_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, identity_conv);
+UVCG_MJPEG_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, identity_conv);
+UVCG_MJPEG_ATTR_RO(bm_interface_flags, bmInterfaceFlags, identity_conv);
+
+#undef identity_conv
+
+#undef UVCG_MJPEG_ATTR
+#undef UVCG_MJPEG_ATTR_RO
+
+static inline ssize_t
+uvcg_mjpeg_bma_controls_show(struct uvcg_mjpeg *unc, char *page)
+{
+ return uvcg_format_bma_controls_show(&unc->fmt, page);
+}
+
+static inline ssize_t
+uvcg_mjpeg_bma_controls_store(struct uvcg_mjpeg *ch,
+ const char *page, size_t len)
+{
+ return uvcg_format_bma_controls_store(&ch->fmt, page, len);
+}
+
+static struct uvcg_mjpeg_attribute uvcg_mjpeg_bma_controls =
+ __CONFIGFS_ATTR(bmaControls, S_IRUGO | S_IWUSR,
+ uvcg_mjpeg_bma_controls_show,
+ uvcg_mjpeg_bma_controls_store);
+
+static struct configfs_attribute *uvcg_mjpeg_attrs[] = {
+ &uvcg_mjpeg_b_default_frame_index.attr,
+ &uvcg_mjpeg_bm_flags.attr,
+ &uvcg_mjpeg_b_aspect_ratio_x.attr,
+ &uvcg_mjpeg_b_aspect_ratio_y.attr,
+ &uvcg_mjpeg_bm_interface_flags.attr,
+ &uvcg_mjpeg_bma_controls.attr,
+ NULL,
+};
+
+static struct config_item_type uvcg_mjpeg_type = {
+ .ct_item_ops = &uvcg_mjpeg_item_ops,
+ .ct_group_ops = &uvcg_mjpeg_group_ops,
+ .ct_attrs = uvcg_mjpeg_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *uvcg_mjpeg_make(struct config_group *group,
+ const char *name)
+{
+ struct uvcg_mjpeg *h;
+
+ h = kzalloc(sizeof(*h), GFP_KERNEL);
+ if (!h)
+ return ERR_PTR(-ENOMEM);
+
+ h->desc.bLength = UVC_DT_FORMAT_MJPEG_SIZE;
+ h->desc.bDescriptorType = USB_DT_CS_INTERFACE;
+ h->desc.bDescriptorSubType = UVC_VS_FORMAT_MJPEG;
+ h->desc.bDefaultFrameIndex = 1;
+ h->desc.bAspectRatioX = 0;
+ h->desc.bAspectRatioY = 0;
+ h->desc.bmInterfaceFlags = 0;
+ h->desc.bCopyProtect = 0;
+
+ h->fmt.type = UVCG_MJPEG;
+ config_group_init_type_name(&h->fmt.group, name,
+ &uvcg_mjpeg_type);
+
+ return &h->fmt.group;
+}
+
+static void uvcg_mjpeg_drop(struct config_group *group,
+ struct config_item *item)
+{
+ struct uvcg_mjpeg *h = to_uvcg_mjpeg(item);
+
+ kfree(h);
+}
+
+static struct configfs_group_operations uvcg_mjpeg_grp_ops = {
+ .make_group = uvcg_mjpeg_make,
+ .drop_item = uvcg_mjpeg_drop,
+};
+
+static struct config_item_type uvcg_mjpeg_grp_type = {
+ .ct_group_ops = &uvcg_mjpeg_grp_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+/* streaming/color_matching/default */
+static struct uvcg_default_color_matching {
+ struct config_group group;
+} uvcg_default_color_matching;
+
+static inline struct uvcg_default_color_matching
+*to_uvcg_default_color_matching(struct config_item *item)
+{
+ return container_of(to_config_group(item),
+ struct uvcg_default_color_matching, group);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_default_color_matching);
+CONFIGFS_ATTR_OPS_RO(uvcg_default_color_matching);
+
+static struct configfs_item_operations uvcg_default_color_matching_item_ops = {
+ .show_attribute = uvcg_default_color_matching_attr_show,
+};
+
+#define UVCG_DEFAULT_COLOR_MATCHING_ATTR(cname, aname, conv) \
+static ssize_t uvcg_default_color_matching_##cname##_show( \
+ struct uvcg_default_color_matching *dc, char *page) \
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex; \
+ struct uvc_color_matching_descriptor *cd; \
+ int result; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent; \
+ opts = to_f_uvc_opts(opts_item); \
+ cd = &opts->uvc_color_matching; \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%d\n", conv(cd->aname)); \
+ mutex_unlock(&opts->lock); \
+ \
+ mutex_unlock(su_mutex); \
+ return result; \
+} \
+ \
+static struct uvcg_default_color_matching_attribute \
+ uvcg_default_color_matching_##cname = \
+ __CONFIGFS_ATTR_RO(aname, uvcg_default_color_matching_##cname##_show)
+
+#define identity_conv(x) (x)
+
+UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_color_primaries, bColorPrimaries,
+ identity_conv);
+UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_transfer_characteristics,
+ bTransferCharacteristics, identity_conv);
+UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_matrix_coefficients, bMatrixCoefficients,
+ identity_conv);
+
+#undef identity_conv
+
+#undef UVCG_DEFAULT_COLOR_MATCHING_ATTR
+
+static struct configfs_attribute *uvcg_default_color_matching_attrs[] = {
+ &uvcg_default_color_matching_b_color_primaries.attr,
+ &uvcg_default_color_matching_b_transfer_characteristics.attr,
+ &uvcg_default_color_matching_b_matrix_coefficients.attr,
+ NULL,
+};
+
+static struct config_item_type uvcg_default_color_matching_type = {
+ .ct_item_ops = &uvcg_default_color_matching_item_ops,
+ .ct_attrs = uvcg_default_color_matching_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/* struct uvcg_color_matching {}; */
+
+static struct config_group *uvcg_color_matching_default_groups[] = {
+ &uvcg_default_color_matching.group,
+ NULL,
+};
+
+/* streaming/color_matching */
+static struct uvcg_color_matching_grp {
+ struct config_group group;
+} uvcg_color_matching_grp;
+
+static struct config_item_type uvcg_color_matching_grp_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+/* streaming/class/{fs|hs|ss} */
+static struct uvcg_streaming_class {
+ struct config_group group;
+} uvcg_streaming_class_fs, uvcg_streaming_class_hs, uvcg_streaming_class_ss;
+
+
+static inline struct uvc_descriptor_header
+***__uvcg_get_stream_class_arr(struct config_item *i, struct f_uvc_opts *o)
+{
+ struct uvcg_streaming_class *cl = container_of(to_config_group(i),
+ struct uvcg_streaming_class, group);
+
+ if (cl == &uvcg_streaming_class_fs)
+ return &o->uvc_fs_streaming_cls;
+
+ if (cl == &uvcg_streaming_class_hs)
+ return &o->uvc_hs_streaming_cls;
+
+ if (cl == &uvcg_streaming_class_ss)
+ return &o->uvc_ss_streaming_cls;
+
+ return NULL;
+}
+
+enum uvcg_strm_type {
+ UVCG_HEADER = 0,
+ UVCG_FORMAT,
+ UVCG_FRAME
+};
+
+/*
+ * Iterate over a hierarchy of streaming descriptors' config items.
+ * The items are created by the user with configfs.
+ *
+ * It "processes" the header pointed to by @priv1, then for each format
+ * that follows the header "processes" the format itself and then for
+ * each frame inside a format "processes" the frame.
+ *
+ * As a "processing" function the @fun is used.
+ *
+ * __uvcg_iter_strm_cls() is used in two context: first, to calculate
+ * the amount of memory needed for an array of streaming descriptors
+ * and second, to actually fill the array.
+ *
+ * @h: streaming header pointer
+ * @priv2: an "inout" parameter (the caller might want to see the changes to it)
+ * @priv3: an "inout" parameter (the caller might want to see the changes to it)
+ * @fun: callback function for processing each level of the hierarchy
+ */
+static int __uvcg_iter_strm_cls(struct uvcg_streaming_header *h,
+ void *priv2, void *priv3,
+ int (*fun)(void *, void *, void *, int, enum uvcg_strm_type type))
+{
+ struct uvcg_format_ptr *f;
+ struct config_group *grp;
+ struct config_item *item;
+ struct uvcg_frame *frm;
+ int ret, i, j;
+
+ if (!fun)
+ return -EINVAL;
+
+ i = j = 0;
+ ret = fun(h, priv2, priv3, 0, UVCG_HEADER);
+ if (ret)
+ return ret;
+ list_for_each_entry(f, &h->formats, entry) {
+ ret = fun(f->fmt, priv2, priv3, i++, UVCG_FORMAT);
+ if (ret)
+ return ret;
+ grp = &f->fmt->group;
+ list_for_each_entry(item, &grp->cg_children, ci_entry) {
+ frm = to_uvcg_frame(item);
+ ret = fun(frm, priv2, priv3, j++, UVCG_FRAME);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * Count how many bytes are needed for an array of streaming descriptors.
+ *
+ * @priv1: pointer to a header, format or frame
+ * @priv2: inout parameter, accumulated size of the array
+ * @priv3: inout parameter, accumulated number of the array elements
+ * @n: unused, this function's prototype must match @fun in __uvcg_iter_strm_cls
+ */
+static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n,
+ enum uvcg_strm_type type)
+{
+ size_t *size = priv2;
+ size_t *count = priv3;
+
+ switch (type) {
+ case UVCG_HEADER: {
+ struct uvcg_streaming_header *h = priv1;
+
+ *size += sizeof(h->desc);
+ /* bmaControls */
+ *size += h->num_fmt * UVCG_STREAMING_CONTROL_SIZE;
+ }
+ break;
+ case UVCG_FORMAT: {
+ struct uvcg_format *fmt = priv1;
+
+ if (fmt->type == UVCG_UNCOMPRESSED) {
+ struct uvcg_uncompressed *u =
+ container_of(fmt, struct uvcg_uncompressed,
+ fmt);
+
+ *size += sizeof(u->desc);
+ } else if (fmt->type == UVCG_MJPEG) {
+ struct uvcg_mjpeg *m =
+ container_of(fmt, struct uvcg_mjpeg, fmt);
+
+ *size += sizeof(m->desc);
+ } else {
+ return -EINVAL;
+ }
+ }
+ break;
+ case UVCG_FRAME: {
+ struct uvcg_frame *frm = priv1;
+ int sz = sizeof(frm->dw_frame_interval);
+
+ *size += sizeof(frm->frame);
+ *size += frm->frame.b_frame_interval_type * sz;
+ }
+ break;
+ }
+
+ ++*count;
+
+ return 0;
+}
+
+/*
+ * Fill an array of streaming descriptors.
+ *
+ * @priv1: pointer to a header, format or frame
+ * @priv2: inout parameter, pointer into a block of memory
+ * @priv3: inout parameter, pointer to a 2-dimensional array
+ */
+static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n,
+ enum uvcg_strm_type type)
+{
+ void **dest = priv2;
+ struct uvc_descriptor_header ***array = priv3;
+ size_t sz;
+
+ **array = *dest;
+ ++*array;
+
+ switch (type) {
+ case UVCG_HEADER: {
+ struct uvc_input_header_descriptor *ihdr = *dest;
+ struct uvcg_streaming_header *h = priv1;
+ struct uvcg_format_ptr *f;
+
+ memcpy(*dest, &h->desc, sizeof(h->desc));
+ *dest += sizeof(h->desc);
+ sz = UVCG_STREAMING_CONTROL_SIZE;
+ list_for_each_entry(f, &h->formats, entry) {
+ memcpy(*dest, f->fmt->bmaControls, sz);
+ *dest += sz;
+ }
+ ihdr->bLength = sizeof(h->desc) + h->num_fmt * sz;
+ ihdr->bNumFormats = h->num_fmt;
+ }
+ break;
+ case UVCG_FORMAT: {
+ struct uvcg_format *fmt = priv1;
+
+ if (fmt->type == UVCG_UNCOMPRESSED) {
+ struct uvc_format_uncompressed *unc = *dest;
+ struct uvcg_uncompressed *u =
+ container_of(fmt, struct uvcg_uncompressed,
+ fmt);
+
+ memcpy(*dest, &u->desc, sizeof(u->desc));
+ *dest += sizeof(u->desc);
+ unc->bNumFrameDescriptors = fmt->num_frames;
+ unc->bFormatIndex = n + 1;
+ } else if (fmt->type == UVCG_MJPEG) {
+ struct uvc_format_mjpeg *mjp = *dest;
+ struct uvcg_mjpeg *m =
+ container_of(fmt, struct uvcg_mjpeg, fmt);
+
+ memcpy(*dest, &m->desc, sizeof(m->desc));
+ *dest += sizeof(m->desc);
+ mjp->bNumFrameDescriptors = fmt->num_frames;
+ mjp->bFormatIndex = n + 1;
+ } else {
+ return -EINVAL;
+ }
+ }
+ break;
+ case UVCG_FRAME: {
+ struct uvcg_frame *frm = priv1;
+ struct uvc_descriptor_header *h = *dest;
+
+ sz = sizeof(frm->frame);
+ memcpy(*dest, &frm->frame, sz);
+ *dest += sz;
+ sz = frm->frame.b_frame_interval_type *
+ sizeof(*frm->dw_frame_interval);
+ memcpy(*dest, frm->dw_frame_interval, sz);
+ *dest += sz;
+ if (frm->fmt_type == UVCG_UNCOMPRESSED)
+ h->bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(
+ frm->frame.b_frame_interval_type);
+ else if (frm->fmt_type == UVCG_MJPEG)
+ h->bLength = UVC_DT_FRAME_MJPEG_SIZE(
+ frm->frame.b_frame_interval_type);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int uvcg_streaming_class_allow_link(struct config_item *src,
+ struct config_item *target)
+{
+ struct config_item *streaming, *header;
+ struct f_uvc_opts *opts;
+ struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+ struct uvc_descriptor_header ***class_array, **cl_arr;
+ struct uvcg_streaming_header *target_hdr;
+ void *data, *data_save;
+ size_t size = 0, count = 0;
+ int ret = -EINVAL;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ streaming = src->ci_parent->ci_parent;
+ header = config_group_find_item(to_config_group(streaming), "header");
+ if (!header || target->ci_parent != header)
+ goto out;
+
+ opts = to_f_uvc_opts(streaming->ci_parent);
+
+ mutex_lock(&opts->lock);
+
+ class_array = __uvcg_get_stream_class_arr(src, opts);
+ if (!class_array || *class_array || opts->refcnt) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ target_hdr = to_uvcg_streaming_header(target);
+ ret = __uvcg_iter_strm_cls(target_hdr, &size, &count, __uvcg_cnt_strm);
+ if (ret)
+ goto unlock;
+
+ count += 2; /* color_matching, NULL */
+ *class_array = kcalloc(count, sizeof(void *), GFP_KERNEL);
+ if (!*class_array) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ data = data_save = kzalloc(size, GFP_KERNEL);
+ if (!data) {
+ kfree(*class_array);
+ *class_array = NULL;
+ ret = PTR_ERR(data);
+ goto unlock;
+ }
+ cl_arr = *class_array;
+ ret = __uvcg_iter_strm_cls(target_hdr, &data, &cl_arr,
+ __uvcg_fill_strm);
+ if (ret) {
+ kfree(*class_array);
+ *class_array = NULL;
+ /*
+ * __uvcg_fill_strm() called from __uvcg_iter_stream_cls()
+ * might have advanced the "data", so use a backup copy
+ */
+ kfree(data_save);
+ goto unlock;
+ }
+ *cl_arr = (struct uvc_descriptor_header *)&opts->uvc_color_matching;
+
+ ++target_hdr->linked;
+ ret = 0;
+
+unlock:
+ mutex_unlock(&opts->lock);
+out:
+ mutex_unlock(su_mutex);
+ return ret;
+}
+
+static int uvcg_streaming_class_drop_link(struct config_item *src,
+ struct config_item *target)
+{
+ struct config_item *streaming, *header;
+ struct f_uvc_opts *opts;
+ struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+ struct uvc_descriptor_header ***class_array;
+ struct uvcg_streaming_header *target_hdr;
+ int ret = -EINVAL;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ streaming = src->ci_parent->ci_parent;
+ header = config_group_find_item(to_config_group(streaming), "header");
+ if (!header || target->ci_parent != header)
+ goto out;
+
+ opts = to_f_uvc_opts(streaming->ci_parent);
+
+ mutex_lock(&opts->lock);
+
+ class_array = __uvcg_get_stream_class_arr(src, opts);
+ if (!class_array || !*class_array)
+ goto unlock;
+
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ target_hdr = to_uvcg_streaming_header(target);
+ --target_hdr->linked;
+ kfree(**class_array);
+ kfree(*class_array);
+ *class_array = NULL;
+ ret = 0;
+
+unlock:
+ mutex_unlock(&opts->lock);
+out:
+ mutex_unlock(su_mutex);
+ return ret;
+}
+
+static struct configfs_item_operations uvcg_streaming_class_item_ops = {
+ .allow_link = uvcg_streaming_class_allow_link,
+ .drop_link = uvcg_streaming_class_drop_link,
+};
+
+static struct config_item_type uvcg_streaming_class_type = {
+ .ct_item_ops = &uvcg_streaming_class_item_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *uvcg_streaming_class_default_groups[] = {
+ &uvcg_streaming_class_fs.group,
+ &uvcg_streaming_class_hs.group,
+ &uvcg_streaming_class_ss.group,
+ NULL,
+};
+
+/* streaming/class */
+static struct uvcg_streaming_class_grp {
+ struct config_group group;
+} uvcg_streaming_class_grp;
+
+static struct config_item_type uvcg_streaming_class_grp_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *uvcg_streaming_default_groups[] = {
+ &uvcg_streaming_header_grp.group,
+ &uvcg_uncompressed_grp.group,
+ &uvcg_mjpeg_grp.group,
+ &uvcg_color_matching_grp.group,
+ &uvcg_streaming_class_grp.group,
+ NULL,
+};
+
+/* streaming */
+static struct uvcg_streaming_grp {
+ struct config_group group;
+} uvcg_streaming_grp;
+
+static struct config_item_type uvcg_streaming_grp_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *uvcg_default_groups[] = {
+ &uvcg_control_grp.group,
+ &uvcg_streaming_grp.group,
+ NULL,
+};
+
+static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_uvc_opts,
+ func_inst.group);
+}
+
+CONFIGFS_ATTR_STRUCT(f_uvc_opts);
+CONFIGFS_ATTR_OPS(f_uvc_opts);
+
+static void uvc_attr_release(struct config_item *item)
+{
+ struct f_uvc_opts *opts = to_f_uvc_opts(item);
+
+ usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations uvc_item_ops = {
+ .release = uvc_attr_release,
+ .show_attribute = f_uvc_opts_attr_show,
+ .store_attribute = f_uvc_opts_attr_store,
+};
+
+#define UVCG_OPTS_ATTR(cname, conv, str2u, uxx, vnoc, limit) \
+static ssize_t f_uvc_opts_##cname##_show( \
+ struct f_uvc_opts *opts, char *page) \
+{ \
+ int result; \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%d\n", conv(opts->cname)); \
+ mutex_unlock(&opts->lock); \
+ \
+ return result; \
+} \
+ \
+static ssize_t \
+f_uvc_opts_##cname##_store(struct f_uvc_opts *opts, \
+ const char *page, size_t len) \
+{ \
+ int ret; \
+ uxx num; \
+ \
+ mutex_lock(&opts->lock); \
+ if (opts->refcnt) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ ret = str2u(page, 0, &num); \
+ if (ret) \
+ goto end; \
+ \
+ if (num > limit) { \
+ ret = -EINVAL; \
+ goto end; \
+ } \
+ opts->cname = vnoc(num); \
+ ret = len; \
+end: \
+ mutex_unlock(&opts->lock); \
+ return ret; \
+} \
+ \
+static struct f_uvc_opts_attribute \
+ f_uvc_opts_attribute_##cname = \
+ __CONFIGFS_ATTR(cname, S_IRUGO | S_IWUSR, \
+ f_uvc_opts_##cname##_show, \
+ f_uvc_opts_##cname##_store)
+
+#define identity_conv(x) (x)
+
+UVCG_OPTS_ATTR(streaming_interval, identity_conv, kstrtou8, u8, identity_conv,
+ 16);
+UVCG_OPTS_ATTR(streaming_maxpacket, le16_to_cpu, kstrtou16, u16, le16_to_cpu,
+ 3072);
+UVCG_OPTS_ATTR(streaming_maxburst, identity_conv, kstrtou8, u8, identity_conv,
+ 15);
+
+#undef identity_conv
+
+#undef UVCG_OPTS_ATTR
+
+static struct configfs_attribute *uvc_attrs[] = {
+ &f_uvc_opts_attribute_streaming_interval.attr,
+ &f_uvc_opts_attribute_streaming_maxpacket.attr,
+ &f_uvc_opts_attribute_streaming_maxburst.attr,
+ NULL,
+};
+
+static struct config_item_type uvc_func_type = {
+ .ct_item_ops = &uvc_item_ops,
+ .ct_attrs = uvc_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static inline void uvcg_init_group(struct config_group *g,
+ struct config_group **default_groups,
+ const char *name,
+ struct config_item_type *type)
+{
+ g->default_groups = default_groups;
+ config_group_init_type_name(g, name, type);
+}
+
+int uvcg_attach_configfs(struct f_uvc_opts *opts)
+{
+ config_group_init_type_name(&uvcg_control_header_grp.group,
+ "header",
+ &uvcg_control_header_grp_type);
+ config_group_init_type_name(&uvcg_default_processing.group,
+ "default",
+ &uvcg_default_processing_type);
+ uvcg_init_group(&uvcg_processing_grp.group,
+ uvcg_processing_default_groups,
+ "processing",
+ &uvcg_processing_grp_type);
+ config_group_init_type_name(&uvcg_default_camera.group,
+ "default",
+ &uvcg_default_camera_type);
+ uvcg_init_group(&uvcg_camera_grp.group,
+ uvcg_camera_default_groups,
+ "camera",
+ &uvcg_camera_grp_type);
+ config_group_init_type_name(&uvcg_default_output.group,
+ "default",
+ &uvcg_default_output_type);
+ uvcg_init_group(&uvcg_output_grp.group,
+ uvcg_output_default_groups,
+ "output",
+ &uvcg_output_grp_type);
+ uvcg_init_group(&uvcg_terminal_grp.group,
+ uvcg_terminal_default_groups,
+ "terminal",
+ &uvcg_terminal_grp_type);
+ config_group_init_type_name(&uvcg_control_class_fs.group,
+ "fs",
+ &uvcg_control_class_type);
+ config_group_init_type_name(&uvcg_control_class_ss.group,
+ "ss",
+ &uvcg_control_class_type);
+ uvcg_init_group(&uvcg_control_class_grp.group,
+ uvcg_control_class_default_groups,
+ "class",
+ &uvcg_control_class_grp_type);
+ uvcg_init_group(&uvcg_control_grp.group,
+ uvcg_control_default_groups,
+ "control",
+ &uvcg_control_grp_type);
+ config_group_init_type_name(&uvcg_streaming_header_grp.group,
+ "header",
+ &uvcg_streaming_header_grp_type);
+ config_group_init_type_name(&uvcg_uncompressed_grp.group,
+ "uncompressed",
+ &uvcg_uncompressed_grp_type);
+ config_group_init_type_name(&uvcg_mjpeg_grp.group,
+ "mjpeg",
+ &uvcg_mjpeg_grp_type);
+ config_group_init_type_name(&uvcg_default_color_matching.group,
+ "default",
+ &uvcg_default_color_matching_type);
+ uvcg_init_group(&uvcg_color_matching_grp.group,
+ uvcg_color_matching_default_groups,
+ "color_matching",
+ &uvcg_color_matching_grp_type);
+ config_group_init_type_name(&uvcg_streaming_class_fs.group,
+ "fs",
+ &uvcg_streaming_class_type);
+ config_group_init_type_name(&uvcg_streaming_class_hs.group,
+ "hs",
+ &uvcg_streaming_class_type);
+ config_group_init_type_name(&uvcg_streaming_class_ss.group,
+ "ss",
+ &uvcg_streaming_class_type);
+ uvcg_init_group(&uvcg_streaming_class_grp.group,
+ uvcg_streaming_class_default_groups,
+ "class",
+ &uvcg_streaming_class_grp_type);
+ uvcg_init_group(&uvcg_streaming_grp.group,
+ uvcg_streaming_default_groups,
+ "streaming",
+ &uvcg_streaming_grp_type);
+ uvcg_init_group(&opts->func_inst.group,
+ uvcg_default_groups,
+ "",
+ &uvc_func_type);
+ return 0;
+}
diff --git a/drivers/usb/gadget/function/uvc_configfs.h b/drivers/usb/gadget/function/uvc_configfs.h
new file mode 100644
index 000000000000..085e67be7c71
--- /dev/null
+++ b/drivers/usb/gadget/function/uvc_configfs.h
@@ -0,0 +1,22 @@
+/*
+ * uvc_configfs.h
+ *
+ * Configfs support for the uvc function.
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef UVC_CONFIGFS_H
+#define UVC_CONFIGFS_H
+
+struct f_uvc_opts;
+
+int uvcg_attach_configfs(struct f_uvc_opts *opts);
+
+#endif /* UVC_CONFIGFS_H */
diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c
index 5aad7fededa5..8b818fd027b3 100644
--- a/drivers/usb/gadget/function/uvc_v4l2.c
+++ b/drivers/usb/gadget/function/uvc_v4l2.c
@@ -27,6 +27,7 @@
#include "uvc.h"
#include "uvc_queue.h"
#include "uvc_video.h"
+#include "uvc_v4l2.h"
/* --------------------------------------------------------------------------
* Requests handling
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index 9cb86bc1a9a5..50a5e637ca35 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -21,6 +21,7 @@
#include "uvc.h"
#include "uvc_queue.h"
+#include "uvc_video.h"
/* --------------------------------------------------------------------------
* Video codecs
diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig
index fd48ef3af4eb..113c87e22117 100644
--- a/drivers/usb/gadget/legacy/Kconfig
+++ b/drivers/usb/gadget/legacy/Kconfig
@@ -40,7 +40,7 @@ config USB_ZERO
dynamically linked module called "g_zero".
config USB_ZERO_HNPTEST
- boolean "HNP Test Device"
+ bool "HNP Test Device"
depends on USB_ZERO && USB_OTG
help
You can configure this device to enumerate using the device
diff --git a/drivers/usb/gadget/legacy/g_ffs.c b/drivers/usb/gadget/legacy/g_ffs.c
index 06acfa55864a..b01b88e1b716 100644
--- a/drivers/usb/gadget/legacy/g_ffs.c
+++ b/drivers/usb/gadget/legacy/g_ffs.c
@@ -133,7 +133,9 @@ struct gfs_configuration {
struct usb_configuration c;
int (*eth)(struct usb_configuration *c);
int num;
-} gfs_configurations[] = {
+};
+
+static struct gfs_configuration gfs_configurations[] = {
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
{
.eth = bind_rndis_config,
@@ -278,7 +280,7 @@ static void *functionfs_acquire_dev(struct ffs_dev *dev)
if (!try_module_get(THIS_MODULE))
return ERR_PTR(-ENOENT);
- return 0;
+ return NULL;
}
static void functionfs_release_dev(struct ffs_dev *dev)
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index db49ec4c748e..200f9a584064 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -74,6 +74,8 @@ MODULE_DESCRIPTION (DRIVER_DESC);
MODULE_AUTHOR ("David Brownell");
MODULE_LICENSE ("GPL");
+static int ep_open(struct inode *, struct file *);
+
/*----------------------------------------------------------------------*/
@@ -283,14 +285,15 @@ static void epio_complete (struct usb_ep *ep, struct usb_request *req)
* still need dev->lock to use epdata->ep.
*/
static int
-get_ready_ep (unsigned f_flags, struct ep_data *epdata)
+get_ready_ep (unsigned f_flags, struct ep_data *epdata, bool is_write)
{
int val;
if (f_flags & O_NONBLOCK) {
if (!mutex_trylock(&epdata->lock))
goto nonblock;
- if (epdata->state != STATE_EP_ENABLED) {
+ if (epdata->state != STATE_EP_ENABLED &&
+ (!is_write || epdata->state != STATE_EP_READY)) {
mutex_unlock(&epdata->lock);
nonblock:
val = -EAGAIN;
@@ -305,18 +308,20 @@ nonblock:
switch (epdata->state) {
case STATE_EP_ENABLED:
+ return 0;
+ case STATE_EP_READY: /* not configured yet */
+ if (is_write)
+ return 0;
+ // FALLTHRU
+ case STATE_EP_UNBOUND: /* clean disconnect */
break;
// case STATE_EP_DISABLED: /* "can't happen" */
- // case STATE_EP_READY: /* "can't happen" */
default: /* error! */
pr_debug ("%s: ep %p not available, state %d\n",
shortname, epdata, epdata->state);
- // FALLTHROUGH
- case STATE_EP_UNBOUND: /* clean disconnect */
- val = -ENODEV;
- mutex_unlock(&epdata->lock);
}
- return val;
+ mutex_unlock(&epdata->lock);
+ return -ENODEV;
}
static ssize_t
@@ -363,97 +368,6 @@ ep_io (struct ep_data *epdata, void *buf, unsigned len)
return value;
}
-
-/* handle a synchronous OUT bulk/intr/iso transfer */
-static ssize_t
-ep_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
-{
- struct ep_data *data = fd->private_data;
- void *kbuf;
- ssize_t value;
-
- if ((value = get_ready_ep (fd->f_flags, data)) < 0)
- return value;
-
- /* halt any endpoint by doing a "wrong direction" i/o call */
- if (usb_endpoint_dir_in(&data->desc)) {
- if (usb_endpoint_xfer_isoc(&data->desc)) {
- mutex_unlock(&data->lock);
- return -EINVAL;
- }
- DBG (data->dev, "%s halt\n", data->name);
- spin_lock_irq (&data->dev->lock);
- if (likely (data->ep != NULL))
- usb_ep_set_halt (data->ep);
- spin_unlock_irq (&data->dev->lock);
- mutex_unlock(&data->lock);
- return -EBADMSG;
- }
-
- /* FIXME readahead for O_NONBLOCK and poll(); careful with ZLPs */
-
- value = -ENOMEM;
- kbuf = kmalloc (len, GFP_KERNEL);
- if (unlikely (!kbuf))
- goto free1;
-
- value = ep_io (data, kbuf, len);
- VDEBUG (data->dev, "%s read %zu OUT, status %d\n",
- data->name, len, (int) value);
- if (value >= 0 && copy_to_user (buf, kbuf, value))
- value = -EFAULT;
-
-free1:
- mutex_unlock(&data->lock);
- kfree (kbuf);
- return value;
-}
-
-/* handle a synchronous IN bulk/intr/iso transfer */
-static ssize_t
-ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
-{
- struct ep_data *data = fd->private_data;
- void *kbuf;
- ssize_t value;
-
- if ((value = get_ready_ep (fd->f_flags, data)) < 0)
- return value;
-
- /* halt any endpoint by doing a "wrong direction" i/o call */
- if (!usb_endpoint_dir_in(&data->desc)) {
- if (usb_endpoint_xfer_isoc(&data->desc)) {
- mutex_unlock(&data->lock);
- return -EINVAL;
- }
- DBG (data->dev, "%s halt\n", data->name);
- spin_lock_irq (&data->dev->lock);
- if (likely (data->ep != NULL))
- usb_ep_set_halt (data->ep);
- spin_unlock_irq (&data->dev->lock);
- mutex_unlock(&data->lock);
- return -EBADMSG;
- }
-
- /* FIXME writebehind for O_NONBLOCK and poll(), qlen = 1 */
-
- value = -ENOMEM;
- kbuf = memdup_user(buf, len);
- if (IS_ERR(kbuf)) {
- value = PTR_ERR(kbuf);
- kbuf = NULL;
- goto free1;
- }
-
- value = ep_io (data, kbuf, len);
- VDEBUG (data->dev, "%s write %zu IN, status %d\n",
- data->name, len, (int) value);
-free1:
- mutex_unlock(&data->lock);
- kfree (kbuf);
- return value;
-}
-
static int
ep_release (struct inode *inode, struct file *fd)
{
@@ -481,7 +395,7 @@ static long ep_ioctl(struct file *fd, unsigned code, unsigned long value)
struct ep_data *data = fd->private_data;
int status;
- if ((status = get_ready_ep (fd->f_flags, data)) < 0)
+ if ((status = get_ready_ep (fd->f_flags, data, false)) < 0)
return status;
spin_lock_irq (&data->dev->lock);
@@ -517,8 +431,8 @@ struct kiocb_priv {
struct mm_struct *mm;
struct work_struct work;
void *buf;
- const struct iovec *iv;
- unsigned long nr_segs;
+ struct iov_iter to;
+ const void *to_free;
unsigned actual;
};
@@ -541,35 +455,6 @@ static int ep_aio_cancel(struct kiocb *iocb)
return value;
}
-static ssize_t ep_copy_to_user(struct kiocb_priv *priv)
-{
- ssize_t len, total;
- void *to_copy;
- int i;
-
- /* copy stuff into user buffers */
- total = priv->actual;
- len = 0;
- to_copy = priv->buf;
- for (i=0; i < priv->nr_segs; i++) {
- ssize_t this = min((ssize_t)(priv->iv[i].iov_len), total);
-
- if (copy_to_user(priv->iv[i].iov_base, to_copy, this)) {
- if (len == 0)
- len = -EFAULT;
- break;
- }
-
- total -= this;
- len += this;
- to_copy += this;
- if (total == 0)
- break;
- }
-
- return len;
-}
-
static void ep_user_copy_worker(struct work_struct *work)
{
struct kiocb_priv *priv = container_of(work, struct kiocb_priv, work);
@@ -578,13 +463,16 @@ static void ep_user_copy_worker(struct work_struct *work)
size_t ret;
use_mm(mm);
- ret = ep_copy_to_user(priv);
+ ret = copy_to_iter(priv->buf, priv->actual, &priv->to);
unuse_mm(mm);
+ if (!ret)
+ ret = -EFAULT;
/* completing the iocb can drop the ctx and mm, don't touch mm after */
aio_complete(iocb, ret, ret);
kfree(priv->buf);
+ kfree(priv->to_free);
kfree(priv);
}
@@ -603,8 +491,9 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
* don't need to copy anything to userspace, so we can
* complete the aio request immediately.
*/
- if (priv->iv == NULL || unlikely(req->actual == 0)) {
+ if (priv->to_free == NULL || unlikely(req->actual == 0)) {
kfree(req->buf);
+ kfree(priv->to_free);
kfree(priv);
iocb->private = NULL;
/* aio_complete() reports bytes-transferred _and_ faults */
@@ -618,6 +507,7 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
priv->buf = req->buf;
priv->actual = req->actual;
+ INIT_WORK(&priv->work, ep_user_copy_worker);
schedule_work(&priv->work);
}
spin_unlock(&epdata->dev->lock);
@@ -626,38 +516,17 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
put_ep(epdata);
}
-static ssize_t
-ep_aio_rwtail(
- struct kiocb *iocb,
- char *buf,
- size_t len,
- struct ep_data *epdata,
- const struct iovec *iv,
- unsigned long nr_segs
-)
+static ssize_t ep_aio(struct kiocb *iocb,
+ struct kiocb_priv *priv,
+ struct ep_data *epdata,
+ char *buf,
+ size_t len)
{
- struct kiocb_priv *priv;
- struct usb_request *req;
- ssize_t value;
+ struct usb_request *req;
+ ssize_t value;
- priv = kmalloc(sizeof *priv, GFP_KERNEL);
- if (!priv) {
- value = -ENOMEM;
-fail:
- kfree(buf);
- return value;
- }
iocb->private = priv;
priv->iocb = iocb;
- priv->iv = iv;
- priv->nr_segs = nr_segs;
- INIT_WORK(&priv->work, ep_user_copy_worker);
-
- value = get_ready_ep(iocb->ki_filp->f_flags, epdata);
- if (unlikely(value < 0)) {
- kfree(priv);
- goto fail;
- }
kiocb_set_cancel_fn(iocb, ep_aio_cancel);
get_ep(epdata);
@@ -669,75 +538,154 @@ fail:
* allocate or submit those if the host disconnected.
*/
spin_lock_irq(&epdata->dev->lock);
- if (likely(epdata->ep)) {
- req = usb_ep_alloc_request(epdata->ep, GFP_ATOMIC);
- if (likely(req)) {
- priv->req = req;
- req->buf = buf;
- req->length = len;
- req->complete = ep_aio_complete;
- req->context = iocb;
- value = usb_ep_queue(epdata->ep, req, GFP_ATOMIC);
- if (unlikely(0 != value))
- usb_ep_free_request(epdata->ep, req);
- } else
- value = -EAGAIN;
- } else
- value = -ENODEV;
- spin_unlock_irq(&epdata->dev->lock);
+ value = -ENODEV;
+ if (unlikely(epdata->ep))
+ goto fail;
- mutex_unlock(&epdata->lock);
+ req = usb_ep_alloc_request(epdata->ep, GFP_ATOMIC);
+ value = -ENOMEM;
+ if (unlikely(!req))
+ goto fail;
- if (unlikely(value)) {
- kfree(priv);
- put_ep(epdata);
- } else
- value = -EIOCBQUEUED;
+ priv->req = req;
+ req->buf = buf;
+ req->length = len;
+ req->complete = ep_aio_complete;
+ req->context = iocb;
+ value = usb_ep_queue(epdata->ep, req, GFP_ATOMIC);
+ if (unlikely(0 != value)) {
+ usb_ep_free_request(epdata->ep, req);
+ goto fail;
+ }
+ spin_unlock_irq(&epdata->dev->lock);
+ return -EIOCBQUEUED;
+
+fail:
+ spin_unlock_irq(&epdata->dev->lock);
+ kfree(priv->to_free);
+ kfree(priv);
+ put_ep(epdata);
return value;
}
static ssize_t
-ep_aio_read(struct kiocb *iocb, const struct iovec *iov,
- unsigned long nr_segs, loff_t o)
+ep_read_iter(struct kiocb *iocb, struct iov_iter *to)
{
- struct ep_data *epdata = iocb->ki_filp->private_data;
- char *buf;
+ struct file *file = iocb->ki_filp;
+ struct ep_data *epdata = file->private_data;
+ size_t len = iov_iter_count(to);
+ ssize_t value;
+ char *buf;
- if (unlikely(usb_endpoint_dir_in(&epdata->desc)))
- return -EINVAL;
+ if ((value = get_ready_ep(file->f_flags, epdata, false)) < 0)
+ return value;
- buf = kmalloc(iocb->ki_nbytes, GFP_KERNEL);
- if (unlikely(!buf))
- return -ENOMEM;
+ /* halt any endpoint by doing a "wrong direction" i/o call */
+ if (usb_endpoint_dir_in(&epdata->desc)) {
+ if (usb_endpoint_xfer_isoc(&epdata->desc) ||
+ !is_sync_kiocb(iocb)) {
+ mutex_unlock(&epdata->lock);
+ return -EINVAL;
+ }
+ DBG (epdata->dev, "%s halt\n", epdata->name);
+ spin_lock_irq(&epdata->dev->lock);
+ if (likely(epdata->ep != NULL))
+ usb_ep_set_halt(epdata->ep);
+ spin_unlock_irq(&epdata->dev->lock);
+ mutex_unlock(&epdata->lock);
+ return -EBADMSG;
+ }
- return ep_aio_rwtail(iocb, buf, iocb->ki_nbytes, epdata, iov, nr_segs);
+ buf = kmalloc(len, GFP_KERNEL);
+ if (unlikely(!buf)) {
+ mutex_unlock(&epdata->lock);
+ return -ENOMEM;
+ }
+ if (is_sync_kiocb(iocb)) {
+ value = ep_io(epdata, buf, len);
+ if (value >= 0 && copy_to_iter(buf, value, to))
+ value = -EFAULT;
+ } else {
+ struct kiocb_priv *priv = kzalloc(sizeof *priv, GFP_KERNEL);
+ value = -ENOMEM;
+ if (!priv)
+ goto fail;
+ priv->to_free = dup_iter(&priv->to, to, GFP_KERNEL);
+ if (!priv->to_free) {
+ kfree(priv);
+ goto fail;
+ }
+ value = ep_aio(iocb, priv, epdata, buf, len);
+ if (value == -EIOCBQUEUED)
+ buf = NULL;
+ }
+fail:
+ kfree(buf);
+ mutex_unlock(&epdata->lock);
+ return value;
}
+static ssize_t ep_config(struct ep_data *, const char *, size_t);
+
static ssize_t
-ep_aio_write(struct kiocb *iocb, const struct iovec *iov,
- unsigned long nr_segs, loff_t o)
+ep_write_iter(struct kiocb *iocb, struct iov_iter *from)
{
- struct ep_data *epdata = iocb->ki_filp->private_data;
- char *buf;
- size_t len = 0;
- int i = 0;
+ struct file *file = iocb->ki_filp;
+ struct ep_data *epdata = file->private_data;
+ size_t len = iov_iter_count(from);
+ bool configured;
+ ssize_t value;
+ char *buf;
+
+ if ((value = get_ready_ep(file->f_flags, epdata, true)) < 0)
+ return value;
- if (unlikely(!usb_endpoint_dir_in(&epdata->desc)))
- return -EINVAL;
+ configured = epdata->state == STATE_EP_ENABLED;
- buf = kmalloc(iocb->ki_nbytes, GFP_KERNEL);
- if (unlikely(!buf))
+ /* halt any endpoint by doing a "wrong direction" i/o call */
+ if (configured && !usb_endpoint_dir_in(&epdata->desc)) {
+ if (usb_endpoint_xfer_isoc(&epdata->desc) ||
+ !is_sync_kiocb(iocb)) {
+ mutex_unlock(&epdata->lock);
+ return -EINVAL;
+ }
+ DBG (epdata->dev, "%s halt\n", epdata->name);
+ spin_lock_irq(&epdata->dev->lock);
+ if (likely(epdata->ep != NULL))
+ usb_ep_set_halt(epdata->ep);
+ spin_unlock_irq(&epdata->dev->lock);
+ mutex_unlock(&epdata->lock);
+ return -EBADMSG;
+ }
+
+ buf = kmalloc(len, GFP_KERNEL);
+ if (unlikely(!buf)) {
+ mutex_unlock(&epdata->lock);
return -ENOMEM;
+ }
- for (i=0; i < nr_segs; i++) {
- if (unlikely(copy_from_user(&buf[len], iov[i].iov_base,
- iov[i].iov_len) != 0)) {
- kfree(buf);
- return -EFAULT;
+ if (unlikely(copy_from_iter(buf, len, from) != len)) {
+ value = -EFAULT;
+ goto out;
+ }
+
+ if (unlikely(!configured)) {
+ value = ep_config(epdata, buf, len);
+ } else if (is_sync_kiocb(iocb)) {
+ value = ep_io(epdata, buf, len);
+ } else {
+ struct kiocb_priv *priv = kzalloc(sizeof *priv, GFP_KERNEL);
+ value = -ENOMEM;
+ if (priv) {
+ value = ep_aio(iocb, priv, epdata, buf, len);
+ if (value == -EIOCBQUEUED)
+ buf = NULL;
}
- len += iov[i].iov_len;
}
- return ep_aio_rwtail(iocb, buf, len, epdata, NULL, 0);
+out:
+ kfree(buf);
+ mutex_unlock(&epdata->lock);
+ return value;
}
/*----------------------------------------------------------------------*/
@@ -745,15 +693,15 @@ ep_aio_write(struct kiocb *iocb, const struct iovec *iov,
/* used after endpoint configuration */
static const struct file_operations ep_io_operations = {
.owner = THIS_MODULE,
- .llseek = no_llseek,
- .read = ep_read,
- .write = ep_write,
- .unlocked_ioctl = ep_ioctl,
+ .open = ep_open,
.release = ep_release,
-
- .aio_read = ep_aio_read,
- .aio_write = ep_aio_write,
+ .llseek = no_llseek,
+ .read = new_sync_read,
+ .write = new_sync_write,
+ .unlocked_ioctl = ep_ioctl,
+ .read_iter = ep_read_iter,
+ .write_iter = ep_write_iter,
};
/* ENDPOINT INITIALIZATION
@@ -770,17 +718,12 @@ static const struct file_operations ep_io_operations = {
* speed descriptor, then optional high speed descriptor.
*/
static ssize_t
-ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
+ep_config (struct ep_data *data, const char *buf, size_t len)
{
- struct ep_data *data = fd->private_data;
struct usb_ep *ep;
u32 tag;
int value, length = len;
- value = mutex_lock_interruptible(&data->lock);
- if (value < 0)
- return value;
-
if (data->state != STATE_EP_READY) {
value = -EL2HLT;
goto fail;
@@ -791,9 +734,7 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
goto fail0;
/* we might need to change message format someday */
- if (copy_from_user (&tag, buf, 4)) {
- goto fail1;
- }
+ memcpy(&tag, buf, 4);
if (tag != 1) {
DBG(data->dev, "config %s, bad tag %d\n", data->name, tag);
goto fail0;
@@ -806,19 +747,15 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
*/
/* full/low speed descriptor, then high speed */
- if (copy_from_user (&data->desc, buf, USB_DT_ENDPOINT_SIZE)) {
- goto fail1;
- }
+ memcpy(&data->desc, buf, USB_DT_ENDPOINT_SIZE);
if (data->desc.bLength != USB_DT_ENDPOINT_SIZE
|| data->desc.bDescriptorType != USB_DT_ENDPOINT)
goto fail0;
if (len != USB_DT_ENDPOINT_SIZE) {
if (len != 2 * USB_DT_ENDPOINT_SIZE)
goto fail0;
- if (copy_from_user (&data->hs_desc, buf + USB_DT_ENDPOINT_SIZE,
- USB_DT_ENDPOINT_SIZE)) {
- goto fail1;
- }
+ memcpy(&data->hs_desc, buf + USB_DT_ENDPOINT_SIZE,
+ USB_DT_ENDPOINT_SIZE);
if (data->hs_desc.bLength != USB_DT_ENDPOINT_SIZE
|| data->hs_desc.bDescriptorType
!= USB_DT_ENDPOINT) {
@@ -840,24 +777,20 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
case USB_SPEED_LOW:
case USB_SPEED_FULL:
ep->desc = &data->desc;
- value = usb_ep_enable(ep);
- if (value == 0)
- data->state = STATE_EP_ENABLED;
break;
case USB_SPEED_HIGH:
/* fails if caller didn't provide that descriptor... */
ep->desc = &data->hs_desc;
- value = usb_ep_enable(ep);
- if (value == 0)
- data->state = STATE_EP_ENABLED;
break;
default:
DBG(data->dev, "unconnected, %s init abandoned\n",
data->name);
value = -EINVAL;
+ goto gone;
}
+ value = usb_ep_enable(ep);
if (value == 0) {
- fd->f_op = &ep_io_operations;
+ data->state = STATE_EP_ENABLED;
value = length;
}
gone:
@@ -867,14 +800,10 @@ fail:
data->desc.bDescriptorType = 0;
data->hs_desc.bDescriptorType = 0;
}
- mutex_unlock(&data->lock);
return value;
fail0:
value = -EINVAL;
goto fail;
-fail1:
- value = -EFAULT;
- goto fail;
}
static int
@@ -902,15 +831,6 @@ ep_open (struct inode *inode, struct file *fd)
return value;
}
-/* used before endpoint configuration */
-static const struct file_operations ep_config_operations = {
- .llseek = no_llseek,
-
- .open = ep_open,
- .write = ep_config,
- .release = ep_release,
-};
-
/*----------------------------------------------------------------------*/
/* EP0 IMPLEMENTATION can be partly in userspace.
@@ -989,6 +909,10 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
enum ep0_state state;
spin_lock_irq (&dev->lock);
+ if (dev->state <= STATE_DEV_OPENED) {
+ retval = -EINVAL;
+ goto done;
+ }
/* report fd mode change before acting on it */
if (dev->setup_abort) {
@@ -1187,8 +1111,6 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
struct dev_data *dev = fd->private_data;
ssize_t retval = -ESRCH;
- spin_lock_irq (&dev->lock);
-
/* report fd mode change before acting on it */
if (dev->setup_abort) {
dev->setup_abort = 0;
@@ -1234,7 +1156,6 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
} else
DBG (dev, "fail %s, state %d\n", __func__, dev->state);
- spin_unlock_irq (&dev->lock);
return retval;
}
@@ -1281,6 +1202,9 @@ ep0_poll (struct file *fd, poll_table *wait)
struct dev_data *dev = fd->private_data;
int mask = 0;
+ if (dev->state <= STATE_DEV_OPENED)
+ return DEFAULT_POLLMASK;
+
poll_wait(fd, &dev->wait, wait);
spin_lock_irq (&dev->lock);
@@ -1316,19 +1240,6 @@ static long dev_ioctl (struct file *fd, unsigned code, unsigned long value)
return ret;
}
-/* used after device configuration */
-static const struct file_operations ep0_io_operations = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
-
- .read = ep0_read,
- .write = ep0_write,
- .fasync = ep0_fasync,
- .poll = ep0_poll,
- .unlocked_ioctl = dev_ioctl,
- .release = dev_release,
-};
-
/*----------------------------------------------------------------------*/
/* The in-kernel gadget driver handles most ep0 issues, in particular
@@ -1650,7 +1561,7 @@ static int activate_ep_files (struct dev_data *dev)
goto enomem1;
data->dentry = gadgetfs_create_file (dev->sb, data->name,
- data, &ep_config_operations);
+ data, &ep_io_operations);
if (!data->dentry)
goto enomem2;
list_add_tail (&data->epfiles, &dev->epfiles);
@@ -1852,6 +1763,14 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
u32 tag;
char *kbuf;
+ spin_lock_irq(&dev->lock);
+ if (dev->state > STATE_DEV_OPENED) {
+ value = ep0_write(fd, buf, len, ptr);
+ spin_unlock_irq(&dev->lock);
+ return value;
+ }
+ spin_unlock_irq(&dev->lock);
+
if (len < (USB_DT_CONFIG_SIZE + USB_DT_DEVICE_SIZE + 4))
return -EINVAL;
@@ -1925,7 +1844,6 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
* on, they can work ... except in cleanup paths that
* kick in after the ep0 descriptor is closed.
*/
- fd->f_op = &ep0_io_operations;
value = len;
}
return value;
@@ -1956,12 +1874,14 @@ dev_open (struct inode *inode, struct file *fd)
return value;
}
-static const struct file_operations dev_init_operations = {
+static const struct file_operations ep0_operations = {
.llseek = no_llseek,
.open = dev_open,
+ .read = ep0_read,
.write = dev_config,
.fasync = ep0_fasync,
+ .poll = ep0_poll,
.unlocked_ioctl = dev_ioctl,
.release = dev_release,
};
@@ -2077,7 +1997,7 @@ gadgetfs_fill_super (struct super_block *sb, void *opts, int silent)
goto Enomem;
dev->sb = sb;
- dev->dentry = gadgetfs_create_file(sb, CHIP, dev, &dev_init_operations);
+ dev->dentry = gadgetfs_create_file(sb, CHIP, dev, &ep0_operations);
if (!dev->dentry) {
put_dev(dev);
goto Enomem;
diff --git a/drivers/usb/gadget/legacy/tcm_usb_gadget.c b/drivers/usb/gadget/legacy/tcm_usb_gadget.c
index 3a494168661e..6e0a019aad54 100644
--- a/drivers/usb/gadget/legacy/tcm_usb_gadget.c
+++ b/drivers/usb/gadget/legacy/tcm_usb_gadget.c
@@ -1740,10 +1740,9 @@ static int tcm_usbg_make_nexus(struct usbg_tpg *tpg, char *name)
goto err_session;
}
/*
- * Now register the TCM vHost virtual I_T Nexus as active with the
- * call to __transport_register_session()
+ * Now register the TCM vHost virtual I_T Nexus as active.
*/
- __transport_register_session(se_tpg, tv_nexus->tvn_se_sess->se_node_acl,
+ transport_register_session(se_tpg, tv_nexus->tvn_se_sess->se_node_acl,
tv_nexus->tvn_se_sess, tv_nexus);
tpg->tpg_nexus = tv_nexus;
mutex_unlock(&tpg->tpg_mutex);
diff --git a/drivers/usb/gadget/legacy/zero.c b/drivers/usb/gadget/legacy/zero.c
index ff97ac93ac03..5ee95152493c 100644
--- a/drivers/usb/gadget/legacy/zero.c
+++ b/drivers/usb/gadget/legacy/zero.c
@@ -68,8 +68,6 @@ static struct usb_zero_options gzero_options = {
.isoc_maxpacket = GZERO_ISOC_MAXPACKET,
.bulk_buflen = GZERO_BULK_BUFLEN,
.qlen = GZERO_QLEN,
- .int_interval = GZERO_INT_INTERVAL,
- .int_maxpacket = GZERO_INT_MAXPACKET,
};
/*-------------------------------------------------------------------------*/
@@ -268,21 +266,6 @@ module_param_named(isoc_maxburst, gzero_options.isoc_maxburst, uint,
S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(isoc_maxburst, "0 - 15 (ss only)");
-module_param_named(int_interval, gzero_options.int_interval, uint,
- S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(int_interval, "1 - 16");
-
-module_param_named(int_maxpacket, gzero_options.int_maxpacket, uint,
- S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(int_maxpacket, "0 - 1023 (fs), 0 - 1024 (hs/ss)");
-
-module_param_named(int_mult, gzero_options.int_mult, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(int_mult, "0 - 2 (hs/ss only)");
-
-module_param_named(int_maxburst, gzero_options.int_maxburst, uint,
- S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(int_maxburst, "0 - 15 (ss only)");
-
static struct usb_function *func_lb;
static struct usb_function_instance *func_inst_lb;
@@ -318,10 +301,6 @@ static int __init zero_bind(struct usb_composite_dev *cdev)
ss_opts->isoc_maxpacket = gzero_options.isoc_maxpacket;
ss_opts->isoc_mult = gzero_options.isoc_mult;
ss_opts->isoc_maxburst = gzero_options.isoc_maxburst;
- ss_opts->int_interval = gzero_options.int_interval;
- ss_opts->int_maxpacket = gzero_options.int_maxpacket;
- ss_opts->int_mult = gzero_options.int_mult;
- ss_opts->int_maxburst = gzero_options.int_maxburst;
ss_opts->bulk_buflen = gzero_options.bulk_buflen;
func_ss = usb_get_function(func_inst_ss);
diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig
index b8e213eb36cc..9a3a6b00391a 100644
--- a/drivers/usb/gadget/udc/Kconfig
+++ b/drivers/usb/gadget/udc/Kconfig
@@ -32,6 +32,7 @@ menu "USB Peripheral Controller"
config USB_AT91
tristate "Atmel AT91 USB Device Port"
depends on ARCH_AT91
+ depends on OF || COMPILE_TEST
help
Many Atmel AT91 processors (such as the AT91RM2000) have a
full speed USB Device Port with support for five configurable
@@ -198,7 +199,7 @@ config USB_S3C2410
S3C2440 processors.
config USB_S3C2410_DEBUG
- boolean "S3C2410 udc debug messages"
+ bool "S3C2410 udc debug messages"
depends on USB_S3C2410
config USB_S3C_HSUDC
@@ -287,7 +288,7 @@ config USB_NET2272
gadget drivers to also be dynamically linked.
config USB_NET2272_DMA
- boolean "Support external DMA controller"
+ bool "Support external DMA controller"
depends on USB_NET2272 && HAS_DMA
help
The NET2272 part can optionally support an external DMA
diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c
index c862656d18b8..2fbedca3c2b4 100644
--- a/drivers/usb/gadget/udc/at91_udc.c
+++ b/drivers/usb/gadget/udc/at91_udc.c
@@ -31,16 +31,9 @@
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_data/atmel.h>
-
-#include <asm/byteorder.h>
-#include <mach/hardware.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/gpio.h>
-
-#include <mach/cpu.h>
-#include <mach/at91sam9261_matrix.h>
-#include <mach/at91_matrix.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/atmel-matrix.h>
#include "at91_udc.h"
@@ -66,7 +59,15 @@
#define DRIVER_VERSION "3 May 2006"
static const char driver_name [] = "at91_udc";
-static const char ep0name[] = "ep0";
+static const char * const ep_names[] = {
+ "ep0",
+ "ep1",
+ "ep2",
+ "ep3-int",
+ "ep4",
+ "ep5",
+};
+#define ep0name ep_names[0]
#define VBUS_POLL_TIMEOUT msecs_to_jiffies(1000)
@@ -176,7 +177,7 @@ static int proc_udc_show(struct seq_file *s, void *unused)
udc->enabled
? (udc->vbus ? "active" : "enabled")
: "disabled",
- udc->selfpowered ? "self" : "VBUS",
+ udc->gadget.is_selfpowered ? "self" : "VBUS",
udc->suspended ? ", suspended" : "",
udc->driver ? udc->driver->driver.name : "(none)");
@@ -895,8 +896,6 @@ static void clk_on(struct at91_udc *udc)
return;
udc->clocked = 1;
- if (IS_ENABLED(CONFIG_COMMON_CLK))
- clk_enable(udc->uclk);
clk_enable(udc->iclk);
clk_enable(udc->fclk);
}
@@ -909,8 +908,6 @@ static void clk_off(struct at91_udc *udc)
udc->gadget.speed = USB_SPEED_UNKNOWN;
clk_disable(udc->fclk);
clk_disable(udc->iclk);
- if (IS_ENABLED(CONFIG_COMMON_CLK))
- clk_disable(udc->uclk);
}
/*
@@ -919,8 +916,6 @@ static void clk_off(struct at91_udc *udc)
*/
static void pullup(struct at91_udc *udc, int is_on)
{
- int active = !udc->board.pullup_active_low;
-
if (!udc->enabled || !udc->vbus)
is_on = 0;
DBG("%sactive\n", is_on ? "" : "in");
@@ -929,40 +924,15 @@ static void pullup(struct at91_udc *udc, int is_on)
clk_on(udc);
at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_RXRSM);
at91_udp_write(udc, AT91_UDP_TXVC, 0);
- if (cpu_is_at91rm9200())
- gpio_set_value(udc->board.pullup_pin, active);
- else if (cpu_is_at91sam9260() || cpu_is_at91sam9263() || cpu_is_at91sam9g20()) {
- u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC);
-
- txvc |= AT91_UDP_TXVC_PUON;
- at91_udp_write(udc, AT91_UDP_TXVC, txvc);
- } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) {
- u32 usbpucr;
-
- usbpucr = at91_matrix_read(AT91_MATRIX_USBPUCR);
- usbpucr |= AT91_MATRIX_USBPUCR_PUON;
- at91_matrix_write(AT91_MATRIX_USBPUCR, usbpucr);
- }
} else {
stop_activity(udc);
at91_udp_write(udc, AT91_UDP_IDR, AT91_UDP_RXRSM);
at91_udp_write(udc, AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS);
- if (cpu_is_at91rm9200())
- gpio_set_value(udc->board.pullup_pin, !active);
- else if (cpu_is_at91sam9260() || cpu_is_at91sam9263() || cpu_is_at91sam9g20()) {
- u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC);
-
- txvc &= ~AT91_UDP_TXVC_PUON;
- at91_udp_write(udc, AT91_UDP_TXVC, txvc);
- } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) {
- u32 usbpucr;
-
- usbpucr = at91_matrix_read(AT91_MATRIX_USBPUCR);
- usbpucr &= ~AT91_MATRIX_USBPUCR_PUON;
- at91_matrix_write(AT91_MATRIX_USBPUCR, usbpucr);
- }
clk_off(udc);
}
+
+ if (udc->caps && udc->caps->pullup)
+ udc->caps->pullup(udc, is_on);
}
/* vbus is here! turn everything on that's ready */
@@ -1000,7 +970,7 @@ static int at91_set_selfpowered(struct usb_gadget *gadget, int is_on)
unsigned long flags;
spin_lock_irqsave(&udc->lock, flags);
- udc->selfpowered = (is_on != 0);
+ gadget->is_selfpowered = (is_on != 0);
spin_unlock_irqrestore(&udc->lock, flags);
return 0;
}
@@ -1149,7 +1119,7 @@ static void handle_setup(struct at91_udc *udc, struct at91_ep *ep, u32 csr)
*/
case ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8)
| USB_REQ_GET_STATUS:
- tmp = (udc->selfpowered << USB_DEVICE_SELF_POWERED);
+ tmp = (udc->gadget.is_selfpowered << USB_DEVICE_SELF_POWERED);
if (at91_udp_read(udc, AT91_UDP_GLB_STAT) & AT91_UDP_ESR)
tmp |= (1 << USB_DEVICE_REMOTE_WAKEUP);
PACKET("get device status\n");
@@ -1535,74 +1505,6 @@ static irqreturn_t at91_udc_irq (int irq, void *_udc)
/*-------------------------------------------------------------------------*/
-static struct at91_udc controller = {
- .gadget = {
- .ops = &at91_udc_ops,
- .ep0 = &controller.ep[0].ep,
- .name = driver_name,
- },
- .ep[0] = {
- .ep = {
- .name = ep0name,
- .ops = &at91_ep_ops,
- },
- .udc = &controller,
- .maxpacket = 8,
- .int_mask = 1 << 0,
- },
- .ep[1] = {
- .ep = {
- .name = "ep1",
- .ops = &at91_ep_ops,
- },
- .udc = &controller,
- .is_pingpong = 1,
- .maxpacket = 64,
- .int_mask = 1 << 1,
- },
- .ep[2] = {
- .ep = {
- .name = "ep2",
- .ops = &at91_ep_ops,
- },
- .udc = &controller,
- .is_pingpong = 1,
- .maxpacket = 64,
- .int_mask = 1 << 2,
- },
- .ep[3] = {
- .ep = {
- /* could actually do bulk too */
- .name = "ep3-int",
- .ops = &at91_ep_ops,
- },
- .udc = &controller,
- .maxpacket = 8,
- .int_mask = 1 << 3,
- },
- .ep[4] = {
- .ep = {
- .name = "ep4",
- .ops = &at91_ep_ops,
- },
- .udc = &controller,
- .is_pingpong = 1,
- .maxpacket = 256,
- .int_mask = 1 << 4,
- },
- .ep[5] = {
- .ep = {
- .name = "ep5",
- .ops = &at91_ep_ops,
- },
- .udc = &controller,
- .is_pingpong = 1,
- .maxpacket = 256,
- .int_mask = 1 << 5,
- },
- /* ep6 and ep7 are also reserved (custom silicon might use them) */
-};
-
static void at91_vbus_update(struct at91_udc *udc, unsigned value)
{
value ^= udc->board.vbus_active_low;
@@ -1653,7 +1555,7 @@ static int at91_start(struct usb_gadget *gadget,
udc->driver = driver;
udc->gadget.dev.of_node = udc->pdev->dev.of_node;
udc->enabled = 1;
- udc->selfpowered = 1;
+ udc->gadget.is_selfpowered = 1;
return 0;
}
@@ -1687,12 +1589,202 @@ static void at91udc_shutdown(struct platform_device *dev)
spin_unlock_irqrestore(&udc->lock, flags);
}
-static void at91udc_of_init(struct at91_udc *udc,
- struct device_node *np)
+static int at91rm9200_udc_init(struct at91_udc *udc)
+{
+ struct at91_ep *ep;
+ int ret;
+ int i;
+
+ for (i = 0; i < NUM_ENDPOINTS; i++) {
+ ep = &udc->ep[i];
+
+ switch (i) {
+ case 0:
+ case 3:
+ ep->maxpacket = 8;
+ break;
+ case 1 ... 2:
+ ep->maxpacket = 64;
+ break;
+ case 4 ... 5:
+ ep->maxpacket = 256;
+ break;
+ }
+ }
+
+ if (!gpio_is_valid(udc->board.pullup_pin)) {
+ DBG("no D+ pullup?\n");
+ return -ENODEV;
+ }
+
+ ret = devm_gpio_request(&udc->pdev->dev, udc->board.pullup_pin,
+ "udc_pullup");
+ if (ret) {
+ DBG("D+ pullup is busy\n");
+ return ret;
+ }
+
+ gpio_direction_output(udc->board.pullup_pin,
+ udc->board.pullup_active_low);
+
+ return 0;
+}
+
+static void at91rm9200_udc_pullup(struct at91_udc *udc, int is_on)
+{
+ int active = !udc->board.pullup_active_low;
+
+ if (is_on)
+ gpio_set_value(udc->board.pullup_pin, active);
+ else
+ gpio_set_value(udc->board.pullup_pin, !active);
+}
+
+static const struct at91_udc_caps at91rm9200_udc_caps = {
+ .init = at91rm9200_udc_init,
+ .pullup = at91rm9200_udc_pullup,
+};
+
+static int at91sam9260_udc_init(struct at91_udc *udc)
+{
+ struct at91_ep *ep;
+ int i;
+
+ for (i = 0; i < NUM_ENDPOINTS; i++) {
+ ep = &udc->ep[i];
+
+ switch (i) {
+ case 0 ... 3:
+ ep->maxpacket = 64;
+ break;
+ case 4 ... 5:
+ ep->maxpacket = 512;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void at91sam9260_udc_pullup(struct at91_udc *udc, int is_on)
+{
+ u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC);
+
+ if (is_on)
+ txvc |= AT91_UDP_TXVC_PUON;
+ else
+ txvc &= ~AT91_UDP_TXVC_PUON;
+
+ at91_udp_write(udc, AT91_UDP_TXVC, txvc);
+}
+
+static const struct at91_udc_caps at91sam9260_udc_caps = {
+ .init = at91sam9260_udc_init,
+ .pullup = at91sam9260_udc_pullup,
+};
+
+static int at91sam9261_udc_init(struct at91_udc *udc)
+{
+ struct at91_ep *ep;
+ int i;
+
+ for (i = 0; i < NUM_ENDPOINTS; i++) {
+ ep = &udc->ep[i];
+
+ switch (i) {
+ case 0:
+ ep->maxpacket = 8;
+ break;
+ case 1 ... 3:
+ ep->maxpacket = 64;
+ break;
+ case 4 ... 5:
+ ep->maxpacket = 256;
+ break;
+ }
+ }
+
+ udc->matrix = syscon_regmap_lookup_by_phandle(udc->pdev->dev.of_node,
+ "atmel,matrix");
+ if (IS_ERR(udc->matrix))
+ return PTR_ERR(udc->matrix);
+
+ return 0;
+}
+
+static void at91sam9261_udc_pullup(struct at91_udc *udc, int is_on)
+{
+ u32 usbpucr = 0;
+
+ if (is_on)
+ usbpucr = AT91_MATRIX_USBPUCR_PUON;
+
+ regmap_update_bits(udc->matrix, AT91SAM9261_MATRIX_USBPUCR,
+ AT91_MATRIX_USBPUCR_PUON, usbpucr);
+}
+
+static const struct at91_udc_caps at91sam9261_udc_caps = {
+ .init = at91sam9261_udc_init,
+ .pullup = at91sam9261_udc_pullup,
+};
+
+static int at91sam9263_udc_init(struct at91_udc *udc)
+{
+ struct at91_ep *ep;
+ int i;
+
+ for (i = 0; i < NUM_ENDPOINTS; i++) {
+ ep = &udc->ep[i];
+
+ switch (i) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ ep->maxpacket = 64;
+ break;
+ case 4:
+ case 5:
+ ep->maxpacket = 256;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static const struct at91_udc_caps at91sam9263_udc_caps = {
+ .init = at91sam9263_udc_init,
+ .pullup = at91sam9260_udc_pullup,
+};
+
+static const struct of_device_id at91_udc_dt_ids[] = {
+ {
+ .compatible = "atmel,at91rm9200-udc",
+ .data = &at91rm9200_udc_caps,
+ },
+ {
+ .compatible = "atmel,at91sam9260-udc",
+ .data = &at91sam9260_udc_caps,
+ },
+ {
+ .compatible = "atmel,at91sam9261-udc",
+ .data = &at91sam9261_udc_caps,
+ },
+ {
+ .compatible = "atmel,at91sam9263-udc",
+ .data = &at91sam9263_udc_caps,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, at91_udc_dt_ids);
+
+static void at91udc_of_init(struct at91_udc *udc, struct device_node *np)
{
struct at91_udc_data *board = &udc->board;
- u32 val;
+ const struct of_device_id *match;
enum of_gpio_flags flags;
+ u32 val;
if (of_property_read_u32(np, "atmel,vbus-polled", &val) == 0)
board->vbus_polled = 1;
@@ -1705,6 +1797,10 @@ static void at91udc_of_init(struct at91_udc *udc,
&flags);
board->pullup_active_low = (flags & OF_GPIO_ACTIVE_LOW) ? 1 : 0;
+
+ match = of_match_node(at91_udc_dt_ids, np);
+ if (match)
+ udc->caps = match->data;
}
static int at91udc_probe(struct platform_device *pdev)
@@ -1713,97 +1809,67 @@ static int at91udc_probe(struct platform_device *pdev)
struct at91_udc *udc;
int retval;
struct resource *res;
+ struct at91_ep *ep;
+ int i;
- if (!dev_get_platdata(dev) && !pdev->dev.of_node) {
- /* small (so we copy it) but critical! */
- DBG("missing platform_data\n");
- return -ENODEV;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENXIO;
-
- if (!request_mem_region(res->start, resource_size(res), driver_name)) {
- DBG("someone's using UDC memory\n");
- return -EBUSY;
- }
+ udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL);
+ if (!udc)
+ return -ENOMEM;
/* init software state */
- udc = &controller;
udc->gadget.dev.parent = dev;
- if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node)
- at91udc_of_init(udc, pdev->dev.of_node);
- else
- memcpy(&udc->board, dev_get_platdata(dev),
- sizeof(struct at91_udc_data));
+ at91udc_of_init(udc, pdev->dev.of_node);
udc->pdev = pdev;
udc->enabled = 0;
spin_lock_init(&udc->lock);
- /* rm9200 needs manual D+ pullup; off by default */
- if (cpu_is_at91rm9200()) {
- if (!gpio_is_valid(udc->board.pullup_pin)) {
- DBG("no D+ pullup?\n");
- retval = -ENODEV;
- goto fail0;
- }
- retval = gpio_request(udc->board.pullup_pin, "udc_pullup");
- if (retval) {
- DBG("D+ pullup is busy\n");
- goto fail0;
- }
- gpio_direction_output(udc->board.pullup_pin,
- udc->board.pullup_active_low);
- }
+ udc->gadget.ops = &at91_udc_ops;
+ udc->gadget.ep0 = &udc->ep[0].ep;
+ udc->gadget.name = driver_name;
+ udc->gadget.dev.init_name = "gadget";
- /* newer chips have more FIFO memory than rm9200 */
- if (cpu_is_at91sam9260() || cpu_is_at91sam9g20()) {
- udc->ep[0].maxpacket = 64;
- udc->ep[3].maxpacket = 64;
- udc->ep[4].maxpacket = 512;
- udc->ep[5].maxpacket = 512;
- } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) {
- udc->ep[3].maxpacket = 64;
- } else if (cpu_is_at91sam9263()) {
- udc->ep[0].maxpacket = 64;
- udc->ep[3].maxpacket = 64;
+ for (i = 0; i < NUM_ENDPOINTS; i++) {
+ ep = &udc->ep[i];
+ ep->ep.name = ep_names[i];
+ ep->ep.ops = &at91_ep_ops;
+ ep->udc = udc;
+ ep->int_mask = BIT(i);
+ if (i != 0 && i != 3)
+ ep->is_pingpong = 1;
}
- udc->udp_baseaddr = ioremap(res->start, resource_size(res));
- if (!udc->udp_baseaddr) {
- retval = -ENOMEM;
- goto fail0a;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ udc->udp_baseaddr = devm_ioremap_resource(dev, res);
+ if (IS_ERR(udc->udp_baseaddr))
+ return PTR_ERR(udc->udp_baseaddr);
+
+ if (udc->caps && udc->caps->init) {
+ retval = udc->caps->init(udc);
+ if (retval)
+ return retval;
}
udc_reinit(udc);
/* get interface and function clocks */
- udc->iclk = clk_get(dev, "udc_clk");
- udc->fclk = clk_get(dev, "udpck");
- if (IS_ENABLED(CONFIG_COMMON_CLK))
- udc->uclk = clk_get(dev, "usb_clk");
- if (IS_ERR(udc->iclk) || IS_ERR(udc->fclk) ||
- (IS_ENABLED(CONFIG_COMMON_CLK) && IS_ERR(udc->uclk))) {
- DBG("clocks missing\n");
- retval = -ENODEV;
- goto fail1;
- }
+ udc->iclk = devm_clk_get(dev, "pclk");
+ if (IS_ERR(udc->iclk))
+ return PTR_ERR(udc->iclk);
+
+ udc->fclk = devm_clk_get(dev, "hclk");
+ if (IS_ERR(udc->fclk))
+ return PTR_ERR(udc->fclk);
/* don't do anything until we have both gadget driver and VBUS */
- if (IS_ENABLED(CONFIG_COMMON_CLK)) {
- clk_set_rate(udc->uclk, 48000000);
- retval = clk_prepare(udc->uclk);
- if (retval)
- goto fail1;
- }
+ clk_set_rate(udc->fclk, 48000000);
retval = clk_prepare(udc->fclk);
if (retval)
- goto fail1a;
+ return retval;
retval = clk_prepare_enable(udc->iclk);
if (retval)
- goto fail1b;
+ goto err_unprepare_fclk;
+
at91_udp_write(udc, AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS);
at91_udp_write(udc, AT91_UDP_IDR, 0xffffffff);
/* Clear all pending interrupts - UDP may be used by bootloader. */
@@ -1812,18 +1878,21 @@ static int at91udc_probe(struct platform_device *pdev)
/* request UDC and maybe VBUS irqs */
udc->udp_irq = platform_get_irq(pdev, 0);
- retval = request_irq(udc->udp_irq, at91_udc_irq,
- 0, driver_name, udc);
- if (retval < 0) {
+ retval = devm_request_irq(dev, udc->udp_irq, at91_udc_irq, 0,
+ driver_name, udc);
+ if (retval) {
DBG("request irq %d failed\n", udc->udp_irq);
- goto fail1c;
+ goto err_unprepare_iclk;
}
+
if (gpio_is_valid(udc->board.vbus_pin)) {
- retval = gpio_request(udc->board.vbus_pin, "udc_vbus");
- if (retval < 0) {
+ retval = devm_gpio_request(dev, udc->board.vbus_pin,
+ "udc_vbus");
+ if (retval) {
DBG("request vbus pin failed\n");
- goto fail2;
+ goto err_unprepare_iclk;
}
+
gpio_direction_input(udc->board.vbus_pin);
/*
@@ -1840,12 +1909,13 @@ static int at91udc_probe(struct platform_device *pdev)
mod_timer(&udc->vbus_timer,
jiffies + VBUS_POLL_TIMEOUT);
} else {
- if (request_irq(gpio_to_irq(udc->board.vbus_pin),
- at91_vbus_irq, 0, driver_name, udc)) {
+ retval = devm_request_irq(dev,
+ gpio_to_irq(udc->board.vbus_pin),
+ at91_vbus_irq, 0, driver_name, udc);
+ if (retval) {
DBG("request vbus irq %d failed\n",
udc->board.vbus_pin);
- retval = -EBUSY;
- goto fail3;
+ goto err_unprepare_iclk;
}
}
} else {
@@ -1854,49 +1924,27 @@ static int at91udc_probe(struct platform_device *pdev)
}
retval = usb_add_gadget_udc(dev, &udc->gadget);
if (retval)
- goto fail4;
+ goto err_unprepare_iclk;
dev_set_drvdata(dev, udc);
device_init_wakeup(dev, 1);
create_debug_file(udc);
INFO("%s version %s\n", driver_name, DRIVER_VERSION);
return 0;
-fail4:
- if (gpio_is_valid(udc->board.vbus_pin) && !udc->board.vbus_polled)
- free_irq(gpio_to_irq(udc->board.vbus_pin), udc);
-fail3:
- if (gpio_is_valid(udc->board.vbus_pin))
- gpio_free(udc->board.vbus_pin);
-fail2:
- free_irq(udc->udp_irq, udc);
-fail1c:
+
+err_unprepare_iclk:
clk_unprepare(udc->iclk);
-fail1b:
+err_unprepare_fclk:
clk_unprepare(udc->fclk);
-fail1a:
- if (IS_ENABLED(CONFIG_COMMON_CLK))
- clk_unprepare(udc->uclk);
-fail1:
- if (IS_ENABLED(CONFIG_COMMON_CLK) && !IS_ERR(udc->uclk))
- clk_put(udc->uclk);
- if (!IS_ERR(udc->fclk))
- clk_put(udc->fclk);
- if (!IS_ERR(udc->iclk))
- clk_put(udc->iclk);
- iounmap(udc->udp_baseaddr);
-fail0a:
- if (cpu_is_at91rm9200())
- gpio_free(udc->board.pullup_pin);
-fail0:
- release_mem_region(res->start, resource_size(res));
+
DBG("%s probe failed, %d\n", driver_name, retval);
+
return retval;
}
static int __exit at91udc_remove(struct platform_device *pdev)
{
struct at91_udc *udc = platform_get_drvdata(pdev);
- struct resource *res;
unsigned long flags;
DBG("remove\n");
@@ -1911,29 +1959,9 @@ static int __exit at91udc_remove(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, 0);
remove_debug_file(udc);
- if (gpio_is_valid(udc->board.vbus_pin)) {
- free_irq(gpio_to_irq(udc->board.vbus_pin), udc);
- gpio_free(udc->board.vbus_pin);
- }
- free_irq(udc->udp_irq, udc);
- iounmap(udc->udp_baseaddr);
-
- if (cpu_is_at91rm9200())
- gpio_free(udc->board.pullup_pin);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, resource_size(res));
-
- if (IS_ENABLED(CONFIG_COMMON_CLK))
- clk_unprepare(udc->uclk);
clk_unprepare(udc->fclk);
clk_unprepare(udc->iclk);
- clk_put(udc->iclk);
- clk_put(udc->fclk);
- if (IS_ENABLED(CONFIG_COMMON_CLK))
- clk_put(udc->uclk);
-
return 0;
}
@@ -1989,15 +2017,6 @@ static int at91udc_resume(struct platform_device *pdev)
#define at91udc_resume NULL
#endif
-#if defined(CONFIG_OF)
-static const struct of_device_id at91_udc_dt_ids[] = {
- { .compatible = "atmel,at91rm9200-udc" },
- { /* sentinel */ }
-};
-
-MODULE_DEVICE_TABLE(of, at91_udc_dt_ids);
-#endif
-
static struct platform_driver at91_udc_driver = {
.remove = __exit_p(at91udc_remove),
.shutdown = at91udc_shutdown,
@@ -2005,7 +2024,7 @@ static struct platform_driver at91_udc_driver = {
.resume = at91udc_resume,
.driver = {
.name = (char *) driver_name,
- .of_match_table = of_match_ptr(at91_udc_dt_ids),
+ .of_match_table = at91_udc_dt_ids,
},
};
diff --git a/drivers/usb/gadget/udc/at91_udc.h b/drivers/usb/gadget/udc/at91_udc.h
index 017524663381..2679c8b217cc 100644
--- a/drivers/usb/gadget/udc/at91_udc.h
+++ b/drivers/usb/gadget/udc/at91_udc.h
@@ -107,6 +107,11 @@ struct at91_ep {
unsigned fifo_bank:1;
};
+struct at91_udc_caps {
+ int (*init)(struct at91_udc *udc);
+ void (*pullup)(struct at91_udc *udc, int is_on);
+};
+
/*
* driver is non-SMP, and just blocks IRQs whenever it needs
* access protection for chip registers or driver state
@@ -115,6 +120,7 @@ struct at91_udc {
struct usb_gadget gadget;
struct at91_ep ep[NUM_ENDPOINTS];
struct usb_gadget_driver *driver;
+ const struct at91_udc_caps *caps;
unsigned vbus:1;
unsigned enabled:1;
unsigned clocked:1;
@@ -122,11 +128,10 @@ struct at91_udc {
unsigned req_pending:1;
unsigned wait_for_addr_ack:1;
unsigned wait_for_config_ack:1;
- unsigned selfpowered:1;
unsigned active_suspend:1;
u8 addr;
struct at91_udc_data board;
- struct clk *iclk, *fclk, *uclk;
+ struct clk *iclk, *fclk;
struct platform_device *pdev;
struct proc_dir_entry *pde;
void __iomem *udp_baseaddr;
@@ -134,6 +139,7 @@ struct at91_udc {
spinlock_t lock;
struct timer_list vbus_timer;
struct work_struct vbus_timer_work;
+ struct regmap *matrix;
};
static inline struct at91_udc *to_udc(struct usb_gadget *g)
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c
index 9f93bed42052..d79cb35dbf8a 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.c
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.c
@@ -8,6 +8,7 @@
* published by the Free Software Foundation.
*/
#include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -264,14 +265,17 @@ static void usba_init_debugfs(struct usba_udc *udc)
goto err_root;
udc->debugfs_root = root;
- regs = debugfs_create_file("regs", 0400, root, udc, &regs_dbg_fops);
- if (!regs)
- goto err_regs;
-
regs_resource = platform_get_resource(udc->pdev, IORESOURCE_MEM,
CTRL_IOMEM_ID);
- regs->d_inode->i_size = resource_size(regs_resource);
- udc->debugfs_regs = regs;
+
+ if (regs_resource) {
+ regs = debugfs_create_file_size("regs", 0400, root, udc,
+ &regs_dbg_fops,
+ resource_size(regs_resource));
+ if (!regs)
+ goto err_regs;
+ udc->debugfs_regs = regs;
+ }
usba_ep_init_debugfs(udc, to_usba_ep(udc->gadget.ep0));
@@ -315,6 +319,17 @@ static inline void usba_cleanup_debugfs(struct usba_udc *udc)
}
#endif
+static inline u32 usba_int_enb_get(struct usba_udc *udc)
+{
+ return udc->int_enb_cache;
+}
+
+static inline void usba_int_enb_set(struct usba_udc *udc, u32 val)
+{
+ usba_writel(udc, INT_ENB, val);
+ udc->int_enb_cache = val;
+}
+
static int vbus_is_present(struct usba_udc *udc)
{
if (gpio_is_valid(udc->vbus_pin))
@@ -324,27 +339,22 @@ static int vbus_is_present(struct usba_udc *udc)
return 1;
}
-#if defined(CONFIG_ARCH_AT91SAM9RL)
-
-#include <linux/clk/at91_pmc.h>
-
-static void toggle_bias(int is_on)
+static void toggle_bias(struct usba_udc *udc, int is_on)
{
- unsigned int uckr = at91_pmc_read(AT91_CKGR_UCKR);
-
- if (is_on)
- at91_pmc_write(AT91_CKGR_UCKR, uckr | AT91_PMC_BIASEN);
- else
- at91_pmc_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN));
+ if (udc->errata && udc->errata->toggle_bias)
+ udc->errata->toggle_bias(udc, is_on);
}
-#else
-
-static void toggle_bias(int is_on)
+static void generate_bias_pulse(struct usba_udc *udc)
{
-}
+ if (!udc->bias_pulse_needed)
+ return;
-#endif /* CONFIG_ARCH_AT91SAM9RL */
+ if (udc->errata && udc->errata->pulse_bias)
+ udc->errata->pulse_bias(udc);
+
+ udc->bias_pulse_needed = false;
+}
static void next_fifo_transaction(struct usba_ep *ep, struct usba_request *req)
{
@@ -601,16 +611,14 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
if (ep->can_dma) {
u32 ctrl;
- usba_writel(udc, INT_ENB,
- (usba_readl(udc, INT_ENB)
- | USBA_BF(EPT_INT, 1 << ep->index)
- | USBA_BF(DMA_INT, 1 << ep->index)));
+ usba_int_enb_set(udc, usba_int_enb_get(udc) |
+ USBA_BF(EPT_INT, 1 << ep->index) |
+ USBA_BF(DMA_INT, 1 << ep->index));
ctrl = USBA_AUTO_VALID | USBA_INTDIS_DMA;
usba_ep_writel(ep, CTL_ENB, ctrl);
} else {
- usba_writel(udc, INT_ENB,
- (usba_readl(udc, INT_ENB)
- | USBA_BF(EPT_INT, 1 << ep->index)));
+ usba_int_enb_set(udc, usba_int_enb_get(udc) |
+ USBA_BF(EPT_INT, 1 << ep->index));
}
spin_unlock_irqrestore(&udc->lock, flags);
@@ -618,7 +626,7 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
DBG(DBG_HW, "EPT_CFG%d after init: %#08lx\n", ep->index,
(unsigned long)usba_ep_readl(ep, CFG));
DBG(DBG_HW, "INT_ENB after init: %#08lx\n",
- (unsigned long)usba_readl(udc, INT_ENB));
+ (unsigned long)usba_int_enb_get(udc));
return 0;
}
@@ -654,9 +662,8 @@ static int usba_ep_disable(struct usb_ep *_ep)
usba_dma_readl(ep, STATUS);
}
usba_ep_writel(ep, CTL_DIS, USBA_EPT_ENABLE);
- usba_writel(udc, INT_ENB,
- usba_readl(udc, INT_ENB)
- & ~USBA_BF(EPT_INT, 1 << ep->index));
+ usba_int_enb_set(udc, usba_int_enb_get(udc) &
+ ~USBA_BF(EPT_INT, 1 << ep->index));
request_complete_list(ep, &req_list, -ESHUTDOWN);
@@ -985,6 +992,7 @@ usba_udc_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered)
struct usba_udc *udc = to_usba_udc(gadget);
unsigned long flags;
+ gadget->is_selfpowered = (is_selfpowered != 0);
spin_lock_irqsave(&udc->lock, flags);
if (is_selfpowered)
udc->devstatus |= 1 << USB_DEVICE_SELF_POWERED;
@@ -1619,18 +1627,21 @@ static void usba_dma_irq(struct usba_udc *udc, struct usba_ep *ep)
static irqreturn_t usba_udc_irq(int irq, void *devid)
{
struct usba_udc *udc = devid;
- u32 status;
+ u32 status, int_enb;
u32 dma_status;
u32 ep_status;
spin_lock(&udc->lock);
- status = usba_readl(udc, INT_STA);
+ int_enb = usba_int_enb_get(udc);
+ status = usba_readl(udc, INT_STA) & int_enb;
DBG(DBG_INT, "irq, status=%#08x\n", status);
if (status & USBA_DET_SUSPEND) {
- toggle_bias(0);
+ toggle_bias(udc, 0);
usba_writel(udc, INT_CLR, USBA_DET_SUSPEND);
+ usba_int_enb_set(udc, int_enb | USBA_WAKE_UP);
+ udc->bias_pulse_needed = true;
DBG(DBG_BUS, "Suspend detected\n");
if (udc->gadget.speed != USB_SPEED_UNKNOWN
&& udc->driver && udc->driver->suspend) {
@@ -1641,13 +1652,15 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
}
if (status & USBA_WAKE_UP) {
- toggle_bias(1);
+ toggle_bias(udc, 1);
usba_writel(udc, INT_CLR, USBA_WAKE_UP);
+ usba_int_enb_set(udc, int_enb & ~USBA_WAKE_UP);
DBG(DBG_BUS, "Wake Up CPU detected\n");
}
if (status & USBA_END_OF_RESUME) {
usba_writel(udc, INT_CLR, USBA_END_OF_RESUME);
+ generate_bias_pulse(udc);
DBG(DBG_BUS, "Resume detected\n");
if (udc->gadget.speed != USB_SPEED_UNKNOWN
&& udc->driver && udc->driver->resume) {
@@ -1683,6 +1696,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
struct usba_ep *ep0;
usba_writel(udc, INT_CLR, USBA_END_OF_RESET);
+ generate_bias_pulse(udc);
reset_all_endpoints(udc);
if (udc->gadget.speed != USB_SPEED_UNKNOWN && udc->driver) {
@@ -1708,11 +1722,8 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
| USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE)));
usba_ep_writel(ep0, CTL_ENB,
USBA_EPT_ENABLE | USBA_RX_SETUP);
- usba_writel(udc, INT_ENB,
- (usba_readl(udc, INT_ENB)
- | USBA_BF(EPT_INT, 1)
- | USBA_DET_SUSPEND
- | USBA_END_OF_RESUME));
+ usba_int_enb_set(udc, int_enb | USBA_BF(EPT_INT, 1) |
+ USBA_DET_SUSPEND | USBA_END_OF_RESUME);
/*
* Unclear why we hit this irregularly, e.g. in usbtest,
@@ -1745,13 +1756,13 @@ static irqreturn_t usba_vbus_irq(int irq, void *devid)
vbus = vbus_is_present(udc);
if (vbus != udc->vbus_prev) {
if (vbus) {
- toggle_bias(1);
+ toggle_bias(udc, 1);
usba_writel(udc, CTRL, USBA_ENABLE_MASK);
- usba_writel(udc, INT_ENB, USBA_END_OF_RESET);
+ usba_int_enb_set(udc, USBA_END_OF_RESET);
} else {
udc->gadget.speed = USB_SPEED_UNKNOWN;
reset_all_endpoints(udc);
- toggle_bias(0);
+ toggle_bias(udc, 0);
usba_writel(udc, CTRL, USBA_DISABLE_MASK);
if (udc->driver->disconnect) {
spin_unlock(&udc->lock);
@@ -1797,9 +1808,9 @@ static int atmel_usba_start(struct usb_gadget *gadget,
/* If Vbus is present, enable the controller and wait for reset */
spin_lock_irqsave(&udc->lock, flags);
if (vbus_is_present(udc) && udc->vbus_prev == 0) {
- toggle_bias(1);
+ toggle_bias(udc, 1);
usba_writel(udc, CTRL, USBA_ENABLE_MASK);
- usba_writel(udc, INT_ENB, USBA_END_OF_RESET);
+ usba_int_enb_set(udc, USBA_END_OF_RESET);
}
spin_unlock_irqrestore(&udc->lock, flags);
@@ -1820,7 +1831,7 @@ static int atmel_usba_stop(struct usb_gadget *gadget)
spin_unlock_irqrestore(&udc->lock, flags);
/* This will also disable the DP pullup */
- toggle_bias(0);
+ toggle_bias(udc, 0);
usba_writel(udc, CTRL, USBA_DISABLE_MASK);
clk_disable_unprepare(udc->hclk);
@@ -1832,6 +1843,41 @@ static int atmel_usba_stop(struct usb_gadget *gadget)
}
#ifdef CONFIG_OF
+static void at91sam9rl_toggle_bias(struct usba_udc *udc, int is_on)
+{
+ unsigned int uckr = at91_pmc_read(AT91_CKGR_UCKR);
+
+ if (is_on)
+ at91_pmc_write(AT91_CKGR_UCKR, uckr | AT91_PMC_BIASEN);
+ else
+ at91_pmc_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN));
+}
+
+static void at91sam9g45_pulse_bias(struct usba_udc *udc)
+{
+ unsigned int uckr = at91_pmc_read(AT91_CKGR_UCKR);
+
+ at91_pmc_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN));
+ at91_pmc_write(AT91_CKGR_UCKR, uckr | AT91_PMC_BIASEN);
+}
+
+static const struct usba_udc_errata at91sam9rl_errata = {
+ .toggle_bias = at91sam9rl_toggle_bias,
+};
+
+static const struct usba_udc_errata at91sam9g45_errata = {
+ .pulse_bias = at91sam9g45_pulse_bias,
+};
+
+static const struct of_device_id atmel_udc_dt_ids[] = {
+ { .compatible = "atmel,at91sam9rl-udc", .data = &at91sam9rl_errata },
+ { .compatible = "atmel,at91sam9g45-udc", .data = &at91sam9g45_errata },
+ { .compatible = "atmel,sama5d3-udc" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, atmel_udc_dt_ids);
+
static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
struct usba_udc *udc)
{
@@ -1839,10 +1885,17 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
const char *name;
enum of_gpio_flags flags;
struct device_node *np = pdev->dev.of_node;
+ const struct of_device_id *match;
struct device_node *pp;
int i, ret;
struct usba_ep *eps, *ep;
+ match = of_match_node(atmel_udc_dt_ids, np);
+ if (!match)
+ return ERR_PTR(-EINVAL);
+
+ udc->errata = match->data;
+
udc->num_ep = 0;
udc->vbus_pin = of_get_named_gpio_flags(np, "atmel,vbus-gpio", 0,
@@ -2033,7 +2086,7 @@ static int usba_udc_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Unable to enable pclk, aborting.\n");
return ret;
}
- toggle_bias(0);
+
usba_writel(udc, CTRL, USBA_DISABLE_MASK);
clk_disable_unprepare(pclk);
@@ -2042,6 +2095,8 @@ static int usba_udc_probe(struct platform_device *pdev)
else
udc->usba_ep = usba_udc_pdata(pdev, udc);
+ toggle_bias(udc, 0);
+
if (IS_ERR(udc->usba_ep))
return PTR_ERR(udc->usba_ep);
@@ -2101,15 +2156,6 @@ static int __exit usba_udc_remove(struct platform_device *pdev)
return 0;
}
-#if defined(CONFIG_OF)
-static const struct of_device_id atmel_udc_dt_ids[] = {
- { .compatible = "atmel,at91sam9rl-udc" },
- { /* sentinel */ }
-};
-
-MODULE_DEVICE_TABLE(of, atmel_udc_dt_ids);
-#endif
-
static struct platform_driver udc_driver = {
.remove = __exit_p(usba_udc_remove),
.driver = {
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h
index a70706e8cb02..497cd18836f3 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.h
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.h
@@ -304,6 +304,11 @@ struct usba_request {
unsigned int mapped:1;
};
+struct usba_udc_errata {
+ void (*toggle_bias)(struct usba_udc *udc, int is_on);
+ void (*pulse_bias)(struct usba_udc *udc);
+};
+
struct usba_udc {
/* Protect hw registers from concurrent modifications */
spinlock_t lock;
@@ -314,6 +319,7 @@ struct usba_udc {
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
struct platform_device *pdev;
+ const struct usba_udc_errata *errata;
int irq;
int vbus_pin;
int vbus_pin_inverted;
@@ -321,12 +327,15 @@ struct usba_udc {
struct clk *pclk;
struct clk *hclk;
struct usba_ep *usba_ep;
+ bool bias_pulse_needed;
u16 devstatus;
u16 test_mode;
int vbus_prev;
+ u32 int_enb_cache;
+
#ifdef CONFIG_USB_GADGET_DEBUG_FS
struct dentry *debugfs_root;
struct dentry *debugfs_regs;
diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c
index c6dfef8c7bbc..5c8f4effb62a 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_core.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_core.c
@@ -521,7 +521,6 @@ static int bdc_remove(struct platform_device *pdev)
static struct platform_driver bdc_driver = {
.driver = {
.name = BRCM_BDC_NAME,
- .owner = THIS_MODULE
},
.probe = bdc_probe,
.remove = bdc_remove,
diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.c b/drivers/usb/gadget/udc/bdc/bdc_ep.c
index d4fe8d769bd6..b04980cf6dc4 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_ep.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_ep.c
@@ -718,7 +718,7 @@ static int ep_queue(struct bdc_ep *ep, struct bdc_req *req)
struct bdc *bdc;
int ret = 0;
- if (!req || !ep || !ep->usb_ep.desc)
+ if (!req || !ep->usb_ep.desc)
return -EINVAL;
bdc = ep->bdc;
@@ -882,8 +882,8 @@ static int ep_set_halt(struct bdc_ep *ep, u32 value)
ret = bdc_ep_set_stall(bdc, ep->ep_num);
if (ret)
- dev_err(bdc->dev, "failed to %s STALL on %s\n",
- value ? "set" : "clear", ep->name);
+ dev_err(bdc->dev, "failed to set STALL on %s\n",
+ ep->name);
else
ep->flags |= BDC_EP_STALL;
} else {
@@ -891,8 +891,8 @@ static int ep_set_halt(struct bdc_ep *ep, u32 value)
dev_dbg(bdc->dev, "Before Clear\n");
ret = bdc_ep_clear_stall(bdc, ep->ep_num);
if (ret)
- dev_err(bdc->dev, "failed to %s STALL on %s\n",
- value ? "set" : "clear", ep->name);
+ dev_err(bdc->dev, "failed to clear STALL on %s\n",
+ ep->name);
else
ep->flags &= ~BDC_EP_STALL;
dev_dbg(bdc->dev, "After Clear\n");
diff --git a/drivers/usb/gadget/udc/bdc/bdc_udc.c b/drivers/usb/gadget/udc/bdc/bdc_udc.c
index 3700ce70b0be..7f77db5d1278 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_udc.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_udc.c
@@ -454,6 +454,7 @@ static int bdc_udc_set_selfpowered(struct usb_gadget *gadget,
unsigned long flags;
dev_dbg(bdc->dev, "%s()\n", __func__);
+ gadget->is_selfpowered = (is_self != 0);
spin_lock_irqsave(&bdc->lock, flags);
if (!is_self)
bdc->devstatus |= 1 << USB_DEVICE_SELF_POWERED;
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index 9c598801404c..8dda48445f6f 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -802,6 +802,7 @@ static int dummy_set_selfpowered(struct usb_gadget *_gadget, int value)
{
struct dummy *dum;
+ _gadget->is_selfpowered = (value != 0);
dum = gadget_to_dummy_hcd(_gadget)->dum;
if (value)
dum->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
@@ -1924,7 +1925,9 @@ ss_hub_descriptor(struct usb_hub_descriptor *desc)
memset(desc, 0, sizeof *desc);
desc->bDescriptorType = 0x2a;
desc->bDescLength = 12;
- desc->wHubCharacteristics = cpu_to_le16(0x0001);
+ desc->wHubCharacteristics = cpu_to_le16(
+ HUB_CHAR_INDV_PORT_LPSM |
+ HUB_CHAR_COMMON_OCPM);
desc->bNbrPorts = 1;
desc->u.ss.bHubHdrDecLat = 0x04; /* Worst case: 0.4 micro sec*/
desc->u.ss.DeviceRemovable = 0xffff;
@@ -1935,7 +1938,9 @@ static inline void hub_descriptor(struct usb_hub_descriptor *desc)
memset(desc, 0, sizeof *desc);
desc->bDescriptorType = 0x29;
desc->bDescLength = 9;
- desc->wHubCharacteristics = cpu_to_le16(0x0001);
+ desc->wHubCharacteristics = cpu_to_le16(
+ HUB_CHAR_INDV_PORT_LPSM |
+ HUB_CHAR_COMMON_OCPM);
desc->bNbrPorts = 1;
desc->u.hs.DeviceRemovable[0] = 0xff;
desc->u.hs.DeviceRemovable[1] = 0xff;
diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c
index 795c99c77697..e0822f1b6639 100644
--- a/drivers/usb/gadget/udc/fsl_qe_udc.c
+++ b/drivers/usb/gadget/udc/fsl_qe_udc.c
@@ -2630,7 +2630,7 @@ static int qe_udc_remove(struct platform_device *ofdev)
struct qe_udc *udc = platform_get_drvdata(ofdev);
struct qe_ep *ep;
unsigned int size;
- DECLARE_COMPLETION(done);
+ DECLARE_COMPLETION_ONSTACK(done);
usb_del_gadget_udc(&udc->gadget);
diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c
index 2df807403f22..55fcb930f92e 100644
--- a/drivers/usb/gadget/udc/fsl_udc_core.c
+++ b/drivers/usb/gadget/udc/fsl_udc_core.c
@@ -1337,7 +1337,7 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value,
if ((request_type & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
/* Get device status */
- tmp = 1 << USB_DEVICE_SELF_POWERED;
+ tmp = udc->gadget.is_selfpowered;
tmp |= udc->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP;
} else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) {
/* Get interface status */
@@ -1948,6 +1948,7 @@ static int fsl_udc_start(struct usb_gadget *g,
/* hook up the driver */
udc_controller->driver = driver;
spin_unlock_irqrestore(&udc_controller->lock, flags);
+ g->is_selfpowered = 1;
if (!IS_ERR_OR_NULL(udc_controller->transceiver)) {
/* Suspend the controller until OTG enable it */
@@ -2529,7 +2530,7 @@ static int __exit 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(done);
+ DECLARE_COMPLETION_ONSTACK(done);
if (!udc_controller)
return -ENODEV;
diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c
index 34d9b7b831b3..27fd41333f71 100644
--- a/drivers/usb/gadget/udc/lpc32xx_udc.c
+++ b/drivers/usb/gadget/udc/lpc32xx_udc.c
@@ -191,7 +191,6 @@ struct lpc32xx_udc {
bool enabled;
bool clocked;
bool suspended;
- bool selfpowered;
int ep0state;
atomic_t enabled_ep_cnt;
wait_queue_head_t ep_disable_wait_queue;
@@ -547,7 +546,7 @@ static int proc_udc_show(struct seq_file *s, void *unused)
udc->vbus ? "present" : "off",
udc->enabled ? (udc->vbus ? "active" : "enabled") :
"disabled",
- udc->selfpowered ? "self" : "VBUS",
+ udc->gadget.is_selfpowered ? "self" : "VBUS",
udc->suspended ? ", suspended" : "",
udc->driver ? udc->driver->driver.name : "(none)");
@@ -2212,7 +2211,7 @@ static int udc_get_status(struct lpc32xx_udc *udc, u16 reqtype, u16 wIndex)
break; /* Not supported */
case USB_RECIP_DEVICE:
- ep0buff = (udc->selfpowered << USB_DEVICE_SELF_POWERED);
+ ep0buff = udc->gadget.is_selfpowered;
if (udc->dev_status & (1 << USB_DEVICE_REMOTE_WAKEUP))
ep0buff |= (1 << USB_DEVICE_REMOTE_WAKEUP);
break;
@@ -2498,10 +2497,7 @@ static int lpc32xx_wakeup(struct usb_gadget *gadget)
static int lpc32xx_set_selfpowered(struct usb_gadget *gadget, int is_on)
{
- struct lpc32xx_udc *udc = to_udc(gadget);
-
- /* Always self-powered */
- udc->selfpowered = (is_on != 0);
+ gadget->is_selfpowered = (is_on != 0);
return 0;
}
@@ -2946,7 +2942,7 @@ static int lpc32xx_start(struct usb_gadget *gadget,
udc->driver = driver;
udc->gadget.dev.of_node = udc->dev->of_node;
udc->enabled = 1;
- udc->selfpowered = 1;
+ udc->gadget.is_selfpowered = 1;
udc->vbus = 0;
/* Force VBUS process once to check for cable insertion */
diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c
index 253f3df8326a..d32160d6463f 100644
--- a/drivers/usb/gadget/udc/mv_udc_core.c
+++ b/drivers/usb/gadget/udc/mv_udc_core.c
@@ -1378,9 +1378,6 @@ static int mv_udc_start(struct usb_gadget *gadget,
}
}
- /* pullup is always on */
- mv_udc_pullup(&udc->gadget, 1);
-
/* When boot with cable attached, there will be no vbus irq occurred */
if (udc->qwork)
queue_work(udc->qwork, &udc->vbus_work);
diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c
index d20de1fab08e..195baf3e1fcd 100644
--- a/drivers/usb/gadget/udc/net2272.c
+++ b/drivers/usb/gadget/udc/net2272.c
@@ -1132,13 +1132,10 @@ net2272_wakeup(struct usb_gadget *_gadget)
static int
net2272_set_selfpowered(struct usb_gadget *_gadget, int value)
{
- struct net2272 *dev;
-
if (!_gadget)
return -ENODEV;
- dev = container_of(_gadget, struct net2272, gadget);
- dev->is_selfpowered = value;
+ _gadget->is_selfpowered = (value != 0);
return 0;
}
@@ -1844,7 +1841,7 @@ net2272_handle_stat0_irqs(struct net2272 *dev, u8 stat)
case USB_RECIP_DEVICE:
if (u.r.wLength > 2)
goto do_stall;
- if (dev->is_selfpowered)
+ if (dev->gadget.is_selfpowered)
status = (1 << USB_DEVICE_SELF_POWERED);
/* don't bother with a request object! */
diff --git a/drivers/usb/gadget/udc/net2272.h b/drivers/usb/gadget/udc/net2272.h
index e59505789359..127ab03fcde3 100644
--- a/drivers/usb/gadget/udc/net2272.h
+++ b/drivers/usb/gadget/udc/net2272.h
@@ -458,7 +458,6 @@ struct net2272 {
struct usb_gadget_driver *driver;
unsigned protocol_stall:1,
softconnect:1,
- is_selfpowered:1,
wakeup:1,
dma_eot_polarity:1,
dma_dack_polarity:1,
diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c
index d6411e0a8e03..d2c0bf65e345 100644
--- a/drivers/usb/gadget/udc/net2280.c
+++ b/drivers/usb/gadget/udc/net2280.c
@@ -12,11 +12,7 @@
* the Mass Storage, Serial, and Ethernet/RNDIS gadget drivers
* as well as Gadget Zero and Gadgetfs.
*
- * DMA is enabled by default. Drivers using transfer queues might use
- * DMA chaining to remove IRQ latencies between transfers. (Except when
- * short OUT transfers happen.) Drivers can use the req->no_interrupt
- * hint to completely eliminate some IRQs, if a later IRQ is guaranteed
- * and DMA chaining is enabled.
+ * DMA is enabled by default.
*
* MSI is enabled by default. The legacy IRQ is used if MSI couldn't
* be enabled.
@@ -84,23 +80,6 @@ static const char *const ep_name[] = {
"ep-e", "ep-f", "ep-g", "ep-h",
};
-/* use_dma -- general goodness, fewer interrupts, less cpu load (vs PIO)
- * use_dma_chaining -- dma descriptor queueing gives even more irq reduction
- *
- * The net2280 DMA engines are not tightly integrated with their FIFOs;
- * not all cases are (yet) handled well in this driver or the silicon.
- * Some gadget drivers work better with the dma support here than others.
- * These two parameters let you use PIO or more aggressive DMA.
- */
-static bool use_dma = true;
-static bool use_dma_chaining;
-static bool use_msi = true;
-
-/* "modprobe net2280 use_dma=n" etc */
-module_param(use_dma, bool, 0444);
-module_param(use_dma_chaining, bool, 0444);
-module_param(use_msi, bool, 0444);
-
/* mode 0 == ep-{a,b,c,d} 1K fifo each
* mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable
* mode 2 == ep-a 2K fifo, ep-{b,c} 1K each, ep-d unavailable
@@ -120,11 +99,6 @@ static bool enable_suspend;
/* "modprobe net2280 enable_suspend=1" etc */
module_param(enable_suspend, bool, 0444);
-/* force full-speed operation */
-static bool full_speed;
-module_param(full_speed, bool, 0444);
-MODULE_PARM_DESC(full_speed, "force full-speed mode -- for testing only!");
-
#define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out")
static char *type_string(u8 bmAttributes)
@@ -202,15 +176,6 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
/* set speed-dependent max packet; may kick in high bandwidth */
set_max_speed(ep, max);
- /* FIFO lines can't go to different packets. PIO is ok, so
- * use it instead of troublesome (non-bulk) multi-packet DMA.
- */
- if (ep->dma && (max % 4) != 0 && use_dma_chaining) {
- ep_dbg(ep->dev, "%s, no dma for maxpacket %d\n",
- ep->ep.name, ep->ep.maxpacket);
- ep->dma = NULL;
- }
-
/* set type, direction, address; reset fifo counters */
writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat);
tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
@@ -478,7 +443,7 @@ static int net2280_disable(struct usb_ep *_ep)
/* synch memory views with the device */
(void)readl(&ep->cfg->ep_cfg);
- if (use_dma && !ep->dma && ep->num >= 1 && ep->num <= 4)
+ if (!ep->dma && ep->num >= 1 && ep->num <= 4)
ep->dma = &ep->dev->dma[ep->num - 1];
spin_unlock_irqrestore(&ep->dev->lock, flags);
@@ -610,9 +575,15 @@ static void out_flush(struct net2280_ep *ep)
u32 __iomem *statp;
u32 tmp;
- ASSERT_OUT_NAKING(ep);
-
statp = &ep->regs->ep_stat;
+
+ tmp = readl(statp);
+ if (tmp & BIT(NAK_OUT_PACKETS)) {
+ ep_dbg(ep->dev, "%s %s %08x !NAK\n",
+ ep->ep.name, __func__, tmp);
+ writel(BIT(SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+ }
+
writel(BIT(DATA_OUT_PING_TOKEN_INTERRUPT) |
BIT(DATA_PACKET_RECEIVED_INTERRUPT),
statp);
@@ -747,8 +718,7 @@ static void fill_dma_desc(struct net2280_ep *ep,
req->valid = valid;
if (valid)
dmacount |= BIT(VALID_BIT);
- if (likely(!req->req.no_interrupt || !use_dma_chaining))
- dmacount |= BIT(DMA_DONE_INTERRUPT_ENABLE);
+ dmacount |= BIT(DMA_DONE_INTERRUPT_ENABLE);
/* td->dmadesc = previously set by caller */
td->dmaaddr = cpu_to_le32 (req->req.dma);
@@ -862,27 +832,11 @@ static void start_dma(struct net2280_ep *ep, struct net2280_request *req)
req->td->dmadesc = cpu_to_le32 (ep->td_dma);
fill_dma_desc(ep, req, 1);
- if (!use_dma_chaining)
- req->td->dmacount |= cpu_to_le32(BIT(END_OF_CHAIN));
+ req->td->dmacount |= cpu_to_le32(BIT(END_OF_CHAIN));
start_queue(ep, tmp, req->td_dma);
}
-static inline void resume_dma(struct net2280_ep *ep)
-{
- writel(readl(&ep->dma->dmactl) | BIT(DMA_ENABLE), &ep->dma->dmactl);
-
- ep->dma_started = true;
-}
-
-static inline void ep_stop_dma(struct net2280_ep *ep)
-{
- writel(readl(&ep->dma->dmactl) & ~BIT(DMA_ENABLE), &ep->dma->dmactl);
- spin_stop_dma(ep->dma);
-
- ep->dma_started = false;
-}
-
static inline void
queue_dma(struct net2280_ep *ep, struct net2280_request *req, int valid)
{
@@ -973,10 +927,8 @@ net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
return ret;
}
-#if 0
ep_vdbg(dev, "%s queue req %p, len %d buf %p\n",
_ep->name, _req, _req->length, _req->buf);
-#endif
spin_lock_irqsave(&dev->lock, flags);
@@ -984,24 +936,12 @@ net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
_req->actual = 0;
/* kickstart this i/o queue? */
- if (list_empty(&ep->queue) && !ep->stopped) {
- /* DMA request while EP halted */
- if (ep->dma &&
- (readl(&ep->regs->ep_rsp) & BIT(CLEAR_ENDPOINT_HALT)) &&
- (dev->quirks & PLX_SUPERSPEED)) {
- int valid = 1;
- if (ep->is_in) {
- int expect;
- expect = likely(req->req.zero ||
- ((req->req.length %
- ep->ep.maxpacket) != 0));
- if (expect != ep->in_fifo_validate)
- valid = 0;
- }
- queue_dma(ep, req, valid);
- }
+ if (list_empty(&ep->queue) && !ep->stopped &&
+ !((dev->quirks & PLX_SUPERSPEED) && ep->dma &&
+ (readl(&ep->regs->ep_rsp) & BIT(CLEAR_ENDPOINT_HALT)))) {
+
/* use DMA if the endpoint supports it, else pio */
- else if (ep->dma)
+ if (ep->dma)
start_dma(ep, req);
else {
/* maybe there's no control data, just status ack */
@@ -1084,8 +1024,6 @@ dma_done(struct net2280_ep *ep, struct net2280_request *req, u32 dmacount,
done(ep, req, status);
}
-static void restart_dma(struct net2280_ep *ep);
-
static void scan_dma_completions(struct net2280_ep *ep)
{
/* only look at descriptors that were "naturally" retired,
@@ -1117,9 +1055,8 @@ static void scan_dma_completions(struct net2280_ep *ep)
dma_done(ep, req, tmp, 0);
break;
} else if (!ep->is_in &&
- (req->req.length % ep->ep.maxpacket) != 0) {
- if (ep->dev->quirks & PLX_SUPERSPEED)
- return dma_done(ep, req, tmp, 0);
+ (req->req.length % ep->ep.maxpacket) &&
+ !(ep->dev->quirks & PLX_SUPERSPEED)) {
tmp = readl(&ep->regs->ep_stat);
/* AVOID TROUBLE HERE by not issuing short reads from
@@ -1150,67 +1087,15 @@ static void scan_dma_completions(struct net2280_ep *ep)
static void restart_dma(struct net2280_ep *ep)
{
struct net2280_request *req;
- u32 dmactl = dmactl_default;
if (ep->stopped)
return;
req = list_entry(ep->queue.next, struct net2280_request, queue);
- if (!use_dma_chaining) {
- start_dma(ep, req);
- return;
- }
-
- /* the 2280 will be processing the queue unless queue hiccups after
- * the previous transfer:
- * IN: wanted automagic zlp, head doesn't (or vice versa)
- * DMA_FIFO_VALIDATE doesn't init from dma descriptors.
- * OUT: was "usb-short", we must restart.
- */
- if (ep->is_in && !req->valid) {
- struct net2280_request *entry, *prev = NULL;
- int reqmode, done = 0;
-
- ep_dbg(ep->dev, "%s dma hiccup td %p\n", ep->ep.name, req->td);
- ep->in_fifo_validate = likely(req->req.zero ||
- (req->req.length % ep->ep.maxpacket) != 0);
- if (ep->in_fifo_validate)
- dmactl |= BIT(DMA_FIFO_VALIDATE);
- list_for_each_entry(entry, &ep->queue, queue) {
- __le32 dmacount;
-
- if (entry == req)
- continue;
- dmacount = entry->td->dmacount;
- if (!done) {
- reqmode = likely(entry->req.zero ||
- (entry->req.length % ep->ep.maxpacket));
- if (reqmode == ep->in_fifo_validate) {
- entry->valid = 1;
- dmacount |= valid_bit;
- entry->td->dmacount = dmacount;
- prev = entry;
- continue;
- } else {
- /* force a hiccup */
- prev->td->dmacount |= dma_done_ie;
- done = 1;
- }
- }
-
- /* walk the rest of the queue so unlinks behave */
- entry->valid = 0;
- dmacount &= ~valid_bit;
- entry->td->dmacount = dmacount;
- prev = entry;
- }
- }
-
- writel(0, &ep->dma->dmactl);
- start_queue(ep, dmactl, req->td_dma);
+ start_dma(ep, req);
}
-static void abort_dma_228x(struct net2280_ep *ep)
+static void abort_dma(struct net2280_ep *ep)
{
/* abort the current transfer */
if (likely(!list_empty(&ep->queue))) {
@@ -1222,19 +1107,6 @@ static void abort_dma_228x(struct net2280_ep *ep)
scan_dma_completions(ep);
}
-static void abort_dma_338x(struct net2280_ep *ep)
-{
- writel(BIT(DMA_ABORT), &ep->dma->dmastat);
- spin_stop_dma(ep->dma);
-}
-
-static void abort_dma(struct net2280_ep *ep)
-{
- if (ep->dev->quirks & PLX_LEGACY)
- return abort_dma_228x(ep);
- return abort_dma_338x(ep);
-}
-
/* dequeue ALL requests */
static void nuke(struct net2280_ep *ep)
{
@@ -1306,25 +1178,6 @@ static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req)
done(ep, req, -ECONNRESET);
}
req = NULL;
-
- /* patch up hardware chaining data */
- } else if (ep->dma && use_dma_chaining) {
- if (req->queue.prev == ep->queue.next) {
- writel(le32_to_cpu(req->td->dmadesc),
- &ep->dma->dmadesc);
- if (req->td->dmacount & dma_done_ie)
- writel(readl(&ep->dma->dmacount) |
- le32_to_cpu(dma_done_ie),
- &ep->dma->dmacount);
- } else {
- struct net2280_request *prev;
-
- prev = list_entry(req->queue.prev,
- struct net2280_request, queue);
- prev->td->dmadesc = req->td->dmadesc;
- if (req->td->dmacount & dma_done_ie)
- prev->td->dmacount |= dma_done_ie;
- }
}
if (req)
@@ -1512,10 +1365,10 @@ static int net2280_set_selfpowered(struct usb_gadget *_gadget, int value)
tmp = readl(&dev->usb->usbctl);
if (value) {
tmp |= BIT(SELF_POWERED_STATUS);
- dev->selfpowered = 1;
+ _gadget->is_selfpowered = 1;
} else {
tmp &= ~BIT(SELF_POWERED_STATUS);
- dev->selfpowered = 0;
+ _gadget->is_selfpowered = 0;
}
writel(tmp, &dev->usb->usbctl);
spin_unlock_irqrestore(&dev->lock, flags);
@@ -1604,14 +1457,11 @@ static ssize_t registers_show(struct device *_dev,
/* Main Control Registers */
t = scnprintf(next, size, "%s version " DRIVER_VERSION
- ", chiprev %04x, dma %s\n\n"
+ ", chiprev %04x\n\n"
"devinit %03x fifoctl %08x gadget '%s'\n"
"pci irqenb0 %02x irqenb1 %08x "
"irqstat0 %04x irqstat1 %08x\n",
driver_name, dev->chiprev,
- use_dma
- ? (use_dma_chaining ? "chaining" : "enabled")
- : "disabled",
readl(&dev->regs->devinit),
readl(&dev->regs->fifoctl),
s,
@@ -1913,76 +1763,73 @@ static void defect7374_disable_data_eps(struct net2280 *dev)
static void defect7374_enable_data_eps_zero(struct net2280 *dev)
{
u32 tmp = 0, tmp_reg;
- u32 fsmvalue, scratch;
+ u32 scratch;
int i;
unsigned char ep_sel;
scratch = get_idx_reg(dev->regs, SCRATCH);
- fsmvalue = scratch & (0xf << DEFECT7374_FSM_FIELD);
+
+ WARN_ON((scratch & (0xf << DEFECT7374_FSM_FIELD))
+ == DEFECT7374_FSM_SS_CONTROL_READ);
+
scratch &= ~(0xf << DEFECT7374_FSM_FIELD);
- /*See if firmware needs to set up for workaround*/
- if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) {
- ep_warn(dev, "Operate Defect 7374 workaround soft this time");
- ep_warn(dev, "It will operate on cold-reboot and SS connect");
-
- /*GPEPs:*/
- tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_DIRECTION) |
- (2 << OUT_ENDPOINT_TYPE) | (2 << IN_ENDPOINT_TYPE) |
- ((dev->enhanced_mode) ?
- BIT(OUT_ENDPOINT_ENABLE) : BIT(ENDPOINT_ENABLE)) |
- BIT(IN_ENDPOINT_ENABLE));
-
- for (i = 1; i < 5; i++)
- writel(tmp, &dev->ep[i].cfg->ep_cfg);
-
- /* CSRIN, PCIIN, STATIN, RCIN*/
- tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_ENABLE));
- writel(tmp, &dev->dep[1].dep_cfg);
- writel(tmp, &dev->dep[3].dep_cfg);
- writel(tmp, &dev->dep[4].dep_cfg);
- writel(tmp, &dev->dep[5].dep_cfg);
-
- /*Implemented for development and debug.
- * Can be refined/tuned later.*/
- for (ep_sel = 0; ep_sel <= 21; ep_sel++) {
- /* Select an endpoint for subsequent operations: */
- tmp_reg = readl(&dev->plregs->pl_ep_ctrl);
- writel(((tmp_reg & ~0x1f) | ep_sel),
- &dev->plregs->pl_ep_ctrl);
-
- if (ep_sel == 1) {
- tmp =
- (readl(&dev->plregs->pl_ep_ctrl) |
- BIT(CLEAR_ACK_ERROR_CODE) | 0);
- writel(tmp, &dev->plregs->pl_ep_ctrl);
- continue;
- }
+ ep_warn(dev, "Operate Defect 7374 workaround soft this time");
+ ep_warn(dev, "It will operate on cold-reboot and SS connect");
- if (ep_sel == 0 || (ep_sel > 9 && ep_sel < 14) ||
- ep_sel == 18 || ep_sel == 20)
- continue;
+ /*GPEPs:*/
+ tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_DIRECTION) |
+ (2 << OUT_ENDPOINT_TYPE) | (2 << IN_ENDPOINT_TYPE) |
+ ((dev->enhanced_mode) ?
+ BIT(OUT_ENDPOINT_ENABLE) : BIT(ENDPOINT_ENABLE)) |
+ BIT(IN_ENDPOINT_ENABLE));
- tmp = (readl(&dev->plregs->pl_ep_cfg_4) |
- BIT(NON_CTRL_IN_TOLERATE_BAD_DIR) | 0);
- writel(tmp, &dev->plregs->pl_ep_cfg_4);
+ for (i = 1; i < 5; i++)
+ writel(tmp, &dev->ep[i].cfg->ep_cfg);
- tmp = readl(&dev->plregs->pl_ep_ctrl) &
- ~BIT(EP_INITIALIZED);
- writel(tmp, &dev->plregs->pl_ep_ctrl);
+ /* CSRIN, PCIIN, STATIN, RCIN*/
+ tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_ENABLE));
+ writel(tmp, &dev->dep[1].dep_cfg);
+ writel(tmp, &dev->dep[3].dep_cfg);
+ writel(tmp, &dev->dep[4].dep_cfg);
+ writel(tmp, &dev->dep[5].dep_cfg);
+
+ /*Implemented for development and debug.
+ * Can be refined/tuned later.*/
+ for (ep_sel = 0; ep_sel <= 21; ep_sel++) {
+ /* Select an endpoint for subsequent operations: */
+ tmp_reg = readl(&dev->plregs->pl_ep_ctrl);
+ writel(((tmp_reg & ~0x1f) | ep_sel),
+ &dev->plregs->pl_ep_ctrl);
+ if (ep_sel == 1) {
+ tmp =
+ (readl(&dev->plregs->pl_ep_ctrl) |
+ BIT(CLEAR_ACK_ERROR_CODE) | 0);
+ writel(tmp, &dev->plregs->pl_ep_ctrl);
+ continue;
}
- /* Set FSM to focus on the first Control Read:
- * - Tip: Connection speed is known upon the first
- * setup request.*/
- scratch |= DEFECT7374_FSM_WAITING_FOR_CONTROL_READ;
- set_idx_reg(dev->regs, SCRATCH, scratch);
+ if (ep_sel == 0 || (ep_sel > 9 && ep_sel < 14) ||
+ ep_sel == 18 || ep_sel == 20)
+ continue;
+
+ tmp = (readl(&dev->plregs->pl_ep_cfg_4) |
+ BIT(NON_CTRL_IN_TOLERATE_BAD_DIR) | 0);
+ writel(tmp, &dev->plregs->pl_ep_cfg_4);
+
+ tmp = readl(&dev->plregs->pl_ep_ctrl) &
+ ~BIT(EP_INITIALIZED);
+ writel(tmp, &dev->plregs->pl_ep_ctrl);
- } else{
- ep_warn(dev, "Defect 7374 workaround soft will NOT operate");
- ep_warn(dev, "It will operate on cold-reboot and SS connect");
}
+
+ /* Set FSM to focus on the first Control Read:
+ * - Tip: Connection speed is known upon the first
+ * setup request.*/
+ scratch |= DEFECT7374_FSM_WAITING_FOR_CONTROL_READ;
+ set_idx_reg(dev->regs, SCRATCH, scratch);
+
}
/* keeping it simple:
@@ -2033,21 +1880,13 @@ static void usb_reset_228x(struct net2280 *dev)
static void usb_reset_338x(struct net2280 *dev)
{
u32 tmp;
- u32 fsmvalue;
dev->gadget.speed = USB_SPEED_UNKNOWN;
(void)readl(&dev->usb->usbctl);
net2280_led_init(dev);
- fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
- (0xf << DEFECT7374_FSM_FIELD);
-
- /* See if firmware needs to set up for workaround: */
- if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) {
- ep_info(dev, "%s: Defect 7374 FsmValue 0x%08x\n", __func__,
- fsmvalue);
- } else {
+ if (dev->bug7734_patched) {
/* disable automatic responses, and irqs */
writel(0, &dev->usb->stdrsp);
writel(0, &dev->regs->pciirqenb0);
@@ -2064,7 +1903,7 @@ static void usb_reset_338x(struct net2280 *dev)
writel(~0, &dev->regs->irqstat0), writel(~0, &dev->regs->irqstat1);
- if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ) {
+ if (dev->bug7734_patched) {
/* reset, and enable pci */
tmp = readl(&dev->regs->devinit) |
BIT(PCI_ENABLE) |
@@ -2093,10 +1932,6 @@ static void usb_reset(struct net2280 *dev)
static void usb_reinit_228x(struct net2280 *dev)
{
u32 tmp;
- int init_dma;
-
- /* use_dma changes are ignored till next device re-init */
- init_dma = use_dma;
/* basic endpoint init */
for (tmp = 0; tmp < 7; tmp++) {
@@ -2108,8 +1943,7 @@ static void usb_reinit_228x(struct net2280 *dev)
if (tmp > 0 && tmp <= 4) {
ep->fifo_size = 1024;
- if (init_dma)
- ep->dma = &dev->dma[tmp - 1];
+ ep->dma = &dev->dma[tmp - 1];
} else
ep->fifo_size = 64;
ep->regs = &dev->epregs[tmp];
@@ -2133,17 +1967,12 @@ static void usb_reinit_228x(struct net2280 *dev)
static void usb_reinit_338x(struct net2280 *dev)
{
- int init_dma;
int i;
u32 tmp, val;
- u32 fsmvalue;
static const u32 ne[9] = { 0, 1, 2, 3, 4, 1, 2, 3, 4 };
static const u32 ep_reg_addr[9] = { 0x00, 0xC0, 0x00, 0xC0, 0x00,
0x00, 0xC0, 0x00, 0xC0 };
- /* use_dma changes are ignored till next device re-init */
- init_dma = use_dma;
-
/* basic endpoint init */
for (i = 0; i < dev->n_ep; i++) {
struct net2280_ep *ep = &dev->ep[i];
@@ -2152,7 +1981,7 @@ static void usb_reinit_338x(struct net2280 *dev)
ep->dev = dev;
ep->num = i;
- if (i > 0 && i <= 4 && init_dma)
+ if (i > 0 && i <= 4)
ep->dma = &dev->dma[i - 1];
if (dev->enhanced_mode) {
@@ -2177,14 +2006,7 @@ static void usb_reinit_338x(struct net2280 *dev)
dev->ep[0].stopped = 0;
/* Link layer set up */
- fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
- (0xf << DEFECT7374_FSM_FIELD);
-
- /* See if driver needs to set up for workaround: */
- if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ)
- ep_info(dev, "%s: Defect 7374 FsmValue %08x\n",
- __func__, fsmvalue);
- else {
+ if (dev->bug7734_patched) {
tmp = readl(&dev->usb_ext->usbctl2) &
~(BIT(U1_ENABLE) | BIT(U2_ENABLE) | BIT(LTM_ENABLE));
writel(tmp, &dev->usb_ext->usbctl2);
@@ -2291,15 +2113,8 @@ static void ep0_start_228x(struct net2280 *dev)
static void ep0_start_338x(struct net2280 *dev)
{
- u32 fsmvalue;
-
- fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
- (0xf << DEFECT7374_FSM_FIELD);
- if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ)
- ep_info(dev, "%s: Defect 7374 FsmValue %08x\n", __func__,
- fsmvalue);
- else
+ if (dev->bug7734_patched)
writel(BIT(CLEAR_NAK_OUT_PACKETS_MODE) |
BIT(SET_EP_HIDE_STATUS_PHASE),
&dev->epregs[0].ep_rsp);
@@ -2382,16 +2197,12 @@ static int net2280_start(struct usb_gadget *_gadget,
if (retval)
goto err_func;
- /* Enable force-full-speed testing mode, if desired */
- if (full_speed && (dev->quirks & PLX_LEGACY))
- writel(BIT(FORCE_FULL_SPEED_MODE), &dev->usb->xcvrdiag);
-
- /* ... then enable host detection and ep0; and we're ready
+ /* enable host detection and ep0; and we're ready
* for set_configuration as well as eventual disconnect.
*/
net2280_led_active(dev, 1);
- if (dev->quirks & PLX_SUPERSPEED)
+ if ((dev->quirks & PLX_SUPERSPEED) && !dev->bug7734_patched)
defect7374_enable_data_eps_zero(dev);
ep0_start(dev);
@@ -2444,10 +2255,6 @@ static int net2280_stop(struct usb_gadget *_gadget)
net2280_led_active(dev, 0);
- /* Disable full-speed test mode */
- if (dev->quirks & PLX_LEGACY)
- writel(0, &dev->usb->xcvrdiag);
-
device_remove_file(&dev->pdev->dev, &dev_attr_function);
device_remove_file(&dev->pdev->dev, &dev_attr_queues);
@@ -2478,10 +2285,10 @@ static void handle_ep_small(struct net2280_ep *ep)
/* ack all, and handle what we care about */
t = readl(&ep->regs->ep_stat);
ep->irqs++;
-#if 0
+
ep_vdbg(ep->dev, "%s ack ep_stat %08x, req %p\n",
- ep->ep.name, t, req ? &req->req : 0);
-#endif
+ ep->ep.name, t, req ? &req->req : NULL);
+
if (!ep->is_in || (ep->dev->quirks & PLX_2280))
writel(t & ~BIT(NAK_OUT_PACKETS), &ep->regs->ep_stat);
else
@@ -2717,6 +2524,7 @@ static void defect7374_workaround(struct net2280 *dev, struct usb_ctrlrequest r)
* run after the next USB connection.
*/
scratch |= DEFECT7374_FSM_NON_SS_CONTROL_READ;
+ dev->bug7734_patched = 1;
goto restore_data_eps;
}
@@ -2730,6 +2538,7 @@ static void defect7374_workaround(struct net2280 *dev, struct usb_ctrlrequest r)
if ((state >= (ACK_GOOD_NORMAL << STATE)) &&
(state <= (ACK_GOOD_MORE_ACKS_TO_COME << STATE))) {
scratch |= DEFECT7374_FSM_SS_CONTROL_READ;
+ dev->bug7734_patched = 1;
break;
}
@@ -2766,80 +2575,19 @@ restore_data_eps:
return;
}
-static void ep_stall(struct net2280_ep *ep, int stall)
+static void ep_clear_seqnum(struct net2280_ep *ep)
{
struct net2280 *dev = ep->dev;
u32 val;
static const u32 ep_pl[9] = { 0, 3, 4, 7, 8, 2, 5, 6, 9 };
- if (stall) {
- writel(BIT(SET_ENDPOINT_HALT) |
- /* BIT(SET_NAK_PACKETS) | */
- BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE),
- &ep->regs->ep_rsp);
- ep->is_halt = 1;
- } else {
- if (dev->gadget.speed == USB_SPEED_SUPER) {
- /*
- * Workaround for SS SeqNum not cleared via
- * Endpoint Halt (Clear) bit. select endpoint
- */
- val = readl(&dev->plregs->pl_ep_ctrl);
- val = (val & ~0x1f) | ep_pl[ep->num];
- writel(val, &dev->plregs->pl_ep_ctrl);
-
- val |= BIT(SEQUENCE_NUMBER_RESET);
- writel(val, &dev->plregs->pl_ep_ctrl);
- }
- val = readl(&ep->regs->ep_rsp);
- val |= BIT(CLEAR_ENDPOINT_HALT) |
- BIT(CLEAR_ENDPOINT_TOGGLE);
- writel(val,
- /* | BIT(CLEAR_NAK_PACKETS),*/
- &ep->regs->ep_rsp);
- ep->is_halt = 0;
- val = readl(&ep->regs->ep_rsp);
- }
-}
-
-static void ep_stdrsp(struct net2280_ep *ep, int value, int wedged)
-{
- /* set/clear, then synch memory views with the device */
- if (value) {
- ep->stopped = 1;
- if (ep->num == 0)
- ep->dev->protocol_stall = 1;
- else {
- if (ep->dma)
- ep_stop_dma(ep);
- ep_stall(ep, true);
- }
-
- if (wedged)
- ep->wedged = 1;
- } else {
- ep->stopped = 0;
- ep->wedged = 0;
-
- ep_stall(ep, false);
+ val = readl(&dev->plregs->pl_ep_ctrl) & ~0x1f;
+ val |= ep_pl[ep->num];
+ writel(val, &dev->plregs->pl_ep_ctrl);
+ val |= BIT(SEQUENCE_NUMBER_RESET);
+ writel(val, &dev->plregs->pl_ep_ctrl);
- /* Flush the queue */
- if (!list_empty(&ep->queue)) {
- struct net2280_request *req =
- list_entry(ep->queue.next, struct net2280_request,
- queue);
- if (ep->dma)
- resume_dma(ep);
- else {
- if (ep->is_in)
- write_fifo(ep, &req->req);
- else {
- if (read_fifo(ep, req))
- done(ep, req, 0);
- }
- }
- }
- }
+ return;
}
static void handle_stat0_irqs_superspeed(struct net2280 *dev,
@@ -2863,7 +2611,7 @@ static void handle_stat0_irqs_superspeed(struct net2280 *dev,
switch (r.bRequestType) {
case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE):
status = dev->wakeup_enable ? 0x02 : 0x00;
- if (dev->selfpowered)
+ if (dev->gadget.is_selfpowered)
status |= BIT(0);
status |= (dev->u1_enable << 2 | dev->u2_enable << 3 |
dev->ltm_enable << 4);
@@ -2940,7 +2688,12 @@ static void handle_stat0_irqs_superspeed(struct net2280 *dev,
if (w_value != USB_ENDPOINT_HALT)
goto do_stall3;
ep_vdbg(dev, "%s clear halt\n", e->ep.name);
- ep_stall(e, false);
+ /*
+ * Workaround for SS SeqNum not cleared via
+ * Endpoint Halt (Clear) bit. select endpoint
+ */
+ ep_clear_seqnum(e);
+ clear_halt(e);
if (!list_empty(&e->queue) && e->td_dma)
restart_dma(e);
allow_status(ep);
@@ -2998,7 +2751,14 @@ static void handle_stat0_irqs_superspeed(struct net2280 *dev,
e = get_ep_by_addr(dev, w_index);
if (!e || (w_value != USB_ENDPOINT_HALT))
goto do_stall3;
- ep_stdrsp(e, true, false);
+ ep->stopped = 1;
+ if (ep->num == 0)
+ ep->dev->protocol_stall = 1;
+ else {
+ if (ep->dma)
+ abort_dma(ep);
+ set_halt(ep);
+ }
allow_status_338x(ep);
break;
@@ -3026,7 +2786,7 @@ do_stall3:
r.bRequestType, r.bRequest, tmp);
dev->protocol_stall = 1;
/* TD 9.9 Halt Endpoint test. TD 9.22 Set feature test */
- ep_stall(ep, true);
+ set_halt(ep);
}
next_endpoints3:
@@ -3091,9 +2851,7 @@ static void handle_stat0_irqs(struct net2280 *dev, u32 stat)
}
ep->stopped = 0;
dev->protocol_stall = 0;
- if (dev->quirks & PLX_SUPERSPEED)
- ep->is_halt = 0;
- else{
+ if (!(dev->quirks & PLX_SUPERSPEED)) {
if (ep->dev->quirks & PLX_2280)
tmp = BIT(FIFO_OVERFLOW) |
BIT(FIFO_UNDERFLOW);
@@ -3120,7 +2878,7 @@ static void handle_stat0_irqs(struct net2280 *dev, u32 stat)
cpu_to_le32s(&u.raw[0]);
cpu_to_le32s(&u.raw[1]);
- if (dev->quirks & PLX_SUPERSPEED)
+ if ((dev->quirks & PLX_SUPERSPEED) && !dev->bug7734_patched)
defect7374_workaround(dev, u.r);
tmp = 0;
@@ -3423,17 +3181,12 @@ static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
continue;
}
- /* chaining should stop on abort, short OUT from fifo,
- * or (stat0 codepath) short OUT transfer.
- */
- if (!use_dma_chaining) {
- if (!(tmp & BIT(DMA_TRANSACTION_DONE_INTERRUPT))) {
- ep_dbg(ep->dev, "%s no xact done? %08x\n",
- ep->ep.name, tmp);
- continue;
- }
- stop_dma(ep->dma);
+ if (!(tmp & BIT(DMA_TRANSACTION_DONE_INTERRUPT))) {
+ ep_dbg(ep->dev, "%s no xact done? %08x\n",
+ ep->ep.name, tmp);
+ continue;
}
+ stop_dma(ep->dma);
/* OUT transfers terminate when the data from the
* host is in our memory. Process whatever's done.
@@ -3448,30 +3201,9 @@ static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
scan_dma_completions(ep);
/* disable dma on inactive queues; else maybe restart */
- if (list_empty(&ep->queue)) {
- if (use_dma_chaining)
- stop_dma(ep->dma);
- } else {
+ if (!list_empty(&ep->queue)) {
tmp = readl(&dma->dmactl);
- if (!use_dma_chaining || (tmp & BIT(DMA_ENABLE)) == 0)
- restart_dma(ep);
- else if (ep->is_in && use_dma_chaining) {
- struct net2280_request *req;
- __le32 dmacount;
-
- /* the descriptor at the head of the chain
- * may still have VALID_BIT clear; that's
- * used to trigger changing DMA_FIFO_VALIDATE
- * (affects automagic zlp writes).
- */
- req = list_entry(ep->queue.next,
- struct net2280_request, queue);
- dmacount = req->td->dmacount;
- dmacount &= cpu_to_le32(BIT(VALID_BIT) |
- DMA_BYTE_COUNT_MASK);
- if (dmacount && (dmacount & valid_bit) == 0)
- restart_dma(ep);
- }
+ restart_dma(ep);
}
ep->irqs++;
}
@@ -3556,7 +3288,7 @@ static void net2280_remove(struct pci_dev *pdev)
}
if (dev->got_irq)
free_irq(pdev->irq, dev);
- if (use_msi && dev->quirks & PLX_SUPERSPEED)
+ if (dev->quirks & PLX_SUPERSPEED)
pci_disable_msi(pdev);
if (dev->regs)
iounmap(dev->regs);
@@ -3581,9 +3313,6 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
void __iomem *base = NULL;
int retval, i;
- if (!use_dma)
- use_dma_chaining = 0;
-
/* alloc, and start init */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
@@ -3663,9 +3392,12 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
(0xf << DEFECT7374_FSM_FIELD);
/* See if firmware needs to set up for workaround: */
- if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ)
+ if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ) {
+ dev->bug7734_patched = 1;
writel(0, &dev->usb->usbctl);
- } else{
+ } else
+ dev->bug7734_patched = 0;
+ } else {
dev->enhanced_mode = 0;
dev->n_ep = 7;
/* put into initial config, link up all endpoints */
@@ -3682,7 +3414,7 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto done;
}
- if (use_msi && (dev->quirks & PLX_SUPERSPEED))
+ if (dev->quirks & PLX_SUPERSPEED)
if (pci_enable_msi(pdev))
ep_err(dev, "Failed to enable MSI mode\n");
@@ -3741,9 +3473,7 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
ep_info(dev, "%s\n", driver_desc);
ep_info(dev, "irq %d, pci mem %p, chip rev %04x\n",
pdev->irq, base, dev->chiprev);
- ep_info(dev, "version: " DRIVER_VERSION "; dma %s %s\n",
- use_dma ? (use_dma_chaining ? "chaining" : "enabled")
- : "disabled",
+ ep_info(dev, "version: " DRIVER_VERSION "; %s\n",
dev->enhanced_mode ? "enhanced mode" : "legacy mode");
retval = device_create_file(&pdev->dev, &dev_attr_registers);
if (retval)
@@ -3776,9 +3506,6 @@ static void net2280_shutdown(struct pci_dev *pdev)
/* disable the pullup so the host will think we're gone */
writel(0, &dev->usb->usbctl);
- /* Disable full-speed test mode */
- if (dev->quirks & PLX_LEGACY)
- writel(0, &dev->usb->xcvrdiag);
}
diff --git a/drivers/usb/gadget/udc/net2280.h b/drivers/usb/gadget/udc/net2280.h
index 03f15242d794..ac8d5a20a378 100644
--- a/drivers/usb/gadget/udc/net2280.h
+++ b/drivers/usb/gadget/udc/net2280.h
@@ -100,7 +100,6 @@ struct net2280_ep {
dma_addr_t td_dma; /* of dummy */
struct net2280 *dev;
unsigned long irqs;
- unsigned is_halt:1, dma_started:1;
/* analogous to a host-side qh */
struct list_head queue;
@@ -126,7 +125,7 @@ static inline void allow_status(struct net2280_ep *ep)
ep->stopped = 1;
}
-static void allow_status_338x(struct net2280_ep *ep)
+static inline void allow_status_338x(struct net2280_ep *ep)
{
/*
* Control Status Phase Handshake was set by the chip when the setup
@@ -165,8 +164,8 @@ struct net2280 {
u2_enable:1,
ltm_enable:1,
wakeup_enable:1,
- selfpowered:1,
- addressed_state:1;
+ addressed_state:1,
+ bug7734_patched:1;
u16 chiprev;
int enhanced_mode;
int n_ep;
@@ -356,23 +355,6 @@ static inline void start_out_naking(struct net2280_ep *ep)
readl(&ep->regs->ep_rsp);
}
-#ifdef DEBUG
-static inline void assert_out_naking(struct net2280_ep *ep, const char *where)
-{
- u32 tmp = readl(&ep->regs->ep_stat);
-
- if ((tmp & BIT(NAK_OUT_PACKETS)) == 0) {
- ep_dbg(ep->dev, "%s %s %08x !NAK\n",
- ep->ep.name, where, tmp);
- writel(BIT(SET_NAK_OUT_PACKETS),
- &ep->regs->ep_rsp);
- }
-}
-#define ASSERT_OUT_NAKING(ep) assert_out_naking(ep, __func__)
-#else
-#define ASSERT_OUT_NAKING(ep) do {} while (0)
-#endif
-
static inline void stop_out_naking(struct net2280_ep *ep)
{
u32 tmp;
diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c
index 288c087220a8..e2fcdb8e5596 100644
--- a/drivers/usb/gadget/udc/omap_udc.c
+++ b/drivers/usb/gadget/udc/omap_udc.c
@@ -1171,6 +1171,7 @@ omap_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered)
unsigned long flags;
u16 syscon1;
+ gadget->is_selfpowered = (is_selfpowered != 0);
udc = container_of(gadget, struct omap_udc, gadget);
spin_lock_irqsave(&udc->lock, flags);
syscon1 = omap_readw(UDC_SYSCON1);
diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c
index 1c7379ac2379..613547f07828 100644
--- a/drivers/usb/gadget/udc/pch_udc.c
+++ b/drivers/usb/gadget/udc/pch_udc.c
@@ -1161,6 +1161,7 @@ static int pch_udc_pcd_selfpowered(struct usb_gadget *gadget, int value)
if (!gadget)
return -EINVAL;
+ gadget->is_selfpowered = (value != 0);
dev = container_of(gadget, struct pch_udc_dev, gadget);
if (value)
pch_udc_set_selfpowered(dev);
diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c
index 8550b2d5db32..f6cbe667ce39 100644
--- a/drivers/usb/gadget/udc/pxa25x_udc.c
+++ b/drivers/usb/gadget/udc/pxa25x_udc.c
@@ -1272,7 +1272,6 @@ static int pxa25x_udc_start(struct usb_gadget *g,
goto bind_fail;
}
- pullup(dev);
dump_state(dev);
return 0;
bind_fail:
@@ -1339,7 +1338,6 @@ static int pxa25x_udc_stop(struct usb_gadget*g)
local_irq_disable();
dev->pullup = 0;
- pullup(dev);
stop_activity(dev, NULL);
local_irq_enable();
diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c
index c61a896061fa..6a855fc9bd84 100644
--- a/drivers/usb/gadget/udc/pxa27x_udc.c
+++ b/drivers/usb/gadget/udc/pxa27x_udc.c
@@ -1809,7 +1809,6 @@ static int pxa27x_udc_start(struct usb_gadget *g,
/* first hook up the driver ... */
udc->driver = driver;
- dplus_pullup(udc, 1);
if (!IS_ERR_OR_NULL(udc->transceiver)) {
retval = otg_set_peripheral(udc->transceiver->otg,
@@ -1862,7 +1861,6 @@ static int pxa27x_udc_stop(struct usb_gadget *g)
stop_activity(udc, NULL);
udc_disable(udc);
- dplus_pullup(udc, 0);
udc->driver = NULL;
diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c
index 06870da0b988..2495fe9c95c5 100644
--- a/drivers/usb/gadget/udc/r8a66597-udc.c
+++ b/drivers/usb/gadget/udc/r8a66597-udc.c
@@ -1803,6 +1803,7 @@ static int r8a66597_set_selfpowered(struct usb_gadget *gadget, int is_self)
{
struct r8a66597 *r8a66597 = gadget_to_r8a66597(gadget);
+ gadget->is_selfpowered = (is_self != 0);
if (is_self)
r8a66597->device_status |= 1 << USB_DEVICE_SELF_POWERED;
else
diff --git a/drivers/usb/gadget/udc/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c
index 824cf12e9add..b808951491cc 100644
--- a/drivers/usb/gadget/udc/s3c2410_udc.c
+++ b/drivers/usb/gadget/udc/s3c2410_udc.c
@@ -238,14 +238,6 @@ static inline void s3c2410_udc_set_ep0_de_out(void __iomem *base)
S3C2410_UDC_EP0_CSR_REG);
}
-static inline void s3c2410_udc_set_ep0_sse_out(void __iomem *base)
-{
- udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
- udc_writeb(base, (S3C2410_UDC_EP0_CSR_SOPKTRDY
- | S3C2410_UDC_EP0_CSR_SSE),
- S3C2410_UDC_EP0_CSR_REG);
-}
-
static inline void s3c2410_udc_set_ep0_de_in(void __iomem *base)
{
udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
@@ -291,18 +283,6 @@ static void s3c2410_udc_nuke(struct s3c2410_udc *udc,
}
}
-static inline void s3c2410_udc_clear_ep_state(struct s3c2410_udc *dev)
-{
- unsigned i;
-
- /* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint
- * fifos, and pending transactions mustn't be continued in any case.
- */
-
- for (i = 1; i < S3C2410_ENDPOINTS; i++)
- s3c2410_udc_nuke(dev, &dev->ep[i], -ECONNABORTED);
-}
-
static inline int s3c2410_udc_fifo_count_out(void)
{
int tmp;
@@ -1454,6 +1434,7 @@ static int s3c2410_udc_set_selfpowered(struct usb_gadget *gadget, int value)
dprintk(DEBUG_NORMAL, "%s()\n", __func__);
+ gadget->is_selfpowered = (value != 0);
if (value)
udc->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
else
diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c
index e31d574d8860..5a81cb086b99 100644
--- a/drivers/usb/gadget/udc/udc-core.c
+++ b/drivers/usb/gadget/udc/udc-core.c
@@ -564,6 +564,7 @@ static USB_UDC_ATTR(is_a_peripheral);
static USB_UDC_ATTR(b_hnp_enable);
static USB_UDC_ATTR(a_hnp_support);
static USB_UDC_ATTR(a_alt_hnp_support);
+static USB_UDC_ATTR(is_selfpowered);
static struct attribute *usb_udc_attrs[] = {
&dev_attr_srp.attr,
@@ -577,6 +578,7 @@ static struct attribute *usb_udc_attrs[] = {
&dev_attr_b_hnp_enable.attr,
&dev_attr_a_hnp_support.attr,
&dev_attr_a_alt_hnp_support.attr,
+ &dev_attr_is_selfpowered.attr,
NULL,
};
diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c
index 1eac56fc384d..dd3e9fd31b80 100644
--- a/drivers/usb/gadget/udc/udc-xilinx.c
+++ b/drivers/usb/gadget/udc/udc-xilinx.c
@@ -2131,8 +2131,8 @@ static int xudc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, udc);
- dev_vdbg(&pdev->dev, "%s at 0x%08X mapped to 0x%08X %s\n",
- driver_name, (u32)res->start, (u32 __force)udc->addr,
+ dev_vdbg(&pdev->dev, "%s at 0x%08X mapped to %p %s\n",
+ driver_name, (u32)res->start, udc->addr,
udc->dma_enabled ? "with DMA" : "without DMA");
return 0;