summaryrefslogtreecommitdiff
path: root/drivers/usb/gadget
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/gadget')
-rw-r--r--drivers/usb/gadget/Kconfig9
-rw-r--r--drivers/usb/gadget/Makefile2
-rw-r--r--drivers/usb/gadget/composite.c4
-rw-r--r--drivers/usb/gadget/configfs.c1
-rw-r--r--drivers/usb/gadget/function/Makefile10
-rw-r--r--drivers/usb/gadget/function/f_acm.c53
-rw-r--r--drivers/usb/gadget/function/f_eem.c1
-rw-r--r--drivers/usb/gadget/function/f_fs.c184
-rw-r--r--drivers/usb/gadget/function/f_hid.c5
-rw-r--r--drivers/usb/gadget/function/f_loopback.c90
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c27
-rw-r--r--drivers/usb/gadget/function/f_ncm.c9
-rw-r--r--drivers/usb/gadget/function/f_obex.c37
-rw-r--r--drivers/usb/gadget/function/f_phonet.c2
-rw-r--r--drivers/usb/gadget/function/f_rndis.c9
-rw-r--r--drivers/usb/gadget/function/f_serial.c19
-rw-r--r--drivers/usb/gadget/function/f_sourcesink.c511
-rw-r--r--drivers/usb/gadget/function/f_subset.c1
-rw-r--r--drivers/usb/gadget/function/f_uac1.c337
-rw-r--r--drivers/usb/gadget/function/f_uac2.c543
-rw-r--r--drivers/usb/gadget/function/f_uvc.c328
-rw-r--r--drivers/usb/gadget/function/f_uvc.h13
-rw-r--r--drivers/usb/gadget/function/g_zero.h13
-rw-r--r--drivers/usb/gadget/function/u_ether.c3
-rw-r--r--drivers/usb/gadget/function/u_fs.h4
-rw-r--r--drivers/usb/gadget/function/u_serial.c30
-rw-r--r--drivers/usb/gadget/function/u_uac1.c38
-rw-r--r--drivers/usb/gadget/function/u_uac1.h31
-rw-r--r--drivers/usb/gadget/function/u_uac2.h42
-rw-r--r--drivers/usb/gadget/function/u_uvc.h39
-rw-r--r--drivers/usb/gadget/function/uvc.h4
-rw-r--r--drivers/usb/gadget/function/uvc_queue.c46
-rw-r--r--drivers/usb/gadget/function/uvc_queue.h33
-rw-r--r--drivers/usb/gadget/function/uvc_v4l2.c329
-rw-r--r--drivers/usb/gadget/function/uvc_v4l2.h22
-rw-r--r--drivers/usb/gadget/function/uvc_video.c50
-rw-r--r--drivers/usb/gadget/function/uvc_video.h24
-rw-r--r--drivers/usb/gadget/legacy/Kconfig3
-rw-r--r--drivers/usb/gadget/legacy/Makefile6
-rw-r--r--drivers/usb/gadget/legacy/audio.c149
-rw-r--r--drivers/usb/gadget/legacy/dbgp.c3
-rw-r--r--drivers/usb/gadget/legacy/inode.c29
-rw-r--r--drivers/usb/gadget/legacy/webcam.c75
-rw-r--r--drivers/usb/gadget/legacy/zero.c21
-rw-r--r--drivers/usb/gadget/udc/Kconfig21
-rw-r--r--drivers/usb/gadget/udc/Makefile1
-rw-r--r--drivers/usb/gadget/udc/amd5536udc.c2
-rw-r--r--drivers/usb/gadget/udc/at91_udc.c2
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.c6
-rw-r--r--drivers/usb/gadget/udc/bcm63xx_udc.c2
-rw-r--r--drivers/usb/gadget/udc/dummy_hcd.c10
-rw-r--r--drivers/usb/gadget/udc/fotg210-udc.c2
-rw-r--r--drivers/usb/gadget/udc/fsl_qe_udc.c6
-rw-r--r--drivers/usb/gadget/udc/fsl_udc_core.c6
-rw-r--r--drivers/usb/gadget/udc/fusb300_udc.c10
-rw-r--r--drivers/usb/gadget/udc/fusb300_udc.h2
-rw-r--r--drivers/usb/gadget/udc/goku_udc.c2
-rw-r--r--drivers/usb/gadget/udc/gr_udc.c83
-rw-r--r--drivers/usb/gadget/udc/gr_udc.h7
-rw-r--r--drivers/usb/gadget/udc/lpc32xx_udc.c2
-rw-r--r--drivers/usb/gadget/udc/m66592-udc.c2
-rw-r--r--drivers/usb/gadget/udc/mv_u3d_core.c8
-rw-r--r--drivers/usb/gadget/udc/mv_udc_core.c8
-rw-r--r--drivers/usb/gadget/udc/net2272.c2
-rw-r--r--drivers/usb/gadget/udc/net2280.c4
-rw-r--r--drivers/usb/gadget/udc/omap_udc.c2
-rw-r--r--drivers/usb/gadget/udc/pch_udc.c24
-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.c16
-rw-r--r--drivers/usb/gadget/udc/s3c-hsudc.c3
-rw-r--r--drivers/usb/gadget/udc/s3c2410_udc.c2
-rw-r--r--drivers/usb/gadget/udc/udc-core.c56
-rw-r--r--drivers/usb/gadget/udc/udc-xilinx.c2180
74 files changed, 4702 insertions, 962 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 5c822afb6d70..c4880fc0d86e 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -181,6 +181,15 @@ config USB_F_MASS_STORAGE
config USB_F_FS
tristate
+config USB_F_UAC1
+ tristate
+
+config USB_F_UAC2
+ tristate
+
+config USB_F_UVC
+ tristate
+
choice
tristate "USB Gadget Drivers"
default USB_ETH
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index a186afeaa700..598a67d6ba05 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -3,7 +3,7 @@
#
subdir-ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG
subdir-ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG
-ccflags-y += -I$(PWD)/drivers/usb/gadget/udc
+ccflags-y += -I$(srctree)/drivers/usb/gadget/udc
obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o
libcomposite-y := usbstring.o config.o epautoconf.o
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 6935a822ce2b..f6a51fddd5b5 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -560,7 +560,7 @@ static int bos_desc(struct usb_composite_dev *cdev)
usb_ext->bLength = USB_DT_USB_EXT_CAP_SIZE;
usb_ext->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
usb_ext->bDevCapabilityType = USB_CAP_TYPE_EXT;
- usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT);
+ usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT | USB_BESL_SUPPORT);
/*
* The Superspeed USB Capability descriptor shall be implemented by all
@@ -1956,7 +1956,6 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev)
}
if (cdev->req) {
kfree(cdev->req->buf);
- usb_ep_dequeue(cdev->gadget->ep0, cdev->req);
usb_ep_free_request(cdev->gadget->ep0, cdev->req);
}
cdev->next_string_id = 0;
@@ -2073,6 +2072,7 @@ static const struct usb_gadget_driver composite_driver_template = {
.unbind = composite_unbind,
.setup = composite_setup,
+ .reset = composite_disconnect,
.disconnect = composite_disconnect,
.suspend = composite_suspend,
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index 811c2c7cc269..34034333f7f6 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -1450,6 +1450,7 @@ static const struct usb_gadget_driver configfs_driver_template = {
.unbind = configfs_composite_unbind,
.setup = composite_setup,
+ .reset = composite_disconnect,
.disconnect = composite_disconnect,
.max_speed = USB_SPEED_SUPER,
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
index 6d91f21b52a6..90701aa5a826 100644
--- a/drivers/usb/gadget/function/Makefile
+++ b/drivers/usb/gadget/function/Makefile
@@ -2,8 +2,8 @@
# USB peripheral controller drivers
#
-ccflags-y := -I$(PWD)/drivers/usb/gadget/
-ccflags-y += -I$(PWD)/drivers/usb/gadget/udc/
+ccflags-y := -I$(srctree)/drivers/usb/gadget/
+ccflags-y += -I$(srctree)/drivers/usb/gadget/udc/
# USB Functions
usb_f_acm-y := f_acm.o
@@ -32,3 +32,9 @@ usb_f_mass_storage-y := f_mass_storage.o storage_common.o
obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o
usb_f_fs-y := f_fs.o
obj-$(CONFIG_USB_F_FS) += usb_f_fs.o
+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
+obj-$(CONFIG_USB_F_UVC) += usb_f_uvc.o
diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c
index ab1065afbbd0..aad8165e98ef 100644
--- a/drivers/usb/gadget/function/f_acm.c
+++ b/drivers/usb/gadget/function/f_acm.c
@@ -313,15 +313,15 @@ static void acm_complete_set_line_coding(struct usb_ep *ep,
struct usb_composite_dev *cdev = acm->port.func.config->cdev;
if (req->status != 0) {
- DBG(cdev, "acm ttyGS%d completion, err %d\n",
- acm->port_num, req->status);
+ dev_dbg(&cdev->gadget->dev, "acm ttyGS%d completion, err %d\n",
+ acm->port_num, req->status);
return;
}
/* normal completion */
if (req->actual != sizeof(acm->port_line_coding)) {
- DBG(cdev, "acm ttyGS%d short resp, len %d\n",
- acm->port_num, req->actual);
+ dev_dbg(&cdev->gadget->dev, "acm ttyGS%d short resp, len %d\n",
+ acm->port_num, req->actual);
usb_ep_set_halt(ep);
} else {
struct usb_cdc_line_coding *value = req->buf;
@@ -397,14 +397,16 @@ static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
default:
invalid:
- VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
- ctrl->bRequestType, ctrl->bRequest,
- w_value, w_index, w_length);
+ dev_vdbg(&cdev->gadget->dev,
+ "invalid control req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
}
/* respond with data transfer or status phase? */
if (value >= 0) {
- DBG(cdev, "acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n",
+ dev_dbg(&cdev->gadget->dev,
+ "acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n",
acm->port_num, ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
req->zero = 0;
@@ -428,23 +430,27 @@ static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (intf == acm->ctrl_id) {
if (acm->notify->driver_data) {
- VDBG(cdev, "reset acm control interface %d\n", intf);
+ dev_vdbg(&cdev->gadget->dev,
+ "reset acm control interface %d\n", intf);
usb_ep_disable(acm->notify);
- } else {
- VDBG(cdev, "init acm ctrl interface %d\n", intf);
+ }
+
+ if (!acm->notify->desc)
if (config_ep_by_speed(cdev->gadget, f, acm->notify))
return -EINVAL;
- }
+
usb_ep_enable(acm->notify);
acm->notify->driver_data = acm;
} else if (intf == acm->data_id) {
if (acm->port.in->driver_data) {
- DBG(cdev, "reset acm ttyGS%d\n", acm->port_num);
+ dev_dbg(&cdev->gadget->dev,
+ "reset acm ttyGS%d\n", acm->port_num);
gserial_disconnect(&acm->port);
}
if (!acm->port.in->desc || !acm->port.out->desc) {
- DBG(cdev, "activate acm ttyGS%d\n", acm->port_num);
+ dev_dbg(&cdev->gadget->dev,
+ "activate acm ttyGS%d\n", acm->port_num);
if (config_ep_by_speed(cdev->gadget, f,
acm->port.in) ||
config_ep_by_speed(cdev->gadget, f,
@@ -467,7 +473,7 @@ static void acm_disable(struct usb_function *f)
struct f_acm *acm = func_to_acm(f);
struct usb_composite_dev *cdev = f->config->cdev;
- DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num);
+ dev_dbg(&cdev->gadget->dev, "acm ttyGS%d deactivated\n", acm->port_num);
gserial_disconnect(&acm->port);
usb_ep_disable(acm->notify);
acm->notify->driver_data = NULL;
@@ -537,8 +543,8 @@ static int acm_notify_serial_state(struct f_acm *acm)
spin_lock(&acm->lock);
if (acm->notify_req) {
- DBG(cdev, "acm ttyGS%d serial state %04x\n",
- acm->port_num, acm->serial_state);
+ dev_dbg(&cdev->gadget->dev, "acm ttyGS%d serial state %04x\n",
+ acm->port_num, acm->serial_state);
status = acm_cdc_notify(acm, USB_CDC_NOTIFY_SERIAL_STATE,
0, &acm->serial_state, sizeof(acm->serial_state));
} else {
@@ -691,12 +697,13 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
if (status)
goto fail;
- DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n",
- acm->port_num,
- gadget_is_superspeed(c->cdev->gadget) ? "super" :
- gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
- acm->port.in->name, acm->port.out->name,
- acm->notify->name);
+ dev_dbg(&cdev->gadget->dev,
+ "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n",
+ acm->port_num,
+ gadget_is_superspeed(c->cdev->gadget) ? "super" :
+ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+ acm->port.in->name, acm->port.out->name,
+ acm->notify->name);
return 0;
fail:
diff --git a/drivers/usb/gadget/function/f_eem.c b/drivers/usb/gadget/function/f_eem.c
index 4d8b236ea608..c9e90de5bdd9 100644
--- a/drivers/usb/gadget/function/f_eem.c
+++ b/drivers/usb/gadget/function/f_eem.c
@@ -325,7 +325,6 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f)
return 0;
fail:
- usb_free_all_descriptors(f);
if (eem->port.out_ep)
eem->port.out_ep->driver_data = NULL;
if (eem->port.in_ep)
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index dc30adf15a01..63314ede7ba6 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -155,13 +155,18 @@ struct ffs_io_data {
struct usb_request *req;
};
+struct ffs_desc_helper {
+ struct ffs_data *ffs;
+ unsigned interfaces_count;
+ unsigned eps_count;
+};
+
static int __must_check ffs_epfiles_create(struct ffs_data *ffs);
static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count);
-static struct inode *__must_check
+static struct dentry *
ffs_sb_create_file(struct super_block *sb, const char *name, void *data,
- const struct file_operations *fops,
- struct dentry **dentry_p);
+ const struct file_operations *fops);
/* Devices management *******************************************************/
@@ -642,15 +647,26 @@ static void ffs_user_copy_worker(struct work_struct *work)
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],
- io_data->iovec[i].iov_len))) {
+ &io_data->buf[pos], len))) {
ret = -EFAULT;
break;
}
- pos += io_data->iovec[i].iov_len;
+ pos += len;
}
unuse_mm(io_data->mm);
}
@@ -682,7 +698,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
struct ffs_epfile *epfile = file->private_data;
struct ffs_ep *ep;
char *data = NULL;
- ssize_t ret, data_len;
+ ssize_t ret, data_len = -EINVAL;
int halt;
/* Are we still active? */
@@ -782,13 +798,30 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
/* Fire the request */
struct usb_request *req;
+ /*
+ * Sanity Check: even though data_len can't be used
+ * uninitialized at the time I write this comment, some
+ * compilers complain about this situation.
+ * In order to keep the code clean from warnings, data_len is
+ * being initialized to -EINVAL during its declaration, which
+ * means we can't rely on compiler anymore to warn no future
+ * changes won't result in data_len being used uninitialized.
+ * For such reason, we're adding this redundant sanity check
+ * here.
+ */
+ if (unlikely(data_len == -EINVAL)) {
+ WARN(1, "%s: data_len == -EINVAL\n", __func__);
+ ret = -EINVAL;
+ goto error_lock;
+ }
+
if (io_data->aio) {
req = usb_ep_alloc_request(ep->ep, GFP_KERNEL);
if (unlikely(!req))
goto error_lock;
req->buf = data;
- req->length = io_data->len;
+ req->length = data_len;
io_data->buf = data;
io_data->ep = ep->ep;
@@ -810,7 +843,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
req = ep->req;
req->buf = data;
- req->length = io_data->len;
+ req->length = data_len;
req->context = &done;
req->complete = ffs_epfile_io_complete;
@@ -1026,6 +1059,29 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
case FUNCTIONFS_ENDPOINT_REVMAP:
ret = epfile->ep->num;
break;
+ case FUNCTIONFS_ENDPOINT_DESC:
+ {
+ int desc_idx;
+ struct usb_endpoint_descriptor *desc;
+
+ switch (epfile->ffs->gadget->speed) {
+ case USB_SPEED_SUPER:
+ desc_idx = 2;
+ break;
+ case USB_SPEED_HIGH:
+ desc_idx = 1;
+ break;
+ default:
+ desc_idx = 0;
+ }
+ desc = epfile->ep->descs[desc_idx];
+
+ spin_unlock_irq(&epfile->ffs->eps_lock);
+ ret = copy_to_user((void *)value, desc, sizeof(*desc));
+ if (ret)
+ ret = -EFAULT;
+ return ret;
+ }
default:
ret = -ENOTTY;
}
@@ -1090,10 +1146,9 @@ ffs_sb_make_inode(struct super_block *sb, void *data,
}
/* Create "regular" file */
-static struct inode *ffs_sb_create_file(struct super_block *sb,
+static struct dentry *ffs_sb_create_file(struct super_block *sb,
const char *name, void *data,
- const struct file_operations *fops,
- struct dentry **dentry_p)
+ const struct file_operations *fops)
{
struct ffs_data *ffs = sb->s_fs_info;
struct dentry *dentry;
@@ -1112,10 +1167,7 @@ static struct inode *ffs_sb_create_file(struct super_block *sb,
}
d_add(dentry, inode);
- if (dentry_p)
- *dentry_p = dentry;
-
- return inode;
+ return dentry;
}
/* Super block */
@@ -1160,7 +1212,7 @@ static int ffs_sb_fill(struct super_block *sb, void *_data, int silent)
/* EP0 file */
if (unlikely(!ffs_sb_create_file(sb, "ep0", ffs,
- &ffs_ep0_operations, NULL)))
+ &ffs_ep0_operations)))
return -ENOMEM;
return 0;
@@ -1528,10 +1580,14 @@ static int ffs_epfiles_create(struct ffs_data *ffs)
epfile->ffs = ffs;
mutex_init(&epfile->mutex);
init_waitqueue_head(&epfile->wait);
- sprintf(epfiles->name, "ep%u", i);
- if (!unlikely(ffs_sb_create_file(ffs->sb, epfiles->name, epfile,
- &ffs_epfile_operations,
- &epfile->dentry))) {
+ if (ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR)
+ sprintf(epfiles->name, "ep%02x", ffs->eps_addrmap[i]);
+ else
+ sprintf(epfiles->name, "ep%u", i);
+ epfile->dentry = ffs_sb_create_file(ffs->sb, epfiles->name,
+ epfile,
+ &ffs_epfile_operations);
+ if (unlikely(!epfile->dentry)) {
ffs_epfiles_destroy(epfiles, i - 1);
return -ENOMEM;
}
@@ -1830,7 +1886,8 @@ static int __ffs_data_do_entity(enum ffs_entity_type type,
u8 *valuep, struct usb_descriptor_header *desc,
void *priv)
{
- struct ffs_data *ffs = priv;
+ struct ffs_desc_helper *helper = priv;
+ struct usb_endpoint_descriptor *d;
ENTER();
@@ -1844,8 +1901,8 @@ static int __ffs_data_do_entity(enum ffs_entity_type type,
* encountered interface "n" then there are at least
* "n+1" interfaces.
*/
- if (*valuep >= ffs->interfaces_count)
- ffs->interfaces_count = *valuep + 1;
+ if (*valuep >= helper->interfaces_count)
+ helper->interfaces_count = *valuep + 1;
break;
case FFS_STRING:
@@ -1853,14 +1910,22 @@ static int __ffs_data_do_entity(enum ffs_entity_type type,
* Strings are indexed from 1 (0 is magic ;) reserved
* for languages list or some such)
*/
- if (*valuep > ffs->strings_count)
- ffs->strings_count = *valuep;
+ if (*valuep > helper->ffs->strings_count)
+ helper->ffs->strings_count = *valuep;
break;
case FFS_ENDPOINT:
- /* Endpoints are indexed from 1 as well. */
- if ((*valuep & USB_ENDPOINT_NUMBER_MASK) > ffs->eps_count)
- ffs->eps_count = (*valuep & USB_ENDPOINT_NUMBER_MASK);
+ d = (void *)desc;
+ helper->eps_count++;
+ if (helper->eps_count >= 15)
+ return -EINVAL;
+ /* Check if descriptors for any speed were already parsed */
+ if (!helper->ffs->eps_count && !helper->ffs->interfaces_count)
+ helper->ffs->eps_addrmap[helper->eps_count] =
+ d->bEndpointAddress;
+ else if (helper->ffs->eps_addrmap[helper->eps_count] !=
+ d->bEndpointAddress)
+ return -EINVAL;
break;
}
@@ -2053,6 +2118,7 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
char *data = _data, *raw_descs;
unsigned os_descs_count = 0, counts[3], flags;
int ret = -EINVAL, i;
+ struct ffs_desc_helper helper;
ENTER();
@@ -2067,10 +2133,12 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
break;
case FUNCTIONFS_DESCRIPTORS_MAGIC_V2:
flags = get_unaligned_le32(data + 8);
+ ffs->user_flags = flags;
if (flags & ~(FUNCTIONFS_HAS_FS_DESC |
FUNCTIONFS_HAS_HS_DESC |
FUNCTIONFS_HAS_SS_DESC |
- FUNCTIONFS_HAS_MS_OS_DESC)) {
+ FUNCTIONFS_HAS_MS_OS_DESC |
+ FUNCTIONFS_VIRTUAL_ADDR)) {
ret = -ENOSYS;
goto error;
}
@@ -2101,13 +2169,29 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
/* Read descriptors */
raw_descs = data;
+ helper.ffs = ffs;
for (i = 0; i < 3; ++i) {
if (!counts[i])
continue;
+ helper.interfaces_count = 0;
+ helper.eps_count = 0;
ret = ffs_do_descs(counts[i], data, len,
- __ffs_data_do_entity, ffs);
+ __ffs_data_do_entity, &helper);
if (ret < 0)
goto error;
+ if (!ffs->eps_count && !ffs->interfaces_count) {
+ ffs->eps_count = helper.eps_count;
+ ffs->interfaces_count = helper.interfaces_count;
+ } else {
+ if (ffs->eps_count != helper.eps_count) {
+ ret = -EINVAL;
+ goto error;
+ }
+ if (ffs->interfaces_count != helper.interfaces_count) {
+ ret = -EINVAL;
+ goto error;
+ }
+ }
data += ret;
len -= ret;
}
@@ -2314,7 +2398,8 @@ static void __ffs_event_add(struct ffs_data *ffs,
break;
default:
- BUG();
+ WARN(1, "%d: unknown event, this should not happen\n", type);
+ return;
}
{
@@ -2342,9 +2427,18 @@ static void ffs_event_add(struct ffs_data *ffs,
spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags);
}
-
/* Bind/unbind USB function hooks *******************************************/
+static int ffs_ep_addr2idx(struct ffs_data *ffs, u8 endpoint_address)
+{
+ int i;
+
+ for (i = 1; i < ARRAY_SIZE(ffs->eps_addrmap); ++i)
+ if (ffs->eps_addrmap[i] == endpoint_address)
+ return i;
+ return -ENOENT;
+}
+
static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
struct usb_descriptor_header *desc,
void *priv)
@@ -2352,7 +2446,8 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
struct usb_endpoint_descriptor *ds = (void *)desc;
struct ffs_function *func = priv;
struct ffs_ep *ffs_ep;
- unsigned ep_desc_id, idx;
+ unsigned ep_desc_id;
+ int idx;
static const char *speed_names[] = { "full", "high", "super" };
if (type != FFS_DESCRIPTOR)
@@ -2378,7 +2473,10 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
if (!desc || desc->bDescriptorType != USB_DT_ENDPOINT)
return 0;
- idx = (ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) - 1;
+ idx = ffs_ep_addr2idx(func->ffs, ds->bEndpointAddress) - 1;
+ if (idx < 0)
+ return idx;
+
ffs_ep = func->eps + idx;
if (unlikely(ffs_ep->descs[ep_desc_id])) {
@@ -2397,7 +2495,13 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
} else {
struct usb_request *req;
struct usb_ep *ep;
+ u8 bEndpointAddress;
+ /*
+ * We back up bEndpointAddress because autoconfig overwrites
+ * it with physical endpoint address.
+ */
+ bEndpointAddress = ds->bEndpointAddress;
pr_vdebug("autoconfig\n");
ep = usb_ep_autoconfig(func->gadget, ds);
if (unlikely(!ep))
@@ -2412,6 +2516,12 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
ffs_ep->req = req;
func->eps_revmap[ds->bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK] = idx + 1;
+ /*
+ * If we use virtual address mapping, we restore
+ * original bEndpointAddress value.
+ */
+ if (func->ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR)
+ ds->bEndpointAddress = bEndpointAddress;
}
ffs_dump_mem(": Rewritten ep desc", ds, ds->bLength);
@@ -2581,8 +2691,6 @@ static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f,
func->conf = c;
func->gadget = c->cdev->gadget;
- ffs_data_get(func->ffs);
-
/*
* in drivers/usb/gadget/configfs.c:configfs_composite_bind()
* configurations are bound in sequence with list_for_each_entry,
@@ -2856,6 +2964,8 @@ static int ffs_func_setup(struct usb_function *f,
ret = ffs_func_revmap_ep(func, le16_to_cpu(creq->wIndex));
if (unlikely(ret < 0))
return ret;
+ if (func->ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR)
+ ret = func->ffs->eps_addrmap[ret];
break;
default:
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index a95290a1289f..59ab62c92b66 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -621,12 +621,14 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
dev = MKDEV(major, hidg->minor);
status = cdev_add(&hidg->cdev, dev, 1);
if (status)
- goto fail;
+ goto fail_free_descs;
device_create(hidg_class, NULL, dev, NULL, "%s%d", "hidg", hidg->minor);
return 0;
+fail_free_descs:
+ usb_free_all_descriptors(f);
fail:
ERROR(f->config->cdev, "hidg_bind FAILED\n");
if (hidg->req != NULL) {
@@ -635,7 +637,6 @@ fail:
usb_ep_free_request(hidg->in_ep, hidg->req);
}
- usb_free_all_descriptors(f);
return status;
}
diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c
index 4557cd03f0b1..298b46112b1a 100644
--- a/drivers/usb/gadget/function/f_loopback.c
+++ b/drivers/usb/gadget/function/f_loopback.c
@@ -253,22 +253,13 @@ static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
case 0: /* normal completion? */
if (ep == loop->out_ep) {
- /* loop this OUT packet back IN to the host */
req->zero = (req->actual < req->length);
req->length = req->actual;
- status = usb_ep_queue(loop->in_ep, req, GFP_ATOMIC);
- if (status == 0)
- return;
-
- /* "should never get here" */
- ERROR(cdev, "can't loop %s to %s: %d\n",
- ep->name, loop->in_ep->name,
- status);
}
/* queue the buffer for some later OUT packet */
req->length = buflen;
- status = usb_ep_queue(loop->out_ep, req, GFP_ATOMIC);
+ status = usb_ep_queue(ep, req, GFP_ATOMIC);
if (status == 0)
return;
@@ -298,7 +289,8 @@ 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);
+ disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL, NULL,
+ NULL);
VDBG(cdev, "%s disabled\n", loop->function.name);
}
@@ -307,60 +299,66 @@ static inline struct usb_request *lb_alloc_ep_req(struct usb_ep *ep, int len)
return alloc_ep_req(ep, len, buflen);
}
-static int
-enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop)
+static int enable_endpoint(struct usb_composite_dev *cdev, struct f_loopback *loop,
+ struct usb_ep *ep)
{
- int result = 0;
- struct usb_ep *ep;
struct usb_request *req;
unsigned i;
+ int result;
- /* one endpoint writes data back IN to the host */
- ep = loop->in_ep;
+ /*
+ * one endpoint writes data back IN to the host while another endpoint
+ * just reads OUT packets
+ */
result = config_ep_by_speed(cdev->gadget, &(loop->function), ep);
if (result)
- return result;
+ goto fail0;
result = usb_ep_enable(ep);
if (result < 0)
- return result;
- ep->driver_data = loop;
-
- /* one endpoint just reads OUT packets */
- ep = loop->out_ep;
- result = config_ep_by_speed(cdev->gadget, &(loop->function), ep);
- if (result)
goto fail0;
-
- result = usb_ep_enable(ep);
- if (result < 0) {
-fail0:
- ep = loop->in_ep;
- usb_ep_disable(ep);
- ep->driver_data = NULL;
- return result;
- }
ep->driver_data = loop;
- /* allocate a bunch of read buffers and queue them all at once.
+ /*
+ * allocate a bunch of read buffers and queue them all at once.
* we buffer at most 'qlen' transfers; fewer if any need more
* than 'buflen' bytes each.
*/
for (i = 0; i < qlen && result == 0; i++) {
req = lb_alloc_ep_req(ep, 0);
- if (req) {
- req->complete = loopback_complete;
- result = usb_ep_queue(ep, req, GFP_ATOMIC);
- if (result)
- ERROR(cdev, "%s queue req --> %d\n",
- ep->name, result);
- } else {
- usb_ep_disable(ep);
- ep->driver_data = NULL;
- result = -ENOMEM;
- goto fail0;
+ if (!req)
+ goto fail1;
+
+ req->complete = loopback_complete;
+ result = usb_ep_queue(ep, req, GFP_ATOMIC);
+ if (result) {
+ ERROR(cdev, "%s queue req --> %d\n",
+ ep->name, result);
+ goto fail1;
}
}
+ return 0;
+
+fail1:
+ usb_ep_disable(ep);
+
+fail0:
+ return result;
+}
+
+static int
+enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop)
+{
+ int result = 0;
+
+ result = enable_endpoint(cdev, loop, loop->in_ep);
+ if (result)
+ return result;
+
+ result = enable_endpoint(cdev, loop, loop->out_ep);
+ if (result)
+ return result;
+
DBG(cdev, "%s enabled\n", loop->function.name);
return result;
}
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index b96393908860..811929cd4c9e 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -566,22 +566,22 @@ static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep,
*pbusy = 1;
*state = BUF_STATE_BUSY;
spin_unlock_irq(&fsg->common->lock);
+
rc = usb_ep_queue(ep, req, GFP_KERNEL);
- if (rc != 0) {
- *pbusy = 0;
- *state = BUF_STATE_EMPTY;
+ if (rc == 0)
+ return; /* All good, we're done */
- /* We can't do much more than wait for a reset */
+ *pbusy = 0;
+ *state = BUF_STATE_EMPTY;
- /*
- * Note: currently the net2280 driver fails zero-length
- * submissions if DMA is enabled.
- */
- if (rc != -ESHUTDOWN &&
- !(rc == -EOPNOTSUPP && req->length == 0))
- WARNING(fsg, "error in submission: %s --> %d\n",
- ep->name, rc);
- }
+ /* We can't do much more than wait for a reset */
+
+ /*
+ * Note: currently the net2280 driver fails zero-length
+ * submissions if DMA is enabled.
+ */
+ if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP && req->length == 0))
+ WARNING(fsg, "error in submission: %s --> %d\n", ep->name, rc);
}
static bool start_in_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
@@ -3665,4 +3665,3 @@ void fsg_config_from_params(struct fsg_config *cfg,
cfg->fsg_num_buffers = fsg_num_buffers;
}
EXPORT_SYMBOL_GPL(fsg_config_from_params);
-
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index bcdc882cd415..16361b0a8b46 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -1101,7 +1101,15 @@ static void ncm_tx_tasklet(unsigned long data)
/* Only send if data is available. */
if (ncm->skb_tx_data) {
ncm->timer_force_tx = true;
+
+ /* XXX This allowance of a NULL skb argument to ndo_start_xmit
+ * XXX is not sane. The gadget layer should be redesigned so
+ * XXX that the dev->wrap() invocations to build SKBs is transparent
+ * XXX and performed in some way outside of the ndo_start_xmit
+ * XXX interface.
+ */
ncm->netdev->netdev_ops->ndo_start_xmit(NULL, ncm->netdev);
+
ncm->timer_force_tx = false;
}
}
@@ -1453,7 +1461,6 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
return 0;
fail:
- usb_free_all_descriptors(f);
if (ncm->notify_req) {
kfree(ncm->notify_req->buf);
usb_ep_free_request(ncm->notify, ncm->notify_req);
diff --git a/drivers/usb/gadget/function/f_obex.c b/drivers/usb/gadget/function/f_obex.c
index aebae1853bce..a1b79c53499c 100644
--- a/drivers/usb/gadget/function/f_obex.c
+++ b/drivers/usb/gadget/function/f_obex.c
@@ -35,6 +35,7 @@ struct f_obex {
struct gserial port;
u8 ctrl_id;
u8 data_id;
+ u8 cur_alt;
u8 port_num;
u8 can_activate;
};
@@ -200,19 +201,22 @@ static int obex_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (alt != 0)
goto fail;
/* NOP */
- DBG(cdev, "reset obex ttyGS%d control\n", obex->port_num);
+ dev_dbg(&cdev->gadget->dev,
+ "reset obex ttyGS%d control\n", obex->port_num);
} else if (intf == obex->data_id) {
if (alt > 1)
goto fail;
if (obex->port.in->driver_data) {
- DBG(cdev, "reset obex ttyGS%d\n", obex->port_num);
+ dev_dbg(&cdev->gadget->dev,
+ "reset obex ttyGS%d\n", obex->port_num);
gserial_disconnect(&obex->port);
}
if (!obex->port.in->desc || !obex->port.out->desc) {
- DBG(cdev, "init obex ttyGS%d\n", obex->port_num);
+ dev_dbg(&cdev->gadget->dev,
+ "init obex ttyGS%d\n", obex->port_num);
if (config_ep_by_speed(cdev->gadget, f,
obex->port.in) ||
config_ep_by_speed(cdev->gadget, f,
@@ -224,13 +228,16 @@ static int obex_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
}
if (alt == 1) {
- DBG(cdev, "activate obex ttyGS%d\n", obex->port_num);
+ dev_dbg(&cdev->gadget->dev,
+ "activate obex ttyGS%d\n", obex->port_num);
gserial_connect(&obex->port, obex->port_num);
}
} else
goto fail;
+ obex->cur_alt = alt;
+
return 0;
fail:
@@ -241,10 +248,7 @@ static int obex_get_alt(struct usb_function *f, unsigned intf)
{
struct f_obex *obex = func_to_obex(f);
- if (intf == obex->ctrl_id)
- return 0;
-
- return obex->port.in->driver_data ? 1 : 0;
+ return obex->cur_alt;
}
static void obex_disable(struct usb_function *f)
@@ -252,7 +256,7 @@ static void obex_disable(struct usb_function *f)
struct f_obex *obex = func_to_obex(f);
struct usb_composite_dev *cdev = f->config->cdev;
- DBG(cdev, "obex ttyGS%d disable\n", obex->port_num);
+ dev_dbg(&cdev->gadget->dev, "obex ttyGS%d disable\n", obex->port_num);
gserial_disconnect(&obex->port);
}
@@ -269,7 +273,8 @@ static void obex_connect(struct gserial *g)
status = usb_function_activate(&g->func);
if (status)
- DBG(cdev, "obex ttyGS%d function activate --> %d\n",
+ dev_dbg(&cdev->gadget->dev,
+ "obex ttyGS%d function activate --> %d\n",
obex->port_num, status);
}
@@ -284,7 +289,8 @@ static void obex_disconnect(struct gserial *g)
status = usb_function_deactivate(&g->func);
if (status)
- DBG(cdev, "obex ttyGS%d function deactivate --> %d\n",
+ dev_dbg(&cdev->gadget->dev,
+ "obex ttyGS%d function deactivate --> %d\n",
obex->port_num, status);
}
@@ -383,15 +389,14 @@ static int obex_bind(struct usb_configuration *c, struct usb_function *f)
obex->can_activate = true;
- DBG(cdev, "obex ttyGS%d: %s speed IN/%s OUT/%s\n",
- obex->port_num,
- gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
- obex->port.in->name, obex->port.out->name);
+ dev_dbg(&cdev->gadget->dev, "obex ttyGS%d: %s speed IN/%s OUT/%s\n",
+ obex->port_num,
+ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+ obex->port.in->name, obex->port.out->name);
return 0;
fail:
- usb_free_all_descriptors(f);
/* we might as well release our claims on endpoints */
if (obex->port.out)
obex->port.out->driver_data = NULL;
diff --git a/drivers/usb/gadget/function/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c
index b9cfc1571d71..1ec8b7ffdccd 100644
--- a/drivers/usb/gadget/function/f_phonet.c
+++ b/drivers/usb/gadget/function/f_phonet.c
@@ -570,8 +570,8 @@ static int pn_bind(struct usb_configuration *c, struct usb_function *f)
err_req:
for (i = 0; i < phonet_rxq_size && fp->out_reqv[i]; i++)
usb_ep_free_request(fp->out_ep, fp->out_reqv[i]);
-err:
usb_free_all_descriptors(f);
+err:
if (fp->out_ep)
fp->out_ep->driver_data = NULL;
if (fp->in_ep)
diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c
index ddb09dc6d1f2..f13fc6a58565 100644
--- a/drivers/usb/gadget/function/f_rndis.c
+++ b/drivers/usb/gadget/function/f_rndis.c
@@ -802,8 +802,10 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
if (rndis->manufacturer && rndis->vendorID &&
rndis_set_param_vendor(rndis->config, rndis->vendorID,
- rndis->manufacturer))
- goto fail;
+ rndis->manufacturer)) {
+ status = -EINVAL;
+ goto fail_free_descs;
+ }
/* NOTE: all that is done without knowing or caring about
* the network link ... which is unavailable to this code
@@ -817,10 +819,11 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
rndis->notify->name);
return 0;
+fail_free_descs:
+ usb_free_all_descriptors(f);
fail:
kfree(f->os_desc_table);
f->os_desc_n = 0;
- usb_free_all_descriptors(f);
if (rndis->notify_req) {
kfree(rndis->notify_req->buf);
diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c
index 9ecbcbf36a45..2e02dfabc7ae 100644
--- a/drivers/usb/gadget/function/f_serial.c
+++ b/drivers/usb/gadget/function/f_serial.c
@@ -155,11 +155,13 @@ static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
/* we know alt == 0, so this is an activation or a reset */
if (gser->port.in->driver_data) {
- DBG(cdev, "reset generic ttyGS%d\n", gser->port_num);
+ dev_dbg(&cdev->gadget->dev,
+ "reset generic ttyGS%d\n", gser->port_num);
gserial_disconnect(&gser->port);
}
if (!gser->port.in->desc || !gser->port.out->desc) {
- DBG(cdev, "activate generic ttyGS%d\n", gser->port_num);
+ dev_dbg(&cdev->gadget->dev,
+ "activate generic ttyGS%d\n", gser->port_num);
if (config_ep_by_speed(cdev->gadget, f, gser->port.in) ||
config_ep_by_speed(cdev->gadget, f, gser->port.out)) {
gser->port.in->desc = NULL;
@@ -176,7 +178,8 @@ static void gser_disable(struct usb_function *f)
struct f_gser *gser = func_to_gser(f);
struct usb_composite_dev *cdev = f->config->cdev;
- DBG(cdev, "generic ttyGS%d deactivated\n", gser->port_num);
+ dev_dbg(&cdev->gadget->dev,
+ "generic ttyGS%d deactivated\n", gser->port_num);
gserial_disconnect(&gser->port);
}
@@ -239,11 +242,11 @@ static int gser_bind(struct usb_configuration *c, struct usb_function *f)
gser_ss_function);
if (status)
goto fail;
- DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n",
- gser->port_num,
- gadget_is_superspeed(c->cdev->gadget) ? "super" :
- gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
- gser->port.in->name, gser->port.out->name);
+ dev_dbg(&cdev->gadget->dev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n",
+ gser->port_num,
+ gadget_is_superspeed(c->cdev->gadget) ? "super" :
+ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+ gser->port.in->name, gser->port.out->name);
return 0;
fail:
diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c
index d3cd52db78fe..80be25b32cd7 100644
--- a/drivers/usb/gadget/function/f_sourcesink.c
+++ b/drivers/usb/gadget/function/f_sourcesink.c
@@ -23,6 +23,15 @@
#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.
@@ -55,6 +64,8 @@ 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;
};
@@ -68,6 +79,10 @@ 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;
/*-------------------------------------------------------------------------*/
@@ -92,6 +107,16 @@ 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 = {
@@ -130,6 +155,26 @@ 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,
@@ -140,6 +185,10 @@ 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,
};
@@ -179,6 +228,24 @@ 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,
@@ -189,6 +256,10 @@ 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,
};
@@ -264,6 +335,42 @@ 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,
@@ -280,6 +387,12 @@ 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,
};
@@ -301,6 +414,21 @@ 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)
{
@@ -328,7 +456,8 @@ 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 *iso_in, struct usb_ep *iso_out,
+ struct usb_ep *int_in, struct usb_ep *int_out)
{
disable_ep(cdev, in);
disable_ep(cdev, out);
@@ -336,6 +465,10 @@ 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
@@ -352,6 +485,7 @@ 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);
@@ -412,14 +546,55 @@ 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 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 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.
*/
hs_iso_source_desc.wMaxPacketSize = isoc_maxpacket;
hs_iso_source_desc.wMaxPacketSize |= isoc_mult << 11;
@@ -432,6 +607,17 @@ no_iso:
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;
@@ -439,9 +625,9 @@ no_iso:
fs_sink_desc.bEndpointAddress;
/*
- * 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.
+ * 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.
*/
ss_iso_source_desc.wMaxPacketSize = isoc_maxpacket;
ss_iso_source_desc.bInterval = isoc_interval;
@@ -460,17 +646,37 @@ no_iso:
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\n",
+ DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s, "
+ "INT-IN/%s, INT-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->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>");
return 0;
}
@@ -601,14 +807,15 @@ 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,
- bool is_iso, int speed)
+ enum eptype ep_type, int speed)
{
struct usb_ep *ep;
struct usb_request *req;
int i, size, status;
for (i = 0; i < 8; i++) {
- if (is_iso) {
+ switch (ep_type) {
+ case EP_ISOC:
switch (speed) {
case USB_SPEED_SUPER:
size = isoc_maxpacket * (isoc_mult + 1) *
@@ -624,9 +831,28 @@ 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);
- } else {
+ 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:
ep = is_in ? ss->in_ep : ss->out_ep;
req = ss_alloc_ep_req(ep, 0);
+ break;
}
if (!req)
@@ -644,12 +870,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",
- is_iso ? "ISO-" : "", is_in ? "IN" : "OUT",
- ep->name, status);
+ get_ep_string(ep_type), is_in ? "IN" : "OUT",
+ ep->name, status);
free_ep_req(ep, req);
}
- if (!is_iso)
+ if (!(ep_type == EP_ISOC))
break;
}
@@ -662,7 +888,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->iso_out_ep, ss->int_in_ep, ss->int_out_ep);
VDBG(cdev, "%s disabled\n", ss->function.name);
}
@@ -674,6 +900,62 @@ 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);
@@ -684,7 +966,7 @@ enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss,
return result;
ep->driver_data = ss;
- result = source_sink_start_ep(ss, true, false, speed);
+ result = source_sink_start_ep(ss, true, EP_BULK, speed);
if (result < 0) {
fail:
ep = ss->in_ep;
@@ -703,7 +985,7 @@ fail:
goto fail;
ep->driver_data = ss;
- result = source_sink_start_ep(ss, false, false, speed);
+ result = source_sink_start_ep(ss, false, EP_BULK, speed);
if (result < 0) {
fail2:
ep = ss->out_ep;
@@ -726,7 +1008,7 @@ fail2:
goto fail2;
ep->driver_data = ss;
- result = source_sink_start_ep(ss, true, true, speed);
+ result = source_sink_start_ep(ss, true, EP_ISOC, speed);
if (result < 0) {
fail3:
ep = ss->iso_in_ep;
@@ -749,13 +1031,14 @@ fail3:
goto fail3;
ep->driver_data = ss;
- result = source_sink_start_ep(ss, false, true, speed);
+ result = source_sink_start_ep(ss, false, EP_ISOC, speed);
if (result < 0) {
usb_ep_disable(ep);
ep->driver_data = NULL;
goto fail3;
}
}
+
out:
ss->cur_alt = alt;
@@ -771,6 +1054,8 @@ 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);
}
@@ -883,6 +1168,10 @@ 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";
@@ -1179,6 +1468,182 @@ 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,
@@ -1186,6 +1651,10 @@ 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,
};
@@ -1215,6 +1684,8 @@ 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_subset.c b/drivers/usb/gadget/function/f_subset.c
index 1ea8baf33333..e3dfa675ff06 100644
--- a/drivers/usb/gadget/function/f_subset.c
+++ b/drivers/usb/gadget/function/f_subset.c
@@ -380,7 +380,6 @@ geth_bind(struct usb_configuration *c, struct usb_function *f)
return 0;
fail:
- usb_free_all_descriptors(f);
/* we might as well release our claims on endpoints */
if (geth->port.out_ep)
geth->port.out_ep->driver_data = NULL;
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index 2b4c82d84bfc..f7b203293205 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -11,24 +11,12 @@
#include <linux/slab.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/device.h>
#include <linux/atomic.h>
#include "u_uac1.h"
-#define OUT_EP_MAX_PACKET_SIZE 200
-static int req_buf_size = OUT_EP_MAX_PACKET_SIZE;
-module_param(req_buf_size, int, S_IRUGO);
-MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size");
-
-static int req_count = 256;
-module_param(req_count, int, S_IRUGO);
-MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count");
-
-static int audio_buf_size = 48000;
-module_param(audio_buf_size, int, S_IRUGO);
-MODULE_PARM_DESC(audio_buf_size, "Audio buffer size");
-
static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value);
static int generic_get_cmd(struct usb_audio_control *con, u8 cmd);
@@ -46,7 +34,7 @@ static int generic_get_cmd(struct usb_audio_control *con, u8 cmd);
#define F_AUDIO_NUM_INTERFACES 2
/* B.3.1 Standard AC Interface Descriptor */
-static struct usb_interface_descriptor ac_interface_desc __initdata = {
+static struct usb_interface_descriptor ac_interface_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bNumEndpoints = 0,
@@ -183,12 +171,12 @@ static struct usb_endpoint_descriptor as_out_ep_desc = {
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_SYNC_ADAPTIVE
| USB_ENDPOINT_XFER_ISOC,
- .wMaxPacketSize = __constant_cpu_to_le16(OUT_EP_MAX_PACKET_SIZE),
+ .wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE),
.bInterval = 4,
};
/* Class-specific AS ISO OUT Endpoint Descriptor */
-static struct uac_iso_endpoint_descriptor as_iso_out_desc __initdata = {
+static struct uac_iso_endpoint_descriptor as_iso_out_desc = {
.bLength = UAC_ISO_ENDPOINT_DESC_SIZE,
.bDescriptorType = USB_DT_CS_ENDPOINT,
.bDescriptorSubtype = UAC_EP_GENERAL,
@@ -197,7 +185,7 @@ static struct uac_iso_endpoint_descriptor as_iso_out_desc __initdata = {
.wLockDelay = __constant_cpu_to_le16(1),
};
-static struct usb_descriptor_header *f_audio_desc[] __initdata = {
+static struct usb_descriptor_header *f_audio_desc[] = {
(struct usb_descriptor_header *)&ac_interface_desc,
(struct usb_descriptor_header *)&ac_header_desc,
@@ -216,6 +204,37 @@ static struct usb_descriptor_header *f_audio_desc[] __initdata = {
NULL,
};
+enum {
+ STR_AC_IF,
+ STR_INPUT_TERMINAL,
+ STR_INPUT_TERMINAL_CH_NAMES,
+ STR_FEAT_DESC_0,
+ STR_OUTPUT_TERMINAL,
+ STR_AS_IF_ALT0,
+ STR_AS_IF_ALT1,
+};
+
+static struct usb_string strings_uac1[] = {
+ [STR_AC_IF].s = "AC Interface",
+ [STR_INPUT_TERMINAL].s = "Input terminal",
+ [STR_INPUT_TERMINAL_CH_NAMES].s = "Channels",
+ [STR_FEAT_DESC_0].s = "Volume control & mute",
+ [STR_OUTPUT_TERMINAL].s = "Output terminal",
+ [STR_AS_IF_ALT0].s = "AS Interface",
+ [STR_AS_IF_ALT1].s = "AS Interface",
+ { },
+};
+
+static struct usb_gadget_strings str_uac1 = {
+ .language = 0x0409, /* en-us */
+ .strings = strings_uac1,
+};
+
+static struct usb_gadget_strings *uac1_strings[] = {
+ &str_uac1,
+ NULL,
+};
+
/*
* This function is an ALSA sound card following USB Audio Class Spec 1.0.
*/
@@ -300,8 +319,14 @@ static int f_audio_out_ep_complete(struct usb_ep *ep, struct usb_request *req)
struct f_audio *audio = req->context;
struct usb_composite_dev *cdev = audio->card.func.config->cdev;
struct f_audio_buf *copy_buf = audio->copy_buf;
+ struct f_uac1_opts *opts;
+ int audio_buf_size;
int err;
+ opts = container_of(audio->card.func.fi, struct f_uac1_opts,
+ func_inst);
+ audio_buf_size = opts->audio_buf_size;
+
if (!copy_buf)
return -EINVAL;
@@ -546,10 +571,17 @@ static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_ep *out_ep = audio->out_ep;
struct usb_request *req;
+ struct f_uac1_opts *opts;
+ int req_buf_size, req_count, audio_buf_size;
int i = 0, err = 0;
DBG(cdev, "intf %d, alt %d\n", intf, alt);
+ opts = container_of(f->fi, struct f_uac1_opts, func_inst);
+ req_buf_size = opts->req_buf_size;
+ req_count = opts->req_count;
+ audio_buf_size = opts->audio_buf_size;
+
if (intf == 1) {
if (alt == 1) {
usb_ep_enable(out_ep);
@@ -625,13 +657,37 @@ static void f_audio_build_desc(struct f_audio *audio)
}
/* audio function driver setup/binding */
-static int __init
+static int
f_audio_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_audio *audio = func_to_audio(f);
+ struct usb_string *us;
int status;
struct usb_ep *ep = NULL;
+ struct f_uac1_opts *audio_opts;
+
+ 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);
+ if (status < 0)
+ return status;
+ audio_opts->bound = true;
+ }
+ us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1));
+ if (IS_ERR(us))
+ return PTR_ERR(us);
+ ac_interface_desc.iInterface = us[STR_AC_IF].id;
+ input_terminal_desc.iTerminal = us[STR_INPUT_TERMINAL].id;
+ input_terminal_desc.iChannelNames = us[STR_INPUT_TERMINAL_CH_NAMES].id;
+ feature_unit_desc.iFeature = us[STR_FEAT_DESC_0].id;
+ output_terminal_desc.iTerminal = us[STR_OUTPUT_TERMINAL].id;
+ as_interface_alt_0_desc.iInterface = us[STR_AS_IF_ALT0].id;
+ as_interface_alt_1_desc.iInterface = us[STR_AS_IF_ALT1].id;
+
f_audio_build_desc(audio);
@@ -666,20 +722,12 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f)
return 0;
fail:
+ gaudio_cleanup(&audio->card);
if (ep)
ep->driver_data = NULL;
return status;
}
-static void
-f_audio_unbind(struct usb_configuration *c, struct usb_function *f)
-{
- struct f_audio *audio = func_to_audio(f);
-
- usb_free_all_descriptors(f);
- kfree(audio);
-}
-
/*-------------------------------------------------------------------------*/
static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value)
@@ -695,7 +743,7 @@ static int generic_get_cmd(struct usb_audio_control *con, u8 cmd)
}
/* Todo: add more control selecotor dynamically */
-static int __init control_selector_init(struct f_audio *audio)
+static int control_selector_init(struct f_audio *audio)
{
INIT_LIST_HEAD(&audio->cs);
list_add(&feature_unit.list, &audio->cs);
@@ -712,57 +760,226 @@ static int __init control_selector_init(struct f_audio *audio)
return 0;
}
-/**
- * audio_bind_config - add USB audio function to a configuration
- * @c: the configuration to supcard the USB audio function
- * Context: single threaded during gadget setup
- *
- * Returns zero on success, else negative errno.
- */
-static int __init audio_bind_config(struct usb_configuration *c)
+static inline struct f_uac1_opts *to_f_uac1_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_uac1_opts,
+ func_inst.group);
+}
+
+CONFIGFS_ATTR_STRUCT(f_uac1_opts);
+CONFIGFS_ATTR_OPS(f_uac1_opts);
+
+static void f_uac1_attr_release(struct config_item *item)
+{
+ struct f_uac1_opts *opts = to_f_uac1_opts(item);
+
+ usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations f_uac1_item_ops = {
+ .release = f_uac1_attr_release,
+ .show_attribute = f_uac1_opts_attr_show,
+ .store_attribute = f_uac1_opts_attr_store,
+};
+
+#define UAC1_INT_ATTRIBUTE(name) \
+static ssize_t f_uac1_opts_##name##_show(struct f_uac1_opts *opts, \
+ char *page) \
+{ \
+ int result; \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%u\n", opts->name); \
+ mutex_unlock(&opts->lock); \
+ \
+ return result; \
+} \
+ \
+static ssize_t f_uac1_opts_##name##_store(struct f_uac1_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; \
+ \
+ opts->name = num; \
+ ret = len; \
+ \
+end: \
+ mutex_unlock(&opts->lock); \
+ return ret; \
+} \
+ \
+static struct f_uac1_opts_attribute f_uac1_opts_##name = \
+ __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, \
+ f_uac1_opts_##name##_show, \
+ f_uac1_opts_##name##_store)
+
+UAC1_INT_ATTRIBUTE(req_buf_size);
+UAC1_INT_ATTRIBUTE(req_count);
+UAC1_INT_ATTRIBUTE(audio_buf_size);
+
+#define UAC1_STR_ATTRIBUTE(name) \
+static ssize_t f_uac1_opts_##name##_show(struct f_uac1_opts *opts, \
+ char *page) \
+{ \
+ int result; \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%s\n", opts->name); \
+ mutex_unlock(&opts->lock); \
+ \
+ return result; \
+} \
+ \
+static ssize_t f_uac1_opts_##name##_store(struct f_uac1_opts *opts, \
+ const char *page, size_t len) \
+{ \
+ int ret = -EBUSY; \
+ char *tmp; \
+ \
+ mutex_lock(&opts->lock); \
+ if (opts->refcnt) \
+ goto end; \
+ \
+ tmp = kstrndup(page, len, GFP_KERNEL); \
+ if (tmp) { \
+ ret = -ENOMEM; \
+ goto end; \
+ } \
+ if (opts->name##_alloc) \
+ kfree(opts->name); \
+ opts->name##_alloc = true; \
+ opts->name = tmp; \
+ ret = len; \
+ \
+end: \
+ mutex_unlock(&opts->lock); \
+ return ret; \
+} \
+ \
+static struct f_uac1_opts_attribute f_uac1_opts_##name = \
+ __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, \
+ f_uac1_opts_##name##_show, \
+ f_uac1_opts_##name##_store)
+
+UAC1_STR_ATTRIBUTE(fn_play);
+UAC1_STR_ATTRIBUTE(fn_cap);
+UAC1_STR_ATTRIBUTE(fn_cntl);
+
+static struct configfs_attribute *f_uac1_attrs[] = {
+ &f_uac1_opts_req_buf_size.attr,
+ &f_uac1_opts_req_count.attr,
+ &f_uac1_opts_audio_buf_size.attr,
+ &f_uac1_opts_fn_play.attr,
+ &f_uac1_opts_fn_cap.attr,
+ &f_uac1_opts_fn_cntl.attr,
+ NULL,
+};
+
+static struct config_item_type f_uac1_func_type = {
+ .ct_item_ops = &f_uac1_item_ops,
+ .ct_attrs = f_uac1_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static void f_audio_free_inst(struct usb_function_instance *f)
+{
+ struct f_uac1_opts *opts;
+
+ opts = container_of(f, struct f_uac1_opts, func_inst);
+ gaudio_cleanup(opts->card);
+ if (opts->fn_play_alloc)
+ kfree(opts->fn_play);
+ if (opts->fn_cap_alloc)
+ kfree(opts->fn_cap);
+ if (opts->fn_cntl_alloc)
+ kfree(opts->fn_cntl);
+ kfree(opts);
+}
+
+static struct usb_function_instance *f_audio_alloc_inst(void)
+{
+ struct f_uac1_opts *opts;
+
+ opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ if (!opts)
+ return ERR_PTR(-ENOMEM);
+
+ mutex_init(&opts->lock);
+ opts->func_inst.free_func_inst = f_audio_free_inst;
+
+ config_group_init_type_name(&opts->func_inst.group, "",
+ &f_uac1_func_type);
+
+ opts->req_buf_size = UAC1_OUT_EP_MAX_PACKET_SIZE;
+ opts->req_count = UAC1_REQ_COUNT;
+ opts->audio_buf_size = UAC1_AUDIO_BUF_SIZE;
+ opts->fn_play = FILE_PCM_PLAYBACK;
+ opts->fn_cap = FILE_PCM_CAPTURE;
+ opts->fn_cntl = FILE_CONTROL;
+ return &opts->func_inst;
+}
+
+static void f_audio_free(struct usb_function *f)
+{
+ struct f_audio *audio = func_to_audio(f);
+ struct f_uac1_opts *opts;
+
+ opts = container_of(f->fi, struct f_uac1_opts, func_inst);
+ kfree(audio);
+ mutex_lock(&opts->lock);
+ --opts->refcnt;
+ mutex_unlock(&opts->lock);
+}
+
+static void f_audio_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ usb_free_all_descriptors(f);
+}
+
+static struct usb_function *f_audio_alloc(struct usb_function_instance *fi)
{
struct f_audio *audio;
- int status;
+ struct f_uac1_opts *opts;
/* allocate and initialize one new instance */
- audio = kzalloc(sizeof *audio, GFP_KERNEL);
+ audio = kzalloc(sizeof(*audio), GFP_KERNEL);
if (!audio)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
audio->card.func.name = "g_audio";
- audio->card.gadget = c->cdev->gadget;
+ opts = container_of(fi, struct f_uac1_opts, func_inst);
+ mutex_lock(&opts->lock);
+ ++opts->refcnt;
+ mutex_unlock(&opts->lock);
INIT_LIST_HEAD(&audio->play_queue);
spin_lock_init(&audio->lock);
- /* set up ASLA audio devices */
- status = gaudio_setup(&audio->card);
- if (status < 0)
- goto setup_fail;
-
- audio->card.func.strings = audio_strings;
audio->card.func.bind = f_audio_bind;
audio->card.func.unbind = f_audio_unbind;
audio->card.func.set_alt = f_audio_set_alt;
audio->card.func.setup = f_audio_setup;
audio->card.func.disable = f_audio_disable;
+ audio->card.func.free_func = f_audio_free;
control_selector_init(audio);
INIT_WORK(&audio->playback_work, f_audio_playback_work);
- status = usb_add_function(c, &audio->card.func);
- if (status)
- goto add_fail;
-
- INFO(c->cdev, "audio_buf_size %d, req_buf_size %d, req_count %d\n",
- audio_buf_size, req_buf_size, req_count);
-
- return status;
-
-add_fail:
- gaudio_cleanup();
-setup_fail:
- kfree(audio);
- return status;
+ return &audio->card.func;
}
+
+DECLARE_USB_FUNCTION_INIT(uac1, f_audio_alloc_inst, f_audio_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Bryan Wu");
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 3ed89ecc8d6d..33e16658e5cf 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -20,35 +20,7 @@
#include <sound/pcm.h>
#include <sound/pcm_params.h>
-/* Playback(USB-IN) Default Stereo - Fl/Fr */
-static int p_chmask = 0x3;
-module_param(p_chmask, uint, S_IRUGO);
-MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
-
-/* Playback Default 48 KHz */
-static int p_srate = 48000;
-module_param(p_srate, uint, S_IRUGO);
-MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
-
-/* Playback Default 16bits/sample */
-static int p_ssize = 2;
-module_param(p_ssize, uint, S_IRUGO);
-MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)");
-
-/* Capture(USB-OUT) Default Stereo - Fl/Fr */
-static int c_chmask = 0x3;
-module_param(c_chmask, uint, S_IRUGO);
-MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
-
-/* Capture Default 64 KHz */
-static int c_srate = 64000;
-module_param(c_srate, uint, S_IRUGO);
-MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
-
-/* Capture Default 16bits/sample */
-static int c_ssize = 2;
-module_param(c_ssize, uint, S_IRUGO);
-MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
+#include "u_uac2.h"
/* Keep everyone on toes */
#define USB_XFERS 2
@@ -120,6 +92,15 @@ struct snd_uac2_chip {
struct snd_card *card;
struct snd_pcm *pcm;
+
+ /* timekeeping for the playback endpoint */
+ unsigned int p_interval;
+ unsigned int p_residue;
+
+ /* pre-calculated values for playback iso completion */
+ unsigned int p_pktsize;
+ unsigned int p_pktsize_residue;
+ unsigned int p_framesize;
};
#define BUFF_SIZE_MAX (PAGE_SIZE * 16)
@@ -149,8 +130,6 @@ struct audio_dev {
struct snd_uac2_chip uac2;
};
-static struct audio_dev *agdev_g;
-
static inline
struct audio_dev *func_to_agdev(struct usb_function *f)
{
@@ -170,6 +149,12 @@ struct snd_uac2_chip *pdev_to_uac2(struct platform_device *p)
}
static inline
+struct f_uac2_opts *agdev_to_uac2_opts(struct audio_dev *agdev)
+{
+ return container_of(agdev->func.fi, struct f_uac2_opts, func_inst);
+}
+
+static inline
uint num_channels(uint chanmask)
{
uint num = 0;
@@ -187,8 +172,8 @@ agdev_iso_complete(struct usb_ep *ep, struct usb_request *req)
{
unsigned pending;
unsigned long flags;
+ unsigned int hw_ptr;
bool update_alsa = false;
- unsigned char *src, *dst;
int status = req->status;
struct uac2_req *ur = req->context;
struct snd_pcm_substream *substream;
@@ -216,12 +201,27 @@ agdev_iso_complete(struct usb_ep *ep, struct usb_request *req)
spin_lock_irqsave(&prm->lock, flags);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- src = prm->dma_area + prm->hw_ptr;
+ /*
+ * For each IN packet, take the quotient of the current data
+ * rate and the endpoint's interval as the base packet size.
+ * If there is a residue from this division, add it to the
+ * residue accumulator.
+ */
+ req->length = uac2->p_pktsize;
+ uac2->p_residue += uac2->p_pktsize_residue;
+
+ /*
+ * Whenever there are more bytes in the accumulator than we
+ * need to add one more sample frame, increase this packet's
+ * size and decrease the accumulator.
+ */
+ if (uac2->p_residue / uac2->p_interval >= uac2->p_framesize) {
+ req->length += uac2->p_framesize;
+ uac2->p_residue -= uac2->p_framesize *
+ uac2->p_interval;
+ }
+
req->actual = req->length;
- dst = req->buf;
- } else {
- dst = prm->dma_area + prm->hw_ptr;
- src = req->buf;
}
pending = prm->hw_ptr % prm->period_size;
@@ -229,12 +229,32 @@ agdev_iso_complete(struct usb_ep *ep, struct usb_request *req)
if (pending >= prm->period_size)
update_alsa = true;
+ hw_ptr = prm->hw_ptr;
prm->hw_ptr = (prm->hw_ptr + req->actual) % prm->dma_bytes;
spin_unlock_irqrestore(&prm->lock, flags);
/* Pack USB load in ALSA ring buffer */
- memcpy(dst, src, req->actual);
+ pending = prm->dma_bytes - hw_ptr;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (unlikely(pending < req->actual)) {
+ memcpy(req->buf, prm->dma_area + hw_ptr, pending);
+ memcpy(req->buf + pending, prm->dma_area,
+ req->actual - pending);
+ } else {
+ memcpy(req->buf, prm->dma_area + hw_ptr, req->actual);
+ }
+ } else {
+ if (unlikely(pending < req->actual)) {
+ memcpy(prm->dma_area + hw_ptr, req->buf, pending);
+ memcpy(prm->dma_area, req->buf + pending,
+ req->actual - pending);
+ } else {
+ memcpy(prm->dma_area + hw_ptr, req->buf, req->actual);
+ }
+ }
+
exit:
if (usb_ep_queue(ep, req, GFP_ATOMIC))
dev_err(&uac2->pdev.dev, "%d Error!\n", __LINE__);
@@ -342,6 +362,21 @@ static int uac2_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_dev *audio_dev;
+ struct f_uac2_opts *opts;
+ int p_ssize, c_ssize;
+ int p_srate, c_srate;
+ int p_chmask, c_chmask;
+
+ audio_dev = uac2_to_agdev(uac2);
+ opts = container_of(audio_dev->func.fi, struct f_uac2_opts, func_inst);
+ p_ssize = opts->p_ssize;
+ c_ssize = opts->c_ssize;
+ p_srate = opts->p_srate;
+ c_srate = opts->c_srate;
+ p_chmask = opts->p_chmask;
+ c_chmask = opts->c_chmask;
+ uac2->p_residue = 0;
runtime->hw = uac2_pcm_hardware;
@@ -411,7 +446,15 @@ static int snd_uac2_probe(struct platform_device *pdev)
struct snd_uac2_chip *uac2 = pdev_to_uac2(pdev);
struct snd_card *card;
struct snd_pcm *pcm;
+ struct audio_dev *audio_dev;
+ struct f_uac2_opts *opts;
int err;
+ int p_chmask, c_chmask;
+
+ audio_dev = uac2_to_agdev(uac2);
+ opts = container_of(audio_dev->func.fi, struct f_uac2_opts, func_inst);
+ p_chmask = opts->p_chmask;
+ c_chmask = opts->c_chmask;
/* Choose any slot, with no id */
err = snd_card_new(&pdev->dev, -1, NULL, THIS_MODULE, 0, &card);
@@ -469,6 +512,11 @@ static int snd_uac2_remove(struct platform_device *pdev)
return 0;
}
+static void snd_uac2_release(struct device *dev)
+{
+ dev_dbg(dev, "releasing '%s'\n", dev_name(dev));
+}
+
static int alsa_uac2_init(struct audio_dev *agdev)
{
struct snd_uac2_chip *uac2 = &agdev->uac2;
@@ -480,6 +528,7 @@ static int alsa_uac2_init(struct audio_dev *agdev)
uac2->pdev.id = 0;
uac2->pdev.name = uac2_name;
+ uac2->pdev.dev.release = snd_uac2_release;
/* Register snd_uac2 driver */
err = platform_driver_register(&uac2->pdrv);
@@ -729,6 +778,7 @@ struct usb_endpoint_descriptor fs_epout_desc = {
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
+ .wMaxPacketSize = cpu_to_le16(1023),
.bInterval = 1,
};
@@ -737,6 +787,7 @@ struct usb_endpoint_descriptor hs_epout_desc = {
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
+ .wMaxPacketSize = cpu_to_le16(1024),
.bInterval = 4,
};
@@ -804,6 +855,7 @@ struct usb_endpoint_descriptor fs_epin_desc = {
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
+ .wMaxPacketSize = cpu_to_le16(1023),
.bInterval = 1,
};
@@ -812,6 +864,7 @@ struct usb_endpoint_descriptor hs_epin_desc = {
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
+ .wMaxPacketSize = cpu_to_le16(1024),
.bInterval = 4,
};
@@ -904,6 +957,9 @@ free_ep(struct uac2_rtd_params *prm, struct usb_ep *ep)
struct snd_uac2_chip *uac2 = prm->uac2;
int i;
+ if (!prm->ep_enabled)
+ return;
+
prm->ep_enabled = false;
for (i = 0; i < USB_XFERS; i++) {
@@ -919,20 +975,58 @@ free_ep(struct uac2_rtd_params *prm, struct usb_ep *ep)
"%s:%d Error!\n", __func__, __LINE__);
}
-static int __init
+static int
afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
{
struct audio_dev *agdev = func_to_agdev(fn);
struct snd_uac2_chip *uac2 = &agdev->uac2;
struct usb_composite_dev *cdev = cfg->cdev;
struct usb_gadget *gadget = cdev->gadget;
+ struct device *dev = &uac2->pdev.dev;
struct uac2_rtd_params *prm;
+ struct f_uac2_opts *uac2_opts;
+ struct usb_string *us;
int ret;
+ uac2_opts = container_of(fn->fi, struct f_uac2_opts, func_inst);
+
+ us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn));
+ if (IS_ERR(us))
+ return PTR_ERR(us);
+ iad_desc.iFunction = us[STR_ASSOC].id;
+ std_ac_if_desc.iInterface = us[STR_IF_CTRL].id;
+ in_clk_src_desc.iClockSource = us[STR_CLKSRC_IN].id;
+ out_clk_src_desc.iClockSource = us[STR_CLKSRC_OUT].id;
+ usb_out_it_desc.iTerminal = us[STR_USB_IT].id;
+ io_in_it_desc.iTerminal = us[STR_IO_IT].id;
+ usb_in_ot_desc.iTerminal = us[STR_USB_OT].id;
+ io_out_ot_desc.iTerminal = us[STR_IO_OT].id;
+ std_as_out_if0_desc.iInterface = us[STR_AS_OUT_ALT0].id;
+ std_as_out_if1_desc.iInterface = us[STR_AS_OUT_ALT1].id;
+ std_as_in_if0_desc.iInterface = us[STR_AS_IN_ALT0].id;
+ std_as_in_if1_desc.iInterface = us[STR_AS_IN_ALT1].id;
+
+
+ /* Initialize the configurable parameters */
+ usb_out_it_desc.bNrChannels = num_channels(uac2_opts->c_chmask);
+ usb_out_it_desc.bmChannelConfig = cpu_to_le32(uac2_opts->c_chmask);
+ io_in_it_desc.bNrChannels = num_channels(uac2_opts->p_chmask);
+ io_in_it_desc.bmChannelConfig = cpu_to_le32(uac2_opts->p_chmask);
+ as_out_hdr_desc.bNrChannels = num_channels(uac2_opts->c_chmask);
+ as_out_hdr_desc.bmChannelConfig = cpu_to_le32(uac2_opts->c_chmask);
+ as_in_hdr_desc.bNrChannels = num_channels(uac2_opts->p_chmask);
+ as_in_hdr_desc.bmChannelConfig = cpu_to_le32(uac2_opts->p_chmask);
+ as_out_fmt1_desc.bSubslotSize = uac2_opts->c_ssize;
+ as_out_fmt1_desc.bBitResolution = uac2_opts->c_ssize * 8;
+ as_in_fmt1_desc.bSubslotSize = uac2_opts->p_ssize;
+ as_in_fmt1_desc.bBitResolution = uac2_opts->p_ssize * 8;
+
+ snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", uac2_opts->p_srate);
+ snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", uac2_opts->c_srate);
+
ret = usb_interface_id(cfg, fn);
if (ret < 0) {
- dev_err(&uac2->pdev.dev,
- "%s:%d Error!\n", __func__, __LINE__);
+ dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
}
std_ac_if_desc.bInterfaceNumber = ret;
@@ -941,8 +1035,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
ret = usb_interface_id(cfg, fn);
if (ret < 0) {
- dev_err(&uac2->pdev.dev,
- "%s:%d Error!\n", __func__, __LINE__);
+ dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
}
std_as_out_if0_desc.bInterfaceNumber = ret;
@@ -952,8 +1045,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
ret = usb_interface_id(cfg, fn);
if (ret < 0) {
- dev_err(&uac2->pdev.dev,
- "%s:%d Error!\n", __func__, __LINE__);
+ dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
}
std_as_in_if0_desc.bInterfaceNumber = ret;
@@ -963,16 +1055,14 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc);
if (!agdev->out_ep) {
- dev_err(&uac2->pdev.dev,
- "%s:%d Error!\n", __func__, __LINE__);
+ dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
goto err;
}
agdev->out_ep->driver_data = agdev;
agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc);
if (!agdev->in_ep) {
- dev_err(&uac2->pdev.dev,
- "%s:%d Error!\n", __func__, __LINE__);
+ dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
goto err;
}
agdev->in_ep->driver_data = agdev;
@@ -994,7 +1084,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL);
if (!prm->rbuf) {
prm->max_psize = 0;
- goto err;
+ goto err_free_descs;
}
prm = &agdev->uac2.p_prm;
@@ -1002,17 +1092,19 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL);
if (!prm->rbuf) {
prm->max_psize = 0;
- goto err;
+ goto err_free_descs;
}
ret = alsa_uac2_init(agdev);
if (ret)
- goto err;
+ goto err_free_descs;
return 0;
+
+err_free_descs:
+ usb_free_all_descriptors(fn);
err:
kfree(agdev->uac2.p_prm.rbuf);
kfree(agdev->uac2.c_prm.rbuf);
- usb_free_all_descriptors(fn);
if (agdev->in_ep)
agdev->in_ep->driver_data = NULL;
if (agdev->out_ep)
@@ -1020,27 +1112,6 @@ err:
return -EINVAL;
}
-static void
-afunc_unbind(struct usb_configuration *cfg, struct usb_function *fn)
-{
- struct audio_dev *agdev = func_to_agdev(fn);
- struct uac2_rtd_params *prm;
-
- alsa_uac2_exit(agdev);
-
- prm = &agdev->uac2.p_prm;
- kfree(prm->rbuf);
-
- prm = &agdev->uac2.c_prm;
- kfree(prm->rbuf);
- usb_free_all_descriptors(fn);
-
- if (agdev->in_ep)
- agdev->in_ep->driver_data = NULL;
- if (agdev->out_ep)
- agdev->out_ep->driver_data = NULL;
-}
-
static int
afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
{
@@ -1048,23 +1119,22 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
struct audio_dev *agdev = func_to_agdev(fn);
struct snd_uac2_chip *uac2 = &agdev->uac2;
struct usb_gadget *gadget = cdev->gadget;
+ struct device *dev = &uac2->pdev.dev;
struct usb_request *req;
struct usb_ep *ep;
struct uac2_rtd_params *prm;
- int i;
+ int req_len, i;
/* No i/f has more than 2 alt settings */
if (alt > 1) {
- dev_err(&uac2->pdev.dev,
- "%s:%d Error!\n", __func__, __LINE__);
+ dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return -EINVAL;
}
if (intf == agdev->ac_intf) {
/* Control I/f has only 1 AltSetting - 0 */
if (alt) {
- dev_err(&uac2->pdev.dev,
- "%s:%d Error!\n", __func__, __LINE__);
+ dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return -EINVAL;
}
return 0;
@@ -1075,14 +1145,43 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
prm = &uac2->c_prm;
config_ep_by_speed(gadget, fn, ep);
agdev->as_out_alt = alt;
+ req_len = prm->max_psize;
} else if (intf == agdev->as_in_intf) {
+ struct f_uac2_opts *opts = agdev_to_uac2_opts(agdev);
+ unsigned int factor, rate;
+ struct usb_endpoint_descriptor *ep_desc;
+
ep = agdev->in_ep;
prm = &uac2->p_prm;
config_ep_by_speed(gadget, fn, ep);
agdev->as_in_alt = alt;
+
+ /* pre-calculate the playback endpoint's interval */
+ if (gadget->speed == USB_SPEED_FULL) {
+ ep_desc = &fs_epin_desc;
+ factor = 1000;
+ } else {
+ ep_desc = &hs_epin_desc;
+ factor = 125;
+ }
+
+ /* pre-compute some values for iso_complete() */
+ uac2->p_framesize = opts->p_ssize *
+ num_channels(opts->p_chmask);
+ rate = opts->p_srate * uac2->p_framesize;
+ uac2->p_interval = (1 << (ep_desc->bInterval - 1)) * factor;
+ uac2->p_pktsize = min_t(unsigned int, rate / uac2->p_interval,
+ prm->max_psize);
+
+ if (uac2->p_pktsize < prm->max_psize)
+ uac2->p_pktsize_residue = rate % uac2->p_interval;
+ else
+ uac2->p_pktsize_residue = 0;
+
+ req_len = uac2->p_pktsize;
+ uac2->p_residue = 0;
} else {
- dev_err(&uac2->pdev.dev,
- "%s:%d Error!\n", __func__, __LINE__);
+ dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return -EINVAL;
}
@@ -1095,31 +1194,23 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
usb_ep_enable(ep);
for (i = 0; i < USB_XFERS; i++) {
- if (prm->ureq[i].req) {
- if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC))
- dev_err(&uac2->pdev.dev, "%d Error!\n",
- __LINE__);
- continue;
- }
-
- req = usb_ep_alloc_request(ep, GFP_ATOMIC);
- if (req == NULL) {
- dev_err(&uac2->pdev.dev,
- "%s:%d Error!\n", __func__, __LINE__);
- return -EINVAL;
+ if (!prm->ureq[i].req) {
+ req = usb_ep_alloc_request(ep, GFP_ATOMIC);
+ if (req == NULL)
+ return -ENOMEM;
+
+ prm->ureq[i].req = req;
+ prm->ureq[i].pp = prm;
+
+ req->zero = 0;
+ req->context = &prm->ureq[i];
+ req->length = req_len;
+ req->complete = agdev_iso_complete;
+ req->buf = prm->rbuf + i * prm->max_psize;
}
- prm->ureq[i].req = req;
- prm->ureq[i].pp = prm;
-
- req->zero = 0;
- req->context = &prm->ureq[i];
- req->length = prm->max_psize;
- req->complete = agdev_iso_complete;
- req->buf = prm->rbuf + i * req->length;
-
- if (usb_ep_queue(ep, req, GFP_ATOMIC))
- dev_err(&uac2->pdev.dev, "%d Error!\n", __LINE__);
+ if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC))
+ dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
}
return 0;
@@ -1164,12 +1255,18 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
struct usb_request *req = fn->config->cdev->req;
struct audio_dev *agdev = func_to_agdev(fn);
struct snd_uac2_chip *uac2 = &agdev->uac2;
+ struct f_uac2_opts *opts;
u16 w_length = le16_to_cpu(cr->wLength);
u16 w_index = le16_to_cpu(cr->wIndex);
u16 w_value = le16_to_cpu(cr->wValue);
u8 entity_id = (w_index >> 8) & 0xff;
u8 control_selector = w_value >> 8;
int value = -EOPNOTSUPP;
+ int p_srate, c_srate;
+
+ opts = agdev_to_uac2_opts(agdev);
+ p_srate = opts->p_srate;
+ c_srate = opts->c_srate;
if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
struct cntrl_cur_lay3 c;
@@ -1199,6 +1296,7 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
struct usb_request *req = fn->config->cdev->req;
struct audio_dev *agdev = func_to_agdev(fn);
struct snd_uac2_chip *uac2 = &agdev->uac2;
+ struct f_uac2_opts *opts;
u16 w_length = le16_to_cpu(cr->wLength);
u16 w_index = le16_to_cpu(cr->wIndex);
u16 w_value = le16_to_cpu(cr->wValue);
@@ -1206,6 +1304,11 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
u8 control_selector = w_value >> 8;
struct cntrl_range_lay3 r;
int value = -EOPNOTSUPP;
+ int p_srate, c_srate;
+
+ opts = agdev_to_uac2_opts(agdev);
+ p_srate = opts->p_srate;
+ c_srate = opts->c_srate;
if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
if (entity_id == USB_IN_CLK_ID)
@@ -1309,66 +1412,184 @@ afunc_setup(struct usb_function *fn, const struct usb_ctrlrequest *cr)
return value;
}
-static int audio_bind_config(struct usb_configuration *cfg)
+static inline struct f_uac2_opts *to_f_uac2_opts(struct config_item *item)
{
- int res;
-
- agdev_g = kzalloc(sizeof *agdev_g, GFP_KERNEL);
- if (agdev_g == NULL)
- return -ENOMEM;
-
- res = usb_string_ids_tab(cfg->cdev, strings_fn);
- if (res)
- return res;
- iad_desc.iFunction = strings_fn[STR_ASSOC].id;
- std_ac_if_desc.iInterface = strings_fn[STR_IF_CTRL].id;
- in_clk_src_desc.iClockSource = strings_fn[STR_CLKSRC_IN].id;
- out_clk_src_desc.iClockSource = strings_fn[STR_CLKSRC_OUT].id;
- usb_out_it_desc.iTerminal = strings_fn[STR_USB_IT].id;
- io_in_it_desc.iTerminal = strings_fn[STR_IO_IT].id;
- usb_in_ot_desc.iTerminal = strings_fn[STR_USB_OT].id;
- io_out_ot_desc.iTerminal = strings_fn[STR_IO_OT].id;
- std_as_out_if0_desc.iInterface = strings_fn[STR_AS_OUT_ALT0].id;
- std_as_out_if1_desc.iInterface = strings_fn[STR_AS_OUT_ALT1].id;
- std_as_in_if0_desc.iInterface = strings_fn[STR_AS_IN_ALT0].id;
- std_as_in_if1_desc.iInterface = strings_fn[STR_AS_IN_ALT1].id;
-
- agdev_g->func.name = "uac2_func";
- agdev_g->func.strings = fn_strings;
- agdev_g->func.bind = afunc_bind;
- agdev_g->func.unbind = afunc_unbind;
- agdev_g->func.set_alt = afunc_set_alt;
- agdev_g->func.get_alt = afunc_get_alt;
- agdev_g->func.disable = afunc_disable;
- agdev_g->func.setup = afunc_setup;
+ return container_of(to_config_group(item), struct f_uac2_opts,
+ func_inst.group);
+}
- /* Initialize the configurable parameters */
- usb_out_it_desc.bNrChannels = num_channels(c_chmask);
- usb_out_it_desc.bmChannelConfig = cpu_to_le32(c_chmask);
- io_in_it_desc.bNrChannels = num_channels(p_chmask);
- io_in_it_desc.bmChannelConfig = cpu_to_le32(p_chmask);
- as_out_hdr_desc.bNrChannels = num_channels(c_chmask);
- as_out_hdr_desc.bmChannelConfig = cpu_to_le32(c_chmask);
- as_in_hdr_desc.bNrChannels = num_channels(p_chmask);
- as_in_hdr_desc.bmChannelConfig = cpu_to_le32(p_chmask);
- as_out_fmt1_desc.bSubslotSize = c_ssize;
- as_out_fmt1_desc.bBitResolution = c_ssize * 8;
- as_in_fmt1_desc.bSubslotSize = p_ssize;
- as_in_fmt1_desc.bBitResolution = p_ssize * 8;
-
- snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", p_srate);
- snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", c_srate);
-
- res = usb_add_function(cfg, &agdev_g->func);
- if (res < 0)
- kfree(agdev_g);
-
- return res;
+CONFIGFS_ATTR_STRUCT(f_uac2_opts);
+CONFIGFS_ATTR_OPS(f_uac2_opts);
+
+static void f_uac2_attr_release(struct config_item *item)
+{
+ struct f_uac2_opts *opts = to_f_uac2_opts(item);
+
+ usb_put_function_instance(&opts->func_inst);
}
-static void
-uac2_unbind_config(struct usb_configuration *cfg)
+static struct configfs_item_operations f_uac2_item_ops = {
+ .release = f_uac2_attr_release,
+ .show_attribute = f_uac2_opts_attr_show,
+ .store_attribute = f_uac2_opts_attr_store,
+};
+
+#define UAC2_ATTRIBUTE(name) \
+static ssize_t f_uac2_opts_##name##_show(struct f_uac2_opts *opts, \
+ char *page) \
+{ \
+ int result; \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%u\n", opts->name); \
+ mutex_unlock(&opts->lock); \
+ \
+ return result; \
+} \
+ \
+static ssize_t f_uac2_opts_##name##_store(struct f_uac2_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; \
+ \
+ opts->name = num; \
+ ret = len; \
+ \
+end: \
+ mutex_unlock(&opts->lock); \
+ return ret; \
+} \
+ \
+static struct f_uac2_opts_attribute f_uac2_opts_##name = \
+ __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, \
+ f_uac2_opts_##name##_show, \
+ f_uac2_opts_##name##_store)
+
+UAC2_ATTRIBUTE(p_chmask);
+UAC2_ATTRIBUTE(p_srate);
+UAC2_ATTRIBUTE(p_ssize);
+UAC2_ATTRIBUTE(c_chmask);
+UAC2_ATTRIBUTE(c_srate);
+UAC2_ATTRIBUTE(c_ssize);
+
+static struct configfs_attribute *f_uac2_attrs[] = {
+ &f_uac2_opts_p_chmask.attr,
+ &f_uac2_opts_p_srate.attr,
+ &f_uac2_opts_p_ssize.attr,
+ &f_uac2_opts_c_chmask.attr,
+ &f_uac2_opts_c_srate.attr,
+ &f_uac2_opts_c_ssize.attr,
+ NULL,
+};
+
+static struct config_item_type f_uac2_func_type = {
+ .ct_item_ops = &f_uac2_item_ops,
+ .ct_attrs = f_uac2_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static void afunc_free_inst(struct usb_function_instance *f)
+{
+ struct f_uac2_opts *opts;
+
+ opts = container_of(f, struct f_uac2_opts, func_inst);
+ kfree(opts);
+}
+
+static struct usb_function_instance *afunc_alloc_inst(void)
{
- kfree(agdev_g);
- agdev_g = NULL;
+ struct f_uac2_opts *opts;
+
+ opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ if (!opts)
+ return ERR_PTR(-ENOMEM);
+
+ mutex_init(&opts->lock);
+ opts->func_inst.free_func_inst = afunc_free_inst;
+
+ config_group_init_type_name(&opts->func_inst.group, "",
+ &f_uac2_func_type);
+
+ opts->p_chmask = UAC2_DEF_PCHMASK;
+ opts->p_srate = UAC2_DEF_PSRATE;
+ opts->p_ssize = UAC2_DEF_PSSIZE;
+ opts->c_chmask = UAC2_DEF_CCHMASK;
+ opts->c_srate = UAC2_DEF_CSRATE;
+ opts->c_ssize = UAC2_DEF_CSSIZE;
+ return &opts->func_inst;
}
+
+static void afunc_free(struct usb_function *f)
+{
+ struct audio_dev *agdev;
+ struct f_uac2_opts *opts;
+
+ agdev = func_to_agdev(f);
+ opts = container_of(f->fi, struct f_uac2_opts, func_inst);
+ kfree(agdev);
+ mutex_lock(&opts->lock);
+ --opts->refcnt;
+ mutex_unlock(&opts->lock);
+}
+
+static void afunc_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct audio_dev *agdev = func_to_agdev(f);
+ struct uac2_rtd_params *prm;
+
+ alsa_uac2_exit(agdev);
+
+ prm = &agdev->uac2.p_prm;
+ kfree(prm->rbuf);
+
+ prm = &agdev->uac2.c_prm;
+ kfree(prm->rbuf);
+ usb_free_all_descriptors(f);
+
+ if (agdev->in_ep)
+ agdev->in_ep->driver_data = NULL;
+ if (agdev->out_ep)
+ agdev->out_ep->driver_data = NULL;
+}
+
+struct usb_function *afunc_alloc(struct usb_function_instance *fi)
+{
+ struct audio_dev *agdev;
+ struct f_uac2_opts *opts;
+
+ agdev = kzalloc(sizeof(*agdev), GFP_KERNEL);
+ if (agdev == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ opts = container_of(fi, struct f_uac2_opts, func_inst);
+ mutex_lock(&opts->lock);
+ ++opts->refcnt;
+ mutex_unlock(&opts->lock);
+
+ agdev->func.name = "uac2_func";
+ agdev->func.bind = afunc_bind;
+ agdev->func.unbind = afunc_unbind;
+ agdev->func.set_alt = afunc_set_alt;
+ agdev->func.get_alt = afunc_get_alt;
+ agdev->func.disable = afunc_disable;
+ agdev->func.setup = afunc_setup;
+ agdev->func.free_func = afunc_free;
+
+ return &agdev->func;
+}
+
+DECLARE_USB_FUNCTION_INIT(uac2, afunc_alloc_inst, afunc_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Yadwinder Singh");
+MODULE_AUTHOR("Jaswinder Singh");
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index e2a1f50bd93c..945b3bd2ca98 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -11,6 +11,7 @@
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/fs.h>
@@ -27,24 +28,12 @@
#include <media/v4l2-event.h>
#include "uvc.h"
+#include "uvc_v4l2.h"
+#include "uvc_video.h"
+#include "u_uvc.h"
unsigned int uvc_gadget_trace_param;
-/*-------------------------------------------------------------------------*/
-
-/* module parameters specific to the Video streaming endpoint */
-static unsigned int streaming_interval = 1;
-module_param(streaming_interval, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(streaming_interval, "1 - 16");
-
-static unsigned int streaming_maxpacket = 1024;
-module_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(streaming_maxpacket, "1 - 1023 (FS), 1 - 3072 (hs/ss)");
-
-static unsigned int streaming_maxburst;
-module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)");
-
/* --------------------------------------------------------------------------
* Function descriptors
*/
@@ -75,7 +64,7 @@ static struct usb_gadget_strings *uvc_function_strings[] = {
#define UVC_STATUS_MAX_PACKET_SIZE 16 /* 16 bytes status */
-static struct usb_interface_assoc_descriptor uvc_iad __initdata = {
+static struct usb_interface_assoc_descriptor uvc_iad = {
.bLength = sizeof(uvc_iad),
.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
.bFirstInterface = 0,
@@ -86,7 +75,7 @@ static struct usb_interface_assoc_descriptor uvc_iad __initdata = {
.iFunction = 0,
};
-static struct usb_interface_descriptor uvc_control_intf __initdata = {
+static struct usb_interface_descriptor uvc_control_intf = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = UVC_INTF_VIDEO_CONTROL,
@@ -98,7 +87,7 @@ static struct usb_interface_descriptor uvc_control_intf __initdata = {
.iInterface = 0,
};
-static struct usb_endpoint_descriptor uvc_control_ep __initdata = {
+static struct usb_endpoint_descriptor uvc_control_ep = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
@@ -107,7 +96,7 @@ static struct usb_endpoint_descriptor uvc_control_ep __initdata = {
.bInterval = 8,
};
-static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp __initdata = {
+static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp = {
.bLength = sizeof(uvc_ss_control_comp),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/* The following 3 values can be tweaked if necessary. */
@@ -116,14 +105,14 @@ static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp __initdata = {
.wBytesPerInterval = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE),
};
-static struct uvc_control_endpoint_descriptor uvc_control_cs_ep __initdata = {
+static struct uvc_control_endpoint_descriptor uvc_control_cs_ep = {
.bLength = UVC_DT_CONTROL_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_CS_ENDPOINT,
.bDescriptorSubType = UVC_EP_INTERRUPT,
.wMaxTransferSize = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE),
};
-static struct usb_interface_descriptor uvc_streaming_intf_alt0 __initdata = {
+static struct usb_interface_descriptor uvc_streaming_intf_alt0 = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = UVC_INTF_VIDEO_STREAMING,
@@ -135,7 +124,7 @@ static struct usb_interface_descriptor uvc_streaming_intf_alt0 __initdata = {
.iInterface = 0,
};
-static struct usb_interface_descriptor uvc_streaming_intf_alt1 __initdata = {
+static struct usb_interface_descriptor uvc_streaming_intf_alt1 = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = UVC_INTF_VIDEO_STREAMING,
@@ -147,7 +136,7 @@ static struct usb_interface_descriptor uvc_streaming_intf_alt1 __initdata = {
.iInterface = 0,
};
-static struct usb_endpoint_descriptor uvc_fs_streaming_ep __initdata = {
+static struct usb_endpoint_descriptor uvc_fs_streaming_ep = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
@@ -158,7 +147,7 @@ static struct usb_endpoint_descriptor uvc_fs_streaming_ep __initdata = {
*/
};
-static struct usb_endpoint_descriptor uvc_hs_streaming_ep __initdata = {
+static struct usb_endpoint_descriptor uvc_hs_streaming_ep = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
@@ -169,7 +158,7 @@ static struct usb_endpoint_descriptor uvc_hs_streaming_ep __initdata = {
*/
};
-static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = {
+static struct usb_endpoint_descriptor uvc_ss_streaming_ep = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -181,7 +170,7 @@ static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = {
*/
};
-static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp __initdata = {
+static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp = {
.bLength = sizeof(uvc_ss_streaming_comp),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/* The bMaxBurst, bmAttributes and wBytesPerInterval values will be
@@ -208,6 +197,12 @@ static const struct usb_descriptor_header * const uvc_ss_streaming[] = {
NULL,
};
+void uvc_set_trace_param(unsigned int trace)
+{
+ uvc_gadget_trace_param = trace;
+}
+EXPORT_SYMBOL(uvc_set_trace_param);
+
/* --------------------------------------------------------------------------
* Control requests
*/
@@ -251,6 +246,12 @@ uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
if (le16_to_cpu(ctrl->wLength) > UVC_MAX_REQUEST_SIZE)
return -EINVAL;
+ /* Tell the complete callback to generate an event for the next request
+ * that will be enqueued by UVCIOC_SEND_RESPONSE.
+ */
+ uvc->event_setup_out = !(ctrl->bRequestType & USB_DIR_IN);
+ uvc->event_length = le16_to_cpu(ctrl->wLength);
+
memset(&v4l2_event, 0, sizeof(v4l2_event));
v4l2_event.type = UVC_EVENT_SETUP;
memcpy(&uvc_event->req, ctrl, sizeof(uvc_event->req));
@@ -278,27 +279,41 @@ uvc_function_get_alt(struct usb_function *f, unsigned interface)
else if (interface != uvc->streaming_intf)
return -EINVAL;
else
- return uvc->state == UVC_STATE_STREAMING ? 1 : 0;
+ return uvc->video.ep->driver_data ? 1 : 0;
}
static int
uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
{
struct uvc_device *uvc = to_uvc(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
struct v4l2_event v4l2_event;
struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
int ret;
- INFO(f->config->cdev, "uvc_function_set_alt(%u, %u)\n", interface, alt);
+ INFO(cdev, "uvc_function_set_alt(%u, %u)\n", interface, alt);
if (interface == uvc->control_intf) {
if (alt)
return -EINVAL;
+ if (uvc->control_ep->driver_data) {
+ INFO(cdev, "reset UVC Control\n");
+ usb_ep_disable(uvc->control_ep);
+ uvc->control_ep->driver_data = NULL;
+ }
+
+ if (!uvc->control_ep->desc)
+ if (config_ep_by_speed(cdev->gadget, f, uvc->control_ep))
+ return -EINVAL;
+
+ usb_ep_enable(uvc->control_ep);
+ uvc->control_ep->driver_data = uvc;
+
if (uvc->state == UVC_STATE_DISCONNECTED) {
memset(&v4l2_event, 0, sizeof(v4l2_event));
v4l2_event.type = UVC_EVENT_CONNECT;
- uvc_event->speed = f->config->cdev->gadget->speed;
+ uvc_event->speed = cdev->gadget->speed;
v4l2_event_queue(uvc->vdev, &v4l2_event);
uvc->state = UVC_STATE_CONNECTED;
@@ -320,8 +335,10 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
if (uvc->state != UVC_STATE_STREAMING)
return 0;
- if (uvc->video.ep)
+ if (uvc->video.ep) {
usb_ep_disable(uvc->video.ep);
+ uvc->video.ep->driver_data = NULL;
+ }
memset(&v4l2_event, 0, sizeof(v4l2_event));
v4l2_event.type = UVC_EVENT_STREAMOFF;
@@ -334,14 +351,22 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
if (uvc->state != UVC_STATE_CONNECTED)
return 0;
- if (uvc->video.ep) {
- ret = config_ep_by_speed(f->config->cdev->gadget,
- &(uvc->func), uvc->video.ep);
- if (ret)
- return ret;
- usb_ep_enable(uvc->video.ep);
+ if (!uvc->video.ep)
+ return -EINVAL;
+
+ if (uvc->video.ep->driver_data) {
+ INFO(cdev, "reset UVC\n");
+ usb_ep_disable(uvc->video.ep);
+ uvc->video.ep->driver_data = NULL;
}
+ ret = config_ep_by_speed(f->config->cdev->gadget,
+ &(uvc->func), uvc->video.ep);
+ if (ret)
+ return ret;
+ usb_ep_enable(uvc->video.ep);
+ uvc->video.ep->driver_data = uvc;
+
memset(&v4l2_event, 0, sizeof(v4l2_event));
v4l2_event.type = UVC_EVENT_STREAMON;
v4l2_event_queue(uvc->vdev, &v4l2_event);
@@ -365,6 +390,16 @@ uvc_function_disable(struct usb_function *f)
v4l2_event_queue(uvc->vdev, &v4l2_event);
uvc->state = UVC_STATE_DISCONNECTED;
+
+ if (uvc->video.ep->driver_data) {
+ usb_ep_disable(uvc->video.ep);
+ uvc->video.ep->driver_data = NULL;
+ }
+
+ if (uvc->control_ep->driver_data) {
+ usb_ep_disable(uvc->control_ep);
+ uvc->control_ep->driver_data = NULL;
+ }
}
/* --------------------------------------------------------------------------
@@ -408,7 +443,9 @@ uvc_register_video(struct uvc_device *uvc)
video->v4l2_dev = &uvc->v4l2_dev;
video->fops = &uvc_v4l2_fops;
+ video->ioctl_ops = &uvc_v4l2_ioctl_ops;
video->release = video_device_release;
+ video->vfl_dir = VFL_DIR_TX;
strlcpy(video->name, cdev->gadget->name, sizeof(video->name));
uvc->vdev = video;
@@ -434,7 +471,7 @@ uvc_register_video(struct uvc_device *uvc)
} \
} while (0)
-static struct usb_descriptor_header ** __init
+static struct usb_descriptor_header **
uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
{
struct uvc_input_header_descriptor *uvc_streaming_header;
@@ -554,45 +591,26 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
return hdr;
}
-static void
-uvc_function_unbind(struct usb_configuration *c, struct usb_function *f)
-{
- struct usb_composite_dev *cdev = c->cdev;
- struct uvc_device *uvc = to_uvc(f);
-
- INFO(cdev, "uvc_function_unbind\n");
-
- video_unregister_device(uvc->vdev);
- v4l2_device_unregister(&uvc->v4l2_dev);
- uvc->control_ep->driver_data = NULL;
- uvc->video.ep->driver_data = NULL;
-
- uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id = 0;
- usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
- kfree(uvc->control_buf);
-
- usb_free_all_descriptors(f);
-
- kfree(uvc);
-}
-
-static int __init
+static int
uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct uvc_device *uvc = to_uvc(f);
+ struct usb_string *us;
unsigned int max_packet_mult;
unsigned int max_packet_size;
struct usb_ep *ep;
+ struct f_uvc_opts *opts;
int ret = -EINVAL;
INFO(cdev, "uvc_function_bind\n");
+ opts = to_f_uvc_opts(f->fi);
/* Sanity check the streaming endpoint module parameters.
*/
- streaming_interval = clamp(streaming_interval, 1U, 16U);
- streaming_maxpacket = clamp(streaming_maxpacket, 1U, 3072U);
- streaming_maxburst = min(streaming_maxburst, 15U);
+ opts->streaming_interval = clamp(opts->streaming_interval, 1U, 16U);
+ opts->streaming_maxpacket = clamp(opts->streaming_maxpacket, 1U, 3072U);
+ opts->streaming_maxburst = min(opts->streaming_maxburst, 15U);
/* Fill in the FS/HS/SS Video Streaming specific descriptors from the
* module parameters.
@@ -600,30 +618,32 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
* NOTE: We assume that the user knows what they are doing and won't
* give parameters that their UDC doesn't support.
*/
- if (streaming_maxpacket <= 1024) {
+ if (opts->streaming_maxpacket <= 1024) {
max_packet_mult = 1;
- max_packet_size = streaming_maxpacket;
- } else if (streaming_maxpacket <= 2048) {
+ max_packet_size = opts->streaming_maxpacket;
+ } else if (opts->streaming_maxpacket <= 2048) {
max_packet_mult = 2;
- max_packet_size = streaming_maxpacket / 2;
+ max_packet_size = opts->streaming_maxpacket / 2;
} else {
max_packet_mult = 3;
- max_packet_size = streaming_maxpacket / 3;
+ max_packet_size = opts->streaming_maxpacket / 3;
}
- uvc_fs_streaming_ep.wMaxPacketSize = min(streaming_maxpacket, 1023U);
- uvc_fs_streaming_ep.bInterval = streaming_interval;
+ uvc_fs_streaming_ep.wMaxPacketSize =
+ cpu_to_le16(min(opts->streaming_maxpacket, 1023U));
+ uvc_fs_streaming_ep.bInterval = opts->streaming_interval;
- uvc_hs_streaming_ep.wMaxPacketSize = max_packet_size;
- uvc_hs_streaming_ep.wMaxPacketSize |= ((max_packet_mult - 1) << 11);
- uvc_hs_streaming_ep.bInterval = streaming_interval;
+ uvc_hs_streaming_ep.wMaxPacketSize =
+ cpu_to_le16(max_packet_size | ((max_packet_mult - 1) << 11));
+ uvc_hs_streaming_ep.bInterval = opts->streaming_interval;
- uvc_ss_streaming_ep.wMaxPacketSize = max_packet_size;
- uvc_ss_streaming_ep.bInterval = streaming_interval;
+ uvc_ss_streaming_ep.wMaxPacketSize = cpu_to_le16(max_packet_size);
+ uvc_ss_streaming_ep.bInterval = opts->streaming_interval;
uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1;
- uvc_ss_streaming_comp.bMaxBurst = streaming_maxburst;
+ uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst;
uvc_ss_streaming_comp.wBytesPerInterval =
- max_packet_size * max_packet_mult * streaming_maxburst;
+ cpu_to_le16(max_packet_size * max_packet_mult *
+ opts->streaming_maxburst);
/* Allocate endpoints. */
ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep);
@@ -653,6 +673,18 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address;
+ us = usb_gstrings_attach(cdev, uvc_function_strings,
+ ARRAY_SIZE(uvc_en_us_strings));
+ if (IS_ERR(us)) {
+ ret = PTR_ERR(us);
+ goto error;
+ }
+ uvc_iad.iFunction = us[UVC_STRING_CONTROL_IDX].id;
+ uvc_control_intf.iInterface = us[UVC_STRING_CONTROL_IDX].id;
+ ret = us[UVC_STRING_STREAMING_IDX].id;
+ uvc_streaming_intf_alt0.iInterface = ret;
+ uvc_streaming_intf_alt1.iInterface = ret;
+
/* Allocate interface IDs. */
if ((ret = usb_interface_id(c, f)) < 0)
goto error;
@@ -697,7 +729,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
}
/* Initialise video. */
- ret = uvc_video_init(&uvc->video);
+ ret = uvcg_video_init(&uvc->video);
if (ret < 0)
goto error;
@@ -720,10 +752,9 @@ error:
if (uvc->video.ep)
uvc->video.ep->driver_data = NULL;
- if (uvc->control_req) {
+ if (uvc->control_req)
usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
- kfree(uvc->control_buf);
- }
+ kfree(uvc->control_buf);
usb_free_all_descriptors(f);
return ret;
@@ -733,104 +764,81 @@ error:
* USB gadget function
*/
-/**
- * uvc_bind_config - add a UVC function to a configuration
- * @c: the configuration to support the UVC instance
- * Context: single threaded during gadget setup
- *
- * Returns zero on success, else negative errno.
- *
- * Caller must have called @uvc_setup(). Caller is also responsible for
- * calling @uvc_cleanup() before module unload.
- */
-int __init
-uvc_bind_config(struct usb_configuration *c,
- const struct uvc_descriptor_header * const *fs_control,
- const struct uvc_descriptor_header * const *ss_control,
- const struct uvc_descriptor_header * const *fs_streaming,
- const struct uvc_descriptor_header * const *hs_streaming,
- const struct uvc_descriptor_header * const *ss_streaming)
+static void uvc_free_inst(struct usb_function_instance *f)
{
- struct uvc_device *uvc;
- int ret = 0;
+ struct f_uvc_opts *opts = to_f_uvc_opts(f);
- /* TODO Check if the USB device controller supports the required
- * features.
- */
- if (!gadget_is_dualspeed(c->cdev->gadget))
- return -EINVAL;
+ kfree(opts);
+}
- uvc = kzalloc(sizeof(*uvc), GFP_KERNEL);
- if (uvc == NULL)
- return -ENOMEM;
+static struct usb_function_instance *uvc_alloc_inst(void)
+{
+ struct f_uvc_opts *opts;
- uvc->state = UVC_STATE_DISCONNECTED;
+ opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ if (!opts)
+ return ERR_PTR(-ENOMEM);
+ opts->func_inst.free_func_inst = uvc_free_inst;
- /* Validate the descriptors. */
- if (fs_control == NULL || fs_control[0] == NULL ||
- fs_control[0]->bDescriptorSubType != UVC_VC_HEADER)
- goto error;
+ return &opts->func_inst;
+}
- if (ss_control == NULL || ss_control[0] == NULL ||
- ss_control[0]->bDescriptorSubType != UVC_VC_HEADER)
- goto error;
+static void uvc_free(struct usb_function *f)
+{
+ struct uvc_device *uvc = to_uvc(f);
- if (fs_streaming == NULL || fs_streaming[0] == NULL ||
- fs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
- goto error;
+ kfree(uvc);
+}
- if (hs_streaming == NULL || hs_streaming[0] == NULL ||
- hs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
- goto error;
+static void uvc_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct uvc_device *uvc = to_uvc(f);
- if (ss_streaming == NULL || ss_streaming[0] == NULL ||
- ss_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
- goto error;
+ INFO(cdev, "%s\n", __func__);
+
+ video_unregister_device(uvc->vdev);
+ v4l2_device_unregister(&uvc->v4l2_dev);
+ uvc->control_ep->driver_data = NULL;
+ uvc->video.ep->driver_data = NULL;
- uvc->desc.fs_control = fs_control;
- uvc->desc.ss_control = ss_control;
- uvc->desc.fs_streaming = fs_streaming;
- uvc->desc.hs_streaming = hs_streaming;
- uvc->desc.ss_streaming = ss_streaming;
+ usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
+ kfree(uvc->control_buf);
- /* String descriptors are global, we only need to allocate string IDs
- * for the first UVC function. UVC functions beyond the first (if any)
- * will reuse the same IDs.
- */
- if (uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id == 0) {
- ret = usb_string_ids_tab(c->cdev, uvc_en_us_strings);
- if (ret)
- goto error;
- uvc_iad.iFunction =
- uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id;
- uvc_control_intf.iInterface =
- uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id;
- ret = uvc_en_us_strings[UVC_STRING_STREAMING_IDX].id;
- uvc_streaming_intf_alt0.iInterface = ret;
- uvc_streaming_intf_alt1.iInterface = ret;
- }
+ usb_free_all_descriptors(f);
+}
+
+static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
+{
+ struct uvc_device *uvc;
+ struct f_uvc_opts *opts;
+
+ uvc = kzalloc(sizeof(*uvc), GFP_KERNEL);
+ if (uvc == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ uvc->state = UVC_STATE_DISCONNECTED;
+ opts = to_f_uvc_opts(fi);
+
+ 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;
/* Register the function. */
uvc->func.name = "uvc";
- uvc->func.strings = uvc_function_strings;
uvc->func.bind = uvc_function_bind;
- uvc->func.unbind = uvc_function_unbind;
+ uvc->func.unbind = uvc_unbind;
uvc->func.get_alt = uvc_function_get_alt;
uvc->func.set_alt = uvc_function_set_alt;
uvc->func.disable = uvc_function_disable;
uvc->func.setup = uvc_function_setup;
+ uvc->func.free_func = uvc_free;
- ret = usb_add_function(c, &uvc->func);
- if (ret)
- kfree(uvc);
-
- return ret;
-
-error:
- kfree(uvc);
- return ret;
+ return &uvc->func;
}
-module_param_named(trace, uvc_gadget_trace_param, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(trace, "Trace level bitmask");
-
+DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, uvc_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Laurent Pinchart");
diff --git a/drivers/usb/gadget/function/f_uvc.h b/drivers/usb/gadget/function/f_uvc.h
index ec52752f7326..d0a73bdcbba1 100644
--- a/drivers/usb/gadget/function/f_uvc.h
+++ b/drivers/usb/gadget/function/f_uvc.h
@@ -16,12 +16,13 @@
#include <linux/usb/composite.h>
#include <linux/usb/video.h>
-int uvc_bind_config(struct usb_configuration *c,
- const struct uvc_descriptor_header * const *fs_control,
- const struct uvc_descriptor_header * const *hs_control,
- const struct uvc_descriptor_header * const *fs_streaming,
- const struct uvc_descriptor_header * const *hs_streaming,
- const struct uvc_descriptor_header * const *ss_streaming);
+#include "uvc.h"
+
+void uvc_function_setup_continue(struct uvc_device *uvc);
+
+void uvc_function_connect(struct uvc_device *uvc);
+
+void uvc_function_disconnect(struct uvc_device *uvc);
#endif /* _F_UVC_H_ */
diff --git a/drivers/usb/gadget/function/g_zero.h b/drivers/usb/gadget/function/g_zero.h
index 15f180904f8a..2ce28b9d97cc 100644
--- a/drivers/usb/gadget/function/g_zero.h
+++ b/drivers/usb/gadget/function/g_zero.h
@@ -10,6 +10,8 @@
#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;
@@ -17,6 +19,10 @@ 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;
};
@@ -28,6 +34,10 @@ 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;
/*
@@ -62,6 +72,7 @@ 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 *iso_in, struct usb_ep *iso_out,
+ struct usb_ep *int_in, struct usb_ep *int_out);
#endif /* __G_ZERO_H */
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index d50adda913cf..6e6f87656e7b 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -1127,10 +1127,7 @@ void gether_disconnect(struct gether *link)
DBG(dev, "%s\n", __func__);
- netif_tx_lock(dev->net);
netif_stop_queue(dev->net);
- netif_tx_unlock(dev->net);
-
netif_carrier_off(dev->net);
/* disable endpoints, forcing (synchronous) completion
diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h
index 63d6e71569c1..cd128e31f808 100644
--- a/drivers/usb/gadget/function/u_fs.h
+++ b/drivers/usb/gadget/function/u_fs.h
@@ -224,6 +224,10 @@ struct ffs_data {
void *ms_os_descs_ext_prop_name_avail;
void *ms_os_descs_ext_prop_data_avail;
+ unsigned user_flags;
+
+ u8 eps_addrmap[15];
+
unsigned short strings_count;
unsigned short interfaces_count;
unsigned short eps_count;
diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c
index ad0aca812002..491082aaf103 100644
--- a/drivers/usb/gadget/function/u_serial.c
+++ b/drivers/usb/gadget/function/u_serial.c
@@ -55,11 +55,8 @@
* for a telephone or fax link. And ttyGS2 might be something that just
* needs a simple byte stream interface for some messaging protocol that
* is managed in userspace ... OBEX, PTP, and MTP have been mentioned.
- */
-
-#define PREFIX "ttyGS"
-
-/*
+ *
+ *
* gserial is the lifecycle interface, used by USB functions
* gs_port is the I/O nexus, used by the tty driver
* tty_struct links to the tty/filesystem framework
@@ -385,9 +382,9 @@ __acquires(&port->port_lock)
list_del(&req->list);
req->zero = (gs_buf_data_avail(&port->port_write_buf) == 0);
- pr_vdebug(PREFIX "%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n",
- port->port_num, len, *((u8 *)req->buf),
- *((u8 *)req->buf+1), *((u8 *)req->buf+2));
+ pr_vdebug("ttyGS%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n",
+ port->port_num, len, *((u8 *)req->buf),
+ *((u8 *)req->buf+1), *((u8 *)req->buf+2));
/* Drop lock while we call out of driver; completions
* could be issued while we do so. Disconnection may
@@ -503,13 +500,13 @@ static void gs_rx_push(unsigned long _port)
switch (req->status) {
case -ESHUTDOWN:
disconnect = true;
- pr_vdebug(PREFIX "%d: shutdown\n", port->port_num);
+ pr_vdebug("ttyGS%d: shutdown\n", port->port_num);
break;
default:
/* presumably a transient fault */
- pr_warning(PREFIX "%d: unexpected RX status %d\n",
- port->port_num, req->status);
+ pr_warn("ttyGS%d: unexpected RX status %d\n",
+ port->port_num, req->status);
/* FALLTHROUGH */
case 0:
/* normal completion */
@@ -537,9 +534,8 @@ static void gs_rx_push(unsigned long _port)
if (count != size) {
/* stop pushing; TTY layer can't handle more */
port->n_read += count;
- pr_vdebug(PREFIX "%d: rx block %d/%d\n",
- port->port_num,
- count, req->actual);
+ pr_vdebug("ttyGS%d: rx block %d/%d\n",
+ port->port_num, count, req->actual);
break;
}
port->n_read = 0;
@@ -569,7 +565,7 @@ static void gs_rx_push(unsigned long _port)
if (do_push)
tasklet_schedule(&port->push);
else
- pr_warning(PREFIX "%d: RX not scheduled?\n",
+ pr_warn("ttyGS%d: RX not scheduled?\n",
port->port_num);
}
}
@@ -985,7 +981,7 @@ static void gs_unthrottle(struct tty_struct *tty)
* read queue backs up enough we'll be NAKing OUT packets.
*/
tasklet_schedule(&port->push);
- pr_vdebug(PREFIX "%d: unthrottle\n", port->port_num);
+ pr_vdebug("ttyGS%d: unthrottle\n", port->port_num);
}
spin_unlock_irqrestore(&port->port_lock, flags);
}
@@ -1295,7 +1291,7 @@ static int userial_init(void)
return -ENOMEM;
gs_tty_driver->driver_name = "g_serial";
- gs_tty_driver->name = PREFIX;
+ gs_tty_driver->name = "ttyGS";
/* uses dynamically assigned dev_t values */
gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
diff --git a/drivers/usb/gadget/function/u_uac1.c b/drivers/usb/gadget/function/u_uac1.c
index 7a55fea43430..a44a07f30281 100644
--- a/drivers/usb/gadget/function/u_uac1.c
+++ b/drivers/usb/gadget/function/u_uac1.c
@@ -10,6 +10,7 @@
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/delay.h>
@@ -23,22 +24,6 @@
* This component encapsulates the ALSA devices for USB audio gadget
*/
-#define FILE_PCM_PLAYBACK "/dev/snd/pcmC0D0p"
-#define FILE_PCM_CAPTURE "/dev/snd/pcmC0D0c"
-#define FILE_CONTROL "/dev/snd/controlC0"
-
-static char *fn_play = FILE_PCM_PLAYBACK;
-module_param(fn_play, charp, S_IRUGO);
-MODULE_PARM_DESC(fn_play, "Playback PCM device file name");
-
-static char *fn_cap = FILE_PCM_CAPTURE;
-module_param(fn_cap, charp, S_IRUGO);
-MODULE_PARM_DESC(fn_cap, "Capture PCM device file name");
-
-static char *fn_cntl = FILE_CONTROL;
-module_param(fn_cntl, charp, S_IRUGO);
-MODULE_PARM_DESC(fn_cntl, "Control device file name");
-
/*-------------------------------------------------------------------------*/
/**
@@ -167,7 +152,7 @@ static int playback_default_hw_params(struct gaudio_snd_dev *snd)
/**
* Playback audio buffer data by ALSA PCM device
*/
-static size_t u_audio_playback(struct gaudio *card, void *buf, size_t count)
+size_t u_audio_playback(struct gaudio *card, void *buf, size_t count)
{
struct gaudio_snd_dev *snd = &card->playback;
struct snd_pcm_substream *substream = snd->substream;
@@ -202,12 +187,12 @@ try_again:
return 0;
}
-static int u_audio_get_playback_channels(struct gaudio *card)
+int u_audio_get_playback_channels(struct gaudio *card)
{
return card->playback.channels;
}
-static int u_audio_get_playback_rate(struct gaudio *card)
+int u_audio_get_playback_rate(struct gaudio *card)
{
return card->playback.rate;
}
@@ -220,6 +205,13 @@ static int gaudio_open_snd_dev(struct gaudio *card)
{
struct snd_pcm_file *pcm_file;
struct gaudio_snd_dev *snd;
+ struct f_uac1_opts *opts;
+ char *fn_play, *fn_cap, *fn_cntl;
+
+ opts = container_of(card->func.fi, struct f_uac1_opts, func_inst);
+ fn_play = opts->fn_play;
+ fn_cap = opts->fn_cap;
+ fn_cntl = opts->fn_cntl;
if (!card)
return -ENODEV;
@@ -293,7 +285,6 @@ static int gaudio_close_snd_dev(struct gaudio *gau)
return 0;
}
-static struct gaudio *the_card;
/**
* gaudio_setup - setup ALSA interface and preparing for USB transfer
*
@@ -301,15 +292,13 @@ static struct gaudio *the_card;
*
* Returns negative errno, or zero on success
*/
-int __init gaudio_setup(struct gaudio *card)
+int gaudio_setup(struct gaudio *card)
{
int ret;
ret = gaudio_open_snd_dev(card);
if (ret)
ERROR(card, "we need at least one control device\n");
- else if (!the_card)
- the_card = card;
return ret;
@@ -320,11 +309,10 @@ int __init gaudio_setup(struct gaudio *card)
*
* This is called to free all resources allocated by @gaudio_setup().
*/
-void gaudio_cleanup(void)
+void gaudio_cleanup(struct gaudio *the_card)
{
if (the_card) {
gaudio_close_snd_dev(the_card);
- the_card = NULL;
}
}
diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h
index 18c2e729faf6..f8b17fe82efe 100644
--- a/drivers/usb/gadget/function/u_uac1.h
+++ b/drivers/usb/gadget/function/u_uac1.h
@@ -23,6 +23,14 @@
#include "gadget_chips.h"
+#define FILE_PCM_PLAYBACK "/dev/snd/pcmC0D0p"
+#define FILE_PCM_CAPTURE "/dev/snd/pcmC0D0c"
+#define FILE_CONTROL "/dev/snd/controlC0"
+
+#define UAC1_OUT_EP_MAX_PACKET_SIZE 200
+#define UAC1_REQ_COUNT 256
+#define UAC1_AUDIO_BUF_SIZE 48000
+
/*
* This represents the USB side of an audio card device, managed by a USB
* function which provides control and stream interfaces.
@@ -50,7 +58,28 @@ struct gaudio {
/* TODO */
};
+struct f_uac1_opts {
+ struct usb_function_instance func_inst;
+ int req_buf_size;
+ int req_count;
+ int audio_buf_size;
+ char *fn_play;
+ char *fn_cap;
+ char *fn_cntl;
+ unsigned bound:1;
+ unsigned fn_play_alloc:1;
+ unsigned fn_cap_alloc:1;
+ unsigned fn_cntl_alloc:1;
+ struct gaudio *card;
+ struct mutex lock;
+ int refcnt;
+};
+
int gaudio_setup(struct gaudio *card);
-void gaudio_cleanup(void);
+void gaudio_cleanup(struct gaudio *the_card);
+
+size_t u_audio_playback(struct gaudio *card, void *buf, size_t count);
+int u_audio_get_playback_channels(struct gaudio *card);
+int u_audio_get_playback_rate(struct gaudio *card);
#endif /* __U_AUDIO_H */
diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h
new file mode 100644
index 000000000000..78dd37279bd4
--- /dev/null
+++ b/drivers/usb/gadget/function/u_uac2.h
@@ -0,0 +1,42 @@
+/*
+ * u_uac2.h
+ *
+ * Utility definitions for UAC2 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 U_UAC2_H
+#define U_UAC2_H
+
+#include <linux/usb/composite.h>
+
+#define UAC2_DEF_PCHMASK 0x3
+#define UAC2_DEF_PSRATE 48000
+#define UAC2_DEF_PSSIZE 2
+#define UAC2_DEF_CCHMASK 0x3
+#define UAC2_DEF_CSRATE 64000
+#define UAC2_DEF_CSSIZE 2
+
+struct f_uac2_opts {
+ struct usb_function_instance func_inst;
+ int p_chmask;
+ int p_srate;
+ int p_ssize;
+ int c_chmask;
+ int c_srate;
+ int c_ssize;
+ bool bound;
+
+ struct mutex lock;
+ int refcnt;
+};
+
+#endif
diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h
new file mode 100644
index 000000000000..2a8dfdff0332
--- /dev/null
+++ b/drivers/usb/gadget/function/u_uvc.h
@@ -0,0 +1,39 @@
+/*
+ * u_uvc.h
+ *
+ * Utility definitions for the uvc function
+ *
+ * Copyright (c) 2013-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 U_UVC_H
+#define U_UVC_H
+
+#include <linux/usb/composite.h>
+
+#define to_f_uvc_opts(f) container_of(f, struct f_uvc_opts, func_inst)
+
+struct f_uvc_opts {
+ struct usb_function_instance func_inst;
+ unsigned int uvc_gadget_trace_param;
+ unsigned int streaming_interval;
+ unsigned int streaming_maxpacket;
+ unsigned int streaming_maxburst;
+ const struct uvc_descriptor_header * const *fs_control;
+ const struct uvc_descriptor_header * const *ss_control;
+ const struct uvc_descriptor_header * const *fs_streaming;
+ const struct uvc_descriptor_header * const *hs_streaming;
+ const struct uvc_descriptor_header * const *ss_streaming;
+};
+
+void uvc_set_trace_param(unsigned int trace);
+
+#endif /* U_UVC_H */
+
diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h
index 7a9111de8054..f67695cb28f8 100644
--- a/drivers/usb/gadget/function/uvc.h
+++ b/drivers/usb/gadget/function/uvc.h
@@ -53,6 +53,7 @@ struct uvc_event
#ifdef __KERNEL__
#include <linux/usb.h> /* For usb_endpoint_* */
+#include <linux/usb/composite.h>
#include <linux/usb/gadget.h>
#include <linux/videodev2.h>
#include <linux/version.h>
@@ -96,9 +97,6 @@ extern unsigned int uvc_gadget_trace_param;
* Driver specific constants
*/
-#define DRIVER_VERSION "0.1.0"
-#define DRIVER_VERSION_NUMBER KERNEL_VERSION(0, 1, 0)
-
#define UVC_NUM_REQUESTS 4
#define UVC_MAX_REQUEST_SIZE 64
#define UVC_MAX_EVENTS 4
diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c
index 1c29bc954db9..8ea8b3b227b4 100644
--- a/drivers/usb/gadget/function/uvc_queue.c
+++ b/drivers/usb/gadget/function/uvc_queue.c
@@ -28,7 +28,7 @@
/* ------------------------------------------------------------------------
* Video buffers queue management.
*
- * Video queues is initialized by uvc_queue_init(). The function performs
+ * Video queues is initialized by uvcg_queue_init(). The function performs
* basic initialization of the uvc_video_queue struct and never fails.
*
* Video buffers are managed by videobuf2. The driver uses a mutex to protect
@@ -126,13 +126,12 @@ static struct vb2_ops uvc_queue_qops = {
.wait_finish = uvc_wait_finish,
};
-static int uvc_queue_init(struct uvc_video_queue *queue,
- enum v4l2_buf_type type)
+int uvcg_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type)
{
int ret;
queue->queue.type = type;
- queue->queue.io_modes = VB2_MMAP | VB2_USERPTR;
+ queue->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
queue->queue.drv_priv = queue;
queue->queue.buf_struct_size = sizeof(struct uvc_buffer);
queue->queue.ops = &uvc_queue_qops;
@@ -154,7 +153,7 @@ static int uvc_queue_init(struct uvc_video_queue *queue,
/*
* Free the video buffers.
*/
-static void uvc_free_buffers(struct uvc_video_queue *queue)
+void uvcg_free_buffers(struct uvc_video_queue *queue)
{
mutex_lock(&queue->mutex);
vb2_queue_release(&queue->queue);
@@ -164,8 +163,8 @@ static void uvc_free_buffers(struct uvc_video_queue *queue)
/*
* Allocate the video buffers.
*/
-static int uvc_alloc_buffers(struct uvc_video_queue *queue,
- struct v4l2_requestbuffers *rb)
+int uvcg_alloc_buffers(struct uvc_video_queue *queue,
+ struct v4l2_requestbuffers *rb)
{
int ret;
@@ -176,8 +175,7 @@ static int uvc_alloc_buffers(struct uvc_video_queue *queue,
return ret ? ret : rb->count;
}
-static int uvc_query_buffer(struct uvc_video_queue *queue,
- struct v4l2_buffer *buf)
+int uvcg_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
{
int ret;
@@ -188,8 +186,7 @@ static int uvc_query_buffer(struct uvc_video_queue *queue,
return ret;
}
-static int uvc_queue_buffer(struct uvc_video_queue *queue,
- struct v4l2_buffer *buf)
+int uvcg_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
{
unsigned long flags;
int ret;
@@ -213,8 +210,8 @@ done:
* Dequeue a video buffer. If nonblocking is false, block until a buffer is
* available.
*/
-static int uvc_dequeue_buffer(struct uvc_video_queue *queue,
- struct v4l2_buffer *buf, int nonblocking)
+int uvcg_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf,
+ int nonblocking)
{
int ret;
@@ -231,8 +228,8 @@ static int uvc_dequeue_buffer(struct uvc_video_queue *queue,
* This function implements video queue polling and is intended to be used by
* the device poll handler.
*/
-static unsigned int uvc_queue_poll(struct uvc_video_queue *queue,
- struct file *file, poll_table *wait)
+unsigned int uvcg_queue_poll(struct uvc_video_queue *queue, struct file *file,
+ poll_table *wait)
{
unsigned int ret;
@@ -243,8 +240,7 @@ static unsigned int uvc_queue_poll(struct uvc_video_queue *queue,
return ret;
}
-static int uvc_queue_mmap(struct uvc_video_queue *queue,
- struct vm_area_struct *vma)
+int uvcg_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma)
{
int ret;
@@ -261,8 +257,8 @@ static int uvc_queue_mmap(struct uvc_video_queue *queue,
*
* NO-MMU arch need this function to make mmap() work correctly.
*/
-static unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue,
- unsigned long pgoff)
+unsigned long uvcg_queue_get_unmapped_area(struct uvc_video_queue *queue,
+ unsigned long pgoff)
{
unsigned long ret;
@@ -285,7 +281,7 @@ static unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue,
* This function acquires the irq spinlock and can be called from interrupt
* context.
*/
-static void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect)
+void uvcg_queue_cancel(struct uvc_video_queue *queue, int disconnect)
{
struct uvc_buffer *buf;
unsigned long flags;
@@ -324,9 +320,9 @@ static void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect)
* the main queue.
*
* This function can't be called from interrupt context. Use
- * uvc_queue_cancel() instead.
+ * uvcg_queue_cancel() instead.
*/
-static int uvc_queue_enable(struct uvc_video_queue *queue, int enable)
+int uvcg_queue_enable(struct uvc_video_queue *queue, int enable)
{
unsigned long flags;
int ret = 0;
@@ -363,8 +359,8 @@ done:
}
/* called with &queue_irqlock held.. */
-static struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
- struct uvc_buffer *buf)
+struct uvc_buffer *uvcg_queue_next_buffer(struct uvc_video_queue *queue,
+ struct uvc_buffer *buf)
{
struct uvc_buffer *nextbuf;
@@ -392,7 +388,7 @@ static struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
return nextbuf;
}
-static struct uvc_buffer *uvc_queue_head(struct uvc_video_queue *queue)
+struct uvc_buffer *uvcg_queue_head(struct uvc_video_queue *queue)
{
struct uvc_buffer *buf = NULL;
diff --git a/drivers/usb/gadget/function/uvc_queue.h b/drivers/usb/gadget/function/uvc_queue.h
index 8e76ce982f1e..03919c724961 100644
--- a/drivers/usb/gadget/function/uvc_queue.h
+++ b/drivers/usb/gadget/function/uvc_queue.h
@@ -57,6 +57,39 @@ static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
return vb2_is_streaming(&queue->queue);
}
+int uvcg_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type);
+
+void uvcg_free_buffers(struct uvc_video_queue *queue);
+
+int uvcg_alloc_buffers(struct uvc_video_queue *queue,
+ struct v4l2_requestbuffers *rb);
+
+int uvcg_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf);
+
+int uvcg_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf);
+
+int uvcg_dequeue_buffer(struct uvc_video_queue *queue,
+ struct v4l2_buffer *buf, int nonblocking);
+
+unsigned int uvcg_queue_poll(struct uvc_video_queue *queue,
+ struct file *file, poll_table *wait);
+
+int uvcg_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma);
+
+#ifndef CONFIG_MMU
+unsigned long uvcg_queue_get_unmapped_area(struct uvc_video_queue *queue,
+ unsigned long pgoff);
+#endif /* CONFIG_MMU */
+
+void uvcg_queue_cancel(struct uvc_video_queue *queue, int disconnect);
+
+int uvcg_queue_enable(struct uvc_video_queue *queue, int enable);
+
+struct uvc_buffer *uvcg_queue_next_buffer(struct uvc_video_queue *queue,
+ struct uvc_buffer *buf);
+
+struct uvc_buffer *uvcg_queue_head(struct uvc_video_queue *queue);
+
#endif /* __KERNEL__ */
#endif /* _UVC_QUEUE_H_ */
diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c
index ad48e81155e2..5aad7fededa5 100644
--- a/drivers/usb/gadget/function/uvc_v4l2.c
+++ b/drivers/usb/gadget/function/uvc_v4l2.c
@@ -23,8 +23,10 @@
#include <media/v4l2-event.h>
#include <media/v4l2-ioctl.h>
+#include "f_uvc.h"
#include "uvc.h"
#include "uvc_queue.h"
+#include "uvc_video.h"
/* --------------------------------------------------------------------------
* Requests handling
@@ -48,7 +50,7 @@ uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data)
}
/* --------------------------------------------------------------------------
- * V4L2
+ * V4L2 ioctls
*/
struct uvc_format
@@ -63,8 +65,29 @@ static struct uvc_format uvc_formats[] = {
};
static int
-uvc_v4l2_get_format(struct uvc_video *video, struct v4l2_format *fmt)
+uvc_v4l2_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
{
+ struct video_device *vdev = video_devdata(file);
+ struct uvc_device *uvc = video_get_drvdata(vdev);
+ struct usb_composite_dev *cdev = uvc->func.config->cdev;
+
+ strlcpy(cap->driver, "g_uvc", sizeof(cap->driver));
+ strlcpy(cap->card, cdev->gadget->name, sizeof(cap->card));
+ strlcpy(cap->bus_info, dev_name(&cdev->gadget->dev),
+ sizeof(cap->bus_info));
+
+ cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static int
+uvc_v4l2_get_format(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct uvc_device *uvc = video_get_drvdata(vdev);
+ struct uvc_video *video = &uvc->video;
+
fmt->fmt.pix.pixelformat = video->fcc;
fmt->fmt.pix.width = video->width;
fmt->fmt.pix.height = video->height;
@@ -78,8 +101,11 @@ uvc_v4l2_get_format(struct uvc_video *video, struct v4l2_format *fmt)
}
static int
-uvc_v4l2_set_format(struct uvc_video *video, struct v4l2_format *fmt)
+uvc_v4l2_set_format(struct file *file, void *fh, struct v4l2_format *fmt)
{
+ struct video_device *vdev = video_devdata(file);
+ struct uvc_device *uvc = video_get_drvdata(vdev);
+ struct uvc_video *video = &uvc->video;
struct uvc_format *format;
unsigned int imagesize;
unsigned int bpl;
@@ -116,209 +142,184 @@ uvc_v4l2_set_format(struct uvc_video *video, struct v4l2_format *fmt)
}
static int
-uvc_v4l2_open(struct file *file)
+uvc_v4l2_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *b)
{
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
- struct uvc_file_handle *handle;
-
- handle = kzalloc(sizeof(*handle), GFP_KERNEL);
- if (handle == NULL)
- return -ENOMEM;
-
- v4l2_fh_init(&handle->vfh, vdev);
- v4l2_fh_add(&handle->vfh);
+ struct uvc_video *video = &uvc->video;
- handle->device = &uvc->video;
- file->private_data = &handle->vfh;
+ if (b->type != video->queue.queue.type)
+ return -EINVAL;
- uvc_function_connect(uvc);
- return 0;
+ return uvcg_alloc_buffers(&video->queue, b);
}
static int
-uvc_v4l2_release(struct file *file)
+uvc_v4l2_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
- struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data);
- struct uvc_video *video = handle->device;
-
- uvc_function_disconnect(uvc);
-
- uvc_video_enable(video, 0);
- uvc_free_buffers(&video->queue);
-
- file->private_data = NULL;
- v4l2_fh_del(&handle->vfh);
- v4l2_fh_exit(&handle->vfh);
- kfree(handle);
+ struct uvc_video *video = &uvc->video;
- return 0;
+ return uvcg_query_buffer(&video->queue, b);
}
-static long
-uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+static int
+uvc_v4l2_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
- struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data);
- struct usb_composite_dev *cdev = uvc->func.config->cdev;
struct uvc_video *video = &uvc->video;
- int ret = 0;
-
- switch (cmd) {
- /* Query capabilities */
- case VIDIOC_QUERYCAP:
- {
- struct v4l2_capability *cap = arg;
-
- memset(cap, 0, sizeof *cap);
- strlcpy(cap->driver, "g_uvc", sizeof(cap->driver));
- strlcpy(cap->card, cdev->gadget->name, sizeof(cap->card));
- strlcpy(cap->bus_info, dev_name(&cdev->gadget->dev),
- sizeof cap->bus_info);
- cap->version = DRIVER_VERSION_NUMBER;
- cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
- break;
- }
-
- /* Get & Set format */
- case VIDIOC_G_FMT:
- {
- struct v4l2_format *fmt = arg;
+ int ret;
- if (fmt->type != video->queue.queue.type)
- return -EINVAL;
+ ret = uvcg_queue_buffer(&video->queue, b);
+ if (ret < 0)
+ return ret;
- return uvc_v4l2_get_format(video, fmt);
- }
+ return uvcg_video_pump(video);
+}
- case VIDIOC_S_FMT:
- {
- struct v4l2_format *fmt = arg;
+static int
+uvc_v4l2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct uvc_device *uvc = video_get_drvdata(vdev);
+ struct uvc_video *video = &uvc->video;
- if (fmt->type != video->queue.queue.type)
- return -EINVAL;
+ return uvcg_dequeue_buffer(&video->queue, b, file->f_flags & O_NONBLOCK);
+}
- return uvc_v4l2_set_format(video, fmt);
- }
+static int
+uvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct uvc_device *uvc = video_get_drvdata(vdev);
+ struct uvc_video *video = &uvc->video;
+ int ret;
- /* Buffers & streaming */
- case VIDIOC_REQBUFS:
- {
- struct v4l2_requestbuffers *rb = arg;
+ if (type != video->queue.queue.type)
+ return -EINVAL;
- if (rb->type != video->queue.queue.type)
- return -EINVAL;
+ /* Enable UVC video. */
+ ret = uvcg_video_enable(video, 1);
+ if (ret < 0)
+ return ret;
- ret = uvc_alloc_buffers(&video->queue, rb);
- if (ret < 0)
- return ret;
+ /*
+ * Complete the alternate setting selection setup phase now that
+ * userspace is ready to provide video frames.
+ */
+ uvc_function_setup_continue(uvc);
+ uvc->state = UVC_STATE_STREAMING;
- ret = 0;
- break;
- }
-
- case VIDIOC_QUERYBUF:
- {
- struct v4l2_buffer *buf = arg;
+ return 0;
+}
- return uvc_query_buffer(&video->queue, buf);
- }
+static int
+uvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct uvc_device *uvc = video_get_drvdata(vdev);
+ struct uvc_video *video = &uvc->video;
- case VIDIOC_QBUF:
- if ((ret = uvc_queue_buffer(&video->queue, arg)) < 0)
- return ret;
+ if (type != video->queue.queue.type)
+ return -EINVAL;
- return uvc_video_pump(video);
+ return uvcg_video_enable(video, 0);
+}
- case VIDIOC_DQBUF:
- return uvc_dequeue_buffer(&video->queue, arg,
- file->f_flags & O_NONBLOCK);
+static int
+uvc_v4l2_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST)
+ return -EINVAL;
- case VIDIOC_STREAMON:
- {
- int *type = arg;
+ return v4l2_event_subscribe(fh, sub, 2, NULL);
+}
- if (*type != video->queue.queue.type)
- return -EINVAL;
+static int
+uvc_v4l2_unsubscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ return v4l2_event_unsubscribe(fh, sub);
+}
- /* Enable UVC video. */
- ret = uvc_video_enable(video, 1);
- if (ret < 0)
- return ret;
+static long
+uvc_v4l2_ioctl_default(struct file *file, void *fh, bool valid_prio,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct uvc_device *uvc = video_get_drvdata(vdev);
- /*
- * Complete the alternate setting selection setup phase now that
- * userspace is ready to provide video frames.
- */
- uvc_function_setup_continue(uvc);
- uvc->state = UVC_STATE_STREAMING;
+ switch (cmd) {
+ case UVCIOC_SEND_RESPONSE:
+ return uvc_send_response(uvc, arg);
- return 0;
+ default:
+ return -ENOIOCTLCMD;
}
+}
- case VIDIOC_STREAMOFF:
- {
- int *type = arg;
-
- if (*type != video->queue.queue.type)
- return -EINVAL;
+const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops = {
+ .vidioc_querycap = uvc_v4l2_querycap,
+ .vidioc_g_fmt_vid_out = uvc_v4l2_get_format,
+ .vidioc_s_fmt_vid_out = uvc_v4l2_set_format,
+ .vidioc_reqbufs = uvc_v4l2_reqbufs,
+ .vidioc_querybuf = uvc_v4l2_querybuf,
+ .vidioc_qbuf = uvc_v4l2_qbuf,
+ .vidioc_dqbuf = uvc_v4l2_dqbuf,
+ .vidioc_streamon = uvc_v4l2_streamon,
+ .vidioc_streamoff = uvc_v4l2_streamoff,
+ .vidioc_subscribe_event = uvc_v4l2_subscribe_event,
+ .vidioc_unsubscribe_event = uvc_v4l2_unsubscribe_event,
+ .vidioc_default = uvc_v4l2_ioctl_default,
+};
- return uvc_video_enable(video, 0);
- }
+/* --------------------------------------------------------------------------
+ * V4L2
+ */
- /* Events */
- case VIDIOC_DQEVENT:
- {
- struct v4l2_event *event = arg;
-
- ret = v4l2_event_dequeue(&handle->vfh, event,
- file->f_flags & O_NONBLOCK);
- if (ret == 0 && event->type == UVC_EVENT_SETUP) {
- struct uvc_event *uvc_event = (void *)&event->u.data;
-
- /* Tell the complete callback to generate an event for
- * the next request that will be enqueued by
- * uvc_event_write.
- */
- uvc->event_setup_out =
- !(uvc_event->req.bRequestType & USB_DIR_IN);
- uvc->event_length = uvc_event->req.wLength;
- }
+static int
+uvc_v4l2_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct uvc_device *uvc = video_get_drvdata(vdev);
+ struct uvc_file_handle *handle;
- return ret;
- }
+ handle = kzalloc(sizeof(*handle), GFP_KERNEL);
+ if (handle == NULL)
+ return -ENOMEM;
- case VIDIOC_SUBSCRIBE_EVENT:
- {
- struct v4l2_event_subscription *sub = arg;
+ v4l2_fh_init(&handle->vfh, vdev);
+ v4l2_fh_add(&handle->vfh);
- if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST)
- return -EINVAL;
+ handle->device = &uvc->video;
+ file->private_data = &handle->vfh;
- return v4l2_event_subscribe(&handle->vfh, arg, 2, NULL);
- }
+ uvc_function_connect(uvc);
+ return 0;
+}
- case VIDIOC_UNSUBSCRIBE_EVENT:
- return v4l2_event_unsubscribe(&handle->vfh, arg);
+static int
+uvc_v4l2_release(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct uvc_device *uvc = video_get_drvdata(vdev);
+ struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data);
+ struct uvc_video *video = handle->device;
- case UVCIOC_SEND_RESPONSE:
- ret = uvc_send_response(uvc, arg);
- break;
+ uvc_function_disconnect(uvc);
- default:
- return -ENOIOCTLCMD;
- }
+ uvcg_video_enable(video, 0);
+ uvcg_free_buffers(&video->queue);
- return ret;
-}
+ file->private_data = NULL;
+ v4l2_fh_del(&handle->vfh);
+ v4l2_fh_exit(&handle->vfh);
+ kfree(handle);
-static long
-uvc_v4l2_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl);
+ return 0;
}
static int
@@ -327,7 +328,7 @@ uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
- return uvc_queue_mmap(&uvc->video.queue, vma);
+ return uvcg_queue_mmap(&uvc->video.queue, vma);
}
static unsigned int
@@ -336,30 +337,30 @@ uvc_v4l2_poll(struct file *file, poll_table *wait)
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
- return uvc_queue_poll(&uvc->video.queue, file, wait);
+ return uvcg_queue_poll(&uvc->video.queue, file, wait);
}
#ifndef CONFIG_MMU
-static unsigned long uvc_v4l2_get_unmapped_area(struct file *file,
+static unsigned long uvcg_v4l2_get_unmapped_area(struct file *file,
unsigned long addr, unsigned long len, unsigned long pgoff,
unsigned long flags)
{
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
- return uvc_queue_get_unmapped_area(&uvc->video.queue, pgoff);
+ return uvcg_queue_get_unmapped_area(&uvc->video.queue, pgoff);
}
#endif
-static struct v4l2_file_operations uvc_v4l2_fops = {
+struct v4l2_file_operations uvc_v4l2_fops = {
.owner = THIS_MODULE,
.open = uvc_v4l2_open,
.release = uvc_v4l2_release,
- .ioctl = uvc_v4l2_ioctl,
+ .ioctl = video_ioctl2,
.mmap = uvc_v4l2_mmap,
.poll = uvc_v4l2_poll,
#ifndef CONFIG_MMU
- .get_unmapped_area = uvc_v4l2_get_unmapped_area,
+ .get_unmapped_area = uvcg_v4l2_get_unmapped_area,
#endif
};
diff --git a/drivers/usb/gadget/function/uvc_v4l2.h b/drivers/usb/gadget/function/uvc_v4l2.h
new file mode 100644
index 000000000000..2683b92fda65
--- /dev/null
+++ b/drivers/usb/gadget/function/uvc_v4l2.h
@@ -0,0 +1,22 @@
+/*
+ * uvc_v4l2.h -- USB Video Class Gadget driver
+ *
+ * Copyright (C) 2009-2010
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * Copyright (c) 2013 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_V4L2_H__
+#define __UVC_V4L2_H__
+
+extern const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops;
+extern struct v4l2_file_operations uvc_v4l2_fops;
+
+#endif /* __UVC_V4L2_H__ */
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index 71e896d4c5ae..9cb86bc1a9a5 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -15,6 +15,7 @@
#include <linux/errno.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
+#include <linux/usb/video.h>
#include <media/v4l2-dev.h>
@@ -85,7 +86,7 @@ uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video,
if (buf->bytesused == video->queue.buf_used) {
video->queue.buf_used = 0;
buf->state = UVC_BUF_STATE_DONE;
- uvc_queue_next_buffer(&video->queue, buf);
+ uvcg_queue_next_buffer(&video->queue, buf);
video->fid ^= UVC_STREAM_FID;
video->payload_size = 0;
@@ -118,7 +119,7 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video,
if (buf->bytesused == video->queue.buf_used) {
video->queue.buf_used = 0;
buf->state = UVC_BUF_STATE_DONE;
- uvc_queue_next_buffer(&video->queue, buf);
+ uvcg_queue_next_buffer(&video->queue, buf);
video->fid ^= UVC_STREAM_FID;
}
}
@@ -171,19 +172,19 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
break;
case -ESHUTDOWN: /* disconnect from host. */
- printk(KERN_INFO "VS request cancelled.\n");
- uvc_queue_cancel(queue, 1);
+ printk(KERN_DEBUG "VS request cancelled.\n");
+ uvcg_queue_cancel(queue, 1);
goto requeue;
default:
printk(KERN_INFO "VS request completed with status %d.\n",
req->status);
- uvc_queue_cancel(queue, 0);
+ uvcg_queue_cancel(queue, 0);
goto requeue;
}
spin_lock_irqsave(&video->queue.irqlock, flags);
- buf = uvc_queue_head(&video->queue);
+ buf = uvcg_queue_head(&video->queue);
if (buf == NULL) {
spin_unlock_irqrestore(&video->queue.irqlock, flags);
goto requeue;
@@ -195,6 +196,7 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
printk(KERN_INFO "Failed to queue request (%d).\n", ret);
usb_ep_set_halt(ep);
spin_unlock_irqrestore(&video->queue.irqlock, flags);
+ uvcg_queue_cancel(queue, 0);
goto requeue;
}
spin_unlock_irqrestore(&video->queue.irqlock, flags);
@@ -273,20 +275,20 @@ error:
*/
/*
- * uvc_video_pump - Pump video data into the USB requests
+ * uvcg_video_pump - Pump video data into the USB requests
*
* This function fills the available USB requests (listed in req_free) with
* video data from the queued buffers.
*/
-static int
-uvc_video_pump(struct uvc_video *video)
+int uvcg_video_pump(struct uvc_video *video)
{
+ struct uvc_video_queue *queue = &video->queue;
struct usb_request *req;
struct uvc_buffer *buf;
unsigned long flags;
int ret;
- /* FIXME TODO Race between uvc_video_pump and requests completion
+ /* FIXME TODO Race between uvcg_video_pump and requests completion
* handler ???
*/
@@ -307,10 +309,10 @@ uvc_video_pump(struct uvc_video *video)
/* Retrieve the first available video buffer and fill the
* request, protected by the video queue irqlock.
*/
- spin_lock_irqsave(&video->queue.irqlock, flags);
- buf = uvc_queue_head(&video->queue);
+ spin_lock_irqsave(&queue->irqlock, flags);
+ buf = uvcg_queue_head(queue);
if (buf == NULL) {
- spin_unlock_irqrestore(&video->queue.irqlock, flags);
+ spin_unlock_irqrestore(&queue->irqlock, flags);
break;
}
@@ -321,10 +323,11 @@ uvc_video_pump(struct uvc_video *video)
if (ret < 0) {
printk(KERN_INFO "Failed to queue request (%d)\n", ret);
usb_ep_set_halt(video->ep);
- spin_unlock_irqrestore(&video->queue.irqlock, flags);
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+ uvcg_queue_cancel(queue, 0);
break;
}
- spin_unlock_irqrestore(&video->queue.irqlock, flags);
+ spin_unlock_irqrestore(&queue->irqlock, flags);
}
spin_lock_irqsave(&video->req_lock, flags);
@@ -336,8 +339,7 @@ uvc_video_pump(struct uvc_video *video)
/*
* Enable or disable the video stream.
*/
-static int
-uvc_video_enable(struct uvc_video *video, int enable)
+int uvcg_video_enable(struct uvc_video *video, int enable)
{
unsigned int i;
int ret;
@@ -350,14 +352,15 @@ uvc_video_enable(struct uvc_video *video, int enable)
if (!enable) {
for (i = 0; i < UVC_NUM_REQUESTS; ++i)
- usb_ep_dequeue(video->ep, video->req[i]);
+ if (video->req[i])
+ usb_ep_dequeue(video->ep, video->req[i]);
uvc_video_free_requests(video);
- uvc_queue_enable(&video->queue, 0);
+ uvcg_queue_enable(&video->queue, 0);
return 0;
}
- if ((ret = uvc_queue_enable(&video->queue, 1)) < 0)
+ if ((ret = uvcg_queue_enable(&video->queue, 1)) < 0)
return ret;
if ((ret = uvc_video_alloc_requests(video)) < 0)
@@ -369,14 +372,13 @@ uvc_video_enable(struct uvc_video *video, int enable)
} else
video->encode = uvc_video_encode_isoc;
- return uvc_video_pump(video);
+ return uvcg_video_pump(video);
}
/*
* Initialize the UVC video stream.
*/
-static int
-uvc_video_init(struct uvc_video *video)
+int uvcg_video_init(struct uvc_video *video)
{
INIT_LIST_HEAD(&video->req_free);
spin_lock_init(&video->req_lock);
@@ -388,7 +390,7 @@ uvc_video_init(struct uvc_video *video)
video->imagesize = 320 * 240 * 2;
/* Initialize the video buffers queue. */
- uvc_queue_init(&video->queue, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ uvcg_queue_init(&video->queue, V4L2_BUF_TYPE_VIDEO_OUTPUT);
return 0;
}
diff --git a/drivers/usb/gadget/function/uvc_video.h b/drivers/usb/gadget/function/uvc_video.h
new file mode 100644
index 000000000000..ef00f06fa00b
--- /dev/null
+++ b/drivers/usb/gadget/function/uvc_video.h
@@ -0,0 +1,24 @@
+/*
+ * uvc_video.h -- USB Video Class Gadget driver
+ *
+ * Copyright (C) 2009-2010
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * Copyright (c) 2013 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_VIDEO_H__
+#define __UVC_VIDEO_H__
+
+int uvcg_video_pump(struct uvc_video *video);
+
+int uvcg_video_enable(struct uvc_video *video, int enable);
+
+int uvcg_video_init(struct uvc_video *video);
+
+#endif /* __UVC_VIDEO_H__ */
diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig
index aa376f006333..24392d269709 100644
--- a/drivers/usb/gadget/legacy/Kconfig
+++ b/drivers/usb/gadget/legacy/Kconfig
@@ -54,6 +54,8 @@ config USB_AUDIO
depends on SND
select USB_LIBCOMPOSITE
select SND_PCM
+ select USB_F_UAC1 if GADGET_UAC1
+ select USB_F_UAC2 if !GADGET_UAC1
help
This Gadget Audio driver is compatible with USB Audio Class
specification 2.0. It implements 1 AudioControl interface,
@@ -466,6 +468,7 @@ config USB_G_WEBCAM
depends on VIDEO_DEV
select USB_LIBCOMPOSITE
select VIDEOBUF2_VMALLOC
+ select USB_F_UVC
help
The Webcam Gadget acts as a composite USB Audio and Video Class
device. It provides a userspace API to process UVC control requests
diff --git a/drivers/usb/gadget/legacy/Makefile b/drivers/usb/gadget/legacy/Makefile
index a11aad5635df..7f485f25705e 100644
--- a/drivers/usb/gadget/legacy/Makefile
+++ b/drivers/usb/gadget/legacy/Makefile
@@ -2,9 +2,9 @@
# USB gadget drivers
#
-ccflags-y := -I$(PWD)/drivers/usb/gadget/
-ccflags-y += -I$(PWD)/drivers/usb/gadget/udc/
-ccflags-y += -I$(PWD)/drivers/usb/gadget/function/
+ccflags-y := -I$(srctree)/drivers/usb/gadget/
+ccflags-y += -I$(srctree)/drivers/usb/gadget/udc/
+ccflags-y += -I$(srctree)/drivers/usb/gadget/function/
g_zero-y := zero.o
g_audio-y := audio.o
diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c
index 6eb695e5e43a..f46a3956e43d 100644
--- a/drivers/usb/gadget/legacy/audio.c
+++ b/drivers/usb/gadget/legacy/audio.c
@@ -21,6 +21,66 @@
USB_GADGET_COMPOSITE_OPTIONS();
+#ifndef CONFIG_GADGET_UAC1
+#include "u_uac2.h"
+
+/* Playback(USB-IN) Default Stereo - Fl/Fr */
+static int p_chmask = UAC2_DEF_PCHMASK;
+module_param(p_chmask, uint, S_IRUGO);
+MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
+
+/* Playback Default 48 KHz */
+static int p_srate = UAC2_DEF_PSRATE;
+module_param(p_srate, uint, S_IRUGO);
+MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
+
+/* Playback Default 16bits/sample */
+static int p_ssize = UAC2_DEF_PSSIZE;
+module_param(p_ssize, uint, S_IRUGO);
+MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)");
+
+/* Capture(USB-OUT) Default Stereo - Fl/Fr */
+static int c_chmask = UAC2_DEF_CCHMASK;
+module_param(c_chmask, uint, S_IRUGO);
+MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
+
+/* Capture Default 64 KHz */
+static int c_srate = UAC2_DEF_CSRATE;
+module_param(c_srate, uint, S_IRUGO);
+MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
+
+/* Capture Default 16bits/sample */
+static int c_ssize = UAC2_DEF_CSSIZE;
+module_param(c_ssize, uint, S_IRUGO);
+MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
+#else
+#include "u_uac1.h"
+
+static char *fn_play = FILE_PCM_PLAYBACK;
+module_param(fn_play, charp, S_IRUGO);
+MODULE_PARM_DESC(fn_play, "Playback PCM device file name");
+
+static char *fn_cap = FILE_PCM_CAPTURE;
+module_param(fn_cap, charp, S_IRUGO);
+MODULE_PARM_DESC(fn_cap, "Capture PCM device file name");
+
+static char *fn_cntl = FILE_CONTROL;
+module_param(fn_cntl, charp, S_IRUGO);
+MODULE_PARM_DESC(fn_cntl, "Control device file name");
+
+static int req_buf_size = UAC1_OUT_EP_MAX_PACKET_SIZE;
+module_param(req_buf_size, int, S_IRUGO);
+MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size");
+
+static int req_count = UAC1_REQ_COUNT;
+module_param(req_count, int, S_IRUGO);
+MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count");
+
+static int audio_buf_size = UAC1_AUDIO_BUF_SIZE;
+module_param(audio_buf_size, int, S_IRUGO);
+MODULE_PARM_DESC(audio_buf_size, "Audio buffer size");
+#endif
+
/* string IDs are assigned dynamically */
static struct usb_string strings_dev[] = {
@@ -40,12 +100,12 @@ static struct usb_gadget_strings *audio_strings[] = {
NULL,
};
-#ifdef CONFIG_GADGET_UAC1
-#include "u_uac1.h"
-#include "u_uac1.c"
-#include "f_uac1.c"
+#ifndef CONFIG_GADGET_UAC1
+static struct usb_function_instance *fi_uac2;
+static struct usb_function *f_uac2;
#else
-#include "f_uac2.c"
+static struct usb_function_instance *fi_uac1;
+static struct usb_function *f_uac1;
#endif
/*-------------------------------------------------------------------------*/
@@ -109,6 +169,8 @@ static const struct usb_descriptor_header *otg_desc[] = {
static int __init audio_do_config(struct usb_configuration *c)
{
+ int status;
+
/* FIXME alloc iConfiguration string, set it in c->strings */
if (gadget_is_otg(c->cdev->gadget)) {
@@ -116,7 +178,31 @@ static int __init audio_do_config(struct usb_configuration *c)
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
- audio_bind_config(c);
+#ifdef CONFIG_GADGET_UAC1
+ f_uac1 = usb_get_function(fi_uac1);
+ if (IS_ERR(f_uac1)) {
+ status = PTR_ERR(f_uac1);
+ return status;
+ }
+
+ status = usb_add_function(c, f_uac1);
+ if (status < 0) {
+ usb_put_function(f_uac1);
+ return status;
+ }
+#else
+ f_uac2 = usb_get_function(fi_uac2);
+ if (IS_ERR(f_uac2)) {
+ status = PTR_ERR(f_uac2);
+ return status;
+ }
+
+ status = usb_add_function(c, f_uac2);
+ if (status < 0) {
+ usb_put_function(f_uac2);
+ return status;
+ }
+#endif
return 0;
}
@@ -126,17 +212,47 @@ static struct usb_configuration audio_config_driver = {
.bConfigurationValue = 1,
/* .iConfiguration = DYNAMIC */
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
-#ifndef CONFIG_GADGET_UAC1
- .unbind = uac2_unbind_config,
-#endif
};
/*-------------------------------------------------------------------------*/
static int __init audio_bind(struct usb_composite_dev *cdev)
{
+#ifndef CONFIG_GADGET_UAC1
+ struct f_uac2_opts *uac2_opts;
+#else
+ struct f_uac1_opts *uac1_opts;
+#endif
int status;
+#ifndef CONFIG_GADGET_UAC1
+ fi_uac2 = usb_get_function_instance("uac2");
+ if (IS_ERR(fi_uac2))
+ return PTR_ERR(fi_uac2);
+#else
+ fi_uac1 = usb_get_function_instance("uac1");
+ if (IS_ERR(fi_uac1))
+ return PTR_ERR(fi_uac1);
+#endif
+
+#ifndef CONFIG_GADGET_UAC1
+ uac2_opts = container_of(fi_uac2, struct f_uac2_opts, func_inst);
+ uac2_opts->p_chmask = p_chmask;
+ uac2_opts->p_srate = p_srate;
+ uac2_opts->p_ssize = p_ssize;
+ uac2_opts->c_chmask = c_chmask;
+ uac2_opts->c_srate = c_srate;
+ uac2_opts->c_ssize = c_ssize;
+#else
+ uac1_opts = container_of(fi_uac1, struct f_uac1_opts, func_inst);
+ uac1_opts->fn_play = fn_play;
+ uac1_opts->fn_cap = fn_cap;
+ uac1_opts->fn_cntl = fn_cntl;
+ uac1_opts->req_buf_size = req_buf_size;
+ uac1_opts->req_count = req_count;
+ uac1_opts->audio_buf_size = audio_buf_size;
+#endif
+
status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
goto fail;
@@ -152,13 +268,26 @@ static int __init audio_bind(struct usb_composite_dev *cdev)
return 0;
fail:
+#ifndef CONFIG_GADGET_UAC1
+ usb_put_function_instance(fi_uac2);
+#else
+ usb_put_function_instance(fi_uac1);
+#endif
return status;
}
static int __exit audio_unbind(struct usb_composite_dev *cdev)
{
#ifdef CONFIG_GADGET_UAC1
- gaudio_cleanup();
+ if (!IS_ERR_OR_NULL(f_uac1))
+ usb_put_function(f_uac1);
+ if (!IS_ERR_OR_NULL(fi_uac1))
+ usb_put_function_instance(fi_uac1);
+#else
+ if (!IS_ERR_OR_NULL(f_uac2))
+ usb_put_function(f_uac2);
+ if (!IS_ERR_OR_NULL(fi_uac2))
+ usb_put_function_instance(fi_uac2);
#endif
return 0;
}
diff --git a/drivers/usb/gadget/legacy/dbgp.c b/drivers/usb/gadget/legacy/dbgp.c
index 986fc511a2ed..1b075132f8f1 100644
--- a/drivers/usb/gadget/legacy/dbgp.c
+++ b/drivers/usb/gadget/legacy/dbgp.c
@@ -222,10 +222,12 @@ static void dbgp_unbind(struct usb_gadget *gadget)
{
#ifdef CONFIG_USB_G_DBGP_SERIAL
kfree(dbgp.serial);
+ dbgp.serial = NULL;
#endif
if (dbgp.req) {
kfree(dbgp.req->buf);
usb_ep_free_request(gadget->ep0, dbgp.req);
+ dbgp.req = NULL;
}
gadget->ep0->driver_data = NULL;
@@ -408,6 +410,7 @@ static __refdata struct usb_gadget_driver dbgp_driver = {
.bind = dbgp_bind,
.unbind = dbgp_unbind,
.setup = dbgp_setup,
+ .reset = dbgp_disconnect,
.disconnect = dbgp_disconnect,
.driver = {
.owner = THIS_MODULE,
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index 2e4ce7704908..c744e4975d74 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -198,7 +198,6 @@ struct ep_data {
struct list_head epfiles;
wait_queue_head_t wait;
struct dentry *dentry;
- struct inode *inode;
};
static inline void get_ep (struct ep_data *data)
@@ -440,7 +439,7 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
value = -ENOMEM;
kbuf = memdup_user(buf, len);
- if (!kbuf) {
+ if (IS_ERR(kbuf)) {
value = PTR_ERR(kbuf);
goto free1;
}
@@ -1618,10 +1617,9 @@ static void destroy_ep_files (struct dev_data *dev)
}
-static struct inode *
+static struct dentry *
gadgetfs_create_file (struct super_block *sb, char const *name,
- void *data, const struct file_operations *fops,
- struct dentry **dentry_p);
+ void *data, const struct file_operations *fops);
static int activate_ep_files (struct dev_data *dev)
{
@@ -1649,10 +1647,9 @@ static int activate_ep_files (struct dev_data *dev)
if (!data->req)
goto enomem1;
- data->inode = gadgetfs_create_file (dev->sb, data->name,
- data, &ep_config_operations,
- &data->dentry);
- if (!data->inode)
+ data->dentry = gadgetfs_create_file (dev->sb, data->name,
+ data, &ep_config_operations);
+ if (!data->dentry)
goto enomem2;
list_add_tail (&data->epfiles, &dev->epfiles);
}
@@ -1775,6 +1772,7 @@ static struct usb_gadget_driver gadgetfs_driver = {
.bind = gadgetfs_bind,
.unbind = gadgetfs_unbind,
.setup = gadgetfs_setup,
+ .reset = gadgetfs_disconnect,
.disconnect = gadgetfs_disconnect,
.suspend = gadgetfs_suspend,
@@ -2011,10 +2009,9 @@ gadgetfs_make_inode (struct super_block *sb,
/* creates in fs root directory, so non-renamable and non-linkable.
* so inode and dentry are paired, until device reconfig.
*/
-static struct inode *
+static struct dentry *
gadgetfs_create_file (struct super_block *sb, char const *name,
- void *data, const struct file_operations *fops,
- struct dentry **dentry_p)
+ void *data, const struct file_operations *fops)
{
struct dentry *dentry;
struct inode *inode;
@@ -2030,8 +2027,7 @@ gadgetfs_create_file (struct super_block *sb, char const *name,
return NULL;
}
d_add (dentry, inode);
- *dentry_p = dentry;
- return inode;
+ return dentry;
}
static const struct super_operations gadget_fs_operations = {
@@ -2079,9 +2075,8 @@ gadgetfs_fill_super (struct super_block *sb, void *opts, int silent)
goto Enomem;
dev->sb = sb;
- if (!gadgetfs_create_file (sb, CHIP,
- dev, &dev_init_operations,
- &dev->dentry)) {
+ dev->dentry = gadgetfs_create_file(sb, CHIP, dev, &dev_init_operations);
+ if (!dev->dentry) {
put_dev(dev);
goto Enomem;
}
diff --git a/drivers/usb/gadget/legacy/webcam.c b/drivers/usb/gadget/legacy/webcam.c
index a11d8e420bfe..04a3da20f742 100644
--- a/drivers/usb/gadget/legacy/webcam.c
+++ b/drivers/usb/gadget/legacy/webcam.c
@@ -12,23 +12,31 @@
#include <linux/kernel.h>
#include <linux/device.h>
+#include <linux/module.h>
#include <linux/usb/video.h>
-#include "f_uvc.h"
-
-/*
- * Kbuild is not very cooperative with respect to linking separately
- * compiled library objects into one module. So for now we won't use
- * separate compilation ... ensuring init/exit sections work to shrink
- * the runtime footprint, and giving us at least some parts of what
- * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
- */
-#include "uvc_queue.c"
-#include "uvc_video.c"
-#include "uvc_v4l2.c"
-#include "f_uvc.c"
+#include "u_uvc.h"
USB_GADGET_COMPOSITE_OPTIONS();
+
+/*-------------------------------------------------------------------------*/
+
+/* module parameters specific to the Video streaming endpoint */
+static unsigned int streaming_interval = 1;
+module_param(streaming_interval, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(streaming_interval, "1 - 16");
+
+static unsigned int streaming_maxpacket = 1024;
+module_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(streaming_maxpacket, "1 - 1023 (FS), 1 - 3072 (hs/ss)");
+
+static unsigned int streaming_maxburst;
+module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)");
+
+static unsigned int trace;
+module_param(trace, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(trace, "Trace level bitmask");
/* --------------------------------------------------------------------------
* Device descriptor
*/
@@ -63,6 +71,9 @@ static struct usb_gadget_strings *webcam_device_strings[] = {
NULL,
};
+static struct usb_function_instance *fi_uvc;
+static struct usb_function *f_uvc;
+
static struct usb_device_descriptor webcam_device_descriptor = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
@@ -326,9 +337,17 @@ static const struct uvc_descriptor_header * const uvc_ss_streaming_cls[] = {
static int __init
webcam_config_bind(struct usb_configuration *c)
{
- return uvc_bind_config(c, uvc_fs_control_cls, uvc_ss_control_cls,
- uvc_fs_streaming_cls, uvc_hs_streaming_cls,
- uvc_ss_streaming_cls);
+ int status = 0;
+
+ f_uvc = usb_get_function(fi_uvc);
+ if (IS_ERR(f_uvc))
+ return PTR_ERR(f_uvc);
+
+ status = usb_add_function(c, f_uvc);
+ if (status < 0)
+ usb_put_function(f_uvc);
+
+ return status;
}
static struct usb_configuration webcam_config_driver = {
@@ -342,14 +361,36 @@ static struct usb_configuration webcam_config_driver = {
static int /* __init_or_exit */
webcam_unbind(struct usb_composite_dev *cdev)
{
+ if (!IS_ERR_OR_NULL(f_uvc))
+ usb_put_function(f_uvc);
+ if (!IS_ERR_OR_NULL(fi_uvc))
+ usb_put_function_instance(fi_uvc);
return 0;
}
static int __init
webcam_bind(struct usb_composite_dev *cdev)
{
+ struct f_uvc_opts *uvc_opts;
int ret;
+ fi_uvc = usb_get_function_instance("uvc");
+ if (IS_ERR(fi_uvc))
+ return PTR_ERR(fi_uvc);
+
+ uvc_opts = container_of(fi_uvc, struct f_uvc_opts, func_inst);
+
+ uvc_opts->streaming_interval = streaming_interval;
+ uvc_opts->streaming_maxpacket = streaming_maxpacket;
+ uvc_opts->streaming_maxburst = streaming_maxburst;
+ uvc_set_trace_param(trace);
+
+ uvc_opts->fs_control = uvc_fs_control_cls;
+ uvc_opts->ss_control = uvc_ss_control_cls;
+ uvc_opts->fs_streaming = uvc_fs_streaming_cls;
+ uvc_opts->hs_streaming = uvc_hs_streaming_cls;
+ uvc_opts->ss_streaming = uvc_ss_streaming_cls;
+
/* Allocate string descriptor numbers ... note that string contents
* can be overridden by the composite_dev glue.
*/
@@ -373,7 +414,7 @@ webcam_bind(struct usb_composite_dev *cdev)
return 0;
error:
- webcam_unbind(cdev);
+ usb_put_function_instance(fi_uvc);
return ret;
}
diff --git a/drivers/usb/gadget/legacy/zero.c b/drivers/usb/gadget/legacy/zero.c
index 5ee95152493c..ff97ac93ac03 100644
--- a/drivers/usb/gadget/legacy/zero.c
+++ b/drivers/usb/gadget/legacy/zero.c
@@ -68,6 +68,8 @@ 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,
};
/*-------------------------------------------------------------------------*/
@@ -266,6 +268,21 @@ 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;
@@ -301,6 +318,10 @@ 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 5151f947a4f5..217365d35a25 100644
--- a/drivers/usb/gadget/udc/Kconfig
+++ b/drivers/usb/gadget/udc/Kconfig
@@ -163,7 +163,7 @@ config USB_R8A66597
config USB_RENESAS_USBHS_UDC
tristate 'Renesas USBHS controller'
- depends on USB_RENESAS_USBHS
+ depends on USB_RENESAS_USBHS && HAS_DMA
help
Renesas USBHS is a discrete USB host and peripheral controller chip
that supports both full and high speed USB 2.0 data transfers.
@@ -332,7 +332,7 @@ config USB_GOKU
gadget drivers to also be dynamically linked.
config USB_EG20T
- tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC"
+ tristate "Intel QUARK X1000/EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC"
depends on PCI
help
This is a USB device driver for EG20T PCH.
@@ -353,6 +353,23 @@ config USB_EG20T
ML7213/ML7831 is companion chip for Intel Atom E6xx series.
ML7213/ML7831 is completely compatible for Intel EG20T PCH.
+ This driver can be used with Intel's Quark X1000 SOC platform
+
+config USB_GADGET_XILINX
+ tristate "Xilinx USB Driver"
+ depends on HAS_DMA
+ depends on OF || COMPILE_TEST
+ help
+ USB peripheral controller driver for Xilinx USB2 device.
+ Xilinx USB2 device is a soft IP which supports both full
+ and high speed USB 2.0 data transfers. It has seven configurable
+ endpoints(bulk or interrupt or isochronous), as well as
+ endpoint zero(for control transfers).
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "udc-xilinx" and force all
+ gadget drivers to also be dynamically linked.
+
#
# LAST -- dummy/emulated controller
#
diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile
index 4096122bb283..a7f4491593f1 100644
--- a/drivers/usb/gadget/udc/Makefile
+++ b/drivers/usb/gadget/udc/Makefile
@@ -29,3 +29,4 @@ obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o
obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o
obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o
obj-$(CONFIG_USB_GR_UDC) += gr_udc.o
+obj-$(CONFIG_USB_GADGET_XILINX) += udc-xilinx.o
diff --git a/drivers/usb/gadget/udc/amd5536udc.c b/drivers/usb/gadget/udc/amd5536udc.c
index 41b062eb4de0..3b9d13848a4f 100644
--- a/drivers/usb/gadget/udc/amd5536udc.c
+++ b/drivers/usb/gadget/udc/amd5536udc.c
@@ -841,7 +841,7 @@ __acquires(ep->dev->lock)
&req->req, req->req.length, ep->ep.name, sts);
spin_unlock(&dev->lock);
- req->req.complete(&ep->ep, &req->req);
+ usb_gadget_giveback_request(&ep->ep, &req->req);
spin_lock(&dev->lock);
ep->halted = halted;
}
diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c
index cfd18bcca723..9968f5331fe4 100644
--- a/drivers/usb/gadget/udc/at91_udc.c
+++ b/drivers/usb/gadget/udc/at91_udc.c
@@ -267,7 +267,7 @@ static void done(struct at91_ep *ep, struct at91_request *req, int status)
ep->stopped = 1;
spin_unlock(&udc->lock);
- req->req.complete(&ep->ep, &req->req);
+ usb_gadget_giveback_request(&ep->ep, &req->req);
spin_lock(&udc->lock);
ep->stopped = stopped;
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c
index 906e65f0e4fa..1529926e20a0 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.c
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.c
@@ -463,7 +463,7 @@ static void receive_data(struct usba_ep *ep)
list_del_init(&req->queue);
usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);
spin_unlock(&udc->lock);
- req->req.complete(&ep->ep, &req->req);
+ usb_gadget_giveback_request(&ep->ep, &req->req);
spin_lock(&udc->lock);
}
@@ -495,7 +495,7 @@ request_complete(struct usba_ep *ep, struct usba_request *req, int status)
ep->ep.name, req, req->req.status, req->req.actual);
spin_unlock(&udc->lock);
- req->req.complete(&ep->ep, &req->req);
+ usb_gadget_giveback_request(&ep->ep, &req->req);
spin_lock(&udc->lock);
}
@@ -1661,7 +1661,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
if (dma_status) {
int i;
- for (i = 1; i < USBA_NR_DMAS; i++)
+ for (i = 1; i <= USBA_NR_DMAS; i++)
if (dma_status & (1 << i))
usba_dma_irq(udc, &udc->usba_ep[i]);
}
diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c
index e969eb809a85..2235b8808700 100644
--- a/drivers/usb/gadget/udc/bcm63xx_udc.c
+++ b/drivers/usb/gadget/udc/bcm63xx_udc.c
@@ -1088,7 +1088,7 @@ static int bcm63xx_ep_disable(struct usb_ep *ep)
breq->req.status = -ESHUTDOWN;
spin_unlock_irqrestore(&udc->lock, flags);
- breq->req.complete(&iudma->bep->ep, &breq->req);
+ usb_gadget_giveback_request(&iudma->bep->ep, &breq->req);
spin_lock_irqsave(&udc->lock, flags);
}
}
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index 2b54955d3166..81dc5959e36b 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -258,7 +258,7 @@ static void nuke(struct dummy *dum, struct dummy_ep *ep)
req->req.status = -ESHUTDOWN;
spin_unlock(&dum->lock);
- req->req.complete(&ep->ep, &req->req);
+ usb_gadget_giveback_request(&ep->ep, &req->req);
spin_lock(&dum->lock);
}
}
@@ -658,7 +658,7 @@ static int dummy_queue(struct usb_ep *_ep, struct usb_request *_req,
spin_unlock(&dum->lock);
_req->actual = _req->length;
_req->status = 0;
- _req->complete(_ep, _req);
+ usb_gadget_giveback_request(_ep, _req);
spin_lock(&dum->lock);
} else
list_add_tail(&req->queue, &ep->queue);
@@ -702,7 +702,7 @@ static int dummy_dequeue(struct usb_ep *_ep, struct usb_request *_req)
dev_dbg(udc_dev(dum),
"dequeued req %p from %s, len %d buf %p\n",
req, _ep->name, _req->length, _req->buf);
- _req->complete(_ep, _req);
+ usb_gadget_giveback_request(_ep, _req);
}
local_irq_restore(flags);
return retval;
@@ -1385,7 +1385,7 @@ top:
list_del_init(&req->queue);
spin_unlock(&dum->lock);
- req->req.complete(&ep->ep, &req->req);
+ usb_gadget_giveback_request(&ep->ep, &req->req);
spin_lock(&dum->lock);
/* requests might have been unlinked... */
@@ -1761,7 +1761,7 @@ restart:
req);
spin_unlock(&dum->lock);
- req->req.complete(&ep->ep, &req->req);
+ usb_gadget_giveback_request(&ep->ep, &req->req);
spin_lock(&dum->lock);
ep->already_seen = 0;
goto restart;
diff --git a/drivers/usb/gadget/udc/fotg210-udc.c b/drivers/usb/gadget/udc/fotg210-udc.c
index e143d69f6017..1d315921bf34 100644
--- a/drivers/usb/gadget/udc/fotg210-udc.c
+++ b/drivers/usb/gadget/udc/fotg210-udc.c
@@ -70,7 +70,7 @@ static void fotg210_done(struct fotg210_ep *ep, struct fotg210_request *req,
req->req.status = status;
spin_unlock(&ep->fotg210->lock);
- req->req.complete(&ep->ep, &req->req);
+ usb_gadget_giveback_request(&ep->ep, &req->req);
spin_lock(&ep->fotg210->lock);
if (ep->epnum) {
diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c
index 732430804841..dd18ea38e391 100644
--- a/drivers/usb/gadget/udc/fsl_qe_udc.c
+++ b/drivers/usb/gadget/udc/fsl_qe_udc.c
@@ -118,10 +118,7 @@ static void done(struct qe_ep *ep, struct qe_req *req, int status)
ep->stopped = 1;
spin_unlock(&udc->lock);
- /* this complete() should a func implemented by gadget layer,
- * eg fsg->bulk_in_complete() */
- if (req->req.complete)
- req->req.complete(&ep->ep, &req->req);
+ usb_gadget_giveback_request(&ep->ep, &req->req);
spin_lock(&udc->lock);
@@ -2728,4 +2725,3 @@ module_platform_driver(udc_driver);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_LICENSE("GPL");
-
diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c
index 75b23ea077a7..c3620791a315 100644
--- a/drivers/usb/gadget/udc/fsl_udc_core.c
+++ b/drivers/usb/gadget/udc/fsl_udc_core.c
@@ -197,10 +197,8 @@ __acquires(ep->udc->lock)
ep->stopped = 1;
spin_unlock(&ep->udc->lock);
- /* complete() is from gadget layer,
- * eg fsg->bulk_in_complete() */
- if (req->req.complete)
- req->req.complete(&ep->ep, &req->req);
+
+ usb_gadget_giveback_request(&ep->ep, &req->req);
spin_lock(&ep->udc->lock);
ep->stopped = stopped;
diff --git a/drivers/usb/gadget/udc/fusb300_udc.c b/drivers/usb/gadget/udc/fusb300_udc.c
index d40255f784df..8286df72add4 100644
--- a/drivers/usb/gadget/udc/fusb300_udc.c
+++ b/drivers/usb/gadget/udc/fusb300_udc.c
@@ -876,7 +876,7 @@ static void done(struct fusb300_ep *ep, struct fusb300_request *req,
req->req.status = status;
spin_unlock(&ep->fusb300->lock);
- req->req.complete(&ep->ep, &req->req);
+ usb_gadget_giveback_request(&ep->ep, &req->req);
spin_lock(&ep->fusb300->lock);
if (ep->epnum) {
@@ -1398,13 +1398,17 @@ static int fusb300_probe(struct platform_device *pdev)
/* initialize udc */
fusb300 = kzalloc(sizeof(struct fusb300), GFP_KERNEL);
- if (fusb300 == NULL)
+ if (fusb300 == NULL) {
+ ret = -ENOMEM;
goto clean_up;
+ }
for (i = 0; i < FUSB300_MAX_NUM_EP; i++) {
_ep[i] = kzalloc(sizeof(struct fusb300_ep), GFP_KERNEL);
- if (_ep[i] == NULL)
+ if (_ep[i] == NULL) {
+ ret = -ENOMEM;
goto clean_up;
+ }
fusb300->ep[i] = _ep[i];
}
diff --git a/drivers/usb/gadget/udc/fusb300_udc.h b/drivers/usb/gadget/udc/fusb300_udc.h
index ae811d8d38b4..ad39f892d200 100644
--- a/drivers/usb/gadget/udc/fusb300_udc.h
+++ b/drivers/usb/gadget/udc/fusb300_udc.h
@@ -12,7 +12,7 @@
#ifndef __FUSB300_UDC_H__
-#define __FUSB300_UDC_H_
+#define __FUSB300_UDC_H__
#include <linux/kernel.h>
diff --git a/drivers/usb/gadget/udc/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c
index 6c85839e15ad..bf9c5ef8b56b 100644
--- a/drivers/usb/gadget/udc/goku_udc.c
+++ b/drivers/usb/gadget/udc/goku_udc.c
@@ -320,7 +320,7 @@ done(struct goku_ep *ep, struct goku_request *req, int status)
/* don't modify queue heads during completion callback */
ep->stopped = 1;
spin_unlock(&dev->lock);
- req->req.complete(&ep->ep, &req->req);
+ usb_gadget_giveback_request(&ep->ep, &req->req);
spin_lock(&dev->lock);
ep->stopped = stopped;
}
diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c
index 08df5c4f46ce..1b3048a6a2a3 100644
--- a/drivers/usb/gadget/udc/gr_udc.c
+++ b/drivers/usb/gadget/udc/gr_udc.c
@@ -318,8 +318,26 @@ static void gr_finish_request(struct gr_ep *ep, struct gr_request *req,
usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in);
gr_free_dma_desc_chain(dev, req);
- if (ep->is_in) /* For OUT, actual gets updated bit by bit */
+ if (ep->is_in) { /* For OUT, req->req.actual gets updated bit by bit */
req->req.actual = req->req.length;
+ } else if (req->oddlen && req->req.actual > req->evenlen) {
+ /*
+ * Copy to user buffer in this case where length was not evenly
+ * divisible by ep->ep.maxpacket and the last descriptor was
+ * actually used.
+ */
+ char *buftail = ((char *)req->req.buf + req->evenlen);
+
+ memcpy(buftail, ep->tailbuf, req->oddlen);
+
+ if (req->req.actual > req->req.length) {
+ /* We got more data than was requested */
+ dev_dbg(ep->dev->dev, "Overflow for ep %s\n",
+ ep->ep.name);
+ gr_dbgprint_request("OVFL", ep, req);
+ req->req.status = -EOVERFLOW;
+ }
+ }
if (!status) {
if (ep->is_in)
@@ -339,7 +357,7 @@ static void gr_finish_request(struct gr_ep *ep, struct gr_request *req,
} else if (req->req.complete) {
spin_unlock(&dev->lock);
- req->req.complete(&ep->ep, &req->req);
+ usb_gadget_giveback_request(&ep->ep, &req->req);
spin_lock(&dev->lock);
}
@@ -379,6 +397,15 @@ static void gr_start_dma(struct gr_ep *ep)
/* A descriptor should already have been allocated */
BUG_ON(!req->curr_desc);
+ /*
+ * The DMA controller can not handle smaller OUT buffers than
+ * ep->ep.maxpacket. It could lead to buffer overruns if an unexpectedly
+ * long packet are received. Therefore an internal bounce buffer gets
+ * used when such a request gets enabled.
+ */
+ if (!ep->is_in && req->oddlen)
+ req->last_desc->data = ep->tailbuf_paddr;
+
wmb(); /* Make sure all is settled before handing it over to DMA */
/* Set the descriptor pointer in the hardware */
@@ -480,11 +507,11 @@ static int gr_setup_out_desc_list(struct gr_ep *ep, struct gr_request *req,
dma_addr_t start = req->req.dma + bytes_used;
u16 size = min(bytes_left, ep->bytes_per_buffer);
- /* Should not happen however - gr_queue stops such lengths */
- if (size < ep->bytes_per_buffer)
- dev_warn(ep->dev->dev,
- "Buffer overrun risk: %u < %u bytes/buffer\n",
- size, ep->bytes_per_buffer);
+ if (size < ep->bytes_per_buffer) {
+ /* Prepare using bounce buffer */
+ req->evenlen = req->req.length - bytes_left;
+ req->oddlen = size;
+ }
ret = gr_add_dma_desc(ep, req, start, size, gfp_flags);
if (ret)
@@ -584,18 +611,6 @@ static int gr_queue(struct gr_ep *ep, struct gr_request *req, gfp_t gfp_flags)
return -EINVAL;
}
- /*
- * The DMA controller can not handle smaller OUT buffers than
- * maxpacket. It could lead to buffer overruns if unexpectedly long
- * packet are received.
- */
- if (!ep->is_in && (req->req.length % ep->ep.maxpacket) != 0) {
- dev_err(dev->dev,
- "OUT request length %d is not multiple of maxpacket\n",
- req->req.length);
- return -EMSGSIZE;
- }
-
if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) {
dev_err(dev->dev, "-ESHUTDOWN");
return -ESHUTDOWN;
@@ -1286,8 +1301,8 @@ static int gr_handle_out_ep(struct gr_ep *ep)
if (ctrl & GR_DESC_OUT_CTRL_SE)
req->setup = 1;
- if (len < ep->ep.maxpacket || req->req.actual == req->req.length) {
- /* Short packet or the expected size - we are done */
+ if (len < ep->ep.maxpacket || req->req.actual >= req->req.length) {
+ /* Short packet or >= expected size - we are done */
if ((ep == &dev->epo[0]) && (dev->ep0state == GR_EP0_OSTATUS)) {
/*
@@ -2015,6 +2030,11 @@ static int gr_ep_init(struct gr_udc *dev, int num, int is_in, u32 maxplimit)
}
list_add_tail(&ep->ep_list, &dev->ep_list);
+ ep->tailbuf = dma_alloc_coherent(dev->dev, ep->ep.maxpacket_limit,
+ &ep->tailbuf_paddr, GFP_ATOMIC);
+ if (!ep->tailbuf)
+ return -ENOMEM;
+
return 0;
}
@@ -2067,9 +2087,24 @@ static int gr_udc_init(struct gr_udc *dev)
return 0;
}
+static void gr_ep_remove(struct gr_udc *dev, int num, int is_in)
+{
+ struct gr_ep *ep;
+
+ if (is_in)
+ ep = &dev->epi[num];
+ else
+ ep = &dev->epo[num];
+
+ if (ep->tailbuf)
+ dma_free_coherent(dev->dev, ep->ep.maxpacket_limit,
+ ep->tailbuf, ep->tailbuf_paddr);
+}
+
static int gr_remove(struct platform_device *pdev)
{
struct gr_udc *dev = platform_get_drvdata(pdev);
+ int i;
if (dev->added)
usb_del_gadget_udc(&dev->gadget); /* Shuts everything down */
@@ -2084,6 +2119,11 @@ static int gr_remove(struct platform_device *pdev)
gr_free_request(&dev->epi[0].ep, &dev->ep0reqi->req);
gr_free_request(&dev->epo[0].ep, &dev->ep0reqo->req);
+ for (i = 0; i < dev->nepo; i++)
+ gr_ep_remove(dev, i, 0);
+ for (i = 0; i < dev->nepi; i++)
+ gr_ep_remove(dev, i, 1);
+
return 0;
}
static int gr_request_irq(struct gr_udc *dev, int irq)
@@ -2131,7 +2171,6 @@ static int gr_probe(struct platform_device *pdev)
dev->gadget.name = driver_name;
dev->gadget.max_speed = USB_SPEED_HIGH;
dev->gadget.ops = &gr_ops;
- dev->gadget.quirk_ep_out_aligned_size = true;
spin_lock_init(&dev->lock);
dev->regs = regs;
diff --git a/drivers/usb/gadget/udc/gr_udc.h b/drivers/usb/gadget/udc/gr_udc.h
index 8388897d9ec3..4297c4e8021f 100644
--- a/drivers/usb/gadget/udc/gr_udc.h
+++ b/drivers/usb/gadget/udc/gr_udc.h
@@ -156,6 +156,10 @@ struct gr_ep {
struct list_head queue;
struct list_head ep_list;
+
+ /* Bounce buffer for end of "odd" sized OUT requests */
+ void *tailbuf;
+ dma_addr_t tailbuf_paddr;
};
struct gr_request {
@@ -167,6 +171,9 @@ struct gr_request {
struct gr_dma_desc *curr_desc; /* Current descriptor */
struct gr_dma_desc *last_desc; /* Last in the chain */
+ u16 evenlen; /* Size of even length head (if oddlen != 0) */
+ u16 oddlen; /* Size of odd length tail if buffer length is "odd" */
+
u8 setup; /* Setup packet */
};
diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c
index 1629ad7dcb80..feab0bac8fdc 100644
--- a/drivers/usb/gadget/udc/lpc32xx_udc.c
+++ b/drivers/usb/gadget/udc/lpc32xx_udc.c
@@ -1479,7 +1479,7 @@ static void done(struct lpc32xx_ep *ep, struct lpc32xx_request *req, int status)
ep->req_pending = 0;
spin_unlock(&udc->lock);
- req->req.complete(&ep->ep, &req->req);
+ usb_gadget_giveback_request(&ep->ep, &req->req);
spin_lock(&udc->lock);
}
diff --git a/drivers/usb/gadget/udc/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c
index de88d33b44b2..898565687a8c 100644
--- a/drivers/usb/gadget/udc/m66592-udc.c
+++ b/drivers/usb/gadget/udc/m66592-udc.c
@@ -729,7 +729,7 @@ __acquires(m66592->lock)
restart = 1;
spin_unlock(&ep->m66592->lock);
- req->req.complete(&ep->ep, &req->req);
+ usb_gadget_giveback_request(&ep->ep, &req->req);
spin_lock(&ep->m66592->lock);
if (restart) {
diff --git a/drivers/usb/gadget/udc/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c
index 16248711c152..046a1f808b0d 100644
--- a/drivers/usb/gadget/udc/mv_u3d_core.c
+++ b/drivers/usb/gadget/udc/mv_u3d_core.c
@@ -222,12 +222,8 @@ void mv_u3d_done(struct mv_u3d_ep *ep, struct mv_u3d_req *req, int status)
}
spin_unlock(&ep->u3d->lock);
- /*
- * complete() is from gadget layer,
- * eg fsg->bulk_in_complete()
- */
- if (req->req.complete)
- req->req.complete(&ep->ep, &req->req);
+
+ usb_gadget_giveback_request(&ep->ep, &req->req);
spin_lock(&ep->u3d->lock);
}
diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c
index 040fb169b162..3c5db80ae325 100644
--- a/drivers/usb/gadget/udc/mv_udc_core.c
+++ b/drivers/usb/gadget/udc/mv_udc_core.c
@@ -248,12 +248,8 @@ static void done(struct mv_ep *ep, struct mv_req *req, int status)
ep->stopped = 1;
spin_unlock(&ep->udc->lock);
- /*
- * complete() is from gadget layer,
- * eg fsg->bulk_in_complete()
- */
- if (req->req.complete)
- req->req.complete(&ep->ep, &req->req);
+
+ usb_gadget_giveback_request(&ep->ep, &req->req);
spin_lock(&ep->udc->lock);
ep->stopped = stopped;
diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c
index 059cfe527982..84d7162a8022 100644
--- a/drivers/usb/gadget/udc/net2272.c
+++ b/drivers/usb/gadget/udc/net2272.c
@@ -394,7 +394,7 @@ net2272_done(struct net2272_ep *ep, struct net2272_request *req, int status)
/* don't modify queue heads during completion callback */
ep->stopped = 1;
spin_unlock(&dev->lock);
- req->req.complete(&ep->ep, &req->req);
+ usb_gadget_giveback_request(&ep->ep, &req->req);
spin_lock(&dev->lock);
ep->stopped = stopped;
}
diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c
index f4eac113690e..8d13337e2dde 100644
--- a/drivers/usb/gadget/udc/net2280.c
+++ b/drivers/usb/gadget/udc/net2280.c
@@ -928,7 +928,7 @@ done(struct net2280_ep *ep, struct net2280_request *req, int status)
/* don't modify queue heads during completion callback */
ep->stopped = 1;
spin_unlock(&dev->lock);
- req->req.complete(&ep->ep, &req->req);
+ usb_gadget_giveback_request(&ep->ep, &req->req);
spin_lock(&dev->lock);
ep->stopped = stopped;
}
@@ -3320,7 +3320,7 @@ static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
if (stat & tmp) {
writel(tmp, &dev->regs->irqstat1);
if ((((stat & BIT(ROOT_PORT_RESET_INTERRUPT)) &&
- (readl(&dev->usb->usbstat) & mask)) ||
+ ((readl(&dev->usb->usbstat) & mask) == 0)) ||
((readl(&dev->usb->usbctl) &
BIT(VBUS_PIN)) == 0)) &&
(dev->gadget.speed != USB_SPEED_UNKNOWN)) {
diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c
index e731373fd4d7..dcdfea46003b 100644
--- a/drivers/usb/gadget/udc/omap_udc.c
+++ b/drivers/usb/gadget/udc/omap_udc.c
@@ -315,7 +315,7 @@ done(struct omap_ep *ep, struct omap_req *req, int status)
/* don't modify queue heads during completion callback */
ep->stopped = 1;
spin_unlock(&ep->udc->lock);
- req->req.complete(&ep->ep, &req->req);
+ usb_gadget_giveback_request(&ep->ep, &req->req);
spin_lock(&ep->udc->lock);
ep->stopped = stopped;
}
diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c
index eb8c3bedb57a..ccbe3d4a2a50 100644
--- a/drivers/usb/gadget/udc/pch_udc.c
+++ b/drivers/usb/gadget/udc/pch_udc.c
@@ -343,6 +343,7 @@ struct pch_vbus_gpio_data {
* @setup_data: Received setup data
* @phys_addr: of device memory
* @base_addr: for mapped device memory
+ * @bar: Indicates which PCI BAR for USB regs
* @irq: IRQ line for the device
* @cfg_data: current cfg, intf, and alt in use
* @vbus_gpio: GPIO informaton for detecting VBUS
@@ -370,14 +371,17 @@ struct pch_udc_dev {
struct usb_ctrlrequest setup_data;
unsigned long phys_addr;
void __iomem *base_addr;
+ unsigned bar;
unsigned irq;
struct pch_udc_cfg_data cfg_data;
struct pch_vbus_gpio_data vbus_gpio;
};
#define to_pch_udc(g) (container_of((g), struct pch_udc_dev, gadget))
+#define PCH_UDC_PCI_BAR_QUARK_X1000 0
#define PCH_UDC_PCI_BAR 1
#define PCI_DEVICE_ID_INTEL_EG20T_UDC 0x8808
+#define PCI_DEVICE_ID_INTEL_QUARK_X1000_UDC 0x0939
#define PCI_VENDOR_ID_ROHM 0x10DB
#define PCI_DEVICE_ID_ML7213_IOH_UDC 0x801D
#define PCI_DEVICE_ID_ML7831_IOH_UDC 0x8808
@@ -1486,7 +1490,7 @@ static void complete_req(struct pch_udc_ep *ep, struct pch_udc_request *req,
spin_unlock(&dev->lock);
if (!ep->in)
pch_udc_ep_clear_rrdy(ep);
- req->req.complete(&ep->ep, &req->req);
+ usb_gadget_giveback_request(&ep->ep, &req->req);
spin_lock(&dev->lock);
ep->halted = halted;
}
@@ -3076,7 +3080,7 @@ static void pch_udc_remove(struct pci_dev *pdev)
iounmap(dev->base_addr);
if (dev->mem_region)
release_mem_region(dev->phys_addr,
- pci_resource_len(pdev, PCH_UDC_PCI_BAR));
+ pci_resource_len(pdev, dev->bar));
if (dev->active)
pci_disable_device(pdev);
kfree(dev);
@@ -3144,9 +3148,15 @@ static int pch_udc_probe(struct pci_dev *pdev,
dev->active = 1;
pci_set_drvdata(pdev, dev);
+ /* Determine BAR based on PCI ID */
+ if (id->device == PCI_DEVICE_ID_INTEL_QUARK_X1000_UDC)
+ dev->bar = PCH_UDC_PCI_BAR_QUARK_X1000;
+ else
+ dev->bar = PCH_UDC_PCI_BAR;
+
/* PCI resource allocation */
- resource = pci_resource_start(pdev, 1);
- len = pci_resource_len(pdev, 1);
+ resource = pci_resource_start(pdev, dev->bar);
+ len = pci_resource_len(pdev, dev->bar);
if (!request_mem_region(resource, len, KBUILD_MODNAME)) {
dev_err(&pdev->dev, "%s: pci device used already\n", __func__);
@@ -3212,6 +3222,12 @@ finished:
static const struct pci_device_id pch_udc_pcidev_id[] = {
{
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_QUARK_X1000_UDC),
+ .class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe,
+ .class_mask = 0xffffffff,
+ },
+ {
PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EG20T_UDC),
.class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe,
.class_mask = 0xffffffff,
diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c
index 251e4d5ee152..42f7eeb8ff6f 100644
--- a/drivers/usb/gadget/udc/pxa25x_udc.c
+++ b/drivers/usb/gadget/udc/pxa25x_udc.c
@@ -347,7 +347,7 @@ static void done(struct pxa25x_ep *ep, struct pxa25x_request *req, int status)
/* don't modify queue heads during completion callback */
ep->stopped = 1;
- req->req.complete(&ep->ep, &req->req);
+ usb_gadget_giveback_request(&ep->ep, &req->req);
ep->stopped = stopped;
}
diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c
index 597d39f89420..4868369eeec6 100644
--- a/drivers/usb/gadget/udc/pxa27x_udc.c
+++ b/drivers/usb/gadget/udc/pxa27x_udc.c
@@ -758,7 +758,7 @@ static void req_done(struct pxa_ep *ep, struct pxa27x_request *req, int status,
if (pflags)
spin_unlock_irqrestore(&ep->lock, *pflags);
local_irq_save(flags);
- req->req.complete(&req->udc_usb_ep->usb_ep, &req->req);
+ usb_gadget_giveback_request(&req->udc_usb_ep->usb_ep, &req->req);
local_irq_restore(flags);
if (pflags)
spin_lock_irqsave(&ep->lock, *pflags);
diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c
index 46008421c1ec..f8186613b53e 100644
--- a/drivers/usb/gadget/udc/r8a66597-udc.c
+++ b/drivers/usb/gadget/udc/r8a66597-udc.c
@@ -430,7 +430,7 @@ static void r8a66597_ep_setting(struct r8a66597 *r8a66597,
ep->pipenum = pipenum;
ep->ep.maxpacket = usb_endpoint_maxp(desc);
r8a66597->pipenum2ep[pipenum] = ep;
- r8a66597->epaddr2ep[desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK]
+ r8a66597->epaddr2ep[usb_endpoint_num(desc)]
= ep;
INIT_LIST_HEAD(&ep->queue);
}
@@ -464,7 +464,7 @@ static int alloc_pipe_config(struct r8a66597_ep *ep,
if (ep->pipenum) /* already allocated pipe */
return 0;
- switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+ switch (usb_endpoint_type(desc)) {
case USB_ENDPOINT_XFER_BULK:
if (r8a66597->bulk >= R8A66597_MAX_NUM_BULK) {
if (r8a66597->isochronous >= R8A66597_MAX_NUM_ISOC) {
@@ -509,7 +509,7 @@ static int alloc_pipe_config(struct r8a66597_ep *ep,
}
ep->type = info.type;
- info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+ info.epnum = usb_endpoint_num(desc);
info.maxpacket = usb_endpoint_maxp(desc);
info.interval = desc->bInterval;
if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
@@ -925,7 +925,7 @@ __acquires(r8a66597->lock)
sudmac_free_channel(ep->r8a66597, ep, req);
spin_unlock(&ep->r8a66597->lock);
- req->req.complete(&ep->ep, &req->req);
+ usb_gadget_giveback_request(&ep->ep, &req->req);
spin_lock(&ep->r8a66597->lock);
if (restart) {
@@ -1846,10 +1846,8 @@ static int r8a66597_sudmac_ioremap(struct r8a66597 *r8a66597,
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sudmac");
r8a66597->sudmac_reg = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(r8a66597->sudmac_reg)) {
- dev_err(&pdev->dev, "ioremap error(sudmac).\n");
+ if (IS_ERR(r8a66597->sudmac_reg))
return PTR_ERR(r8a66597->sudmac_reg);
- }
return 0;
}
@@ -1868,8 +1866,8 @@ static int r8a66597_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
reg = devm_ioremap_resource(&pdev->dev, res);
- if (!reg)
- return -ENODEV;
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
irq = ires->start;
diff --git a/drivers/usb/gadget/udc/s3c-hsudc.c b/drivers/usb/gadget/udc/s3c-hsudc.c
index 10c6a128250c..dfbf55797360 100644
--- a/drivers/usb/gadget/udc/s3c-hsudc.c
+++ b/drivers/usb/gadget/udc/s3c-hsudc.c
@@ -258,8 +258,7 @@ static void s3c_hsudc_complete_request(struct s3c_hsudc_ep *hsep,
hsep->stopped = 1;
spin_unlock(&hsudc->lock);
- if (hsreq->req.complete != NULL)
- hsreq->req.complete(&hsep->ep, &hsreq->req);
+ usb_gadget_giveback_request(&hsep->ep, &hsreq->req);
spin_lock(&hsudc->lock);
hsep->stopped = stopped;
}
diff --git a/drivers/usb/gadget/udc/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c
index 357b58e0087b..ff423d15beff 100644
--- a/drivers/usb/gadget/udc/s3c2410_udc.c
+++ b/drivers/usb/gadget/udc/s3c2410_udc.c
@@ -272,7 +272,7 @@ static void s3c2410_udc_done(struct s3c2410_ep *ep,
status = req->req.status;
ep->halted = 1;
- req->req.complete(&ep->ep, &req->req);
+ usb_gadget_giveback_request(&ep->ep, &req->req);
ep->halted = halted;
}
diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c
index b0d98172bc07..f2054659f25b 100644
--- a/drivers/usb/gadget/udc/udc-core.c
+++ b/drivers/usb/gadget/udc/udc-core.c
@@ -27,6 +27,7 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
+#include <linux/usb.h>
/**
* struct usb_udc - describes one usb device controller
@@ -106,11 +107,42 @@ EXPORT_SYMBOL_GPL(usb_gadget_unmap_request);
/* ------------------------------------------------------------------------- */
+/**
+ * usb_gadget_giveback_request - give the request back to the gadget layer
+ * Context: in_interrupt()
+ *
+ * This is called by device controller drivers in order to return the
+ * completed request back to the gadget layer.
+ */
+void usb_gadget_giveback_request(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ if (likely(req->status == 0))
+ usb_led_activity(USB_LED_EVENT_GADGET);
+
+ req->complete(ep, req);
+}
+EXPORT_SYMBOL_GPL(usb_gadget_giveback_request);
+
+/* ------------------------------------------------------------------------- */
+
static void usb_gadget_state_work(struct work_struct *work)
{
struct usb_gadget *gadget = work_to_gadget(work);
+ struct usb_udc *udc = NULL;
+
+ mutex_lock(&udc_lock);
+ list_for_each_entry(udc, &udc_list, list)
+ if (udc->gadget == gadget)
+ goto found;
+ mutex_unlock(&udc_lock);
+
+ return;
+
+found:
+ mutex_unlock(&udc_lock);
- sysfs_notify(&gadget->dev.kobj, NULL, "state");
+ sysfs_notify(&udc->dev.kobj, NULL, "state");
}
void usb_gadget_set_state(struct usb_gadget *gadget,
@@ -124,6 +156,23 @@ EXPORT_SYMBOL_GPL(usb_gadget_set_state);
/* ------------------------------------------------------------------------- */
/**
+ * usb_gadget_udc_reset - notifies the udc core that bus reset occurs
+ * @gadget: The gadget which bus reset occurs
+ * @driver: The gadget driver we want to notify
+ *
+ * If the udc driver has bus reset handler, it needs to call this when the bus
+ * reset occurs, it notifies the gadget driver that the bus reset occurs as
+ * well as updates gadget state.
+ */
+void usb_gadget_udc_reset(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ driver->reset(gadget);
+ usb_gadget_set_state(gadget, USB_STATE_DEFAULT);
+}
+EXPORT_SYMBOL_GPL(usb_gadget_udc_reset);
+
+/**
* usb_gadget_udc_start - tells usb device controller to start up
* @gadget: The gadget we want to get started
* @driver: The driver we want to bind to @gadget
@@ -458,6 +507,11 @@ static ssize_t usb_udc_softconn_store(struct device *dev,
{
struct usb_udc *udc = container_of(dev, struct usb_udc, dev);
+ if (!udc->driver) {
+ dev_err(dev, "soft-connect without a gadget driver\n");
+ return -EOPNOTSUPP;
+ }
+
if (sysfs_streq(buf, "connect")) {
usb_gadget_udc_start(udc->gadget, udc->driver);
usb_gadget_connect(udc->gadget);
diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c
new file mode 100644
index 000000000000..ed27e1687a4e
--- /dev/null
+++ b/drivers/usb/gadget/udc/udc-xilinx.c
@@ -0,0 +1,2180 @@
+/*
+ * Xilinx USB peripheral controller driver
+ *
+ * Copyright (C) 2004 by Thomas Rathbone
+ * Copyright (C) 2005 by HP Labs
+ * Copyright (C) 2005 by David Brownell
+ * Copyright (C) 2010 - 2014 Xilinx, Inc.
+ *
+ * Some parts of this driver code is based on the driver for at91-series
+ * USB peripheral controller (at91_udc.c).
+ *
+ * This program is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * either version 2 of the License, or (at your option) any
+ * later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/prefetch.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+/* Register offsets for the USB device.*/
+#define XUSB_EP0_CONFIG_OFFSET 0x0000 /* EP0 Config Reg Offset */
+#define XUSB_SETUP_PKT_ADDR_OFFSET 0x0080 /* Setup Packet Address */
+#define XUSB_ADDRESS_OFFSET 0x0100 /* Address Register */
+#define XUSB_CONTROL_OFFSET 0x0104 /* Control Register */
+#define XUSB_STATUS_OFFSET 0x0108 /* Status Register */
+#define XUSB_FRAMENUM_OFFSET 0x010C /* Frame Number Register */
+#define XUSB_IER_OFFSET 0x0110 /* Interrupt Enable Register */
+#define XUSB_BUFFREADY_OFFSET 0x0114 /* Buffer Ready Register */
+#define XUSB_TESTMODE_OFFSET 0x0118 /* Test Mode Register */
+#define XUSB_DMA_RESET_OFFSET 0x0200 /* DMA Soft Reset Register */
+#define XUSB_DMA_CONTROL_OFFSET 0x0204 /* DMA Control Register */
+#define XUSB_DMA_DSAR_ADDR_OFFSET 0x0208 /* DMA source Address Reg */
+#define XUSB_DMA_DDAR_ADDR_OFFSET 0x020C /* DMA destination Addr Reg */
+#define XUSB_DMA_LENGTH_OFFSET 0x0210 /* DMA Length Register */
+#define XUSB_DMA_STATUS_OFFSET 0x0214 /* DMA Status Register */
+
+/* Endpoint Configuration Space offsets */
+#define XUSB_EP_CFGSTATUS_OFFSET 0x00 /* Endpoint Config Status */
+#define XUSB_EP_BUF0COUNT_OFFSET 0x08 /* Buffer 0 Count */
+#define XUSB_EP_BUF1COUNT_OFFSET 0x0C /* Buffer 1 Count */
+
+#define XUSB_CONTROL_USB_READY_MASK 0x80000000 /* USB ready Mask */
+#define XUSB_CONTROL_USB_RMTWAKE_MASK 0x40000000 /* Remote wake up mask */
+
+/* Interrupt register related masks.*/
+#define XUSB_STATUS_GLOBAL_INTR_MASK 0x80000000 /* Global Intr Enable */
+#define XUSB_STATUS_DMADONE_MASK 0x04000000 /* DMA done Mask */
+#define XUSB_STATUS_DMAERR_MASK 0x02000000 /* DMA Error Mask */
+#define XUSB_STATUS_DMABUSY_MASK 0x80000000 /* DMA Error Mask */
+#define XUSB_STATUS_RESUME_MASK 0x01000000 /* USB Resume Mask */
+#define XUSB_STATUS_RESET_MASK 0x00800000 /* USB Reset Mask */
+#define XUSB_STATUS_SUSPEND_MASK 0x00400000 /* USB Suspend Mask */
+#define XUSB_STATUS_DISCONNECT_MASK 0x00200000 /* USB Disconnect Mask */
+#define XUSB_STATUS_FIFO_BUFF_RDY_MASK 0x00100000 /* FIFO Buff Ready Mask */
+#define XUSB_STATUS_FIFO_BUFF_FREE_MASK 0x00080000 /* FIFO Buff Free Mask */
+#define XUSB_STATUS_SETUP_PACKET_MASK 0x00040000 /* Setup packet received */
+#define XUSB_STATUS_EP1_BUFF2_COMP_MASK 0x00000200 /* EP 1 Buff 2 Processed */
+#define XUSB_STATUS_EP1_BUFF1_COMP_MASK 0x00000002 /* EP 1 Buff 1 Processed */
+#define XUSB_STATUS_EP0_BUFF2_COMP_MASK 0x00000100 /* EP 0 Buff 2 Processed */
+#define XUSB_STATUS_EP0_BUFF1_COMP_MASK 0x00000001 /* EP 0 Buff 1 Processed */
+#define XUSB_STATUS_HIGH_SPEED_MASK 0x00010000 /* USB Speed Mask */
+/* Suspend,Reset,Suspend and Disconnect Mask */
+#define XUSB_STATUS_INTR_EVENT_MASK 0x01E00000
+/* Buffers completion Mask */
+#define XUSB_STATUS_INTR_BUFF_COMP_ALL_MASK 0x0000FEFF
+/* Mask for buffer 0 and buffer 1 completion for all Endpoints */
+#define XUSB_STATUS_INTR_BUFF_COMP_SHIFT_MASK 0x00000101
+#define XUSB_STATUS_EP_BUFF2_SHIFT 8 /* EP buffer offset */
+
+/* Endpoint Configuration Status Register */
+#define XUSB_EP_CFG_VALID_MASK 0x80000000 /* Endpoint Valid bit */
+#define XUSB_EP_CFG_STALL_MASK 0x40000000 /* Endpoint Stall bit */
+#define XUSB_EP_CFG_DATA_TOGGLE_MASK 0x08000000 /* Endpoint Data toggle */
+
+/* USB device specific global configuration constants.*/
+#define XUSB_MAX_ENDPOINTS 8 /* Maximum End Points */
+#define XUSB_EP_NUMBER_ZERO 0 /* End point Zero */
+/* DPRAM is the source address for DMA transfer */
+#define XUSB_DMA_READ_FROM_DPRAM 0x80000000
+#define XUSB_DMA_DMASR_BUSY 0x80000000 /* DMA busy */
+#define XUSB_DMA_DMASR_ERROR 0x40000000 /* DMA Error */
+/*
+ * When this bit is set, the DMA buffer ready bit is set by hardware upon
+ * DMA transfer completion.
+ */
+#define XUSB_DMA_BRR_CTRL 0x40000000 /* DMA bufready ctrl bit */
+/* Phase States */
+#define SETUP_PHASE 0x0000 /* Setup Phase */
+#define DATA_PHASE 0x0001 /* Data Phase */
+#define STATUS_PHASE 0x0002 /* Status Phase */
+
+#define EP0_MAX_PACKET 64 /* Endpoint 0 maximum packet length */
+#define STATUSBUFF_SIZE 2 /* Buffer size for GET_STATUS command */
+#define EPNAME_SIZE 4 /* Buffer size for endpoint name */
+
+/* container_of helper macros */
+#define to_udc(g) container_of((g), struct xusb_udc, gadget)
+#define to_xusb_ep(ep) container_of((ep), struct xusb_ep, ep_usb)
+#define to_xusb_req(req) container_of((req), struct xusb_req, usb_req)
+
+/**
+ * struct xusb_req - Xilinx USB device request structure
+ * @usb_req: Linux usb request structure
+ * @queue: usb device request queue
+ * @ep: pointer to xusb_endpoint structure
+ */
+struct xusb_req {
+ struct usb_request usb_req;
+ struct list_head queue;
+ struct xusb_ep *ep;
+};
+
+/**
+ * struct xusb_ep - USB end point structure.
+ * @ep_usb: usb endpoint instance
+ * @queue: endpoint message queue
+ * @udc: xilinx usb peripheral driver instance pointer
+ * @desc: pointer to the usb endpoint descriptor
+ * @rambase: the endpoint buffer address
+ * @offset: the endpoint register offset value
+ * @name: name of the endpoint
+ * @epnumber: endpoint number
+ * @maxpacket: maximum packet size the endpoint can store
+ * @buffer0count: the size of the packet recieved in the first buffer
+ * @buffer1count: the size of the packet received in the second buffer
+ * @curbufnum: current buffer of endpoint that will be processed next
+ * @buffer0ready: the busy state of first buffer
+ * @buffer1ready: the busy state of second buffer
+ * @is_in: endpoint direction (IN or OUT)
+ * @is_iso: endpoint type(isochronous or non isochronous)
+ */
+struct xusb_ep {
+ struct usb_ep ep_usb;
+ struct list_head queue;
+ struct xusb_udc *udc;
+ const struct usb_endpoint_descriptor *desc;
+ u32 rambase;
+ u32 offset;
+ char name[4];
+ u16 epnumber;
+ u16 maxpacket;
+ u16 buffer0count;
+ u16 buffer1count;
+ u8 curbufnum;
+ bool buffer0ready;
+ bool buffer1ready;
+ bool is_in;
+ bool is_iso;
+};
+
+/**
+ * struct xusb_udc - USB peripheral driver structure
+ * @gadget: USB gadget driver instance
+ * @ep: an array of endpoint structures
+ * @driver: pointer to the usb gadget driver instance
+ * @setup: usb_ctrlrequest structure for control requests
+ * @req: pointer to dummy request for get status command
+ * @dev: pointer to device structure in gadget
+ * @usb_state: device in suspended state or not
+ * @remote_wkp: remote wakeup enabled by host
+ * @setupseqtx: tx status
+ * @setupseqrx: rx status
+ * @addr: the usb device base address
+ * @lock: instance of spinlock
+ * @dma_enabled: flag indicating whether the dma is included in the system
+ * @read_fn: function pointer to read device registers
+ * @write_fn: function pointer to write to device registers
+ */
+struct xusb_udc {
+ struct usb_gadget gadget;
+ struct xusb_ep ep[8];
+ struct usb_gadget_driver *driver;
+ struct usb_ctrlrequest setup;
+ struct xusb_req *req;
+ struct device *dev;
+ u32 usb_state;
+ u32 remote_wkp;
+ u32 setupseqtx;
+ u32 setupseqrx;
+ void __iomem *addr;
+ spinlock_t lock;
+ bool dma_enabled;
+
+ unsigned int (*read_fn)(void __iomem *);
+ void (*write_fn)(void __iomem *, u32, u32);
+};
+
+/* Endpoint buffer start addresses in the core */
+static u32 rambase[8] = { 0x22, 0x1000, 0x1100, 0x1200, 0x1300, 0x1400, 0x1500,
+ 0x1600 };
+
+static const char driver_name[] = "xilinx-udc";
+static const char ep0name[] = "ep0";
+
+/* Control endpoint configuration.*/
+static const struct usb_endpoint_descriptor config_bulk_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(EP0_MAX_PACKET),
+};
+
+/**
+ * xudc_write32 - little endian write to device registers
+ * @addr: base addr of device registers
+ * @offset: register offset
+ * @val: data to be written
+ */
+static void xudc_write32(void __iomem *addr, u32 offset, u32 val)
+{
+ iowrite32(val, addr + offset);
+}
+
+/**
+ * xudc_read32 - little endian read from device registers
+ * @addr: addr of device register
+ * Return: value at addr
+ */
+static unsigned int xudc_read32(void __iomem *addr)
+{
+ return ioread32(addr);
+}
+
+/**
+ * xudc_write32_be - big endian write to device registers
+ * @addr: base addr of device registers
+ * @offset: register offset
+ * @val: data to be written
+ */
+static void xudc_write32_be(void __iomem *addr, u32 offset, u32 val)
+{
+ iowrite32be(val, addr + offset);
+}
+
+/**
+ * xudc_read32_be - big endian read from device registers
+ * @addr: addr of device register
+ * Return: value at addr
+ */
+static unsigned int xudc_read32_be(void __iomem *addr)
+{
+ return ioread32be(addr);
+}
+
+/**
+ * xudc_wrstatus - Sets up the usb device status stages.
+ * @udc: pointer to the usb device controller structure.
+ */
+static void xudc_wrstatus(struct xusb_udc *udc)
+{
+ struct xusb_ep *ep0 = &udc->ep[XUSB_EP_NUMBER_ZERO];
+ u32 epcfgreg;
+
+ epcfgreg = udc->read_fn(udc->addr + ep0->offset)|
+ XUSB_EP_CFG_DATA_TOGGLE_MASK;
+ udc->write_fn(udc->addr, ep0->offset, epcfgreg);
+ udc->write_fn(udc->addr, ep0->offset + XUSB_EP_BUF0COUNT_OFFSET, 0);
+ udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1);
+}
+
+/**
+ * xudc_epconfig - Configures the given endpoint.
+ * @ep: pointer to the usb device endpoint structure.
+ * @udc: pointer to the usb peripheral controller structure.
+ *
+ * This function configures a specific endpoint with the given configuration
+ * data.
+ */
+static void xudc_epconfig(struct xusb_ep *ep, struct xusb_udc *udc)
+{
+ u32 epcfgreg;
+
+ /*
+ * Configure the end point direction, type, Max Packet Size and the
+ * EP buffer location.
+ */
+ epcfgreg = ((ep->is_in << 29) | (ep->is_iso << 28) |
+ (ep->ep_usb.maxpacket << 15) | (ep->rambase));
+ udc->write_fn(udc->addr, ep->offset, epcfgreg);
+
+ /* Set the Buffer count and the Buffer ready bits.*/
+ udc->write_fn(udc->addr, ep->offset + XUSB_EP_BUF0COUNT_OFFSET,
+ ep->buffer0count);
+ udc->write_fn(udc->addr, ep->offset + XUSB_EP_BUF1COUNT_OFFSET,
+ ep->buffer1count);
+ if (ep->buffer0ready)
+ udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET,
+ 1 << ep->epnumber);
+ if (ep->buffer1ready)
+ udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET,
+ 1 << (ep->epnumber + XUSB_STATUS_EP_BUFF2_SHIFT));
+}
+
+/**
+ * xudc_start_dma - Starts DMA transfer.
+ * @ep: pointer to the usb device endpoint structure.
+ * @src: DMA source address.
+ * @dst: DMA destination address.
+ * @length: number of bytes to transfer.
+ *
+ * Return: 0 on success, error code on failure
+ *
+ * This function starts DMA transfer by writing to DMA source,
+ * destination and lenth registers.
+ */
+static int xudc_start_dma(struct xusb_ep *ep, dma_addr_t src,
+ dma_addr_t dst, u32 length)
+{
+ struct xusb_udc *udc = ep->udc;
+ int rc = 0;
+ u32 timeout = 500;
+ u32 reg;
+
+ /*
+ * Set the addresses in the DMA source and
+ * destination registers and then set the length
+ * into the DMA length register.
+ */
+ udc->write_fn(udc->addr, XUSB_DMA_DSAR_ADDR_OFFSET, src);
+ udc->write_fn(udc->addr, XUSB_DMA_DDAR_ADDR_OFFSET, dst);
+ udc->write_fn(udc->addr, XUSB_DMA_LENGTH_OFFSET, length);
+
+ /*
+ * Wait till DMA transaction is complete and
+ * check whether the DMA transaction was
+ * successful.
+ */
+ do {
+ reg = udc->read_fn(udc->addr + XUSB_DMA_STATUS_OFFSET);
+ if (!(reg & XUSB_DMA_DMASR_BUSY))
+ break;
+
+ /*
+ * We can't sleep here, because it's also called from
+ * interrupt context.
+ */
+ timeout--;
+ if (!timeout) {
+ dev_err(udc->dev, "DMA timeout\n");
+ return -ETIMEDOUT;
+ }
+ udelay(1);
+ } while (1);
+
+ if ((udc->read_fn(udc->addr + XUSB_DMA_STATUS_OFFSET) &
+ XUSB_DMA_DMASR_ERROR) == XUSB_DMA_DMASR_ERROR){
+ dev_err(udc->dev, "DMA Error\n");
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+/**
+ * xudc_dma_send - Sends IN data using DMA.
+ * @ep: pointer to the usb device endpoint structure.
+ * @req: pointer to the usb request structure.
+ * @buffer: pointer to data to be sent.
+ * @length: number of bytes to send.
+ *
+ * Return: 0 on success, -EAGAIN if no buffer is free and error
+ * code on failure.
+ *
+ * This function sends data using DMA.
+ */
+static int xudc_dma_send(struct xusb_ep *ep, struct xusb_req *req,
+ u8 *buffer, u32 length)
+{
+ u32 *eprambase;
+ dma_addr_t src;
+ dma_addr_t dst;
+ struct xusb_udc *udc = ep->udc;
+
+ src = req->usb_req.dma + req->usb_req.actual;
+ if (req->usb_req.length)
+ dma_sync_single_for_device(udc->dev, src,
+ length, DMA_TO_DEVICE);
+ if (!ep->curbufnum && !ep->buffer0ready) {
+ /* Get the Buffer address and copy the transmit data.*/
+ eprambase = (u32 __force *)(udc->addr + ep->rambase);
+ dst = virt_to_phys(eprambase);
+ udc->write_fn(udc->addr, ep->offset +
+ XUSB_EP_BUF0COUNT_OFFSET, length);
+ udc->write_fn(udc->addr, XUSB_DMA_CONTROL_OFFSET,
+ XUSB_DMA_BRR_CTRL | (1 << ep->epnumber));
+ ep->buffer0ready = 1;
+ ep->curbufnum = 1;
+ } else if (ep->curbufnum && !ep->buffer1ready) {
+ /* Get the Buffer address and copy the transmit data.*/
+ eprambase = (u32 __force *)(udc->addr + ep->rambase +
+ ep->ep_usb.maxpacket);
+ dst = virt_to_phys(eprambase);
+ udc->write_fn(udc->addr, ep->offset +
+ XUSB_EP_BUF1COUNT_OFFSET, length);
+ udc->write_fn(udc->addr, XUSB_DMA_CONTROL_OFFSET,
+ XUSB_DMA_BRR_CTRL | (1 << (ep->epnumber +
+ XUSB_STATUS_EP_BUFF2_SHIFT)));
+ ep->buffer1ready = 1;
+ ep->curbufnum = 0;
+ } else {
+ /* None of ping pong buffers are ready currently .*/
+ return -EAGAIN;
+ }
+
+ return xudc_start_dma(ep, src, dst, length);
+}
+
+/**
+ * xudc_dma_receive - Receives OUT data using DMA.
+ * @ep: pointer to the usb device endpoint structure.
+ * @req: pointer to the usb request structure.
+ * @buffer: pointer to storage buffer of received data.
+ * @length: number of bytes to receive.
+ *
+ * Return: 0 on success, -EAGAIN if no buffer is free and error
+ * code on failure.
+ *
+ * This function receives data using DMA.
+ */
+static int xudc_dma_receive(struct xusb_ep *ep, struct xusb_req *req,
+ u8 *buffer, u32 length)
+{
+ u32 *eprambase;
+ dma_addr_t src;
+ dma_addr_t dst;
+ struct xusb_udc *udc = ep->udc;
+
+ dst = req->usb_req.dma + req->usb_req.actual;
+ if (!ep->curbufnum && !ep->buffer0ready) {
+ /* Get the Buffer address and copy the transmit data */
+ eprambase = (u32 __force *)(udc->addr + ep->rambase);
+ src = virt_to_phys(eprambase);
+ udc->write_fn(udc->addr, XUSB_DMA_CONTROL_OFFSET,
+ XUSB_DMA_BRR_CTRL | XUSB_DMA_READ_FROM_DPRAM |
+ (1 << ep->epnumber));
+ ep->buffer0ready = 1;
+ ep->curbufnum = 1;
+ } else if (ep->curbufnum && !ep->buffer1ready) {
+ /* Get the Buffer address and copy the transmit data */
+ eprambase = (u32 __force *)(udc->addr +
+ ep->rambase + ep->ep_usb.maxpacket);
+ src = virt_to_phys(eprambase);
+ udc->write_fn(udc->addr, XUSB_DMA_CONTROL_OFFSET,
+ XUSB_DMA_BRR_CTRL | XUSB_DMA_READ_FROM_DPRAM |
+ (1 << (ep->epnumber +
+ XUSB_STATUS_EP_BUFF2_SHIFT)));
+ ep->buffer1ready = 1;
+ ep->curbufnum = 0;
+ } else {
+ /* None of the ping-pong buffers are ready currently */
+ return -EAGAIN;
+ }
+
+ return xudc_start_dma(ep, src, dst, length);
+}
+
+/**
+ * xudc_eptxrx - Transmits or receives data to or from an endpoint.
+ * @ep: pointer to the usb endpoint configuration structure.
+ * @req: pointer to the usb request structure.
+ * @bufferptr: pointer to buffer containing the data to be sent.
+ * @bufferlen: The number of data bytes to be sent.
+ *
+ * Return: 0 on success, -EAGAIN if no buffer is free.
+ *
+ * This function copies the transmit/receive data to/from the end point buffer
+ * and enables the buffer for transmission/reception.
+ */
+static int xudc_eptxrx(struct xusb_ep *ep, struct xusb_req *req,
+ u8 *bufferptr, u32 bufferlen)
+{
+ u32 *eprambase;
+ u32 bytestosend;
+ int rc = 0;
+ struct xusb_udc *udc = ep->udc;
+
+ bytestosend = bufferlen;
+ if (udc->dma_enabled) {
+ if (ep->is_in)
+ rc = xudc_dma_send(ep, req, bufferptr, bufferlen);
+ else
+ rc = xudc_dma_receive(ep, req, bufferptr, bufferlen);
+ return rc;
+ }
+ /* Put the transmit buffer into the correct ping-pong buffer.*/
+ if (!ep->curbufnum && !ep->buffer0ready) {
+ /* Get the Buffer address and copy the transmit data.*/
+ eprambase = (u32 __force *)(udc->addr + ep->rambase);
+ if (ep->is_in) {
+ memcpy(eprambase, bufferptr, bytestosend);
+ udc->write_fn(udc->addr, ep->offset +
+ XUSB_EP_BUF0COUNT_OFFSET, bufferlen);
+ } else {
+ memcpy(bufferptr, eprambase, bytestosend);
+ }
+ /*
+ * Enable the buffer for transmission.
+ */
+ udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET,
+ 1 << ep->epnumber);
+ ep->buffer0ready = 1;
+ ep->curbufnum = 1;
+ } else if (ep->curbufnum && !ep->buffer1ready) {
+ /* Get the Buffer address and copy the transmit data.*/
+ eprambase = (u32 __force *)(udc->addr + ep->rambase +
+ ep->ep_usb.maxpacket);
+ if (ep->is_in) {
+ memcpy(eprambase, bufferptr, bytestosend);
+ udc->write_fn(udc->addr, ep->offset +
+ XUSB_EP_BUF1COUNT_OFFSET, bufferlen);
+ } else {
+ memcpy(bufferptr, eprambase, bytestosend);
+ }
+ /*
+ * Enable the buffer for transmission.
+ */
+ udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET,
+ 1 << (ep->epnumber + XUSB_STATUS_EP_BUFF2_SHIFT));
+ ep->buffer1ready = 1;
+ ep->curbufnum = 0;
+ } else {
+ /* None of the ping-pong buffers are ready currently */
+ return -EAGAIN;
+ }
+ return rc;
+}
+
+/**
+ * xudc_done - Exeutes the endpoint data transfer completion tasks.
+ * @ep: pointer to the usb device endpoint structure.
+ * @req: pointer to the usb request structure.
+ * @status: Status of the data transfer.
+ *
+ * Deletes the message from the queue and updates data transfer completion
+ * status.
+ */
+static void xudc_done(struct xusb_ep *ep, struct xusb_req *req, int status)
+{
+ struct xusb_udc *udc = ep->udc;
+
+ list_del_init(&req->queue);
+
+ if (req->usb_req.status == -EINPROGRESS)
+ req->usb_req.status = status;
+ else
+ status = req->usb_req.status;
+
+ if (status && status != -ESHUTDOWN)
+ dev_dbg(udc->dev, "%s done %p, status %d\n",
+ ep->ep_usb.name, req, status);
+ /* unmap request if DMA is present*/
+ if (udc->dma_enabled && ep->epnumber && req->usb_req.length)
+ usb_gadget_unmap_request(&udc->gadget, &req->usb_req,
+ ep->is_in);
+
+ if (req->usb_req.complete) {
+ spin_unlock(&udc->lock);
+ req->usb_req.complete(&ep->ep_usb, &req->usb_req);
+ spin_lock(&udc->lock);
+ }
+}
+
+/**
+ * xudc_read_fifo - Reads the data from the given endpoint buffer.
+ * @ep: pointer to the usb device endpoint structure.
+ * @req: pointer to the usb request structure.
+ *
+ * Return: 0 if request is completed and -EAGAIN if not completed.
+ *
+ * Pulls OUT packet data from the endpoint buffer.
+ */
+static int xudc_read_fifo(struct xusb_ep *ep, struct xusb_req *req)
+{
+ u8 *buf;
+ u32 is_short, count, bufferspace;
+ u8 bufoffset;
+ u8 two_pkts = 0;
+ int ret;
+ int retval = -EAGAIN;
+ struct xusb_udc *udc = ep->udc;
+
+ if (ep->buffer0ready && ep->buffer1ready) {
+ dev_dbg(udc->dev, "Packet NOT ready!\n");
+ return retval;
+ }
+top:
+ if (ep->curbufnum)
+ bufoffset = XUSB_EP_BUF1COUNT_OFFSET;
+ else
+ bufoffset = XUSB_EP_BUF0COUNT_OFFSET;
+
+ count = udc->read_fn(udc->addr + ep->offset + bufoffset);
+
+ if (!ep->buffer0ready && !ep->buffer1ready)
+ two_pkts = 1;
+
+ buf = req->usb_req.buf + req->usb_req.actual;
+ prefetchw(buf);
+ bufferspace = req->usb_req.length - req->usb_req.actual;
+ is_short = count < ep->ep_usb.maxpacket;
+
+ if (unlikely(!bufferspace)) {
+ /*
+ * This happens when the driver's buffer
+ * is smaller than what the host sent.
+ * discard the extra data.
+ */
+ if (req->usb_req.status != -EOVERFLOW)
+ dev_dbg(udc->dev, "%s overflow %d\n",
+ ep->ep_usb.name, count);
+ req->usb_req.status = -EOVERFLOW;
+ xudc_done(ep, req, -EOVERFLOW);
+ return 0;
+ }
+
+ ret = xudc_eptxrx(ep, req, buf, count);
+ switch (ret) {
+ case 0:
+ req->usb_req.actual += min(count, bufferspace);
+ dev_dbg(udc->dev, "read %s, %d bytes%s req %p %d/%d\n",
+ ep->ep_usb.name, count, is_short ? "/S" : "", req,
+ req->usb_req.actual, req->usb_req.length);
+ bufferspace -= count;
+ /* Completion */
+ if ((req->usb_req.actual == req->usb_req.length) || is_short) {
+ if (udc->dma_enabled && req->usb_req.length)
+ dma_sync_single_for_cpu(udc->dev,
+ req->usb_req.dma,
+ req->usb_req.actual,
+ DMA_FROM_DEVICE);
+ xudc_done(ep, req, 0);
+ return 0;
+ }
+ if (two_pkts) {
+ two_pkts = 0;
+ goto top;
+ }
+ break;
+ case -EAGAIN:
+ dev_dbg(udc->dev, "receive busy\n");
+ break;
+ case -EINVAL:
+ case -ETIMEDOUT:
+ /* DMA error, dequeue the request */
+ xudc_done(ep, req, -ECONNRESET);
+ retval = 0;
+ break;
+ }
+
+ return retval;
+}
+
+/**
+ * xudc_write_fifo - Writes data into the given endpoint buffer.
+ * @ep: pointer to the usb device endpoint structure.
+ * @req: pointer to the usb request structure.
+ *
+ * Return: 0 if request is completed and -EAGAIN if not completed.
+ *
+ * Loads endpoint buffer for an IN packet.
+ */
+static int xudc_write_fifo(struct xusb_ep *ep, struct xusb_req *req)
+{
+ u32 max;
+ u32 length;
+ int ret;
+ int retval = -EAGAIN;
+ struct xusb_udc *udc = ep->udc;
+ int is_last, is_short = 0;
+ u8 *buf;
+
+ max = le16_to_cpu(ep->desc->wMaxPacketSize);
+ buf = req->usb_req.buf + req->usb_req.actual;
+ prefetch(buf);
+ length = req->usb_req.length - req->usb_req.actual;
+ length = min(length, max);
+
+ ret = xudc_eptxrx(ep, req, buf, length);
+ switch (ret) {
+ case 0:
+ req->usb_req.actual += length;
+ if (unlikely(length != max)) {
+ is_last = is_short = 1;
+ } else {
+ if (likely(req->usb_req.length !=
+ req->usb_req.actual) || req->usb_req.zero)
+ is_last = 0;
+ else
+ is_last = 1;
+ }
+ dev_dbg(udc->dev, "%s: wrote %s %d bytes%s%s %d left %p\n",
+ __func__, ep->ep_usb.name, length, is_last ? "/L" : "",
+ is_short ? "/S" : "",
+ req->usb_req.length - req->usb_req.actual, req);
+ /* completion */
+ if (is_last) {
+ xudc_done(ep, req, 0);
+ retval = 0;
+ }
+ break;
+ case -EAGAIN:
+ dev_dbg(udc->dev, "Send busy\n");
+ break;
+ case -EINVAL:
+ case -ETIMEDOUT:
+ /* DMA error, dequeue the request */
+ xudc_done(ep, req, -ECONNRESET);
+ retval = 0;
+ break;
+ }
+
+ return retval;
+}
+
+/**
+ * xudc_nuke - Cleans up the data transfer message list.
+ * @ep: pointer to the usb device endpoint structure.
+ * @status: Status of the data transfer.
+ */
+static void xudc_nuke(struct xusb_ep *ep, int status)
+{
+ struct xusb_req *req;
+
+ while (!list_empty(&ep->queue)) {
+ req = list_first_entry(&ep->queue, struct xusb_req, queue);
+ xudc_done(ep, req, status);
+ }
+}
+
+/**
+ * xudc_ep_set_halt - Stalls/unstalls the given endpoint.
+ * @_ep: pointer to the usb device endpoint structure.
+ * @value: value to indicate stall/unstall.
+ *
+ * Return: 0 for success and error value on failure
+ */
+static int xudc_ep_set_halt(struct usb_ep *_ep, int value)
+{
+ struct xusb_ep *ep = to_xusb_ep(_ep);
+ struct xusb_udc *udc;
+ unsigned long flags;
+ u32 epcfgreg;
+
+ if (!_ep || (!ep->desc && ep->epnumber)) {
+ pr_debug("%s: bad ep or descriptor\n", __func__);
+ return -EINVAL;
+ }
+ udc = ep->udc;
+
+ if (ep->is_in && (!list_empty(&ep->queue)) && value) {
+ dev_dbg(udc->dev, "requests pending can't halt\n");
+ return -EAGAIN;
+ }
+
+ if (ep->buffer0ready || ep->buffer1ready) {
+ dev_dbg(udc->dev, "HW buffers busy can't halt\n");
+ return -EAGAIN;
+ }
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ if (value) {
+ /* Stall the device.*/
+ epcfgreg = udc->read_fn(udc->addr + ep->offset);
+ epcfgreg |= XUSB_EP_CFG_STALL_MASK;
+ udc->write_fn(udc->addr, ep->offset, epcfgreg);
+ } else {
+ /* Unstall the device.*/
+ epcfgreg = udc->read_fn(udc->addr + ep->offset);
+ epcfgreg &= ~XUSB_EP_CFG_STALL_MASK;
+ udc->write_fn(udc->addr, ep->offset, epcfgreg);
+ if (ep->epnumber) {
+ /* Reset the toggle bit.*/
+ epcfgreg = udc->read_fn(ep->udc->addr + ep->offset);
+ epcfgreg &= ~XUSB_EP_CFG_DATA_TOGGLE_MASK;
+ udc->write_fn(udc->addr, ep->offset, epcfgreg);
+ }
+ }
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return 0;
+}
+
+/**
+ * xudc_ep_enable - Enables the given endpoint.
+ * @ep: pointer to the xusb endpoint structure.
+ * @desc: pointer to usb endpoint descriptor.
+ *
+ * Return: 0 for success and error value on failure
+ */
+static int __xudc_ep_enable(struct xusb_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct xusb_udc *udc = ep->udc;
+ u32 tmp;
+ u32 epcfg;
+ u32 ier;
+ u16 maxpacket;
+
+ ep->is_in = ((desc->bEndpointAddress & USB_DIR_IN) != 0);
+ /* Bit 3...0:endpoint number */
+ ep->epnumber = (desc->bEndpointAddress & 0x0f);
+ ep->desc = desc;
+ ep->ep_usb.desc = desc;
+ tmp = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+ ep->ep_usb.maxpacket = maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+
+ switch (tmp) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ dev_dbg(udc->dev, "only one control endpoint\n");
+ /* NON- ISO */
+ ep->is_iso = 0;
+ return -EINVAL;
+ case USB_ENDPOINT_XFER_INT:
+ /* NON- ISO */
+ ep->is_iso = 0;
+ if (maxpacket > 64) {
+ dev_dbg(udc->dev, "bogus maxpacket %d\n", maxpacket);
+ return -EINVAL;
+ }
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ /* NON- ISO */
+ ep->is_iso = 0;
+ if (!(is_power_of_2(maxpacket) && maxpacket >= 8 &&
+ maxpacket <= 512)) {
+ dev_dbg(udc->dev, "bogus maxpacket %d\n", maxpacket);
+ return -EINVAL;
+ }
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ /* ISO */
+ ep->is_iso = 1;
+ break;
+ }
+
+ ep->buffer0ready = 0;
+ ep->buffer1ready = 0;
+ ep->curbufnum = 0;
+ ep->rambase = rambase[ep->epnumber];
+ xudc_epconfig(ep, udc);
+
+ dev_dbg(udc->dev, "Enable Endpoint %d max pkt is %d\n",
+ ep->epnumber, maxpacket);
+
+ /* Enable the End point.*/
+ epcfg = udc->read_fn(udc->addr + ep->offset);
+ epcfg |= XUSB_EP_CFG_VALID_MASK;
+ udc->write_fn(udc->addr, ep->offset, epcfg);
+ if (ep->epnumber)
+ ep->rambase <<= 2;
+
+ /* Enable buffer completion interrupts for endpoint */
+ ier = udc->read_fn(udc->addr + XUSB_IER_OFFSET);
+ ier |= (XUSB_STATUS_INTR_BUFF_COMP_SHIFT_MASK << ep->epnumber);
+ udc->write_fn(udc->addr, XUSB_IER_OFFSET, ier);
+
+ /* for OUT endpoint set buffers ready to receive */
+ if (ep->epnumber && !ep->is_in) {
+ udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET,
+ 1 << ep->epnumber);
+ ep->buffer0ready = 1;
+ udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET,
+ (1 << (ep->epnumber +
+ XUSB_STATUS_EP_BUFF2_SHIFT)));
+ ep->buffer1ready = 1;
+ }
+
+ return 0;
+}
+
+/**
+ * xudc_ep_enable - Enables the given endpoint.
+ * @_ep: pointer to the usb endpoint structure.
+ * @desc: pointer to usb endpoint descriptor.
+ *
+ * Return: 0 for success and error value on failure
+ */
+static int xudc_ep_enable(struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct xusb_ep *ep;
+ struct xusb_udc *udc;
+ unsigned long flags;
+ int ret;
+
+ if (!_ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
+ pr_debug("%s: bad ep or descriptor\n", __func__);
+ return -EINVAL;
+ }
+
+ ep = to_xusb_ep(_ep);
+ udc = ep->udc;
+
+ if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) {
+ dev_dbg(udc->dev, "bogus device state\n");
+ return -ESHUTDOWN;
+ }
+
+ spin_lock_irqsave(&udc->lock, flags);
+ ret = __xudc_ep_enable(ep, desc);
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return ret;
+}
+
+/**
+ * xudc_ep_disable - Disables the given endpoint.
+ * @_ep: pointer to the usb endpoint structure.
+ *
+ * Return: 0 for success and error value on failure
+ */
+static int xudc_ep_disable(struct usb_ep *_ep)
+{
+ struct xusb_ep *ep;
+ unsigned long flags;
+ u32 epcfg;
+ struct xusb_udc *udc;
+
+ if (!_ep) {
+ pr_debug("%s: invalid ep\n", __func__);
+ return -EINVAL;
+ }
+
+ ep = to_xusb_ep(_ep);
+ udc = ep->udc;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ xudc_nuke(ep, -ESHUTDOWN);
+
+ /* Restore the endpoint's pristine config */
+ ep->desc = NULL;
+ ep->ep_usb.desc = NULL;
+
+ dev_dbg(udc->dev, "USB Ep %d disable\n ", ep->epnumber);
+ /* Disable the endpoint.*/
+ epcfg = udc->read_fn(udc->addr + ep->offset);
+ epcfg &= ~XUSB_EP_CFG_VALID_MASK;
+ udc->write_fn(udc->addr, ep->offset, epcfg);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return 0;
+}
+
+/**
+ * xudc_ep_alloc_request - Initializes the request queue.
+ * @_ep: pointer to the usb endpoint structure.
+ * @gfp_flags: Flags related to the request call.
+ *
+ * Return: pointer to request structure on success and a NULL on failure.
+ */
+static struct usb_request *xudc_ep_alloc_request(struct usb_ep *_ep,
+ gfp_t gfp_flags)
+{
+ struct xusb_ep *ep = to_xusb_ep(_ep);
+ struct xusb_udc *udc;
+ struct xusb_req *req;
+
+ udc = ep->udc;
+ req = kzalloc(sizeof(*req), gfp_flags);
+ if (!req) {
+ dev_err(udc->dev, "%s:not enough memory", __func__);
+ return NULL;
+ }
+
+ req->ep = ep;
+ INIT_LIST_HEAD(&req->queue);
+ return &req->usb_req;
+}
+
+/**
+ * xudc_free_request - Releases the request from queue.
+ * @_ep: pointer to the usb device endpoint structure.
+ * @_req: pointer to the usb request structure.
+ */
+static void xudc_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct xusb_req *req = to_xusb_req(_req);
+
+ kfree(req);
+}
+
+/**
+ * xudc_ep0_queue - Adds the request to endpoint 0 queue.
+ * @ep0: pointer to the xusb endpoint 0 structure.
+ * @req: pointer to the xusb request structure.
+ *
+ * Return: 0 for success and error value on failure
+ */
+static int __xudc_ep0_queue(struct xusb_ep *ep0, struct xusb_req *req)
+{
+ struct xusb_udc *udc = ep0->udc;
+ u32 length;
+ u8 *corebuf;
+
+ if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) {
+ dev_dbg(udc->dev, "%s, bogus device state\n", __func__);
+ return -EINVAL;
+ }
+ if (!list_empty(&ep0->queue)) {
+ dev_dbg(udc->dev, "%s:ep0 busy\n", __func__);
+ return -EBUSY;
+ }
+
+ req->usb_req.status = -EINPROGRESS;
+ req->usb_req.actual = 0;
+
+ list_add_tail(&req->queue, &ep0->queue);
+
+ if (udc->setup.bRequestType & USB_DIR_IN) {
+ prefetch(req->usb_req.buf);
+ length = req->usb_req.length;
+ corebuf = (void __force *) ((ep0->rambase << 2) +
+ udc->addr);
+ length = req->usb_req.actual = min_t(u32, length,
+ EP0_MAX_PACKET);
+ memcpy(corebuf, req->usb_req.buf, length);
+ udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, length);
+ udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1);
+ } else {
+ if (udc->setup.wLength) {
+ /* Enable EP0 buffer to receive data */
+ udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, 0);
+ udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1);
+ } else {
+ xudc_wrstatus(udc);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * xudc_ep0_queue - Adds the request to endpoint 0 queue.
+ * @_ep: pointer to the usb endpoint 0 structure.
+ * @_req: pointer to the usb request structure.
+ * @gfp_flags: Flags related to the request call.
+ *
+ * Return: 0 for success and error value on failure
+ */
+static int xudc_ep0_queue(struct usb_ep *_ep, struct usb_request *_req,
+ gfp_t gfp_flags)
+{
+ struct xusb_req *req = to_xusb_req(_req);
+ struct xusb_ep *ep0 = to_xusb_ep(_ep);
+ struct xusb_udc *udc = ep0->udc;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ ret = __xudc_ep0_queue(ep0, req);
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return ret;
+}
+
+/**
+ * xudc_ep_queue - Adds the request to endpoint queue.
+ * @_ep: pointer to the usb endpoint structure.
+ * @_req: pointer to the usb request structure.
+ * @gfp_flags: Flags related to the request call.
+ *
+ * Return: 0 for success and error value on failure
+ */
+static int xudc_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
+ gfp_t gfp_flags)
+{
+ struct xusb_req *req = to_xusb_req(_req);
+ struct xusb_ep *ep = to_xusb_ep(_ep);
+ struct xusb_udc *udc = ep->udc;
+ int ret;
+ unsigned long flags;
+
+ if (!ep->desc) {
+ dev_dbg(udc->dev, "%s:queing request to disabled %s\n",
+ __func__, ep->name);
+ return -ESHUTDOWN;
+ }
+
+ if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) {
+ dev_dbg(udc->dev, "%s, bogus device state\n", __func__);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ _req->status = -EINPROGRESS;
+ _req->actual = 0;
+
+ if (udc->dma_enabled) {
+ ret = usb_gadget_map_request(&udc->gadget, &req->usb_req,
+ ep->is_in);
+ if (ret) {
+ dev_dbg(udc->dev, "gadget_map failed ep%d\n",
+ ep->epnumber);
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return -EAGAIN;
+ }
+ }
+
+ if (list_empty(&ep->queue)) {
+ if (ep->is_in) {
+ dev_dbg(udc->dev, "xudc_write_fifo from ep_queue\n");
+ if (!xudc_write_fifo(ep, req))
+ req = NULL;
+ } else {
+ dev_dbg(udc->dev, "xudc_read_fifo from ep_queue\n");
+ if (!xudc_read_fifo(ep, req))
+ req = NULL;
+ }
+ }
+
+ if (req != NULL)
+ list_add_tail(&req->queue, &ep->queue);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return 0;
+}
+
+/**
+ * xudc_ep_dequeue - Removes the request from the queue.
+ * @_ep: pointer to the usb device endpoint structure.
+ * @_req: pointer to the usb request structure.
+ *
+ * Return: 0 for success and error value on failure
+ */
+static int xudc_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct xusb_ep *ep = to_xusb_ep(_ep);
+ struct xusb_req *req = to_xusb_req(_req);
+ struct xusb_udc *udc = ep->udc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ /* Make sure it's actually queued on this endpoint */
+ list_for_each_entry(req, &ep->queue, queue) {
+ if (&req->usb_req == _req)
+ break;
+ }
+ if (&req->usb_req != _req) {
+ spin_unlock_irqrestore(&ep->udc->lock, flags);
+ return -EINVAL;
+ }
+ xudc_done(ep, req, -ECONNRESET);
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+/**
+ * xudc_ep0_enable - Enables the given endpoint.
+ * @ep: pointer to the usb endpoint structure.
+ * @desc: pointer to usb endpoint descriptor.
+ *
+ * Return: error always.
+ *
+ * endpoint 0 enable should not be called by gadget layer.
+ */
+static int xudc_ep0_enable(struct usb_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ return -EINVAL;
+}
+
+/**
+ * xudc_ep0_disable - Disables the given endpoint.
+ * @ep: pointer to the usb endpoint structure.
+ *
+ * Return: error always.
+ *
+ * endpoint 0 disable should not be called by gadget layer.
+ */
+static int xudc_ep0_disable(struct usb_ep *ep)
+{
+ return -EINVAL;
+}
+
+static const struct usb_ep_ops xusb_ep0_ops = {
+ .enable = xudc_ep0_enable,
+ .disable = xudc_ep0_disable,
+ .alloc_request = xudc_ep_alloc_request,
+ .free_request = xudc_free_request,
+ .queue = xudc_ep0_queue,
+ .dequeue = xudc_ep_dequeue,
+ .set_halt = xudc_ep_set_halt,
+};
+
+static const struct usb_ep_ops xusb_ep_ops = {
+ .enable = xudc_ep_enable,
+ .disable = xudc_ep_disable,
+ .alloc_request = xudc_ep_alloc_request,
+ .free_request = xudc_free_request,
+ .queue = xudc_ep_queue,
+ .dequeue = xudc_ep_dequeue,
+ .set_halt = xudc_ep_set_halt,
+};
+
+/**
+ * xudc_get_frame - Reads the current usb frame number.
+ * @gadget: pointer to the usb gadget structure.
+ *
+ * Return: current frame number for success and error value on failure.
+ */
+static int xudc_get_frame(struct usb_gadget *gadget)
+{
+ struct xusb_udc *udc;
+ int frame;
+
+ if (!gadget)
+ return -ENODEV;
+
+ udc = to_udc(gadget);
+ frame = udc->read_fn(udc->addr + XUSB_FRAMENUM_OFFSET);
+ return frame;
+}
+
+/**
+ * xudc_wakeup - Send remote wakeup signal to host
+ * @gadget: pointer to the usb gadget structure.
+ *
+ * Return: 0 on success and error on failure
+ */
+static int xudc_wakeup(struct usb_gadget *gadget)
+{
+ struct xusb_udc *udc = to_udc(gadget);
+ u32 crtlreg;
+ int status = -EINVAL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ /* Remote wake up not enabled by host */
+ if (!udc->remote_wkp)
+ goto done;
+
+ crtlreg = udc->read_fn(udc->addr + XUSB_CONTROL_OFFSET);
+ crtlreg |= XUSB_CONTROL_USB_RMTWAKE_MASK;
+ /* set remote wake up bit */
+ udc->write_fn(udc->addr, XUSB_CONTROL_OFFSET, crtlreg);
+ /*
+ * wait for a while and reset remote wake up bit since this bit
+ * is not cleared by HW after sending remote wakeup to host.
+ */
+ mdelay(2);
+
+ crtlreg &= ~XUSB_CONTROL_USB_RMTWAKE_MASK;
+ udc->write_fn(udc->addr, XUSB_CONTROL_OFFSET, crtlreg);
+ status = 0;
+done:
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return status;
+}
+
+/**
+ * xudc_pullup - start/stop USB traffic
+ * @gadget: pointer to the usb gadget structure.
+ * @is_on: flag to start or stop
+ *
+ * Return: 0 always
+ *
+ * This function starts/stops SIE engine of IP based on is_on.
+ */
+static int xudc_pullup(struct usb_gadget *gadget, int is_on)
+{
+ struct xusb_udc *udc = to_udc(gadget);
+ unsigned long flags;
+ u32 crtlreg;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ crtlreg = udc->read_fn(udc->addr + XUSB_CONTROL_OFFSET);
+ if (is_on)
+ crtlreg |= XUSB_CONTROL_USB_READY_MASK;
+ else
+ crtlreg &= ~XUSB_CONTROL_USB_READY_MASK;
+
+ udc->write_fn(udc->addr, XUSB_CONTROL_OFFSET, crtlreg);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+/**
+ * xudc_eps_init - initialize endpoints.
+ * @udc: pointer to the usb device controller structure.
+ */
+static void xudc_eps_init(struct xusb_udc *udc)
+{
+ u32 ep_number;
+
+ INIT_LIST_HEAD(&udc->gadget.ep_list);
+
+ for (ep_number = 0; ep_number < XUSB_MAX_ENDPOINTS; ep_number++) {
+ struct xusb_ep *ep = &udc->ep[ep_number];
+
+ if (ep_number) {
+ list_add_tail(&ep->ep_usb.ep_list,
+ &udc->gadget.ep_list);
+ usb_ep_set_maxpacket_limit(&ep->ep_usb,
+ (unsigned short) ~0);
+ snprintf(ep->name, EPNAME_SIZE, "ep%d", ep_number);
+ ep->ep_usb.name = ep->name;
+ ep->ep_usb.ops = &xusb_ep_ops;
+ } else {
+ ep->ep_usb.name = ep0name;
+ usb_ep_set_maxpacket_limit(&ep->ep_usb, EP0_MAX_PACKET);
+ ep->ep_usb.ops = &xusb_ep0_ops;
+ }
+
+ ep->udc = udc;
+ ep->epnumber = ep_number;
+ ep->desc = NULL;
+ /*
+ * The configuration register address offset between
+ * each endpoint is 0x10.
+ */
+ ep->offset = XUSB_EP0_CONFIG_OFFSET + (ep_number * 0x10);
+ ep->is_in = 0;
+ ep->is_iso = 0;
+ ep->maxpacket = 0;
+ xudc_epconfig(ep, udc);
+
+ /* Initialize one queue per endpoint */
+ INIT_LIST_HEAD(&ep->queue);
+ }
+}
+
+/**
+ * xudc_stop_activity - Stops any further activity on the device.
+ * @udc: pointer to the usb device controller structure.
+ */
+static void xudc_stop_activity(struct xusb_udc *udc)
+{
+ int i;
+ struct xusb_ep *ep;
+
+ for (i = 0; i < XUSB_MAX_ENDPOINTS; i++) {
+ ep = &udc->ep[i];
+ xudc_nuke(ep, -ESHUTDOWN);
+ }
+}
+
+/**
+ * xudc_start - Starts the device.
+ * @gadget: pointer to the usb gadget structure
+ * @driver: pointer to gadget driver structure
+ *
+ * Return: zero on success and error on failure
+ */
+static int xudc_start(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct xusb_udc *udc = to_udc(gadget);
+ struct xusb_ep *ep0 = &udc->ep[XUSB_EP_NUMBER_ZERO];
+ const struct usb_endpoint_descriptor *desc = &config_bulk_out_desc;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ if (udc->driver) {
+ dev_err(udc->dev, "%s is already bound to %s\n",
+ udc->gadget.name, udc->driver->driver.name);
+ ret = -EBUSY;
+ goto err;
+ }
+
+ /* hook up the driver */
+ udc->driver = driver;
+ udc->gadget.speed = driver->max_speed;
+
+ /* Enable the control endpoint. */
+ ret = __xudc_ep_enable(ep0, desc);
+
+ /* Set device address and remote wakeup to 0 */
+ udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, 0);
+ udc->remote_wkp = 0;
+err:
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return ret;
+}
+
+/**
+ * xudc_stop - stops the device.
+ * @gadget: pointer to the usb gadget structure
+ * @driver: pointer to usb gadget driver structure
+ *
+ * Return: zero always
+ */
+static int xudc_stop(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct xusb_udc *udc = to_udc(gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ udc->driver = NULL;
+
+ /* Set device address and remote wakeup to 0 */
+ udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, 0);
+ udc->remote_wkp = 0;
+
+ xudc_stop_activity(udc);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+static const struct usb_gadget_ops xusb_udc_ops = {
+ .get_frame = xudc_get_frame,
+ .wakeup = xudc_wakeup,
+ .pullup = xudc_pullup,
+ .udc_start = xudc_start,
+ .udc_stop = xudc_stop,
+};
+
+/**
+ * xudc_clear_stall_all_ep - clears stall of every endpoint.
+ * @udc: pointer to the udc structure.
+ */
+static void xudc_clear_stall_all_ep(struct xusb_udc *udc)
+{
+ struct xusb_ep *ep;
+ u32 epcfgreg;
+ int i;
+
+ for (i = 0; i < XUSB_MAX_ENDPOINTS; i++) {
+ ep = &udc->ep[i];
+ epcfgreg = udc->read_fn(udc->addr + ep->offset);
+ epcfgreg &= ~XUSB_EP_CFG_STALL_MASK;
+ udc->write_fn(udc->addr, ep->offset, epcfgreg);
+ if (ep->epnumber) {
+ /* Reset the toggle bit.*/
+ epcfgreg = udc->read_fn(udc->addr + ep->offset);
+ epcfgreg &= ~XUSB_EP_CFG_DATA_TOGGLE_MASK;
+ udc->write_fn(udc->addr, ep->offset, epcfgreg);
+ }
+ }
+}
+
+/**
+ * xudc_startup_handler - The usb device controller interrupt handler.
+ * @udc: pointer to the udc structure.
+ * @intrstatus: The mask value containing the interrupt sources.
+ *
+ * This function handles the RESET,SUSPEND,RESUME and DISCONNECT interrupts.
+ */
+static void xudc_startup_handler(struct xusb_udc *udc, u32 intrstatus)
+{
+ u32 intrreg;
+
+ if (intrstatus & XUSB_STATUS_RESET_MASK) {
+
+ dev_dbg(udc->dev, "Reset\n");
+
+ if (intrstatus & XUSB_STATUS_HIGH_SPEED_MASK)
+ udc->gadget.speed = USB_SPEED_HIGH;
+ else
+ udc->gadget.speed = USB_SPEED_FULL;
+
+ xudc_stop_activity(udc);
+ xudc_clear_stall_all_ep(udc);
+ udc->write_fn(udc->addr, XUSB_TESTMODE_OFFSET, 0);
+
+ /* Set device address and remote wakeup to 0 */
+ udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, 0);
+ udc->remote_wkp = 0;
+
+ /* Enable the suspend, resume and disconnect */
+ intrreg = udc->read_fn(udc->addr + XUSB_IER_OFFSET);
+ intrreg |= XUSB_STATUS_SUSPEND_MASK | XUSB_STATUS_RESUME_MASK |
+ XUSB_STATUS_DISCONNECT_MASK;
+ udc->write_fn(udc->addr, XUSB_IER_OFFSET, intrreg);
+ }
+ if (intrstatus & XUSB_STATUS_SUSPEND_MASK) {
+
+ dev_dbg(udc->dev, "Suspend\n");
+
+ /* Enable the reset, resume and disconnect */
+ intrreg = udc->read_fn(udc->addr + XUSB_IER_OFFSET);
+ intrreg |= XUSB_STATUS_RESET_MASK | XUSB_STATUS_RESUME_MASK |
+ XUSB_STATUS_DISCONNECT_MASK;
+ udc->write_fn(udc->addr, XUSB_IER_OFFSET, intrreg);
+
+ udc->usb_state = USB_STATE_SUSPENDED;
+
+ if (udc->driver->suspend) {
+ spin_unlock(&udc->lock);
+ udc->driver->suspend(&udc->gadget);
+ spin_lock(&udc->lock);
+ }
+ }
+ if (intrstatus & XUSB_STATUS_RESUME_MASK) {
+ bool condition = (udc->usb_state != USB_STATE_SUSPENDED);
+
+ dev_WARN_ONCE(udc->dev, condition,
+ "Resume IRQ while not suspended\n");
+
+ dev_dbg(udc->dev, "Resume\n");
+
+ /* Enable the reset, suspend and disconnect */
+ intrreg = udc->read_fn(udc->addr + XUSB_IER_OFFSET);
+ intrreg |= XUSB_STATUS_RESET_MASK | XUSB_STATUS_SUSPEND_MASK |
+ XUSB_STATUS_DISCONNECT_MASK;
+ udc->write_fn(udc->addr, XUSB_IER_OFFSET, intrreg);
+
+ udc->usb_state = 0;
+
+ if (udc->driver->resume) {
+ spin_unlock(&udc->lock);
+ udc->driver->resume(&udc->gadget);
+ spin_lock(&udc->lock);
+ }
+ }
+ if (intrstatus & XUSB_STATUS_DISCONNECT_MASK) {
+
+ dev_dbg(udc->dev, "Disconnect\n");
+
+ /* Enable the reset, resume and suspend */
+ intrreg = udc->read_fn(udc->addr + XUSB_IER_OFFSET);
+ intrreg |= XUSB_STATUS_RESET_MASK | XUSB_STATUS_RESUME_MASK |
+ XUSB_STATUS_SUSPEND_MASK;
+ udc->write_fn(udc->addr, XUSB_IER_OFFSET, intrreg);
+
+ if (udc->driver && udc->driver->disconnect) {
+ spin_unlock(&udc->lock);
+ udc->driver->disconnect(&udc->gadget);
+ spin_lock(&udc->lock);
+ }
+ }
+}
+
+/**
+ * xudc_ep0_stall - Stall endpoint zero.
+ * @udc: pointer to the udc structure.
+ *
+ * This function stalls endpoint zero.
+ */
+static void xudc_ep0_stall(struct xusb_udc *udc)
+{
+ u32 epcfgreg;
+ struct xusb_ep *ep0 = &udc->ep[XUSB_EP_NUMBER_ZERO];
+
+ epcfgreg = udc->read_fn(udc->addr + ep0->offset);
+ epcfgreg |= XUSB_EP_CFG_STALL_MASK;
+ udc->write_fn(udc->addr, ep0->offset, epcfgreg);
+}
+
+/**
+ * xudc_setaddress - executes SET_ADDRESS command
+ * @udc: pointer to the udc structure.
+ *
+ * This function executes USB SET_ADDRESS command
+ */
+static void xudc_setaddress(struct xusb_udc *udc)
+{
+ struct xusb_ep *ep0 = &udc->ep[0];
+ struct xusb_req *req = udc->req;
+ int ret;
+
+ req->usb_req.length = 0;
+ ret = __xudc_ep0_queue(ep0, req);
+ if (ret == 0)
+ return;
+
+ dev_err(udc->dev, "Can't respond to SET ADDRESS request\n");
+ xudc_ep0_stall(udc);
+}
+
+/**
+ * xudc_getstatus - executes GET_STATUS command
+ * @udc: pointer to the udc structure.
+ *
+ * This function executes USB GET_STATUS command
+ */
+static void xudc_getstatus(struct xusb_udc *udc)
+{
+ struct xusb_ep *ep0 = &udc->ep[0];
+ struct xusb_req *req = udc->req;
+ struct xusb_ep *target_ep;
+ u16 status = 0;
+ u32 epcfgreg;
+ int epnum;
+ u32 halt;
+ int ret;
+
+ switch (udc->setup.bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ /* Get device status */
+ status = 1 << USB_DEVICE_SELF_POWERED;
+ if (udc->remote_wkp)
+ status |= (1 << USB_DEVICE_REMOTE_WAKEUP);
+ break;
+ case USB_RECIP_INTERFACE:
+ break;
+ case USB_RECIP_ENDPOINT:
+ epnum = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK;
+ target_ep = &udc->ep[epnum];
+ epcfgreg = udc->read_fn(udc->addr + target_ep->offset);
+ halt = epcfgreg & XUSB_EP_CFG_STALL_MASK;
+ if (udc->setup.wIndex & USB_DIR_IN) {
+ if (!target_ep->is_in)
+ goto stall;
+ } else {
+ if (target_ep->is_in)
+ goto stall;
+ }
+ if (halt)
+ status = 1 << USB_ENDPOINT_HALT;
+ break;
+ default:
+ goto stall;
+ }
+
+ req->usb_req.length = 2;
+ *(u16 *)req->usb_req.buf = cpu_to_le16(status);
+ ret = __xudc_ep0_queue(ep0, req);
+ if (ret == 0)
+ return;
+stall:
+ dev_err(udc->dev, "Can't respond to getstatus request\n");
+ xudc_ep0_stall(udc);
+}
+
+/**
+ * xudc_set_clear_feature - Executes the set feature and clear feature commands.
+ * @udc: pointer to the usb device controller structure.
+ *
+ * Processes the SET_FEATURE and CLEAR_FEATURE commands.
+ */
+static void xudc_set_clear_feature(struct xusb_udc *udc)
+{
+ struct xusb_ep *ep0 = &udc->ep[0];
+ struct xusb_req *req = udc->req;
+ struct xusb_ep *target_ep;
+ u8 endpoint;
+ u8 outinbit;
+ u32 epcfgreg;
+ int flag = (udc->setup.bRequest == USB_REQ_SET_FEATURE ? 1 : 0);
+ int ret;
+
+ switch (udc->setup.bRequestType) {
+ case USB_RECIP_DEVICE:
+ switch (udc->setup.wValue) {
+ case USB_DEVICE_TEST_MODE:
+ /*
+ * The Test Mode will be executed
+ * after the status phase.
+ */
+ break;
+ case USB_DEVICE_REMOTE_WAKEUP:
+ if (flag)
+ udc->remote_wkp = 1;
+ else
+ udc->remote_wkp = 0;
+ break;
+ default:
+ xudc_ep0_stall(udc);
+ break;
+ }
+ break;
+ case USB_RECIP_ENDPOINT:
+ if (!udc->setup.wValue) {
+ endpoint = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK;
+ target_ep = &udc->ep[endpoint];
+ outinbit = udc->setup.wIndex & USB_ENDPOINT_DIR_MASK;
+ outinbit = outinbit >> 7;
+
+ /* Make sure direction matches.*/
+ if (outinbit != target_ep->is_in) {
+ xudc_ep0_stall(udc);
+ return;
+ }
+ epcfgreg = udc->read_fn(udc->addr + target_ep->offset);
+ if (!endpoint) {
+ /* Clear the stall.*/
+ epcfgreg &= ~XUSB_EP_CFG_STALL_MASK;
+ udc->write_fn(udc->addr,
+ target_ep->offset, epcfgreg);
+ } else {
+ if (flag) {
+ epcfgreg |= XUSB_EP_CFG_STALL_MASK;
+ udc->write_fn(udc->addr,
+ target_ep->offset,
+ epcfgreg);
+ } else {
+ /* Unstall the endpoint.*/
+ epcfgreg &= ~(XUSB_EP_CFG_STALL_MASK |
+ XUSB_EP_CFG_DATA_TOGGLE_MASK);
+ udc->write_fn(udc->addr,
+ target_ep->offset,
+ epcfgreg);
+ }
+ }
+ }
+ break;
+ default:
+ xudc_ep0_stall(udc);
+ return;
+ }
+
+ req->usb_req.length = 0;
+ ret = __xudc_ep0_queue(ep0, req);
+ if (ret == 0)
+ return;
+
+ dev_err(udc->dev, "Can't respond to SET/CLEAR FEATURE\n");
+ xudc_ep0_stall(udc);
+}
+
+/**
+ * xudc_handle_setup - Processes the setup packet.
+ * @udc: pointer to the usb device controller structure.
+ *
+ * Process setup packet and delegate to gadget layer.
+ */
+static void xudc_handle_setup(struct xusb_udc *udc)
+{
+ struct xusb_ep *ep0 = &udc->ep[0];
+ struct usb_ctrlrequest setup;
+ u32 *ep0rambase;
+
+ /* Load up the chapter 9 command buffer.*/
+ ep0rambase = (u32 __force *) (udc->addr + XUSB_SETUP_PKT_ADDR_OFFSET);
+ memcpy(&setup, ep0rambase, 8);
+
+ udc->setup = setup;
+ udc->setup.wValue = cpu_to_le16(setup.wValue);
+ udc->setup.wIndex = cpu_to_le16(setup.wIndex);
+ udc->setup.wLength = cpu_to_le16(setup.wLength);
+
+ /* Clear previous requests */
+ xudc_nuke(ep0, -ECONNRESET);
+
+ if (udc->setup.bRequestType & USB_DIR_IN) {
+ /* Execute the get command.*/
+ udc->setupseqrx = STATUS_PHASE;
+ udc->setupseqtx = DATA_PHASE;
+ } else {
+ /* Execute the put command.*/
+ udc->setupseqrx = DATA_PHASE;
+ udc->setupseqtx = STATUS_PHASE;
+ }
+
+ switch (udc->setup.bRequest) {
+ case USB_REQ_GET_STATUS:
+ /* Data+Status phase form udc */
+ if ((udc->setup.bRequestType &
+ (USB_DIR_IN | USB_TYPE_MASK)) !=
+ (USB_DIR_IN | USB_TYPE_STANDARD))
+ break;
+ xudc_getstatus(udc);
+ return;
+ case USB_REQ_SET_ADDRESS:
+ /* Status phase from udc */
+ if (udc->setup.bRequestType != (USB_DIR_OUT |
+ USB_TYPE_STANDARD | USB_RECIP_DEVICE))
+ break;
+ xudc_setaddress(udc);
+ return;
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_FEATURE:
+ /* Requests with no data phase, status phase from udc */
+ if ((udc->setup.bRequestType & USB_TYPE_MASK)
+ != USB_TYPE_STANDARD)
+ break;
+ xudc_set_clear_feature(udc);
+ return;
+ default:
+ break;
+ }
+
+ spin_unlock(&udc->lock);
+ if (udc->driver->setup(&udc->gadget, &setup) < 0)
+ xudc_ep0_stall(udc);
+ spin_lock(&udc->lock);
+}
+
+/**
+ * xudc_ep0_out - Processes the endpoint 0 OUT token.
+ * @udc: pointer to the usb device controller structure.
+ */
+static void xudc_ep0_out(struct xusb_udc *udc)
+{
+ struct xusb_ep *ep0 = &udc->ep[0];
+ struct xusb_req *req;
+ u8 *ep0rambase;
+ unsigned int bytes_to_rx;
+ void *buffer;
+
+ req = list_first_entry(&ep0->queue, struct xusb_req, queue);
+
+ switch (udc->setupseqrx) {
+ case STATUS_PHASE:
+ /*
+ * This resets both state machines for the next
+ * Setup packet.
+ */
+ udc->setupseqrx = SETUP_PHASE;
+ udc->setupseqtx = SETUP_PHASE;
+ req->usb_req.actual = req->usb_req.length;
+ xudc_done(ep0, req, 0);
+ break;
+ case DATA_PHASE:
+ bytes_to_rx = udc->read_fn(udc->addr +
+ XUSB_EP_BUF0COUNT_OFFSET);
+ /* Copy the data to be received from the DPRAM. */
+ ep0rambase = (u8 __force *) (udc->addr +
+ (ep0->rambase << 2));
+ buffer = req->usb_req.buf + req->usb_req.actual;
+ req->usb_req.actual = req->usb_req.actual + bytes_to_rx;
+ memcpy(buffer, ep0rambase, bytes_to_rx);
+
+ if (req->usb_req.length == req->usb_req.actual) {
+ /* Data transfer completed get ready for Status stage */
+ xudc_wrstatus(udc);
+ } else {
+ /* Enable EP0 buffer to receive data */
+ udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, 0);
+ udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * xudc_ep0_in - Processes the endpoint 0 IN token.
+ * @udc: pointer to the usb device controller structure.
+ */
+static void xudc_ep0_in(struct xusb_udc *udc)
+{
+ struct xusb_ep *ep0 = &udc->ep[0];
+ struct xusb_req *req;
+ unsigned int bytes_to_tx;
+ void *buffer;
+ u32 epcfgreg;
+ u16 count = 0;
+ u16 length;
+ u8 *ep0rambase;
+ u8 test_mode = udc->setup.wIndex >> 8;
+
+ req = list_first_entry(&ep0->queue, struct xusb_req, queue);
+ bytes_to_tx = req->usb_req.length - req->usb_req.actual;
+
+ switch (udc->setupseqtx) {
+ case STATUS_PHASE:
+ switch (udc->setup.bRequest) {
+ case USB_REQ_SET_ADDRESS:
+ /* Set the address of the device.*/
+ udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET,
+ udc->setup.wValue);
+ break;
+ case USB_REQ_SET_FEATURE:
+ if (udc->setup.bRequestType ==
+ USB_RECIP_DEVICE) {
+ if (udc->setup.wValue ==
+ USB_DEVICE_TEST_MODE)
+ udc->write_fn(udc->addr,
+ XUSB_TESTMODE_OFFSET,
+ test_mode);
+ }
+ break;
+ }
+ req->usb_req.actual = req->usb_req.length;
+ xudc_done(ep0, req, 0);
+ break;
+ case DATA_PHASE:
+ if (!bytes_to_tx) {
+ /*
+ * We're done with data transfer, next
+ * will be zero length OUT with data toggle of
+ * 1. Setup data_toggle.
+ */
+ epcfgreg = udc->read_fn(udc->addr + ep0->offset);
+ epcfgreg |= XUSB_EP_CFG_DATA_TOGGLE_MASK;
+ udc->write_fn(udc->addr, ep0->offset, epcfgreg);
+ udc->setupseqtx = STATUS_PHASE;
+ } else {
+ length = count = min_t(u32, bytes_to_tx,
+ EP0_MAX_PACKET);
+ /* Copy the data to be transmitted into the DPRAM. */
+ ep0rambase = (u8 __force *) (udc->addr +
+ (ep0->rambase << 2));
+ buffer = req->usb_req.buf + req->usb_req.actual;
+ req->usb_req.actual = req->usb_req.actual + length;
+ memcpy(ep0rambase, buffer, length);
+ }
+ udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, count);
+ udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1);
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * xudc_ctrl_ep_handler - Endpoint 0 interrupt handler.
+ * @udc: pointer to the udc structure.
+ * @intrstatus: It's the mask value for the interrupt sources on endpoint 0.
+ *
+ * Processes the commands received during enumeration phase.
+ */
+static void xudc_ctrl_ep_handler(struct xusb_udc *udc, u32 intrstatus)
+{
+
+ if (intrstatus & XUSB_STATUS_SETUP_PACKET_MASK) {
+ xudc_handle_setup(udc);
+ } else {
+ if (intrstatus & XUSB_STATUS_FIFO_BUFF_RDY_MASK)
+ xudc_ep0_out(udc);
+ else if (intrstatus & XUSB_STATUS_FIFO_BUFF_FREE_MASK)
+ xudc_ep0_in(udc);
+ }
+}
+
+/**
+ * xudc_nonctrl_ep_handler - Non control endpoint interrupt handler.
+ * @udc: pointer to the udc structure.
+ * @epnum: End point number for which the interrupt is to be processed
+ * @intrstatus: mask value for interrupt sources of endpoints other
+ * than endpoint 0.
+ *
+ * Processes the buffer completion interrupts.
+ */
+static void xudc_nonctrl_ep_handler(struct xusb_udc *udc, u8 epnum,
+ u32 intrstatus)
+{
+
+ struct xusb_req *req;
+ struct xusb_ep *ep;
+
+ ep = &udc->ep[epnum];
+ /* Process the End point interrupts.*/
+ if (intrstatus & (XUSB_STATUS_EP0_BUFF1_COMP_MASK << epnum))
+ ep->buffer0ready = 0;
+ if (intrstatus & (XUSB_STATUS_EP0_BUFF2_COMP_MASK << epnum))
+ ep->buffer1ready = 0;
+
+ if (list_empty(&ep->queue))
+ return;
+
+ req = list_first_entry(&ep->queue, struct xusb_req, queue);
+
+ if (ep->is_in)
+ xudc_write_fifo(ep, req);
+ else
+ xudc_read_fifo(ep, req);
+}
+
+/**
+ * xudc_irq - The main interrupt handler.
+ * @irq: The interrupt number.
+ * @_udc: pointer to the usb device controller structure.
+ *
+ * Return: IRQ_HANDLED after the interrupt is handled.
+ */
+static irqreturn_t xudc_irq(int irq, void *_udc)
+{
+ struct xusb_udc *udc = _udc;
+ u32 intrstatus;
+ u32 ier;
+ u8 index;
+ u32 bufintr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ /*
+ * Event interrupts are level sensitive hence first disable
+ * IER, read ISR and figure out active interrupts.
+ */
+ ier = udc->read_fn(udc->addr + XUSB_IER_OFFSET);
+ ier &= ~XUSB_STATUS_INTR_EVENT_MASK;
+ udc->write_fn(udc->addr, XUSB_IER_OFFSET, ier);
+
+ /* Read the Interrupt Status Register.*/
+ intrstatus = udc->read_fn(udc->addr + XUSB_STATUS_OFFSET);
+
+ /* Call the handler for the event interrupt.*/
+ if (intrstatus & XUSB_STATUS_INTR_EVENT_MASK) {
+ /*
+ * Check if there is any action to be done for :
+ * - USB Reset received {XUSB_STATUS_RESET_MASK}
+ * - USB Suspend received {XUSB_STATUS_SUSPEND_MASK}
+ * - USB Resume received {XUSB_STATUS_RESUME_MASK}
+ * - USB Disconnect received {XUSB_STATUS_DISCONNECT_MASK}
+ */
+ xudc_startup_handler(udc, intrstatus);
+ }
+
+ /* Check the buffer completion interrupts */
+ if (intrstatus & XUSB_STATUS_INTR_BUFF_COMP_ALL_MASK) {
+ /* Enable Reset, Suspend, Resume and Disconnect */
+ ier = udc->read_fn(udc->addr + XUSB_IER_OFFSET);
+ ier |= XUSB_STATUS_INTR_EVENT_MASK;
+ udc->write_fn(udc->addr, XUSB_IER_OFFSET, ier);
+
+ if (intrstatus & XUSB_STATUS_EP0_BUFF1_COMP_MASK)
+ xudc_ctrl_ep_handler(udc, intrstatus);
+
+ for (index = 1; index < 8; index++) {
+ bufintr = ((intrstatus &
+ (XUSB_STATUS_EP1_BUFF1_COMP_MASK <<
+ (index - 1))) || (intrstatus &
+ (XUSB_STATUS_EP1_BUFF2_COMP_MASK <<
+ (index - 1))));
+ if (bufintr) {
+ xudc_nonctrl_ep_handler(udc, index,
+ intrstatus);
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return IRQ_HANDLED;
+}
+
+/**
+ * xudc_probe - The device probe function for driver initialization.
+ * @pdev: pointer to the platform device structure.
+ *
+ * Return: 0 for success and error value on failure
+ */
+static int xudc_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct resource *res;
+ struct xusb_udc *udc;
+ struct xusb_ep *ep0;
+ int irq;
+ int ret;
+ u32 ier;
+ u8 *buff;
+
+ udc = devm_kzalloc(&pdev->dev, sizeof(*udc), GFP_KERNEL);
+ if (!udc)
+ return -ENOMEM;
+
+ /* Create a dummy request for GET_STATUS, SET_ADDRESS */
+ udc->req = devm_kzalloc(&pdev->dev, sizeof(struct xusb_req),
+ GFP_KERNEL);
+ if (!udc->req)
+ return -ENOMEM;
+
+ buff = devm_kzalloc(&pdev->dev, STATUSBUFF_SIZE, GFP_KERNEL);
+ if (!buff)
+ return -ENOMEM;
+
+ udc->req->usb_req.buf = buff;
+
+ /* Map the registers */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ udc->addr = devm_ioremap_resource(&pdev->dev, res);
+ if (!udc->addr)
+ return -ENOMEM;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "unable to get irq\n");
+ return irq;
+ }
+ ret = devm_request_irq(&pdev->dev, irq, xudc_irq, 0,
+ dev_name(&pdev->dev), udc);
+ if (ret < 0) {
+ dev_dbg(&pdev->dev, "unable to request irq %d", irq);
+ goto fail;
+ }
+
+ udc->dma_enabled = of_property_read_bool(np, "xlnx,has-builtin-dma");
+
+ /* Setup gadget structure */
+ udc->gadget.ops = &xusb_udc_ops;
+ udc->gadget.max_speed = USB_SPEED_HIGH;
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ udc->gadget.ep0 = &udc->ep[XUSB_EP_NUMBER_ZERO].ep_usb;
+ udc->gadget.name = driver_name;
+
+ spin_lock_init(&udc->lock);
+
+ /* Check for IP endianness */
+ udc->write_fn = xudc_write32_be;
+ udc->read_fn = xudc_read32_be;
+ udc->write_fn(udc->addr, XUSB_TESTMODE_OFFSET, TEST_J);
+ if ((udc->read_fn(udc->addr + XUSB_TESTMODE_OFFSET))
+ != TEST_J) {
+ udc->write_fn = xudc_write32;
+ udc->read_fn = xudc_read32;
+ }
+ udc->write_fn(udc->addr, XUSB_TESTMODE_OFFSET, 0);
+
+ xudc_eps_init(udc);
+
+ ep0 = &udc->ep[0];
+
+ /* Set device address to 0.*/
+ udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, 0);
+
+ ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
+ if (ret)
+ goto fail;
+
+ udc->dev = &udc->gadget.dev;
+
+ /* Enable the interrupts.*/
+ ier = XUSB_STATUS_GLOBAL_INTR_MASK | XUSB_STATUS_INTR_EVENT_MASK |
+ XUSB_STATUS_FIFO_BUFF_RDY_MASK | XUSB_STATUS_FIFO_BUFF_FREE_MASK |
+ XUSB_STATUS_SETUP_PACKET_MASK |
+ XUSB_STATUS_INTR_BUFF_COMP_ALL_MASK;
+
+ udc->write_fn(udc->addr, XUSB_IER_OFFSET, ier);
+
+ 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,
+ udc->dma_enabled ? "with DMA" : "without DMA");
+
+ return 0;
+fail:
+ dev_err(&pdev->dev, "probe failed, %d\n", ret);
+ return ret;
+}
+
+/**
+ * xudc_remove - Releases the resources allocated during the initialization.
+ * @pdev: pointer to the platform device structure.
+ *
+ * Return: 0 always
+ */
+static int xudc_remove(struct platform_device *pdev)
+{
+ struct xusb_udc *udc = platform_get_drvdata(pdev);
+
+ usb_del_gadget_udc(&udc->gadget);
+
+ return 0;
+}
+
+/* Match table for of_platform binding */
+static const struct of_device_id usb_of_match[] = {
+ { .compatible = "xlnx,usb2-device-4.00.a", },
+ { /* end of list */ },
+};
+MODULE_DEVICE_TABLE(of, usb_of_match);
+
+static struct platform_driver xudc_driver = {
+ .driver = {
+ .name = driver_name,
+ .of_match_table = usb_of_match,
+ },
+ .probe = xudc_probe,
+ .remove = xudc_remove,
+};
+
+module_platform_driver(xudc_driver);
+
+MODULE_DESCRIPTION("Xilinx udc driver");
+MODULE_AUTHOR("Xilinx, Inc");
+MODULE_LICENSE("GPL");