diff options
Diffstat (limited to 'drivers/usb/gadget')
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"); |