summaryrefslogtreecommitdiff
path: root/drivers/usb/gadget
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/gadget')
-rw-r--r--drivers/usb/gadget/Kconfig17
-rw-r--r--drivers/usb/gadget/composite.c17
-rw-r--r--drivers/usb/gadget/function/Makefile2
-rw-r--r--drivers/usb/gadget/function/f_hid.c1
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c109
-rw-r--r--drivers/usb/gadget/function/f_printer.c1471
-rw-r--r--drivers/usb/gadget/function/u_printer.h37
-rw-r--r--drivers/usb/gadget/function/u_serial.c2
-rw-r--r--drivers/usb/gadget/legacy/Kconfig1
-rw-r--r--drivers/usb/gadget/legacy/printer.c1239
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.c212
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.h26
-rw-r--r--drivers/usb/gadget/udc/dummy_hcd.c6
-rw-r--r--drivers/usb/gadget/udc/goku_udc.c233
-rw-r--r--drivers/usb/gadget/udc/lpc32xx_udc.c15
-rw-r--r--drivers/usb/gadget/udc/net2280.c182
-rw-r--r--drivers/usb/gadget/udc/net2280.h2
-rw-r--r--drivers/usb/gadget/udc/pxa27x_udc.c134
-rw-r--r--drivers/usb/gadget/udc/udc-core.c68
19 files changed, 2162 insertions, 1612 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index b454d05be583..bcf83c0a6e62 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -196,6 +196,9 @@ config USB_F_MIDI
config USB_F_HID
tristate
+config USB_F_PRINTER
+ tristate
+
choice
tristate "USB Gadget Drivers"
default USB_ETH
@@ -434,6 +437,20 @@ config USB_CONFIGFS_F_UVC
device. It provides a userspace API to process UVC control requests
and stream video data to the host.
+config USB_CONFIGFS_F_PRINTER
+ bool "Printer function"
+ select USB_F_PRINTER
+ depends on USB_CONFIGFS
+ help
+ The Printer function channels data between the USB host and a
+ userspace program driving the print engine. The user space
+ program reads and writes the device file /dev/g_printer<X> to
+ receive or send printer data. It can use ioctl calls to
+ the device file to get or set printer status.
+
+ For more information, see Documentation/usb/gadget_printer.txt
+ which includes sample code for accessing the device file.
+
source "drivers/usb/gadget/legacy/Kconfig"
endchoice
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 13adfd1a3f54..4e3447bbd097 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -1161,11 +1161,11 @@ static struct usb_gadget_string_container *copy_gadget_strings(
* This function will create a deep copy of usb_gadget_strings and usb_string
* and attach it to the cdev. The actual string (usb_string.s) will not be
* copied but only a referenced will be made. The struct usb_gadget_strings
- * array may contain multiple languges and should be NULL terminated.
+ * array may contain multiple languages and should be NULL terminated.
* The ->language pointer of each struct usb_gadget_strings has to contain the
* same amount of entries.
* For instance: sp[0] is en-US, sp[1] is es-ES. It is expected that the first
- * usb_string entry of es-ES containts the translation of the first usb_string
+ * usb_string entry of es-ES contains the translation of the first usb_string
* entry of en-US. Therefore both entries become the same id assign.
*/
struct usb_string *usb_gstrings_attach(struct usb_composite_dev *cdev,
@@ -1472,6 +1472,13 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
req->length = 0;
gadget->ep0->driver_data = cdev;
+ /*
+ * Don't let non-standard requests match any of the cases below
+ * by accident.
+ */
+ if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
+ goto unknown;
+
switch (ctrl->bRequest) {
/* we handle all standard USB descriptors */
@@ -1751,6 +1758,10 @@ unknown:
* take such requests too, if that's ever needed: to work
* in config 0, etc.
*/
+ list_for_each_entry(f, &cdev->config->functions, list)
+ if (f->req_match && f->req_match(f, ctrl))
+ goto try_fun_setup;
+ f = NULL;
switch (ctrl->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_INTERFACE:
if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
@@ -1768,7 +1779,7 @@ unknown:
f = NULL;
break;
}
-
+try_fun_setup:
if (f && f->setup)
value = f->setup(f, ctrl);
else {
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
index f71b1aaa0edf..bd7def576955 100644
--- a/drivers/usb/gadget/function/Makefile
+++ b/drivers/usb/gadget/function/Makefile
@@ -42,3 +42,5 @@ usb_f_midi-y := f_midi.o
obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o
usb_f_hid-y := f_hid.o
obj-$(CONFIG_USB_F_HID) += usb_f_hid.o
+usb_f_printer-y := f_printer.o
+obj-$(CONFIG_USB_F_PRINTER) += usb_f_printer.o
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index a2612fb79eff..13dfc9915b1d 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -908,7 +908,6 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
/* disable/free request and end point */
usb_ep_disable(hidg->in_ep);
- usb_ep_dequeue(hidg->in_ep, hidg->req);
kfree(hidg->req->buf);
usb_ep_free_request(hidg->in_ep, hidg->req);
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index 811929cd4c9e..3cc109f3c9c8 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -1085,7 +1085,7 @@ static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh)
if (!curlun) { /* Unsupported LUNs are okay */
common->bad_lun_okay = 1;
memset(buf, 0, 36);
- buf[0] = 0x7f; /* Unsupported, no device-type */
+ buf[0] = TYPE_NO_LUN; /* Unsupported, no device-type */
buf[4] = 31; /* Additional length */
return 36;
}
@@ -2624,13 +2624,10 @@ static ssize_t file_store(struct device *dev, struct device_attribute *attr,
return fsg_store_file(curlun, filesem, buf, count);
}
-static DEVICE_ATTR_RW(ro);
static DEVICE_ATTR_RW(nofua);
-static DEVICE_ATTR_RW(file);
-
-static struct device_attribute dev_attr_ro_cdrom = __ATTR_RO(ro);
-static struct device_attribute dev_attr_file_nonremovable = __ATTR_RO(file);
-
+/* mode wil be set in fsg_lun_attr_is_visible() */
+static DEVICE_ATTR(ro, 0, ro_show, ro_store);
+static DEVICE_ATTR(file, 0, file_show, file_store);
/****************************** FSG COMMON ******************************/
@@ -2745,40 +2742,10 @@ error_release:
}
EXPORT_SYMBOL_GPL(fsg_common_set_num_buffers);
-static inline void fsg_common_remove_sysfs(struct fsg_lun *lun)
-{
- device_remove_file(&lun->dev, &dev_attr_nofua);
- /*
- * device_remove_file() =>
- *
- * here the attr (e.g. dev_attr_ro) is only used to be passed to:
- *
- * sysfs_remove_file() =>
- *
- * here e.g. both dev_attr_ro_cdrom and dev_attr_ro are in
- * the same namespace and
- * from here only attr->name is passed to:
- *
- * sysfs_hash_and_remove()
- *
- * attr->name is the same for dev_attr_ro_cdrom and
- * dev_attr_ro
- * attr->name is the same for dev_attr_file and
- * dev_attr_file_nonremovable
- *
- * so we don't differentiate between removing e.g. dev_attr_ro_cdrom
- * and dev_attr_ro
- */
- device_remove_file(&lun->dev, &dev_attr_ro);
- device_remove_file(&lun->dev, &dev_attr_file);
-}
-
void fsg_common_remove_lun(struct fsg_lun *lun, bool sysfs)
{
- if (sysfs) {
- fsg_common_remove_sysfs(lun);
+ if (sysfs)
device_unregister(&lun->dev);
- }
fsg_lun_close(lun);
kfree(lun);
}
@@ -2877,41 +2844,35 @@ int fsg_common_set_cdev(struct fsg_common *common,
}
EXPORT_SYMBOL_GPL(fsg_common_set_cdev);
-static inline int fsg_common_add_sysfs(struct fsg_common *common,
- struct fsg_lun *lun)
-{
- int rc;
+static struct attribute *fsg_lun_dev_attrs[] = {
+ &dev_attr_ro.attr,
+ &dev_attr_file.attr,
+ &dev_attr_nofua.attr,
+ NULL
+};
- rc = device_register(&lun->dev);
- if (rc) {
- put_device(&lun->dev);
- return rc;
- }
+static umode_t fsg_lun_dev_is_visible(struct kobject *kobj,
+ struct attribute *attr, int idx)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct fsg_lun *lun = fsg_lun_from_dev(dev);
- rc = device_create_file(&lun->dev,
- lun->cdrom
- ? &dev_attr_ro_cdrom
- : &dev_attr_ro);
- if (rc)
- goto error;
- rc = device_create_file(&lun->dev,
- lun->removable
- ? &dev_attr_file
- : &dev_attr_file_nonremovable);
- if (rc)
- goto error;
- rc = device_create_file(&lun->dev, &dev_attr_nofua);
- if (rc)
- goto error;
+ if (attr == &dev_attr_ro.attr)
+ return lun->cdrom ? S_IRUGO : (S_IWUSR | S_IRUGO);
+ if (attr == &dev_attr_file.attr)
+ return lun->removable ? (S_IWUSR | S_IRUGO) : S_IRUGO;
+ return attr->mode;
+}
- return 0;
+static const struct attribute_group fsg_lun_dev_group = {
+ .attrs = fsg_lun_dev_attrs,
+ .is_visible = fsg_lun_dev_is_visible,
+};
-error:
- /* removing nonexistent files is a no-op */
- fsg_common_remove_sysfs(lun);
- device_unregister(&lun->dev);
- return rc;
-}
+static const struct attribute_group *fsg_lun_dev_groups[] = {
+ &fsg_lun_dev_group,
+ NULL
+};
int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg,
unsigned int id, const char *name,
@@ -2949,13 +2910,15 @@ int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg,
} else {
lun->dev.release = fsg_lun_release;
lun->dev.parent = &common->gadget->dev;
+ lun->dev.groups = fsg_lun_dev_groups;
dev_set_drvdata(&lun->dev, &common->filesem);
dev_set_name(&lun->dev, "%s", name);
lun->name = dev_name(&lun->dev);
- rc = fsg_common_add_sysfs(common, lun);
+ rc = device_register(&lun->dev);
if (rc) {
pr_info("failed to register LUN%d: %d\n", id, rc);
+ put_device(&lun->dev);
goto error_sysfs;
}
}
@@ -2988,10 +2951,8 @@ int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg,
return 0;
error_lun:
- if (common->sysfs) {
- fsg_common_remove_sysfs(lun);
+ if (common->sysfs)
device_unregister(&lun->dev);
- }
fsg_lun_close(lun);
common->luns[id] = NULL;
error_sysfs:
@@ -3077,8 +3038,6 @@ static void fsg_common_release(struct kref *ref)
struct fsg_lun *lun = *lun_it;
if (!lun)
continue;
- if (common->sysfs)
- fsg_common_remove_sysfs(lun);
fsg_lun_close(lun);
if (common->sysfs)
device_unregister(&lun->dev);
diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c
new file mode 100644
index 000000000000..44173df27273
--- /dev/null
+++ b/drivers/usb/gadget/function/f_printer.c
@@ -0,0 +1,1471 @@
+/*
+ * f_printer.c - USB printer function driver
+ *
+ * Copied from drivers/usb/gadget/legacy/printer.c,
+ * which was:
+ *
+ * printer.c -- Printer gadget driver
+ *
+ * Copyright (C) 2003-2005 David Brownell
+ * Copyright (C) 2006 Craig W. Nadler
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/idr.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/moduleparam.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/types.h>
+#include <linux/ctype.h>
+#include <linux/cdev.h>
+
+#include <asm/byteorder.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/uaccess.h>
+#include <asm/unaligned.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/g_printer.h>
+
+#include "u_printer.h"
+
+#define PNP_STRING_LEN 1024
+#define PRINTER_MINORS 4
+#define GET_DEVICE_ID 0
+#define GET_PORT_STATUS 1
+#define SOFT_RESET 2
+
+static int major, minors;
+static struct class *usb_gadget_class;
+static DEFINE_IDA(printer_ida);
+static DEFINE_MUTEX(printer_ida_lock); /* protects access do printer_ida */
+
+/*-------------------------------------------------------------------------*/
+
+struct printer_dev {
+ spinlock_t lock; /* lock this structure */
+ /* lock buffer lists during read/write calls */
+ struct mutex lock_printer_io;
+ struct usb_gadget *gadget;
+ s8 interface;
+ struct usb_ep *in_ep, *out_ep;
+
+ struct list_head rx_reqs; /* List of free RX structs */
+ struct list_head rx_reqs_active; /* List of Active RX xfers */
+ struct list_head rx_buffers; /* List of completed xfers */
+ /* wait until there is data to be read. */
+ wait_queue_head_t rx_wait;
+ struct list_head tx_reqs; /* List of free TX structs */
+ struct list_head tx_reqs_active; /* List of Active TX xfers */
+ /* Wait until there are write buffers available to use. */
+ wait_queue_head_t tx_wait;
+ /* Wait until all write buffers have been sent. */
+ wait_queue_head_t tx_flush_wait;
+ struct usb_request *current_rx_req;
+ size_t current_rx_bytes;
+ u8 *current_rx_buf;
+ u8 printer_status;
+ u8 reset_printer;
+ int minor;
+ struct cdev printer_cdev;
+ u8 printer_cdev_open;
+ wait_queue_head_t wait;
+ unsigned q_len;
+ char *pnp_string; /* We don't own memory! */
+ struct usb_function function;
+};
+
+static inline struct printer_dev *func_to_printer(struct usb_function *f)
+{
+ return container_of(f, struct printer_dev, function);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * DESCRIPTORS ... most are static, but strings and (full) configuration
+ * descriptors are built on demand.
+ */
+
+/* holds our biggest descriptor */
+#define USB_DESC_BUFSIZE 256
+#define USB_BUFSIZE 8192
+
+static struct usb_interface_descriptor intf_desc = {
+ .bLength = sizeof(intf_desc),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_PRINTER,
+ .bInterfaceSubClass = 1, /* Printer Sub-Class */
+ .bInterfaceProtocol = 2, /* Bi-Directional */
+ .iInterface = 0
+};
+
+static struct usb_endpoint_descriptor fs_ep_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK
+};
+
+static struct usb_endpoint_descriptor fs_ep_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK
+};
+
+static struct usb_descriptor_header *fs_printer_function[] = {
+ (struct usb_descriptor_header *) &intf_desc,
+ (struct usb_descriptor_header *) &fs_ep_in_desc,
+ (struct usb_descriptor_header *) &fs_ep_out_desc,
+ NULL
+};
+
+/*
+ * usb 2.0 devices need to expose both high speed and full speed
+ * descriptors, unless they only run at full speed.
+ */
+
+static struct usb_endpoint_descriptor hs_ep_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512)
+};
+
+static struct usb_endpoint_descriptor hs_ep_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512)
+};
+
+static struct usb_qualifier_descriptor dev_qualifier = {
+ .bLength = sizeof(dev_qualifier),
+ .bDescriptorType = USB_DT_DEVICE_QUALIFIER,
+ .bcdUSB = cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_PRINTER,
+ .bNumConfigurations = 1
+};
+
+static struct usb_descriptor_header *hs_printer_function[] = {
+ (struct usb_descriptor_header *) &intf_desc,
+ (struct usb_descriptor_header *) &hs_ep_in_desc,
+ (struct usb_descriptor_header *) &hs_ep_out_desc,
+ NULL
+};
+
+/*
+ * Added endpoint descriptors for 3.0 devices
+ */
+
+static struct usb_endpoint_descriptor ss_ep_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ss_ep_in_comp_desc = {
+ .bLength = sizeof(ss_ep_in_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_endpoint_descriptor ss_ep_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ss_ep_out_comp_desc = {
+ .bLength = sizeof(ss_ep_out_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_descriptor_header *ss_printer_function[] = {
+ (struct usb_descriptor_header *) &intf_desc,
+ (struct usb_descriptor_header *) &ss_ep_in_desc,
+ (struct usb_descriptor_header *) &ss_ep_in_comp_desc,
+ (struct usb_descriptor_header *) &ss_ep_out_desc,
+ (struct usb_descriptor_header *) &ss_ep_out_comp_desc,
+ NULL
+};
+
+/* maxpacket and other transfer characteristics vary by speed. */
+static inline struct usb_endpoint_descriptor *ep_desc(struct usb_gadget *gadget,
+ struct usb_endpoint_descriptor *fs,
+ struct usb_endpoint_descriptor *hs,
+ struct usb_endpoint_descriptor *ss)
+{
+ switch (gadget->speed) {
+ case USB_SPEED_SUPER:
+ return ss;
+ case USB_SPEED_HIGH:
+ return hs;
+ default:
+ return fs;
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_request *
+printer_req_alloc(struct usb_ep *ep, unsigned len, gfp_t gfp_flags)
+{
+ struct usb_request *req;
+
+ req = usb_ep_alloc_request(ep, gfp_flags);
+
+ if (req != NULL) {
+ req->length = len;
+ req->buf = kmalloc(len, gfp_flags);
+ if (req->buf == NULL) {
+ usb_ep_free_request(ep, req);
+ return NULL;
+ }
+ }
+
+ return req;
+}
+
+static void
+printer_req_free(struct usb_ep *ep, struct usb_request *req)
+{
+ if (ep != NULL && req != NULL) {
+ kfree(req->buf);
+ usb_ep_free_request(ep, req);
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void rx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct printer_dev *dev = ep->driver_data;
+ int status = req->status;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ list_del_init(&req->list); /* Remode from Active List */
+
+ switch (status) {
+
+ /* normal completion */
+ case 0:
+ if (req->actual > 0) {
+ list_add_tail(&req->list, &dev->rx_buffers);
+ DBG(dev, "G_Printer : rx length %d\n", req->actual);
+ } else {
+ list_add(&req->list, &dev->rx_reqs);
+ }
+ break;
+
+ /* software-driven interface shutdown */
+ case -ECONNRESET: /* unlink */
+ case -ESHUTDOWN: /* disconnect etc */
+ VDBG(dev, "rx shutdown, code %d\n", status);
+ list_add(&req->list, &dev->rx_reqs);
+ break;
+
+ /* for hardware automagic (such as pxa) */
+ case -ECONNABORTED: /* endpoint reset */
+ DBG(dev, "rx %s reset\n", ep->name);
+ list_add(&req->list, &dev->rx_reqs);
+ break;
+
+ /* data overrun */
+ case -EOVERFLOW:
+ /* FALLTHROUGH */
+
+ default:
+ DBG(dev, "rx status %d\n", status);
+ list_add(&req->list, &dev->rx_reqs);
+ break;
+ }
+
+ wake_up_interruptible(&dev->rx_wait);
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void tx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct printer_dev *dev = ep->driver_data;
+
+ switch (req->status) {
+ default:
+ VDBG(dev, "tx err %d\n", req->status);
+ /* FALLTHROUGH */
+ case -ECONNRESET: /* unlink */
+ case -ESHUTDOWN: /* disconnect etc */
+ break;
+ case 0:
+ break;
+ }
+
+ spin_lock(&dev->lock);
+ /* Take the request struct off the active list and put it on the
+ * free list.
+ */
+ list_del_init(&req->list);
+ list_add(&req->list, &dev->tx_reqs);
+ wake_up_interruptible(&dev->tx_wait);
+ if (likely(list_empty(&dev->tx_reqs_active)))
+ wake_up_interruptible(&dev->tx_flush_wait);
+
+ spin_unlock(&dev->lock);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int
+printer_open(struct inode *inode, struct file *fd)
+{
+ struct printer_dev *dev;
+ unsigned long flags;
+ int ret = -EBUSY;
+
+ dev = container_of(inode->i_cdev, struct printer_dev, printer_cdev);
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ if (!dev->printer_cdev_open) {
+ dev->printer_cdev_open = 1;
+ fd->private_data = dev;
+ ret = 0;
+ /* Change the printer status to show that it's on-line. */
+ dev->printer_status |= PRINTER_SELECTED;
+ }
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ DBG(dev, "printer_open returned %x\n", ret);
+ return ret;
+}
+
+static int
+printer_close(struct inode *inode, struct file *fd)
+{
+ struct printer_dev *dev = fd->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->printer_cdev_open = 0;
+ fd->private_data = NULL;
+ /* Change printer status to show that the printer is off-line. */
+ dev->printer_status &= ~PRINTER_SELECTED;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ DBG(dev, "printer_close\n");
+
+ return 0;
+}
+
+/* This function must be called with interrupts turned off. */
+static void
+setup_rx_reqs(struct printer_dev *dev)
+{
+ struct usb_request *req;
+
+ while (likely(!list_empty(&dev->rx_reqs))) {
+ int error;
+
+ req = container_of(dev->rx_reqs.next,
+ struct usb_request, list);
+ list_del_init(&req->list);
+
+ /* The USB Host sends us whatever amount of data it wants to
+ * so we always set the length field to the full USB_BUFSIZE.
+ * If the amount of data is more than the read() caller asked
+ * for it will be stored in the request buffer until it is
+ * asked for by read().
+ */
+ req->length = USB_BUFSIZE;
+ req->complete = rx_complete;
+
+ /* here, we unlock, and only unlock, to avoid deadlock. */
+ spin_unlock(&dev->lock);
+ error = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC);
+ spin_lock(&dev->lock);
+ if (error) {
+ DBG(dev, "rx submit --> %d\n", error);
+ list_add(&req->list, &dev->rx_reqs);
+ break;
+ }
+ /* if the req is empty, then add it into dev->rx_reqs_active. */
+ else if (list_empty(&req->list))
+ list_add(&req->list, &dev->rx_reqs_active);
+ }
+}
+
+static ssize_t
+printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
+{
+ struct printer_dev *dev = fd->private_data;
+ unsigned long flags;
+ size_t size;
+ size_t bytes_copied;
+ struct usb_request *req;
+ /* This is a pointer to the current USB rx request. */
+ struct usb_request *current_rx_req;
+ /* This is the number of bytes in the current rx buffer. */
+ size_t current_rx_bytes;
+ /* This is a pointer to the current rx buffer. */
+ u8 *current_rx_buf;
+
+ if (len == 0)
+ return -EINVAL;
+
+ DBG(dev, "printer_read trying to read %d bytes\n", (int)len);
+
+ mutex_lock(&dev->lock_printer_io);
+ spin_lock_irqsave(&dev->lock, flags);
+
+ /* We will use this flag later to check if a printer reset happened
+ * after we turn interrupts back on.
+ */
+ dev->reset_printer = 0;
+
+ setup_rx_reqs(dev);
+
+ bytes_copied = 0;
+ current_rx_req = dev->current_rx_req;
+ current_rx_bytes = dev->current_rx_bytes;
+ current_rx_buf = dev->current_rx_buf;
+ dev->current_rx_req = NULL;
+ dev->current_rx_bytes = 0;
+ dev->current_rx_buf = NULL;
+
+ /* Check if there is any data in the read buffers. Please note that
+ * current_rx_bytes is the number of bytes in the current rx buffer.
+ * If it is zero then check if there are any other rx_buffers that
+ * are on the completed list. We are only out of data if all rx
+ * buffers are empty.
+ */
+ if ((current_rx_bytes == 0) &&
+ (likely(list_empty(&dev->rx_buffers)))) {
+ /* Turn interrupts back on before sleeping. */
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ /*
+ * If no data is available check if this is a NON-Blocking
+ * call or not.
+ */
+ if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) {
+ mutex_unlock(&dev->lock_printer_io);
+ return -EAGAIN;
+ }
+
+ /* Sleep until data is available */
+ wait_event_interruptible(dev->rx_wait,
+ (likely(!list_empty(&dev->rx_buffers))));
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+
+ /* We have data to return then copy it to the caller's buffer.*/
+ while ((current_rx_bytes || likely(!list_empty(&dev->rx_buffers)))
+ && len) {
+ if (current_rx_bytes == 0) {
+ req = container_of(dev->rx_buffers.next,
+ struct usb_request, list);
+ list_del_init(&req->list);
+
+ if (req->actual && req->buf) {
+ current_rx_req = req;
+ current_rx_bytes = req->actual;
+ current_rx_buf = req->buf;
+ } else {
+ list_add(&req->list, &dev->rx_reqs);
+ continue;
+ }
+ }
+
+ /* Don't leave irqs off while doing memory copies */
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ if (len > current_rx_bytes)
+ size = current_rx_bytes;
+ else
+ size = len;
+
+ size -= copy_to_user(buf, current_rx_buf, size);
+ bytes_copied += size;
+ len -= size;
+ buf += size;
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ /* We've disconnected or reset so return. */
+ if (dev->reset_printer) {
+ list_add(&current_rx_req->list, &dev->rx_reqs);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ mutex_unlock(&dev->lock_printer_io);
+ return -EAGAIN;
+ }
+
+ /* If we not returning all the data left in this RX request
+ * buffer then adjust the amount of data left in the buffer.
+ * Othewise if we are done with this RX request buffer then
+ * requeue it to get any incoming data from the USB host.
+ */
+ if (size < current_rx_bytes) {
+ current_rx_bytes -= size;
+ current_rx_buf += size;
+ } else {
+ list_add(&current_rx_req->list, &dev->rx_reqs);
+ current_rx_bytes = 0;
+ current_rx_buf = NULL;
+ current_rx_req = NULL;
+ }
+ }
+
+ dev->current_rx_req = current_rx_req;
+ dev->current_rx_bytes = current_rx_bytes;
+ dev->current_rx_buf = current_rx_buf;
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+ mutex_unlock(&dev->lock_printer_io);
+
+ DBG(dev, "printer_read returned %d bytes\n", (int)bytes_copied);
+
+ if (bytes_copied)
+ return bytes_copied;
+ else
+ return -EAGAIN;
+}
+
+static ssize_t
+printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
+{
+ struct printer_dev *dev = fd->private_data;
+ unsigned long flags;
+ size_t size; /* Amount of data in a TX request. */
+ size_t bytes_copied = 0;
+ struct usb_request *req;
+
+ DBG(dev, "printer_write trying to send %d bytes\n", (int)len);
+
+ if (len == 0)
+ return -EINVAL;
+
+ mutex_lock(&dev->lock_printer_io);
+ spin_lock_irqsave(&dev->lock, flags);
+
+ /* Check if a printer reset happens while we have interrupts on */
+ dev->reset_printer = 0;
+
+ /* Check if there is any available write buffers */
+ if (likely(list_empty(&dev->tx_reqs))) {
+ /* Turn interrupts back on before sleeping. */
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ /*
+ * If write buffers are available check if this is
+ * a NON-Blocking call or not.
+ */
+ if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) {
+ mutex_unlock(&dev->lock_printer_io);
+ return -EAGAIN;
+ }
+
+ /* Sleep until a write buffer is available */
+ wait_event_interruptible(dev->tx_wait,
+ (likely(!list_empty(&dev->tx_reqs))));
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+
+ while (likely(!list_empty(&dev->tx_reqs)) && len) {
+
+ if (len > USB_BUFSIZE)
+ size = USB_BUFSIZE;
+ else
+ size = len;
+
+ req = container_of(dev->tx_reqs.next, struct usb_request,
+ list);
+ list_del_init(&req->list);
+
+ req->complete = tx_complete;
+ req->length = size;
+
+ /* Check if we need to send a zero length packet. */
+ if (len > size)
+ /* They will be more TX requests so no yet. */
+ req->zero = 0;
+ else
+ /* If the data amount is not a multiple of the
+ * maxpacket size then send a zero length packet.
+ */
+ req->zero = ((len % dev->in_ep->maxpacket) == 0);
+
+ /* Don't leave irqs off while doing memory copies */
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ if (copy_from_user(req->buf, buf, size)) {
+ list_add(&req->list, &dev->tx_reqs);
+ mutex_unlock(&dev->lock_printer_io);
+ return bytes_copied;
+ }
+
+ bytes_copied += size;
+ len -= size;
+ buf += size;
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ /* We've disconnected or reset so free the req and buffer */
+ if (dev->reset_printer) {
+ list_add(&req->list, &dev->tx_reqs);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ mutex_unlock(&dev->lock_printer_io);
+ return -EAGAIN;
+ }
+
+ if (usb_ep_queue(dev->in_ep, req, GFP_ATOMIC)) {
+ list_add(&req->list, &dev->tx_reqs);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ mutex_unlock(&dev->lock_printer_io);
+ return -EAGAIN;
+ }
+
+ list_add(&req->list, &dev->tx_reqs_active);
+
+ }
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+ mutex_unlock(&dev->lock_printer_io);
+
+ DBG(dev, "printer_write sent %d bytes\n", (int)bytes_copied);
+
+ if (bytes_copied)
+ return bytes_copied;
+ else
+ return -EAGAIN;
+}
+
+static int
+printer_fsync(struct file *fd, loff_t start, loff_t end, int datasync)
+{
+ struct printer_dev *dev = fd->private_data;
+ struct inode *inode = file_inode(fd);
+ unsigned long flags;
+ int tx_list_empty;
+
+ mutex_lock(&inode->i_mutex);
+ spin_lock_irqsave(&dev->lock, flags);
+ tx_list_empty = (likely(list_empty(&dev->tx_reqs)));
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ if (!tx_list_empty) {
+ /* Sleep until all data has been sent */
+ wait_event_interruptible(dev->tx_flush_wait,
+ (likely(list_empty(&dev->tx_reqs_active))));
+ }
+ mutex_unlock(&inode->i_mutex);
+
+ return 0;
+}
+
+static unsigned int
+printer_poll(struct file *fd, poll_table *wait)
+{
+ struct printer_dev *dev = fd->private_data;
+ unsigned long flags;
+ int status = 0;
+
+ mutex_lock(&dev->lock_printer_io);
+ spin_lock_irqsave(&dev->lock, flags);
+ setup_rx_reqs(dev);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ mutex_unlock(&dev->lock_printer_io);
+
+ poll_wait(fd, &dev->rx_wait, wait);
+ poll_wait(fd, &dev->tx_wait, wait);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (likely(!list_empty(&dev->tx_reqs)))
+ status |= POLLOUT | POLLWRNORM;
+
+ if (likely(dev->current_rx_bytes) ||
+ likely(!list_empty(&dev->rx_buffers)))
+ status |= POLLIN | POLLRDNORM;
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return status;
+}
+
+static long
+printer_ioctl(struct file *fd, unsigned int code, unsigned long arg)
+{
+ struct printer_dev *dev = fd->private_data;
+ unsigned long flags;
+ int status = 0;
+
+ DBG(dev, "printer_ioctl: cmd=0x%4.4x, arg=%lu\n", code, arg);
+
+ /* handle ioctls */
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ switch (code) {
+ case GADGET_GET_PRINTER_STATUS:
+ status = (int)dev->printer_status;
+ break;
+ case GADGET_SET_PRINTER_STATUS:
+ dev->printer_status = (u8)arg;
+ break;
+ default:
+ /* could not handle ioctl */
+ DBG(dev, "printer_ioctl: ERROR cmd=0x%4.4xis not supported\n",
+ code);
+ status = -ENOTTY;
+ }
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return status;
+}
+
+/* used after endpoint configuration */
+static const struct file_operations printer_io_operations = {
+ .owner = THIS_MODULE,
+ .open = printer_open,
+ .read = printer_read,
+ .write = printer_write,
+ .fsync = printer_fsync,
+ .poll = printer_poll,
+ .unlocked_ioctl = printer_ioctl,
+ .release = printer_close,
+ .llseek = noop_llseek,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int
+set_printer_interface(struct printer_dev *dev)
+{
+ int result = 0;
+
+ dev->in_ep->desc = ep_desc(dev->gadget, &fs_ep_in_desc, &hs_ep_in_desc,
+ &ss_ep_in_desc);
+ dev->in_ep->driver_data = dev;
+
+ dev->out_ep->desc = ep_desc(dev->gadget, &fs_ep_out_desc,
+ &hs_ep_out_desc, &ss_ep_out_desc);
+ dev->out_ep->driver_data = dev;
+
+ result = usb_ep_enable(dev->in_ep);
+ if (result != 0) {
+ DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result);
+ goto done;
+ }
+
+ result = usb_ep_enable(dev->out_ep);
+ if (result != 0) {
+ DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result);
+ goto done;
+ }
+
+done:
+ /* on error, disable any endpoints */
+ if (result != 0) {
+ (void) usb_ep_disable(dev->in_ep);
+ (void) usb_ep_disable(dev->out_ep);
+ dev->in_ep->desc = NULL;
+ dev->out_ep->desc = NULL;
+ }
+
+ /* caller is responsible for cleanup on error */
+ return result;
+}
+
+static void printer_reset_interface(struct printer_dev *dev)
+{
+ if (dev->interface < 0)
+ return;
+
+ DBG(dev, "%s\n", __func__);
+
+ if (dev->in_ep->desc)
+ usb_ep_disable(dev->in_ep);
+
+ if (dev->out_ep->desc)
+ usb_ep_disable(dev->out_ep);
+
+ dev->in_ep->desc = NULL;
+ dev->out_ep->desc = NULL;
+ dev->interface = -1;
+}
+
+/* Change our operational Interface. */
+static int set_interface(struct printer_dev *dev, unsigned number)
+{
+ int result = 0;
+
+ /* Free the current interface */
+ printer_reset_interface(dev);
+
+ result = set_printer_interface(dev);
+ if (result)
+ printer_reset_interface(dev);
+ else
+ dev->interface = number;
+
+ if (!result)
+ INFO(dev, "Using interface %x\n", number);
+
+ return result;
+}
+
+static void printer_soft_reset(struct printer_dev *dev)
+{
+ struct usb_request *req;
+
+ INFO(dev, "Received Printer Reset Request\n");
+
+ if (usb_ep_disable(dev->in_ep))
+ DBG(dev, "Failed to disable USB in_ep\n");
+ if (usb_ep_disable(dev->out_ep))
+ DBG(dev, "Failed to disable USB out_ep\n");
+
+ if (dev->current_rx_req != NULL) {
+ list_add(&dev->current_rx_req->list, &dev->rx_reqs);
+ dev->current_rx_req = NULL;
+ }
+ dev->current_rx_bytes = 0;
+ dev->current_rx_buf = NULL;
+ dev->reset_printer = 1;
+
+ while (likely(!(list_empty(&dev->rx_buffers)))) {
+ req = container_of(dev->rx_buffers.next, struct usb_request,
+ list);
+ list_del_init(&req->list);
+ list_add(&req->list, &dev->rx_reqs);
+ }
+
+ while (likely(!(list_empty(&dev->rx_reqs_active)))) {
+ req = container_of(dev->rx_buffers.next, struct usb_request,
+ list);
+ list_del_init(&req->list);
+ list_add(&req->list, &dev->rx_reqs);
+ }
+
+ while (likely(!(list_empty(&dev->tx_reqs_active)))) {
+ req = container_of(dev->tx_reqs_active.next,
+ struct usb_request, list);
+ list_del_init(&req->list);
+ list_add(&req->list, &dev->tx_reqs);
+ }
+
+ if (usb_ep_enable(dev->in_ep))
+ DBG(dev, "Failed to enable USB in_ep\n");
+ if (usb_ep_enable(dev->out_ep))
+ DBG(dev, "Failed to enable USB out_ep\n");
+
+ wake_up_interruptible(&dev->rx_wait);
+ wake_up_interruptible(&dev->tx_wait);
+ wake_up_interruptible(&dev->tx_flush_wait);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static bool gprinter_req_match(struct usb_function *f,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct printer_dev *dev = func_to_printer(f);
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+
+ if ((ctrl->bRequestType & USB_RECIP_MASK) != USB_RECIP_INTERFACE ||
+ (ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS)
+ return false;
+
+ switch (ctrl->bRequest) {
+ case GET_DEVICE_ID:
+ w_index >>= 8;
+ if (w_length <= PNP_STRING_LEN &&
+ (USB_DIR_IN & ctrl->bRequestType))
+ break;
+ return false;
+ case GET_PORT_STATUS:
+ if (!w_value && w_length == 1 &&
+ (USB_DIR_IN & ctrl->bRequestType))
+ break;
+ return false;
+ case SOFT_RESET:
+ if (!w_value && !w_length &&
+ !(USB_DIR_IN & ctrl->bRequestType))
+ break;
+ /* fall through */
+ default:
+ return false;
+ }
+ return w_index == dev->interface;
+}
+
+/*
+ * The setup() callback implements all the ep0 functionality that's not
+ * handled lower down.
+ */
+static int printer_func_setup(struct usb_function *f,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct printer_dev *dev = func_to_printer(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_request *req = cdev->req;
+ int value = -EOPNOTSUPP;
+ u16 wIndex = le16_to_cpu(ctrl->wIndex);
+ u16 wValue = le16_to_cpu(ctrl->wValue);
+ u16 wLength = le16_to_cpu(ctrl->wLength);
+
+ DBG(dev, "ctrl req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest, wValue, wIndex, wLength);
+
+ switch (ctrl->bRequestType&USB_TYPE_MASK) {
+ case USB_TYPE_CLASS:
+ switch (ctrl->bRequest) {
+ case GET_DEVICE_ID: /* Get the IEEE-1284 PNP String */
+ /* Only one printer interface is supported. */
+ if ((wIndex>>8) != dev->interface)
+ break;
+
+ value = (dev->pnp_string[0] << 8) | dev->pnp_string[1];
+ memcpy(req->buf, dev->pnp_string, value);
+ DBG(dev, "1284 PNP String: %x %s\n", value,
+ &dev->pnp_string[2]);
+ break;
+
+ case GET_PORT_STATUS: /* Get Port Status */
+ /* Only one printer interface is supported. */
+ if (wIndex != dev->interface)
+ break;
+
+ *(u8 *)req->buf = dev->printer_status;
+ value = min_t(u16, wLength, 1);
+ break;
+
+ case SOFT_RESET: /* Soft Reset */
+ /* Only one printer interface is supported. */
+ if (wIndex != dev->interface)
+ break;
+
+ printer_soft_reset(dev);
+
+ value = 0;
+ break;
+
+ default:
+ goto unknown;
+ }
+ break;
+
+ default:
+unknown:
+ VDBG(dev,
+ "unknown ctrl req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ wValue, wIndex, wLength);
+ break;
+ }
+ /* host either stalls (value < 0) or reports success */
+ if (value >= 0) {
+ req->length = value;
+ req->zero = value < wLength;
+ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+ if (value < 0) {
+ ERROR(dev, "%s:%d Error!\n", __func__, __LINE__);
+ req->status = 0;
+ }
+ }
+ return value;
+}
+
+static int printer_func_bind(struct usb_configuration *c,
+ struct usb_function *f)
+{
+ struct usb_gadget *gadget = c->cdev->gadget;
+ struct printer_dev *dev = func_to_printer(f);
+ struct device *pdev;
+ struct usb_composite_dev *cdev = c->cdev;
+ struct usb_ep *in_ep;
+ struct usb_ep *out_ep = NULL;
+ struct usb_request *req;
+ dev_t devt;
+ int id;
+ int ret;
+ u32 i;
+
+ id = usb_interface_id(c, f);
+ if (id < 0)
+ return id;
+ intf_desc.bInterfaceNumber = id;
+
+ /* finish hookup to lower layer ... */
+ dev->gadget = gadget;
+
+ /* all we really need is bulk IN/OUT */
+ in_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_in_desc);
+ if (!in_ep) {
+autoconf_fail:
+ dev_err(&cdev->gadget->dev, "can't autoconfigure on %s\n",
+ cdev->gadget->name);
+ return -ENODEV;
+ }
+ in_ep->driver_data = in_ep; /* claim */
+
+ out_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_out_desc);
+ if (!out_ep)
+ goto autoconf_fail;
+ out_ep->driver_data = out_ep; /* claim */
+
+ /* assumes that all endpoints are dual-speed */
+ hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
+ hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
+ ss_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
+ ss_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
+
+ ret = usb_assign_descriptors(f, fs_printer_function,
+ hs_printer_function, ss_printer_function);
+ if (ret)
+ return ret;
+
+ dev->in_ep = in_ep;
+ dev->out_ep = out_ep;
+
+ ret = -ENOMEM;
+ for (i = 0; i < dev->q_len; i++) {
+ req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL);
+ if (!req)
+ goto fail_tx_reqs;
+ list_add(&req->list, &dev->tx_reqs);
+ }
+
+ for (i = 0; i < dev->q_len; i++) {
+ req = printer_req_alloc(dev->out_ep, USB_BUFSIZE, GFP_KERNEL);
+ if (!req)
+ goto fail_rx_reqs;
+ list_add(&req->list, &dev->rx_reqs);
+ }
+
+ /* Setup the sysfs files for the printer gadget. */
+ devt = MKDEV(major, dev->minor);
+ pdev = device_create(usb_gadget_class, NULL, devt,
+ NULL, "g_printer%d", dev->minor);
+ if (IS_ERR(pdev)) {
+ ERROR(dev, "Failed to create device: g_printer\n");
+ ret = PTR_ERR(pdev);
+ goto fail_rx_reqs;
+ }
+
+ /*
+ * Register a character device as an interface to a user mode
+ * program that handles the printer specific functionality.
+ */
+ cdev_init(&dev->printer_cdev, &printer_io_operations);
+ dev->printer_cdev.owner = THIS_MODULE;
+ ret = cdev_add(&dev->printer_cdev, devt, 1);
+ if (ret) {
+ ERROR(dev, "Failed to open char device\n");
+ goto fail_cdev_add;
+ }
+
+ return 0;
+
+fail_cdev_add:
+ device_destroy(usb_gadget_class, devt);
+
+fail_rx_reqs:
+ while (!list_empty(&dev->rx_reqs)) {
+ req = container_of(dev->rx_reqs.next, struct usb_request, list);
+ list_del(&req->list);
+ printer_req_free(dev->out_ep, req);
+ }
+
+fail_tx_reqs:
+ while (!list_empty(&dev->tx_reqs)) {
+ req = container_of(dev->tx_reqs.next, struct usb_request, list);
+ list_del(&req->list);
+ printer_req_free(dev->in_ep, req);
+ }
+
+ return ret;
+
+}
+
+static int printer_func_set_alt(struct usb_function *f,
+ unsigned intf, unsigned alt)
+{
+ struct printer_dev *dev = func_to_printer(f);
+ int ret = -ENOTSUPP;
+
+ if (!alt)
+ ret = set_interface(dev, intf);
+
+ return ret;
+}
+
+static void printer_func_disable(struct usb_function *f)
+{
+ struct printer_dev *dev = func_to_printer(f);
+ unsigned long flags;
+
+ DBG(dev, "%s\n", __func__);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ printer_reset_interface(dev);
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static inline struct f_printer_opts
+*to_f_printer_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_printer_opts,
+ func_inst.group);
+}
+
+CONFIGFS_ATTR_STRUCT(f_printer_opts);
+CONFIGFS_ATTR_OPS(f_printer_opts);
+
+static void printer_attr_release(struct config_item *item)
+{
+ struct f_printer_opts *opts = to_f_printer_opts(item);
+
+ usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations printer_item_ops = {
+ .release = printer_attr_release,
+ .show_attribute = f_printer_opts_attr_show,
+ .store_attribute = f_printer_opts_attr_store,
+};
+
+static ssize_t f_printer_opts_pnp_string_show(struct f_printer_opts *opts,
+ char *page)
+{
+ int result;
+
+ mutex_lock(&opts->lock);
+ result = strlcpy(page, opts->pnp_string + 2, PNP_STRING_LEN - 2);
+ mutex_unlock(&opts->lock);
+
+ return result;
+}
+
+static ssize_t f_printer_opts_pnp_string_store(struct f_printer_opts *opts,
+ const char *page, size_t len)
+{
+ int result, l;
+
+ mutex_lock(&opts->lock);
+ result = strlcpy(opts->pnp_string + 2, page, PNP_STRING_LEN - 2);
+ l = strlen(opts->pnp_string + 2) + 2;
+ opts->pnp_string[0] = (l >> 8) & 0xFF;
+ opts->pnp_string[1] = l & 0xFF;
+ mutex_unlock(&opts->lock);
+
+ return result;
+}
+
+static struct f_printer_opts_attribute f_printer_opts_pnp_string =
+ __CONFIGFS_ATTR(pnp_string, S_IRUGO | S_IWUSR,
+ f_printer_opts_pnp_string_show,
+ f_printer_opts_pnp_string_store);
+
+static ssize_t f_printer_opts_q_len_show(struct f_printer_opts *opts,
+ char *page)
+{
+ int result;
+
+ mutex_lock(&opts->lock);
+ result = sprintf(page, "%d\n", opts->q_len);
+ mutex_unlock(&opts->lock);
+
+ return result;
+}
+
+static ssize_t f_printer_opts_q_len_store(struct f_printer_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;
+
+ opts->q_len = (unsigned)num;
+ ret = len;
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+static struct f_printer_opts_attribute f_printer_opts_q_len =
+ __CONFIGFS_ATTR(q_len, S_IRUGO | S_IWUSR, f_printer_opts_q_len_show,
+ f_printer_opts_q_len_store);
+
+static struct configfs_attribute *printer_attrs[] = {
+ &f_printer_opts_pnp_string.attr,
+ &f_printer_opts_q_len.attr,
+ NULL,
+};
+
+static struct config_item_type printer_func_type = {
+ .ct_item_ops = &printer_item_ops,
+ .ct_attrs = printer_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static inline int gprinter_get_minor(void)
+{
+ return ida_simple_get(&printer_ida, 0, 0, GFP_KERNEL);
+}
+
+static inline void gprinter_put_minor(int minor)
+{
+ ida_simple_remove(&printer_ida, minor);
+}
+
+static int gprinter_setup(int);
+static void gprinter_cleanup(void);
+
+static void gprinter_free_inst(struct usb_function_instance *f)
+{
+ struct f_printer_opts *opts;
+
+ opts = container_of(f, struct f_printer_opts, func_inst);
+
+ mutex_lock(&printer_ida_lock);
+
+ gprinter_put_minor(opts->minor);
+ if (idr_is_empty(&printer_ida.idr))
+ gprinter_cleanup();
+
+ mutex_unlock(&printer_ida_lock);
+
+ kfree(opts);
+}
+
+static struct usb_function_instance *gprinter_alloc_inst(void)
+{
+ struct f_printer_opts *opts;
+ struct usb_function_instance *ret;
+ int status = 0;
+
+ opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ if (!opts)
+ return ERR_PTR(-ENOMEM);
+
+ mutex_init(&opts->lock);
+ opts->func_inst.free_func_inst = gprinter_free_inst;
+ ret = &opts->func_inst;
+
+ mutex_lock(&printer_ida_lock);
+
+ if (idr_is_empty(&printer_ida.idr)) {
+ status = gprinter_setup(PRINTER_MINORS);
+ if (status) {
+ ret = ERR_PTR(status);
+ kfree(opts);
+ goto unlock;
+ }
+ }
+
+ opts->minor = gprinter_get_minor();
+ if (opts->minor < 0) {
+ ret = ERR_PTR(opts->minor);
+ kfree(opts);
+ if (idr_is_empty(&printer_ida.idr))
+ gprinter_cleanup();
+ goto unlock;
+ }
+ config_group_init_type_name(&opts->func_inst.group, "",
+ &printer_func_type);
+
+unlock:
+ mutex_unlock(&printer_ida_lock);
+ return ret;
+}
+
+static void gprinter_free(struct usb_function *f)
+{
+ struct printer_dev *dev = func_to_printer(f);
+ struct f_printer_opts *opts;
+
+ opts = container_of(f->fi, struct f_printer_opts, func_inst);
+ kfree(dev);
+ mutex_lock(&opts->lock);
+ --opts->refcnt;
+ mutex_unlock(&opts->lock);
+}
+
+static void printer_func_unbind(struct usb_configuration *c,
+ struct usb_function *f)
+{
+ struct printer_dev *dev;
+ struct usb_request *req;
+
+ dev = func_to_printer(f);
+
+ device_destroy(usb_gadget_class, MKDEV(major, dev->minor));
+
+ /* Remove Character Device */
+ cdev_del(&dev->printer_cdev);
+
+ /* we must already have been disconnected ... no i/o may be active */
+ WARN_ON(!list_empty(&dev->tx_reqs_active));
+ WARN_ON(!list_empty(&dev->rx_reqs_active));
+
+ /* Free all memory for this driver. */
+ while (!list_empty(&dev->tx_reqs)) {
+ req = container_of(dev->tx_reqs.next, struct usb_request,
+ list);
+ list_del(&req->list);
+ printer_req_free(dev->in_ep, req);
+ }
+
+ if (dev->current_rx_req != NULL)
+ printer_req_free(dev->out_ep, dev->current_rx_req);
+
+ while (!list_empty(&dev->rx_reqs)) {
+ req = container_of(dev->rx_reqs.next,
+ struct usb_request, list);
+ list_del(&req->list);
+ printer_req_free(dev->out_ep, req);
+ }
+
+ while (!list_empty(&dev->rx_buffers)) {
+ req = container_of(dev->rx_buffers.next,
+ struct usb_request, list);
+ list_del(&req->list);
+ printer_req_free(dev->out_ep, req);
+ }
+ usb_free_all_descriptors(f);
+}
+
+static struct usb_function *gprinter_alloc(struct usb_function_instance *fi)
+{
+ struct printer_dev *dev;
+ struct f_printer_opts *opts;
+
+ opts = container_of(fi, struct f_printer_opts, func_inst);
+
+ mutex_lock(&opts->lock);
+ if (opts->minor >= minors) {
+ mutex_unlock(&opts->lock);
+ return ERR_PTR(-ENOENT);
+ }
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ mutex_unlock(&opts->lock);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ ++opts->refcnt;
+ dev->minor = opts->minor;
+ dev->pnp_string = opts->pnp_string;
+ dev->q_len = opts->q_len;
+ mutex_unlock(&opts->lock);
+
+ dev->function.name = "printer";
+ dev->function.bind = printer_func_bind;
+ dev->function.setup = printer_func_setup;
+ dev->function.unbind = printer_func_unbind;
+ dev->function.set_alt = printer_func_set_alt;
+ dev->function.disable = printer_func_disable;
+ dev->function.req_match = gprinter_req_match;
+ dev->function.free_func = gprinter_free;
+
+ INIT_LIST_HEAD(&dev->tx_reqs);
+ INIT_LIST_HEAD(&dev->rx_reqs);
+ INIT_LIST_HEAD(&dev->rx_buffers);
+ INIT_LIST_HEAD(&dev->tx_reqs_active);
+ INIT_LIST_HEAD(&dev->rx_reqs_active);
+
+ spin_lock_init(&dev->lock);
+ mutex_init(&dev->lock_printer_io);
+ init_waitqueue_head(&dev->rx_wait);
+ init_waitqueue_head(&dev->tx_wait);
+ init_waitqueue_head(&dev->tx_flush_wait);
+
+ dev->interface = -1;
+ dev->printer_cdev_open = 0;
+ dev->printer_status = PRINTER_NOT_ERROR;
+ dev->current_rx_req = NULL;
+ dev->current_rx_bytes = 0;
+ dev->current_rx_buf = NULL;
+
+ return &dev->function;
+}
+
+DECLARE_USB_FUNCTION_INIT(printer, gprinter_alloc_inst, gprinter_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Craig Nadler");
+
+static int gprinter_setup(int count)
+{
+ int status;
+ dev_t devt;
+
+ usb_gadget_class = class_create(THIS_MODULE, "usb_printer_gadget");
+ if (IS_ERR(usb_gadget_class)) {
+ status = PTR_ERR(usb_gadget_class);
+ usb_gadget_class = NULL;
+ pr_err("unable to create usb_gadget class %d\n", status);
+ return status;
+ }
+
+ status = alloc_chrdev_region(&devt, 0, count, "USB printer gadget");
+ if (status) {
+ pr_err("alloc_chrdev_region %d\n", status);
+ class_destroy(usb_gadget_class);
+ usb_gadget_class = NULL;
+ return status;
+ }
+
+ major = MAJOR(devt);
+ minors = count;
+
+ return status;
+}
+
+static void gprinter_cleanup(void)
+{
+ if (major) {
+ unregister_chrdev_region(MKDEV(major, 0), minors);
+ major = minors = 0;
+ }
+ class_destroy(usb_gadget_class);
+ usb_gadget_class = NULL;
+}
diff --git a/drivers/usb/gadget/function/u_printer.h b/drivers/usb/gadget/function/u_printer.h
new file mode 100644
index 000000000000..0e2c49d4274e
--- /dev/null
+++ b/drivers/usb/gadget/function/u_printer.h
@@ -0,0 +1,37 @@
+/*
+ * u_printer.h
+ *
+ * Utility definitions for the printer function
+ *
+ * Copyright (c) 2015 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_PRINTER_H
+#define U_PRINTER_H
+
+#include <linux/usb/composite.h>
+
+#define PNP_STRING_LEN 1024
+
+struct f_printer_opts {
+ struct usb_function_instance func_inst;
+ int minor;
+ char pnp_string[PNP_STRING_LEN];
+ unsigned q_len;
+
+ /*
+ * Protect the data from concurrent access by read/write
+ * and create symlink/remove symlink
+ */
+ struct mutex lock;
+ int refcnt;
+};
+
+#endif /* U_PRINTER_H */
diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c
index 491082aaf103..89179ab20c10 100644
--- a/drivers/usb/gadget/function/u_serial.c
+++ b/drivers/usb/gadget/function/u_serial.c
@@ -912,7 +912,7 @@ static int gs_put_char(struct tty_struct *tty, unsigned char ch)
unsigned long flags;
int status;
- pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %pf\n",
+ pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %ps\n",
port->port_num, tty, ch, __builtin_return_address(0));
spin_lock_irqsave(&port->port_lock, flags);
diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig
index 113c87e22117..d5a7102de696 100644
--- a/drivers/usb/gadget/legacy/Kconfig
+++ b/drivers/usb/gadget/legacy/Kconfig
@@ -301,6 +301,7 @@ config USB_MIDI_GADGET
config USB_G_PRINTER
tristate "Printer Gadget"
select USB_LIBCOMPOSITE
+ select USB_F_PRINTER
help
The Printer Gadget channels data between the USB host and a
userspace program driving the print engine. The user space
diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c
index 90545980542f..d5b6ee725a2a 100644
--- a/drivers/usb/gadget/legacy/printer.c
+++ b/drivers/usb/gadget/legacy/printer.c
@@ -12,29 +12,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/mutex.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/timer.h>
-#include <linux/list.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/moduleparam.h>
-#include <linux/fs.h>
-#include <linux/poll.h>
-#include <linux/types.h>
-#include <linux/ctype.h>
-#include <linux/cdev.h>
-
#include <asm/byteorder.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/uaccess.h>
-#include <asm/unaligned.h>
#include <linux/usb/ch9.h>
#include <linux/usb/composite.h>
@@ -46,50 +24,12 @@
USB_GADGET_COMPOSITE_OPTIONS();
#define DRIVER_DESC "Printer Gadget"
-#define DRIVER_VERSION "2007 OCT 06"
+#define DRIVER_VERSION "2015 FEB 17"
-static DEFINE_MUTEX(printer_mutex);
static const char shortname [] = "printer";
static const char driver_desc [] = DRIVER_DESC;
-static dev_t g_printer_devno;
-
-static struct class *usb_gadget_class;
-
-/*-------------------------------------------------------------------------*/
-
-struct printer_dev {
- spinlock_t lock; /* lock this structure */
- /* lock buffer lists during read/write calls */
- struct mutex lock_printer_io;
- struct usb_gadget *gadget;
- s8 interface;
- struct usb_ep *in_ep, *out_ep;
-
- struct list_head rx_reqs; /* List of free RX structs */
- struct list_head rx_reqs_active; /* List of Active RX xfers */
- struct list_head rx_buffers; /* List of completed xfers */
- /* wait until there is data to be read. */
- wait_queue_head_t rx_wait;
- struct list_head tx_reqs; /* List of free TX structs */
- struct list_head tx_reqs_active; /* List of Active TX xfers */
- /* Wait until there are write buffers available to use. */
- wait_queue_head_t tx_wait;
- /* Wait until all write buffers have been sent. */
- wait_queue_head_t tx_flush_wait;
- struct usb_request *current_rx_req;
- size_t current_rx_bytes;
- u8 *current_rx_buf;
- u8 printer_status;
- u8 reset_printer;
- struct cdev printer_cdev;
- struct device *pdev;
- u8 printer_cdev_open;
- wait_queue_head_t wait;
- struct usb_function function;
-};
-
-static struct printer_dev usb_printer_gadget;
+#include "u_printer.h"
/*-------------------------------------------------------------------------*/
@@ -120,6 +60,9 @@ module_param(qlen, uint, S_IRUGO|S_IWUSR);
#define QLEN qlen
+static struct usb_function_instance *fi_printer;
+static struct usb_function *f_printer;
+
/*-------------------------------------------------------------------------*/
/*
@@ -127,10 +70,6 @@ module_param(qlen, uint, S_IRUGO|S_IWUSR);
* descriptors are built on demand.
*/
-/* holds our biggest descriptor */
-#define USB_DESC_BUFSIZE 256
-#define USB_BUFSIZE 8192
-
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
@@ -143,108 +82,6 @@ static struct usb_device_descriptor device_desc = {
.bNumConfigurations = 1
};
-static struct usb_interface_descriptor intf_desc = {
- .bLength = sizeof intf_desc,
- .bDescriptorType = USB_DT_INTERFACE,
- .bNumEndpoints = 2,
- .bInterfaceClass = USB_CLASS_PRINTER,
- .bInterfaceSubClass = 1, /* Printer Sub-Class */
- .bInterfaceProtocol = 2, /* Bi-Directional */
- .iInterface = 0
-};
-
-static struct usb_endpoint_descriptor fs_ep_in_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_BULK
-};
-
-static struct usb_endpoint_descriptor fs_ep_out_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = USB_DIR_OUT,
- .bmAttributes = USB_ENDPOINT_XFER_BULK
-};
-
-static struct usb_descriptor_header *fs_printer_function[] = {
- (struct usb_descriptor_header *) &intf_desc,
- (struct usb_descriptor_header *) &fs_ep_in_desc,
- (struct usb_descriptor_header *) &fs_ep_out_desc,
- NULL
-};
-
-/*
- * usb 2.0 devices need to expose both high speed and full speed
- * descriptors, unless they only run at full speed.
- */
-
-static struct usb_endpoint_descriptor hs_ep_in_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = cpu_to_le16(512)
-};
-
-static struct usb_endpoint_descriptor hs_ep_out_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = cpu_to_le16(512)
-};
-
-static struct usb_qualifier_descriptor dev_qualifier = {
- .bLength = sizeof dev_qualifier,
- .bDescriptorType = USB_DT_DEVICE_QUALIFIER,
- .bcdUSB = cpu_to_le16(0x0200),
- .bDeviceClass = USB_CLASS_PRINTER,
- .bNumConfigurations = 1
-};
-
-static struct usb_descriptor_header *hs_printer_function[] = {
- (struct usb_descriptor_header *) &intf_desc,
- (struct usb_descriptor_header *) &hs_ep_in_desc,
- (struct usb_descriptor_header *) &hs_ep_out_desc,
- NULL
-};
-
-/*
- * Added endpoint descriptors for 3.0 devices
- */
-
-static struct usb_endpoint_descriptor ss_ep_in_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = cpu_to_le16(1024),
-};
-
-static struct usb_ss_ep_comp_descriptor ss_ep_in_comp_desc = {
- .bLength = sizeof(ss_ep_in_comp_desc),
- .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
-};
-
-static struct usb_endpoint_descriptor ss_ep_out_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = cpu_to_le16(1024),
-};
-
-static struct usb_ss_ep_comp_descriptor ss_ep_out_comp_desc = {
- .bLength = sizeof(ss_ep_out_comp_desc),
- .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
-};
-
-static struct usb_descriptor_header *ss_printer_function[] = {
- (struct usb_descriptor_header *) &intf_desc,
- (struct usb_descriptor_header *) &ss_ep_in_desc,
- (struct usb_descriptor_header *) &ss_ep_in_comp_desc,
- (struct usb_descriptor_header *) &ss_ep_out_desc,
- (struct usb_descriptor_header *) &ss_ep_out_comp_desc,
- NULL
-};
-
static struct usb_otg_descriptor otg_descriptor = {
.bLength = sizeof otg_descriptor,
.bDescriptorType = USB_DT_OTG,
@@ -256,29 +93,13 @@ static const struct usb_descriptor_header *otg_desc[] = {
NULL,
};
-/* maxpacket and other transfer characteristics vary by speed. */
-static inline struct usb_endpoint_descriptor *ep_desc(struct usb_gadget *gadget,
- struct usb_endpoint_descriptor *fs,
- struct usb_endpoint_descriptor *hs,
- struct usb_endpoint_descriptor *ss)
-{
- switch (gadget->speed) {
- case USB_SPEED_SUPER:
- return ss;
- case USB_SPEED_HIGH:
- return hs;
- default:
- return fs;
- }
-}
-
/*-------------------------------------------------------------------------*/
/* descriptors that are built on-demand */
static char product_desc [40] = DRIVER_DESC;
static char serial_num [40] = "1";
-static char pnp_string [1024] =
+static char pnp_string[PNP_STRING_LEN] =
"XXMFG:linux;MDL:g_printer;CLS:PRINTER;SN:1;";
/* static strings, in UTF-8 */
@@ -299,921 +120,19 @@ static struct usb_gadget_strings *dev_strings[] = {
NULL,
};
-/*-------------------------------------------------------------------------*/
-
-static struct usb_request *
-printer_req_alloc(struct usb_ep *ep, unsigned len, gfp_t gfp_flags)
-{
- struct usb_request *req;
-
- req = usb_ep_alloc_request(ep, gfp_flags);
-
- if (req != NULL) {
- req->length = len;
- req->buf = kmalloc(len, gfp_flags);
- if (req->buf == NULL) {
- usb_ep_free_request(ep, req);
- return NULL;
- }
- }
-
- return req;
-}
-
-static void
-printer_req_free(struct usb_ep *ep, struct usb_request *req)
-{
- if (ep != NULL && req != NULL) {
- kfree(req->buf);
- usb_ep_free_request(ep, req);
- }
-}
-
-/*-------------------------------------------------------------------------*/
-
-static void rx_complete(struct usb_ep *ep, struct usb_request *req)
-{
- struct printer_dev *dev = ep->driver_data;
- int status = req->status;
- unsigned long flags;
-
- spin_lock_irqsave(&dev->lock, flags);
-
- list_del_init(&req->list); /* Remode from Active List */
-
- switch (status) {
-
- /* normal completion */
- case 0:
- if (req->actual > 0) {
- list_add_tail(&req->list, &dev->rx_buffers);
- DBG(dev, "G_Printer : rx length %d\n", req->actual);
- } else {
- list_add(&req->list, &dev->rx_reqs);
- }
- break;
-
- /* software-driven interface shutdown */
- case -ECONNRESET: /* unlink */
- case -ESHUTDOWN: /* disconnect etc */
- VDBG(dev, "rx shutdown, code %d\n", status);
- list_add(&req->list, &dev->rx_reqs);
- break;
-
- /* for hardware automagic (such as pxa) */
- case -ECONNABORTED: /* endpoint reset */
- DBG(dev, "rx %s reset\n", ep->name);
- list_add(&req->list, &dev->rx_reqs);
- break;
-
- /* data overrun */
- case -EOVERFLOW:
- /* FALLTHROUGH */
-
- default:
- DBG(dev, "rx status %d\n", status);
- list_add(&req->list, &dev->rx_reqs);
- break;
- }
-
- wake_up_interruptible(&dev->rx_wait);
- spin_unlock_irqrestore(&dev->lock, flags);
-}
-
-static void tx_complete(struct usb_ep *ep, struct usb_request *req)
-{
- struct printer_dev *dev = ep->driver_data;
-
- switch (req->status) {
- default:
- VDBG(dev, "tx err %d\n", req->status);
- /* FALLTHROUGH */
- case -ECONNRESET: /* unlink */
- case -ESHUTDOWN: /* disconnect etc */
- break;
- case 0:
- break;
- }
-
- spin_lock(&dev->lock);
- /* Take the request struct off the active list and put it on the
- * free list.
- */
- list_del_init(&req->list);
- list_add(&req->list, &dev->tx_reqs);
- wake_up_interruptible(&dev->tx_wait);
- if (likely(list_empty(&dev->tx_reqs_active)))
- wake_up_interruptible(&dev->tx_flush_wait);
-
- spin_unlock(&dev->lock);
-}
-
-/*-------------------------------------------------------------------------*/
-
-static int
-printer_open(struct inode *inode, struct file *fd)
-{
- struct printer_dev *dev;
- unsigned long flags;
- int ret = -EBUSY;
-
- mutex_lock(&printer_mutex);
- dev = container_of(inode->i_cdev, struct printer_dev, printer_cdev);
-
- spin_lock_irqsave(&dev->lock, flags);
-
- if (!dev->printer_cdev_open) {
- dev->printer_cdev_open = 1;
- fd->private_data = dev;
- ret = 0;
- /* Change the printer status to show that it's on-line. */
- dev->printer_status |= PRINTER_SELECTED;
- }
-
- spin_unlock_irqrestore(&dev->lock, flags);
-
- DBG(dev, "printer_open returned %x\n", ret);
- mutex_unlock(&printer_mutex);
- return ret;
-}
-
-static int
-printer_close(struct inode *inode, struct file *fd)
-{
- struct printer_dev *dev = fd->private_data;
- unsigned long flags;
-
- spin_lock_irqsave(&dev->lock, flags);
- dev->printer_cdev_open = 0;
- fd->private_data = NULL;
- /* Change printer status to show that the printer is off-line. */
- dev->printer_status &= ~PRINTER_SELECTED;
- spin_unlock_irqrestore(&dev->lock, flags);
-
- DBG(dev, "printer_close\n");
-
- return 0;
-}
-
-/* This function must be called with interrupts turned off. */
-static void
-setup_rx_reqs(struct printer_dev *dev)
-{
- struct usb_request *req;
-
- while (likely(!list_empty(&dev->rx_reqs))) {
- int error;
-
- req = container_of(dev->rx_reqs.next,
- struct usb_request, list);
- list_del_init(&req->list);
-
- /* The USB Host sends us whatever amount of data it wants to
- * so we always set the length field to the full USB_BUFSIZE.
- * If the amount of data is more than the read() caller asked
- * for it will be stored in the request buffer until it is
- * asked for by read().
- */
- req->length = USB_BUFSIZE;
- req->complete = rx_complete;
-
- /* here, we unlock, and only unlock, to avoid deadlock. */
- spin_unlock(&dev->lock);
- error = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC);
- spin_lock(&dev->lock);
- if (error) {
- DBG(dev, "rx submit --> %d\n", error);
- list_add(&req->list, &dev->rx_reqs);
- break;
- }
- /* if the req is empty, then add it into dev->rx_reqs_active. */
- else if (list_empty(&req->list)) {
- list_add(&req->list, &dev->rx_reqs_active);
- }
- }
-}
-
-static ssize_t
-printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
-{
- struct printer_dev *dev = fd->private_data;
- unsigned long flags;
- size_t size;
- size_t bytes_copied;
- struct usb_request *req;
- /* This is a pointer to the current USB rx request. */
- struct usb_request *current_rx_req;
- /* This is the number of bytes in the current rx buffer. */
- size_t current_rx_bytes;
- /* This is a pointer to the current rx buffer. */
- u8 *current_rx_buf;
-
- if (len == 0)
- return -EINVAL;
-
- DBG(dev, "printer_read trying to read %d bytes\n", (int)len);
-
- mutex_lock(&dev->lock_printer_io);
- spin_lock_irqsave(&dev->lock, flags);
-
- /* We will use this flag later to check if a printer reset happened
- * after we turn interrupts back on.
- */
- dev->reset_printer = 0;
-
- setup_rx_reqs(dev);
-
- bytes_copied = 0;
- current_rx_req = dev->current_rx_req;
- current_rx_bytes = dev->current_rx_bytes;
- current_rx_buf = dev->current_rx_buf;
- dev->current_rx_req = NULL;
- dev->current_rx_bytes = 0;
- dev->current_rx_buf = NULL;
-
- /* Check if there is any data in the read buffers. Please note that
- * current_rx_bytes is the number of bytes in the current rx buffer.
- * If it is zero then check if there are any other rx_buffers that
- * are on the completed list. We are only out of data if all rx
- * buffers are empty.
- */
- if ((current_rx_bytes == 0) &&
- (likely(list_empty(&dev->rx_buffers)))) {
- /* Turn interrupts back on before sleeping. */
- spin_unlock_irqrestore(&dev->lock, flags);
-
- /*
- * If no data is available check if this is a NON-Blocking
- * call or not.
- */
- if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) {
- mutex_unlock(&dev->lock_printer_io);
- return -EAGAIN;
- }
-
- /* Sleep until data is available */
- wait_event_interruptible(dev->rx_wait,
- (likely(!list_empty(&dev->rx_buffers))));
- spin_lock_irqsave(&dev->lock, flags);
- }
-
- /* We have data to return then copy it to the caller's buffer.*/
- while ((current_rx_bytes || likely(!list_empty(&dev->rx_buffers)))
- && len) {
- if (current_rx_bytes == 0) {
- req = container_of(dev->rx_buffers.next,
- struct usb_request, list);
- list_del_init(&req->list);
-
- if (req->actual && req->buf) {
- current_rx_req = req;
- current_rx_bytes = req->actual;
- current_rx_buf = req->buf;
- } else {
- list_add(&req->list, &dev->rx_reqs);
- continue;
- }
- }
-
- /* Don't leave irqs off while doing memory copies */
- spin_unlock_irqrestore(&dev->lock, flags);
-
- if (len > current_rx_bytes)
- size = current_rx_bytes;
- else
- size = len;
-
- size -= copy_to_user(buf, current_rx_buf, size);
- bytes_copied += size;
- len -= size;
- buf += size;
-
- spin_lock_irqsave(&dev->lock, flags);
-
- /* We've disconnected or reset so return. */
- if (dev->reset_printer) {
- list_add(&current_rx_req->list, &dev->rx_reqs);
- spin_unlock_irqrestore(&dev->lock, flags);
- mutex_unlock(&dev->lock_printer_io);
- return -EAGAIN;
- }
-
- /* If we not returning all the data left in this RX request
- * buffer then adjust the amount of data left in the buffer.
- * Othewise if we are done with this RX request buffer then
- * requeue it to get any incoming data from the USB host.
- */
- if (size < current_rx_bytes) {
- current_rx_bytes -= size;
- current_rx_buf += size;
- } else {
- list_add(&current_rx_req->list, &dev->rx_reqs);
- current_rx_bytes = 0;
- current_rx_buf = NULL;
- current_rx_req = NULL;
- }
- }
-
- dev->current_rx_req = current_rx_req;
- dev->current_rx_bytes = current_rx_bytes;
- dev->current_rx_buf = current_rx_buf;
-
- spin_unlock_irqrestore(&dev->lock, flags);
- mutex_unlock(&dev->lock_printer_io);
-
- DBG(dev, "printer_read returned %d bytes\n", (int)bytes_copied);
-
- if (bytes_copied)
- return bytes_copied;
- else
- return -EAGAIN;
-}
-
-static ssize_t
-printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
-{
- struct printer_dev *dev = fd->private_data;
- unsigned long flags;
- size_t size; /* Amount of data in a TX request. */
- size_t bytes_copied = 0;
- struct usb_request *req;
-
- DBG(dev, "printer_write trying to send %d bytes\n", (int)len);
-
- if (len == 0)
- return -EINVAL;
-
- mutex_lock(&dev->lock_printer_io);
- spin_lock_irqsave(&dev->lock, flags);
-
- /* Check if a printer reset happens while we have interrupts on */
- dev->reset_printer = 0;
-
- /* Check if there is any available write buffers */
- if (likely(list_empty(&dev->tx_reqs))) {
- /* Turn interrupts back on before sleeping. */
- spin_unlock_irqrestore(&dev->lock, flags);
-
- /*
- * If write buffers are available check if this is
- * a NON-Blocking call or not.
- */
- if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) {
- mutex_unlock(&dev->lock_printer_io);
- return -EAGAIN;
- }
-
- /* Sleep until a write buffer is available */
- wait_event_interruptible(dev->tx_wait,
- (likely(!list_empty(&dev->tx_reqs))));
- spin_lock_irqsave(&dev->lock, flags);
- }
-
- while (likely(!list_empty(&dev->tx_reqs)) && len) {
-
- if (len > USB_BUFSIZE)
- size = USB_BUFSIZE;
- else
- size = len;
-
- req = container_of(dev->tx_reqs.next, struct usb_request,
- list);
- list_del_init(&req->list);
-
- req->complete = tx_complete;
- req->length = size;
-
- /* Check if we need to send a zero length packet. */
- if (len > size)
- /* They will be more TX requests so no yet. */
- req->zero = 0;
- else
- /* If the data amount is not a multple of the
- * maxpacket size then send a zero length packet.
- */
- req->zero = ((len % dev->in_ep->maxpacket) == 0);
-
- /* Don't leave irqs off while doing memory copies */
- spin_unlock_irqrestore(&dev->lock, flags);
-
- if (copy_from_user(req->buf, buf, size)) {
- list_add(&req->list, &dev->tx_reqs);
- mutex_unlock(&dev->lock_printer_io);
- return bytes_copied;
- }
-
- bytes_copied += size;
- len -= size;
- buf += size;
-
- spin_lock_irqsave(&dev->lock, flags);
-
- /* We've disconnected or reset so free the req and buffer */
- if (dev->reset_printer) {
- list_add(&req->list, &dev->tx_reqs);
- spin_unlock_irqrestore(&dev->lock, flags);
- mutex_unlock(&dev->lock_printer_io);
- return -EAGAIN;
- }
-
- if (usb_ep_queue(dev->in_ep, req, GFP_ATOMIC)) {
- list_add(&req->list, &dev->tx_reqs);
- spin_unlock_irqrestore(&dev->lock, flags);
- mutex_unlock(&dev->lock_printer_io);
- return -EAGAIN;
- }
-
- list_add(&req->list, &dev->tx_reqs_active);
-
- }
-
- spin_unlock_irqrestore(&dev->lock, flags);
- mutex_unlock(&dev->lock_printer_io);
-
- DBG(dev, "printer_write sent %d bytes\n", (int)bytes_copied);
-
- if (bytes_copied) {
- return bytes_copied;
- } else {
- return -EAGAIN;
- }
-}
-
-static int
-printer_fsync(struct file *fd, loff_t start, loff_t end, int datasync)
-{
- struct printer_dev *dev = fd->private_data;
- struct inode *inode = file_inode(fd);
- unsigned long flags;
- int tx_list_empty;
-
- mutex_lock(&inode->i_mutex);
- spin_lock_irqsave(&dev->lock, flags);
- tx_list_empty = (likely(list_empty(&dev->tx_reqs)));
- spin_unlock_irqrestore(&dev->lock, flags);
-
- if (!tx_list_empty) {
- /* Sleep until all data has been sent */
- wait_event_interruptible(dev->tx_flush_wait,
- (likely(list_empty(&dev->tx_reqs_active))));
- }
- mutex_unlock(&inode->i_mutex);
-
- return 0;
-}
-
-static unsigned int
-printer_poll(struct file *fd, poll_table *wait)
-{
- struct printer_dev *dev = fd->private_data;
- unsigned long flags;
- int status = 0;
-
- mutex_lock(&dev->lock_printer_io);
- spin_lock_irqsave(&dev->lock, flags);
- setup_rx_reqs(dev);
- spin_unlock_irqrestore(&dev->lock, flags);
- mutex_unlock(&dev->lock_printer_io);
-
- poll_wait(fd, &dev->rx_wait, wait);
- poll_wait(fd, &dev->tx_wait, wait);
-
- spin_lock_irqsave(&dev->lock, flags);
- if (likely(!list_empty(&dev->tx_reqs)))
- status |= POLLOUT | POLLWRNORM;
-
- if (likely(dev->current_rx_bytes) ||
- likely(!list_empty(&dev->rx_buffers)))
- status |= POLLIN | POLLRDNORM;
-
- spin_unlock_irqrestore(&dev->lock, flags);
-
- return status;
-}
-
-static long
-printer_ioctl(struct file *fd, unsigned int code, unsigned long arg)
-{
- struct printer_dev *dev = fd->private_data;
- unsigned long flags;
- int status = 0;
-
- DBG(dev, "printer_ioctl: cmd=0x%4.4x, arg=%lu\n", code, arg);
-
- /* handle ioctls */
-
- spin_lock_irqsave(&dev->lock, flags);
-
- switch (code) {
- case GADGET_GET_PRINTER_STATUS:
- status = (int)dev->printer_status;
- break;
- case GADGET_SET_PRINTER_STATUS:
- dev->printer_status = (u8)arg;
- break;
- default:
- /* could not handle ioctl */
- DBG(dev, "printer_ioctl: ERROR cmd=0x%4.4xis not supported\n",
- code);
- status = -ENOTTY;
- }
-
- spin_unlock_irqrestore(&dev->lock, flags);
-
- return status;
-}
-
-/* used after endpoint configuration */
-static const struct file_operations printer_io_operations = {
- .owner = THIS_MODULE,
- .open = printer_open,
- .read = printer_read,
- .write = printer_write,
- .fsync = printer_fsync,
- .poll = printer_poll,
- .unlocked_ioctl = printer_ioctl,
- .release = printer_close,
- .llseek = noop_llseek,
-};
-
-/*-------------------------------------------------------------------------*/
-
-static int
-set_printer_interface(struct printer_dev *dev)
-{
- int result = 0;
-
- dev->in_ep->desc = ep_desc(dev->gadget, &fs_ep_in_desc, &hs_ep_in_desc,
- &ss_ep_in_desc);
- dev->in_ep->driver_data = dev;
-
- dev->out_ep->desc = ep_desc(dev->gadget, &fs_ep_out_desc,
- &hs_ep_out_desc, &ss_ep_out_desc);
- dev->out_ep->driver_data = dev;
-
- result = usb_ep_enable(dev->in_ep);
- if (result != 0) {
- DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result);
- goto done;
- }
-
- result = usb_ep_enable(dev->out_ep);
- if (result != 0) {
- DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result);
- goto done;
- }
-
-done:
- /* on error, disable any endpoints */
- if (result != 0) {
- (void) usb_ep_disable(dev->in_ep);
- (void) usb_ep_disable(dev->out_ep);
- dev->in_ep->desc = NULL;
- dev->out_ep->desc = NULL;
- }
-
- /* caller is responsible for cleanup on error */
- return result;
-}
-
-static void printer_reset_interface(struct printer_dev *dev)
-{
- if (dev->interface < 0)
- return;
-
- DBG(dev, "%s\n", __func__);
-
- if (dev->in_ep->desc)
- usb_ep_disable(dev->in_ep);
-
- if (dev->out_ep->desc)
- usb_ep_disable(dev->out_ep);
-
- dev->in_ep->desc = NULL;
- dev->out_ep->desc = NULL;
- dev->interface = -1;
-}
-
-/* Change our operational Interface. */
-static int set_interface(struct printer_dev *dev, unsigned number)
-{
- int result = 0;
-
- /* Free the current interface */
- printer_reset_interface(dev);
-
- result = set_printer_interface(dev);
- if (result)
- printer_reset_interface(dev);
- else
- dev->interface = number;
-
- if (!result)
- INFO(dev, "Using interface %x\n", number);
-
- return result;
-}
-
-static void printer_soft_reset(struct printer_dev *dev)
-{
- struct usb_request *req;
-
- INFO(dev, "Received Printer Reset Request\n");
-
- if (usb_ep_disable(dev->in_ep))
- DBG(dev, "Failed to disable USB in_ep\n");
- if (usb_ep_disable(dev->out_ep))
- DBG(dev, "Failed to disable USB out_ep\n");
-
- if (dev->current_rx_req != NULL) {
- list_add(&dev->current_rx_req->list, &dev->rx_reqs);
- dev->current_rx_req = NULL;
- }
- dev->current_rx_bytes = 0;
- dev->current_rx_buf = NULL;
- dev->reset_printer = 1;
-
- while (likely(!(list_empty(&dev->rx_buffers)))) {
- req = container_of(dev->rx_buffers.next, struct usb_request,
- list);
- list_del_init(&req->list);
- list_add(&req->list, &dev->rx_reqs);
- }
-
- while (likely(!(list_empty(&dev->rx_reqs_active)))) {
- req = container_of(dev->rx_buffers.next, struct usb_request,
- list);
- list_del_init(&req->list);
- list_add(&req->list, &dev->rx_reqs);
- }
-
- while (likely(!(list_empty(&dev->tx_reqs_active)))) {
- req = container_of(dev->tx_reqs_active.next,
- struct usb_request, list);
- list_del_init(&req->list);
- list_add(&req->list, &dev->tx_reqs);
- }
-
- if (usb_ep_enable(dev->in_ep))
- DBG(dev, "Failed to enable USB in_ep\n");
- if (usb_ep_enable(dev->out_ep))
- DBG(dev, "Failed to enable USB out_ep\n");
-
- wake_up_interruptible(&dev->rx_wait);
- wake_up_interruptible(&dev->tx_wait);
- wake_up_interruptible(&dev->tx_flush_wait);
-}
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * The setup() callback implements all the ep0 functionality that's not
- * handled lower down.
- */
-static int printer_func_setup(struct usb_function *f,
- const struct usb_ctrlrequest *ctrl)
-{
- struct printer_dev *dev = container_of(f, struct printer_dev, function);
- struct usb_composite_dev *cdev = f->config->cdev;
- struct usb_request *req = cdev->req;
- int value = -EOPNOTSUPP;
- u16 wIndex = le16_to_cpu(ctrl->wIndex);
- u16 wValue = le16_to_cpu(ctrl->wValue);
- u16 wLength = le16_to_cpu(ctrl->wLength);
-
- DBG(dev, "ctrl req%02x.%02x v%04x i%04x l%d\n",
- ctrl->bRequestType, ctrl->bRequest, wValue, wIndex, wLength);
-
- switch (ctrl->bRequestType&USB_TYPE_MASK) {
- case USB_TYPE_CLASS:
- switch (ctrl->bRequest) {
- case 0: /* Get the IEEE-1284 PNP String */
- /* Only one printer interface is supported. */
- if ((wIndex>>8) != dev->interface)
- break;
-
- value = (pnp_string[0]<<8)|pnp_string[1];
- memcpy(req->buf, pnp_string, value);
- DBG(dev, "1284 PNP String: %x %s\n", value,
- &pnp_string[2]);
- break;
-
- case 1: /* Get Port Status */
- /* Only one printer interface is supported. */
- if (wIndex != dev->interface)
- break;
-
- *(u8 *)req->buf = dev->printer_status;
- value = min(wLength, (u16) 1);
- break;
-
- case 2: /* Soft Reset */
- /* Only one printer interface is supported. */
- if (wIndex != dev->interface)
- break;
-
- printer_soft_reset(dev);
-
- value = 0;
- break;
-
- default:
- goto unknown;
- }
- break;
-
- default:
-unknown:
- VDBG(dev,
- "unknown ctrl req%02x.%02x v%04x i%04x l%d\n",
- ctrl->bRequestType, ctrl->bRequest,
- wValue, wIndex, wLength);
- break;
- }
- /* host either stalls (value < 0) or reports success */
- return value;
-}
-
-static int __init printer_func_bind(struct usb_configuration *c,
- struct usb_function *f)
-{
- struct printer_dev *dev = container_of(f, struct printer_dev, function);
- struct usb_composite_dev *cdev = c->cdev;
- struct usb_ep *in_ep;
- struct usb_ep *out_ep = NULL;
- int id;
- int ret;
-
- id = usb_interface_id(c, f);
- if (id < 0)
- return id;
- intf_desc.bInterfaceNumber = id;
-
- /* all we really need is bulk IN/OUT */
- in_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_in_desc);
- if (!in_ep) {
-autoconf_fail:
- dev_err(&cdev->gadget->dev, "can't autoconfigure on %s\n",
- cdev->gadget->name);
- return -ENODEV;
- }
- in_ep->driver_data = in_ep; /* claim */
-
- out_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_out_desc);
- if (!out_ep)
- goto autoconf_fail;
- out_ep->driver_data = out_ep; /* claim */
-
- /* assumes that all endpoints are dual-speed */
- hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
- hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
- ss_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
- ss_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
-
- ret = usb_assign_descriptors(f, fs_printer_function,
- hs_printer_function, ss_printer_function);
- if (ret)
- return ret;
-
- dev->in_ep = in_ep;
- dev->out_ep = out_ep;
- return 0;
-}
-
-static void printer_func_unbind(struct usb_configuration *c,
- struct usb_function *f)
-{
- usb_free_all_descriptors(f);
-}
-
-static int printer_func_set_alt(struct usb_function *f,
- unsigned intf, unsigned alt)
-{
- struct printer_dev *dev = container_of(f, struct printer_dev, function);
- int ret = -ENOTSUPP;
-
- if (!alt)
- ret = set_interface(dev, intf);
-
- return ret;
-}
-
-static void printer_func_disable(struct usb_function *f)
-{
- struct printer_dev *dev = container_of(f, struct printer_dev, function);
- unsigned long flags;
-
- DBG(dev, "%s\n", __func__);
-
- spin_lock_irqsave(&dev->lock, flags);
- printer_reset_interface(dev);
- spin_unlock_irqrestore(&dev->lock, flags);
-}
-
-static void printer_cfg_unbind(struct usb_configuration *c)
-{
- struct printer_dev *dev;
- struct usb_request *req;
-
- dev = &usb_printer_gadget;
-
- DBG(dev, "%s\n", __func__);
-
- /* Remove sysfs files */
- device_destroy(usb_gadget_class, g_printer_devno);
-
- /* Remove Character Device */
- cdev_del(&dev->printer_cdev);
-
- /* we must already have been disconnected ... no i/o may be active */
- WARN_ON(!list_empty(&dev->tx_reqs_active));
- WARN_ON(!list_empty(&dev->rx_reqs_active));
-
- /* Free all memory for this driver. */
- while (!list_empty(&dev->tx_reqs)) {
- req = container_of(dev->tx_reqs.next, struct usb_request,
- list);
- list_del(&req->list);
- printer_req_free(dev->in_ep, req);
- }
-
- if (dev->current_rx_req != NULL)
- printer_req_free(dev->out_ep, dev->current_rx_req);
-
- while (!list_empty(&dev->rx_reqs)) {
- req = container_of(dev->rx_reqs.next,
- struct usb_request, list);
- list_del(&req->list);
- printer_req_free(dev->out_ep, req);
- }
-
- while (!list_empty(&dev->rx_buffers)) {
- req = container_of(dev->rx_buffers.next,
- struct usb_request, list);
- list_del(&req->list);
- printer_req_free(dev->out_ep, req);
- }
-}
-
static struct usb_configuration printer_cfg_driver = {
.label = "printer",
- .unbind = printer_cfg_unbind,
.bConfigurationValue = 1,
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
};
-static int __init printer_bind_config(struct usb_configuration *c)
+static int __init printer_do_config(struct usb_configuration *c)
{
struct usb_gadget *gadget = c->cdev->gadget;
- struct printer_dev *dev;
- int status = -ENOMEM;
- size_t len;
- u32 i;
- struct usb_request *req;
+ int status = 0;
usb_ep_autoconfig_reset(gadget);
- dev = &usb_printer_gadget;
-
- dev->function.name = shortname;
- dev->function.bind = printer_func_bind;
- dev->function.setup = printer_func_setup;
- dev->function.unbind = printer_func_unbind;
- dev->function.set_alt = printer_func_set_alt;
- dev->function.disable = printer_func_disable;
-
- status = usb_add_function(c, &dev->function);
- if (status)
- return status;
-
- /* Setup the sysfs files for the printer gadget. */
- dev->pdev = device_create(usb_gadget_class, NULL, g_printer_devno,
- NULL, "g_printer");
- if (IS_ERR(dev->pdev)) {
- ERROR(dev, "Failed to create device: g_printer\n");
- status = PTR_ERR(dev->pdev);
- goto fail;
- }
-
- /*
- * Register a character device as an interface to a user mode
- * program that handles the printer specific functionality.
- */
- cdev_init(&dev->printer_cdev, &printer_io_operations);
- dev->printer_cdev.owner = THIS_MODULE;
- status = cdev_add(&dev->printer_cdev, g_printer_devno, 1);
- if (status) {
- ERROR(dev, "Failed to open char device\n");
- goto fail;
- }
-
- if (iPNPstring)
- strlcpy(&pnp_string[2], iPNPstring, (sizeof pnp_string)-2);
-
- len = strlen(pnp_string);
- pnp_string[0] = (len >> 8) & 0xFF;
- pnp_string[1] = len & 0xFF;
-
usb_gadget_set_selfpowered(gadget);
if (gadget_is_otg(gadget)) {
@@ -1222,86 +141,64 @@ static int __init printer_bind_config(struct usb_configuration *c)
printer_cfg_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
- spin_lock_init(&dev->lock);
- mutex_init(&dev->lock_printer_io);
- INIT_LIST_HEAD(&dev->tx_reqs);
- INIT_LIST_HEAD(&dev->tx_reqs_active);
- INIT_LIST_HEAD(&dev->rx_reqs);
- INIT_LIST_HEAD(&dev->rx_reqs_active);
- INIT_LIST_HEAD(&dev->rx_buffers);
- init_waitqueue_head(&dev->rx_wait);
- init_waitqueue_head(&dev->tx_wait);
- init_waitqueue_head(&dev->tx_flush_wait);
-
- dev->interface = -1;
- dev->printer_cdev_open = 0;
- dev->printer_status = PRINTER_NOT_ERROR;
- dev->current_rx_req = NULL;
- dev->current_rx_bytes = 0;
- dev->current_rx_buf = NULL;
-
- for (i = 0; i < QLEN; i++) {
- req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL);
- if (!req) {
- while (!list_empty(&dev->tx_reqs)) {
- req = container_of(dev->tx_reqs.next,
- struct usb_request, list);
- list_del(&req->list);
- printer_req_free(dev->in_ep, req);
- }
- return -ENOMEM;
- }
- list_add(&req->list, &dev->tx_reqs);
- }
-
- for (i = 0; i < QLEN; i++) {
- req = printer_req_alloc(dev->out_ep, USB_BUFSIZE, GFP_KERNEL);
- if (!req) {
- while (!list_empty(&dev->rx_reqs)) {
- req = container_of(dev->rx_reqs.next,
- struct usb_request, list);
- list_del(&req->list);
- printer_req_free(dev->out_ep, req);
- }
- return -ENOMEM;
- }
- list_add(&req->list, &dev->rx_reqs);
- }
-
- /* finish hookup to lower layer ... */
- dev->gadget = gadget;
+ f_printer = usb_get_function(fi_printer);
+ if (IS_ERR(f_printer))
+ return PTR_ERR(f_printer);
- INFO(dev, "%s, version: " DRIVER_VERSION "\n", driver_desc);
- return 0;
+ status = usb_add_function(c, f_printer);
+ if (status < 0)
+ usb_put_function(f_printer);
-fail:
- printer_cfg_unbind(c);
return status;
}
-static int printer_unbind(struct usb_composite_dev *cdev)
-{
- return 0;
-}
-
static int __init printer_bind(struct usb_composite_dev *cdev)
{
- int ret;
+ struct f_printer_opts *opts;
+ int ret, len;
+
+ fi_printer = usb_get_function_instance("printer");
+ if (IS_ERR(fi_printer))
+ return PTR_ERR(fi_printer);
+
+ if (iPNPstring)
+ strlcpy(&pnp_string[2], iPNPstring, PNP_STRING_LEN - 2);
+
+ len = strlen(pnp_string);
+ pnp_string[0] = (len >> 8) & 0xFF;
+ pnp_string[1] = len & 0xFF;
+
+ opts = container_of(fi_printer, struct f_printer_opts, func_inst);
+ opts->minor = 0;
+ memcpy(opts->pnp_string, pnp_string, PNP_STRING_LEN);
+ opts->q_len = QLEN;
ret = usb_string_ids_tab(cdev, strings);
- if (ret < 0)
+ if (ret < 0) {
+ usb_put_function_instance(fi_printer);
return ret;
+ }
device_desc.iManufacturer = strings[USB_GADGET_MANUFACTURER_IDX].id;
device_desc.iProduct = strings[USB_GADGET_PRODUCT_IDX].id;
device_desc.iSerialNumber = strings[USB_GADGET_SERIAL_IDX].id;
- ret = usb_add_config(cdev, &printer_cfg_driver, printer_bind_config);
- if (ret)
+ ret = usb_add_config(cdev, &printer_cfg_driver, printer_do_config);
+ if (ret) {
+ usb_put_function_instance(fi_printer);
return ret;
+ }
usb_composite_overwrite_options(cdev, &coverwrite);
return ret;
}
+static int __exit printer_unbind(struct usb_composite_dev *cdev)
+{
+ usb_put_function(f_printer);
+ usb_put_function_instance(fi_printer);
+
+ return 0;
+}
+
static __refdata struct usb_composite_driver printer_driver = {
.name = shortname,
.dev = &device_desc,
@@ -1311,47 +208,7 @@ static __refdata struct usb_composite_driver printer_driver = {
.unbind = printer_unbind,
};
-static int __init
-init(void)
-{
- int status;
-
- usb_gadget_class = class_create(THIS_MODULE, "usb_printer_gadget");
- if (IS_ERR(usb_gadget_class)) {
- status = PTR_ERR(usb_gadget_class);
- pr_err("unable to create usb_gadget class %d\n", status);
- return status;
- }
-
- status = alloc_chrdev_region(&g_printer_devno, 0, 1,
- "USB printer gadget");
- if (status) {
- pr_err("alloc_chrdev_region %d\n", status);
- class_destroy(usb_gadget_class);
- return status;
- }
-
- status = usb_composite_probe(&printer_driver);
- if (status) {
- class_destroy(usb_gadget_class);
- unregister_chrdev_region(g_printer_devno, 1);
- pr_err("usb_gadget_probe_driver %x\n", status);
- }
-
- return status;
-}
-module_init(init);
-
-static void __exit
-cleanup(void)
-{
- mutex_lock(&usb_printer_gadget.lock_printer_io);
- usb_composite_unregister(&printer_driver);
- unregister_chrdev_region(g_printer_devno, 1);
- class_destroy(usb_gadget_class);
- mutex_unlock(&usb_printer_gadget.lock_printer_io);
-}
-module_exit(cleanup);
+module_usb_composite_driver(printer_driver);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Craig Nadler");
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c
index d79cb35dbf8a..4c01953a0869 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.c
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.c
@@ -152,7 +152,7 @@ static int regs_dbg_open(struct inode *inode, struct file *file)
spin_lock_irq(&udc->lock);
for (i = 0; i < inode->i_size / 4; i++)
- data[i] = __raw_readl(udc->regs + i * 4);
+ data[i] = usba_io_readl(udc->regs + i * 4);
spin_unlock_irq(&udc->lock);
file->private_data = data;
@@ -1249,7 +1249,7 @@ static int handle_ep0_setup(struct usba_udc *udc, struct usba_ep *ep,
if (crq->wLength != cpu_to_le16(sizeof(status)))
goto stall;
ep->state = DATA_STAGE_IN;
- __raw_writew(status, ep->fifo);
+ usba_io_writew(status, ep->fifo);
usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);
break;
}
@@ -1739,7 +1739,72 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
return IRQ_HANDLED;
}
-static irqreturn_t usba_vbus_irq(int irq, void *devid)
+static int start_clock(struct usba_udc *udc)
+{
+ int ret;
+
+ if (udc->clocked)
+ return 0;
+
+ ret = clk_prepare_enable(udc->pclk);
+ if (ret)
+ return ret;
+ ret = clk_prepare_enable(udc->hclk);
+ if (ret) {
+ clk_disable_unprepare(udc->pclk);
+ return ret;
+ }
+
+ udc->clocked = true;
+ return 0;
+}
+
+static void stop_clock(struct usba_udc *udc)
+{
+ if (!udc->clocked)
+ return;
+
+ clk_disable_unprepare(udc->hclk);
+ clk_disable_unprepare(udc->pclk);
+
+ udc->clocked = false;
+}
+
+static int usba_start(struct usba_udc *udc)
+{
+ unsigned long flags;
+ int ret;
+
+ ret = start_clock(udc);
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ toggle_bias(udc, 1);
+ usba_writel(udc, CTRL, USBA_ENABLE_MASK);
+ usba_int_enb_set(udc, USBA_END_OF_RESET);
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+static void usba_stop(struct usba_udc *udc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ reset_all_endpoints(udc);
+
+ /* This will also disable the DP pullup */
+ toggle_bias(udc, 0);
+ usba_writel(udc, CTRL, USBA_DISABLE_MASK);
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ stop_clock(udc);
+}
+
+static irqreturn_t usba_vbus_irq_thread(int irq, void *devid)
{
struct usba_udc *udc = devid;
int vbus;
@@ -1747,35 +1812,22 @@ static irqreturn_t usba_vbus_irq(int irq, void *devid)
/* debounce */
udelay(10);
- spin_lock(&udc->lock);
-
- /* May happen if Vbus pin toggles during probe() */
- if (!udc->driver)
- goto out;
+ mutex_lock(&udc->vbus_mutex);
vbus = vbus_is_present(udc);
if (vbus != udc->vbus_prev) {
if (vbus) {
- toggle_bias(udc, 1);
- usba_writel(udc, CTRL, USBA_ENABLE_MASK);
- usba_int_enb_set(udc, USBA_END_OF_RESET);
+ usba_start(udc);
} else {
- udc->gadget.speed = USB_SPEED_UNKNOWN;
- reset_all_endpoints(udc);
- toggle_bias(udc, 0);
- usba_writel(udc, CTRL, USBA_DISABLE_MASK);
- if (udc->driver->disconnect) {
- spin_unlock(&udc->lock);
+ usba_stop(udc);
+
+ if (udc->driver->disconnect)
udc->driver->disconnect(&udc->gadget);
- spin_lock(&udc->lock);
- }
}
udc->vbus_prev = vbus;
}
-out:
- spin_unlock(&udc->lock);
-
+ mutex_unlock(&udc->vbus_mutex);
return IRQ_HANDLED;
}
@@ -1787,55 +1839,47 @@ static int atmel_usba_start(struct usb_gadget *gadget,
unsigned long flags;
spin_lock_irqsave(&udc->lock, flags);
-
udc->devstatus = 1 << USB_DEVICE_SELF_POWERED;
udc->driver = driver;
spin_unlock_irqrestore(&udc->lock, flags);
- ret = clk_prepare_enable(udc->pclk);
- if (ret)
- return ret;
- ret = clk_prepare_enable(udc->hclk);
- if (ret) {
- clk_disable_unprepare(udc->pclk);
- return ret;
- }
+ mutex_lock(&udc->vbus_mutex);
- udc->vbus_prev = 0;
if (gpio_is_valid(udc->vbus_pin))
enable_irq(gpio_to_irq(udc->vbus_pin));
/* If Vbus is present, enable the controller and wait for reset */
- spin_lock_irqsave(&udc->lock, flags);
- if (vbus_is_present(udc) && udc->vbus_prev == 0) {
- toggle_bias(udc, 1);
- usba_writel(udc, CTRL, USBA_ENABLE_MASK);
- usba_int_enb_set(udc, USBA_END_OF_RESET);
+ udc->vbus_prev = vbus_is_present(udc);
+ if (udc->vbus_prev) {
+ ret = usba_start(udc);
+ if (ret)
+ goto err;
}
- spin_unlock_irqrestore(&udc->lock, flags);
+ mutex_unlock(&udc->vbus_mutex);
return 0;
+
+err:
+ if (gpio_is_valid(udc->vbus_pin))
+ disable_irq(gpio_to_irq(udc->vbus_pin));
+
+ mutex_unlock(&udc->vbus_mutex);
+
+ spin_lock_irqsave(&udc->lock, flags);
+ udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
+ udc->driver = NULL;
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return ret;
}
static int atmel_usba_stop(struct usb_gadget *gadget)
{
struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget);
- unsigned long flags;
if (gpio_is_valid(udc->vbus_pin))
disable_irq(gpio_to_irq(udc->vbus_pin));
- spin_lock_irqsave(&udc->lock, flags);
- udc->gadget.speed = USB_SPEED_UNKNOWN;
- reset_all_endpoints(udc);
- spin_unlock_irqrestore(&udc->lock, flags);
-
- /* This will also disable the DP pullup */
- toggle_bias(udc, 0);
- usba_writel(udc, CTRL, USBA_DISABLE_MASK);
-
- clk_disable_unprepare(udc->hclk);
- clk_disable_unprepare(udc->pclk);
+ usba_stop(udc);
udc->driver = NULL;
@@ -2057,6 +2101,7 @@ static int usba_udc_probe(struct platform_device *pdev)
return PTR_ERR(hclk);
spin_lock_init(&udc->lock);
+ mutex_init(&udc->vbus_mutex);
udc->pdev = pdev;
udc->pclk = pclk;
udc->hclk = hclk;
@@ -2111,17 +2156,17 @@ static int usba_udc_probe(struct platform_device *pdev)
if (gpio_is_valid(udc->vbus_pin)) {
if (!devm_gpio_request(&pdev->dev, udc->vbus_pin, "atmel_usba_udc")) {
- ret = devm_request_irq(&pdev->dev,
- gpio_to_irq(udc->vbus_pin),
- usba_vbus_irq, 0,
+ irq_set_status_flags(gpio_to_irq(udc->vbus_pin),
+ IRQ_NOAUTOEN);
+ ret = devm_request_threaded_irq(&pdev->dev,
+ gpio_to_irq(udc->vbus_pin), NULL,
+ usba_vbus_irq_thread, IRQF_ONESHOT,
"atmel_usba_udc", udc);
if (ret) {
udc->vbus_pin = -ENODEV;
dev_warn(&udc->pdev->dev,
"failed to request vbus irq; "
"assuming always on\n");
- } else {
- disable_irq(gpio_to_irq(udc->vbus_pin));
}
} else {
/* gpio_request fail so use -EINVAL for gpio_is_valid */
@@ -2132,6 +2177,7 @@ static int usba_udc_probe(struct platform_device *pdev)
ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
if (ret)
return ret;
+ device_init_wakeup(&pdev->dev, 1);
usba_init_debugfs(udc);
for (i = 1; i < udc->num_ep; i++)
@@ -2147,6 +2193,7 @@ static int __exit usba_udc_remove(struct platform_device *pdev)
udc = platform_get_drvdata(pdev);
+ device_init_wakeup(&pdev->dev, 0);
usb_del_gadget_udc(&udc->gadget);
for (i = 1; i < udc->num_ep; i++)
@@ -2156,10 +2203,65 @@ static int __exit usba_udc_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM
+static int usba_udc_suspend(struct device *dev)
+{
+ struct usba_udc *udc = dev_get_drvdata(dev);
+
+ /* Not started */
+ if (!udc->driver)
+ return 0;
+
+ mutex_lock(&udc->vbus_mutex);
+
+ if (!device_may_wakeup(dev)) {
+ usba_stop(udc);
+ goto out;
+ }
+
+ /*
+ * Device may wake up. We stay clocked if we failed
+ * to request vbus irq, assuming always on.
+ */
+ if (gpio_is_valid(udc->vbus_pin)) {
+ usba_stop(udc);
+ enable_irq_wake(gpio_to_irq(udc->vbus_pin));
+ }
+
+out:
+ mutex_unlock(&udc->vbus_mutex);
+ return 0;
+}
+
+static int usba_udc_resume(struct device *dev)
+{
+ struct usba_udc *udc = dev_get_drvdata(dev);
+
+ /* Not started */
+ if (!udc->driver)
+ return 0;
+
+ if (device_may_wakeup(dev) && gpio_is_valid(udc->vbus_pin))
+ disable_irq_wake(gpio_to_irq(udc->vbus_pin));
+
+ /* If Vbus is present, enable the controller and wait for reset */
+ mutex_lock(&udc->vbus_mutex);
+ udc->vbus_prev = vbus_is_present(udc);
+ if (udc->vbus_prev)
+ usba_start(udc);
+ mutex_unlock(&udc->vbus_mutex);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(usba_udc_pm_ops, usba_udc_suspend, usba_udc_resume);
+
static struct platform_driver udc_driver = {
.remove = __exit_p(usba_udc_remove),
.driver = {
.name = "atmel_usba_udc",
+ .pm = &usba_udc_pm_ops,
.of_match_table = of_match_ptr(atmel_udc_dt_ids),
},
};
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h
index 497cd18836f3..ea448a344767 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.h
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.h
@@ -191,18 +191,28 @@
| USBA_BF(name, value))
/* Register access macros */
+#ifdef CONFIG_AVR32
+#define usba_io_readl __raw_readl
+#define usba_io_writel __raw_writel
+#define usba_io_writew __raw_writew
+#else
+#define usba_io_readl readl_relaxed
+#define usba_io_writel writel_relaxed
+#define usba_io_writew writew_relaxed
+#endif
+
#define usba_readl(udc, reg) \
- __raw_readl((udc)->regs + USBA_##reg)
+ usba_io_readl((udc)->regs + USBA_##reg)
#define usba_writel(udc, reg, value) \
- __raw_writel((value), (udc)->regs + USBA_##reg)
+ usba_io_writel((value), (udc)->regs + USBA_##reg)
#define usba_ep_readl(ep, reg) \
- __raw_readl((ep)->ep_regs + USBA_EPT_##reg)
+ usba_io_readl((ep)->ep_regs + USBA_EPT_##reg)
#define usba_ep_writel(ep, reg, value) \
- __raw_writel((value), (ep)->ep_regs + USBA_EPT_##reg)
+ usba_io_writel((value), (ep)->ep_regs + USBA_EPT_##reg)
#define usba_dma_readl(ep, reg) \
- __raw_readl((ep)->dma_regs + USBA_DMA_##reg)
+ usba_io_readl((ep)->dma_regs + USBA_DMA_##reg)
#define usba_dma_writel(ep, reg, value) \
- __raw_writel((value), (ep)->dma_regs + USBA_DMA_##reg)
+ usba_io_writel((value), (ep)->dma_regs + USBA_DMA_##reg)
/* Calculate base address for a given endpoint or DMA controller */
#define USBA_EPT_BASE(x) (0x100 + (x) * 0x20)
@@ -313,6 +323,9 @@ struct usba_udc {
/* Protect hw registers from concurrent modifications */
spinlock_t lock;
+ /* Mutex to prevent concurrent start or stop */
+ struct mutex vbus_mutex;
+
void __iomem *regs;
void __iomem *fifo;
@@ -328,6 +341,7 @@ struct usba_udc {
struct clk *hclk;
struct usba_ep *usba_ep;
bool bias_pulse_needed;
+ bool clocked;
u16 devstatus;
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index 8dda48445f6f..181112c88f43 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -1923,7 +1923,7 @@ static inline void
ss_hub_descriptor(struct usb_hub_descriptor *desc)
{
memset(desc, 0, sizeof *desc);
- desc->bDescriptorType = 0x2a;
+ desc->bDescriptorType = USB_DT_SS_HUB;
desc->bDescLength = 12;
desc->wHubCharacteristics = cpu_to_le16(
HUB_CHAR_INDV_PORT_LPSM |
@@ -1936,7 +1936,7 @@ ss_hub_descriptor(struct usb_hub_descriptor *desc)
static inline void hub_descriptor(struct usb_hub_descriptor *desc)
{
memset(desc, 0, sizeof *desc);
- desc->bDescriptorType = 0x29;
+ desc->bDescriptorType = USB_DT_HUB;
desc->bDescLength = 9;
desc->wHubCharacteristics = cpu_to_le16(
HUB_CHAR_INDV_PORT_LPSM |
@@ -2631,7 +2631,7 @@ static int __init init(void)
return -EINVAL;
if (mod_data.num < 1 || mod_data.num > MAX_NUM_UDC) {
- pr_err("Number of emulated UDC must be in range of 1…%d\n",
+ pr_err("Number of emulated UDC must be in range of 1...%d\n",
MAX_NUM_UDC);
return -EINVAL;
}
diff --git a/drivers/usb/gadget/udc/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c
index 5b9176e7202a..9e8d842e8c08 100644
--- a/drivers/usb/gadget/udc/goku_udc.c
+++ b/drivers/usb/gadget/udc/goku_udc.c
@@ -1024,35 +1024,79 @@ static const char proc_node_name [] = "driver/udc";
static void dump_intmask(struct seq_file *m, const char *label, u32 mask)
{
/* int_status is the same format ... */
- seq_printf(m,
- "%s %05X =" FOURBITS EIGHTBITS EIGHTBITS "\n",
- label, mask,
- (mask & INT_PWRDETECT) ? " power" : "",
- (mask & INT_SYSERROR) ? " sys" : "",
- (mask & INT_MSTRDEND) ? " in-dma" : "",
- (mask & INT_MSTWRTMOUT) ? " wrtmo" : "",
-
- (mask & INT_MSTWREND) ? " out-dma" : "",
- (mask & INT_MSTWRSET) ? " wrset" : "",
- (mask & INT_ERR) ? " err" : "",
- (mask & INT_SOF) ? " sof" : "",
-
- (mask & INT_EP3NAK) ? " ep3nak" : "",
- (mask & INT_EP2NAK) ? " ep2nak" : "",
- (mask & INT_EP1NAK) ? " ep1nak" : "",
- (mask & INT_EP3DATASET) ? " ep3" : "",
-
- (mask & INT_EP2DATASET) ? " ep2" : "",
- (mask & INT_EP1DATASET) ? " ep1" : "",
- (mask & INT_STATUSNAK) ? " ep0snak" : "",
- (mask & INT_STATUS) ? " ep0status" : "",
-
- (mask & INT_SETUP) ? " setup" : "",
- (mask & INT_ENDPOINT0) ? " ep0" : "",
- (mask & INT_USBRESET) ? " reset" : "",
- (mask & INT_SUSPEND) ? " suspend" : "");
+ seq_printf(m, "%s %05X =" FOURBITS EIGHTBITS EIGHTBITS "\n",
+ label, mask,
+ (mask & INT_PWRDETECT) ? " power" : "",
+ (mask & INT_SYSERROR) ? " sys" : "",
+ (mask & INT_MSTRDEND) ? " in-dma" : "",
+ (mask & INT_MSTWRTMOUT) ? " wrtmo" : "",
+
+ (mask & INT_MSTWREND) ? " out-dma" : "",
+ (mask & INT_MSTWRSET) ? " wrset" : "",
+ (mask & INT_ERR) ? " err" : "",
+ (mask & INT_SOF) ? " sof" : "",
+
+ (mask & INT_EP3NAK) ? " ep3nak" : "",
+ (mask & INT_EP2NAK) ? " ep2nak" : "",
+ (mask & INT_EP1NAK) ? " ep1nak" : "",
+ (mask & INT_EP3DATASET) ? " ep3" : "",
+
+ (mask & INT_EP2DATASET) ? " ep2" : "",
+ (mask & INT_EP1DATASET) ? " ep1" : "",
+ (mask & INT_STATUSNAK) ? " ep0snak" : "",
+ (mask & INT_STATUS) ? " ep0status" : "",
+
+ (mask & INT_SETUP) ? " setup" : "",
+ (mask & INT_ENDPOINT0) ? " ep0" : "",
+ (mask & INT_USBRESET) ? " reset" : "",
+ (mask & INT_SUSPEND) ? " suspend" : "");
+}
+
+static const char *udc_ep_state(enum ep0state state)
+{
+ switch (state) {
+ case EP0_DISCONNECT:
+ return "ep0_disconnect";
+ case EP0_IDLE:
+ return "ep0_idle";
+ case EP0_IN:
+ return "ep0_in";
+ case EP0_OUT:
+ return "ep0_out";
+ case EP0_STATUS:
+ return "ep0_status";
+ case EP0_STALL:
+ return "ep0_stall";
+ case EP0_SUSPEND:
+ return "ep0_suspend";
+ }
+
+ return "ep0_?";
}
+static const char *udc_ep_status(u32 status)
+{
+ switch (status & EPxSTATUS_EP_MASK) {
+ case EPxSTATUS_EP_READY:
+ return "ready";
+ case EPxSTATUS_EP_DATAIN:
+ return "packet";
+ case EPxSTATUS_EP_FULL:
+ return "full";
+ case EPxSTATUS_EP_TX_ERR: /* host will retry */
+ return "tx_err";
+ case EPxSTATUS_EP_RX_ERR:
+ return "rx_err";
+ case EPxSTATUS_EP_BUSY: /* ep0 only */
+ return "busy";
+ case EPxSTATUS_EP_STALL:
+ return "stall";
+ case EPxSTATUS_EP_INVALID: /* these "can't happen" */
+ return "invalid";
+ }
+
+ return "?";
+}
static int udc_proc_read(struct seq_file *m, void *v)
{
@@ -1068,29 +1112,18 @@ static int udc_proc_read(struct seq_file *m, void *v)
tmp = readl(&regs->power_detect);
is_usb_connected = tmp & PW_DETECT;
seq_printf(m,
- "%s - %s\n"
- "%s version: %s %s\n"
- "Gadget driver: %s\n"
- "Host %s, %s\n"
- "\n",
- pci_name(dev->pdev), driver_desc,
- driver_name, DRIVER_VERSION, dmastr(),
- dev->driver ? dev->driver->driver.name : "(none)",
- is_usb_connected
- ? ((tmp & PW_PULLUP) ? "full speed" : "powered")
- : "disconnected",
- ({const char *state;
- switch(dev->ep0state){
- case EP0_DISCONNECT: state = "ep0_disconnect"; break;
- case EP0_IDLE: state = "ep0_idle"; break;
- case EP0_IN: state = "ep0_in"; break;
- case EP0_OUT: state = "ep0_out"; break;
- case EP0_STATUS: state = "ep0_status"; break;
- case EP0_STALL: state = "ep0_stall"; break;
- case EP0_SUSPEND: state = "ep0_suspend"; break;
- default: state = "ep0_?"; break;
- } state; })
- );
+ "%s - %s\n"
+ "%s version: %s %s\n"
+ "Gadget driver: %s\n"
+ "Host %s, %s\n"
+ "\n",
+ pci_name(dev->pdev), driver_desc,
+ driver_name, DRIVER_VERSION, dmastr(),
+ dev->driver ? dev->driver->driver.name : "(none)",
+ is_usb_connected
+ ? ((tmp & PW_PULLUP) ? "full speed" : "powered")
+ : "disconnected",
+ udc_ep_state(dev->ep0state));
dump_intmask(m, "int_status", readl(&regs->int_status));
dump_intmask(m, "int_enable", readl(&regs->int_enable));
@@ -1099,31 +1132,30 @@ static int udc_proc_read(struct seq_file *m, void *v)
goto done;
/* registers for (active) device and ep0 */
- if (seq_printf(m, "\nirqs %lu\ndataset %02x "
- "single.bcs %02x.%02x state %x addr %u\n",
- dev->irqs, readl(&regs->DataSet),
- readl(&regs->EPxSingle), readl(&regs->EPxBCS),
- readl(&regs->UsbState),
- readl(&regs->address)) < 0)
+ seq_printf(m, "\nirqs %lu\ndataset %02x single.bcs %02x.%02x state %x addr %u\n",
+ dev->irqs, readl(&regs->DataSet),
+ readl(&regs->EPxSingle), readl(&regs->EPxBCS),
+ readl(&regs->UsbState),
+ readl(&regs->address));
+ if (seq_has_overflowed(m))
goto done;
tmp = readl(&regs->dma_master);
- if (seq_printf(m,
- "dma %03X =" EIGHTBITS "%s %s\n", tmp,
- (tmp & MST_EOPB_DIS) ? " eopb-" : "",
- (tmp & MST_EOPB_ENA) ? " eopb+" : "",
- (tmp & MST_TIMEOUT_DIS) ? " tmo-" : "",
- (tmp & MST_TIMEOUT_ENA) ? " tmo+" : "",
-
- (tmp & MST_RD_EOPB) ? " eopb" : "",
- (tmp & MST_RD_RESET) ? " in_reset" : "",
- (tmp & MST_WR_RESET) ? " out_reset" : "",
- (tmp & MST_RD_ENA) ? " IN" : "",
-
- (tmp & MST_WR_ENA) ? " OUT" : "",
- (tmp & MST_CONNECTION)
- ? "ep1in/ep2out"
- : "ep1out/ep2in") < 0)
+ seq_printf(m, "dma %03X =" EIGHTBITS "%s %s\n",
+ tmp,
+ (tmp & MST_EOPB_DIS) ? " eopb-" : "",
+ (tmp & MST_EOPB_ENA) ? " eopb+" : "",
+ (tmp & MST_TIMEOUT_DIS) ? " tmo-" : "",
+ (tmp & MST_TIMEOUT_ENA) ? " tmo+" : "",
+
+ (tmp & MST_RD_EOPB) ? " eopb" : "",
+ (tmp & MST_RD_RESET) ? " in_reset" : "",
+ (tmp & MST_WR_RESET) ? " out_reset" : "",
+ (tmp & MST_RD_ENA) ? " IN" : "",
+
+ (tmp & MST_WR_ENA) ? " OUT" : "",
+ (tmp & MST_CONNECTION) ? "ep1in/ep2out" : "ep1out/ep2in");
+ if (seq_has_overflowed(m))
goto done;
/* dump endpoint queues */
@@ -1135,44 +1167,23 @@ static int udc_proc_read(struct seq_file *m, void *v)
continue;
tmp = readl(ep->reg_status);
- if (seq_printf(m,
- "%s %s max %u %s, irqs %lu, "
- "status %02x (%s) " FOURBITS "\n",
- ep->ep.name,
- ep->is_in ? "in" : "out",
- ep->ep.maxpacket,
- ep->dma ? "dma" : "pio",
- ep->irqs,
- tmp, ({ char *s;
- switch (tmp & EPxSTATUS_EP_MASK) {
- case EPxSTATUS_EP_READY:
- s = "ready"; break;
- case EPxSTATUS_EP_DATAIN:
- s = "packet"; break;
- case EPxSTATUS_EP_FULL:
- s = "full"; break;
- case EPxSTATUS_EP_TX_ERR: // host will retry
- s = "tx_err"; break;
- case EPxSTATUS_EP_RX_ERR:
- s = "rx_err"; break;
- case EPxSTATUS_EP_BUSY: /* ep0 only */
- s = "busy"; break;
- case EPxSTATUS_EP_STALL:
- s = "stall"; break;
- case EPxSTATUS_EP_INVALID: // these "can't happen"
- s = "invalid"; break;
- default:
- s = "?"; break;
- } s; }),
- (tmp & EPxSTATUS_TOGGLE) ? "data1" : "data0",
- (tmp & EPxSTATUS_SUSPEND) ? " suspend" : "",
- (tmp & EPxSTATUS_FIFO_DISABLE) ? " disable" : "",
- (tmp & EPxSTATUS_STAGE_ERROR) ? " ep0stat" : ""
- ) < 0)
+ seq_printf(m, "%s %s max %u %s, irqs %lu, status %02x (%s) " FOURBITS "\n",
+ ep->ep.name,
+ ep->is_in ? "in" : "out",
+ ep->ep.maxpacket,
+ ep->dma ? "dma" : "pio",
+ ep->irqs,
+ tmp, udc_ep_status(tmp),
+ (tmp & EPxSTATUS_TOGGLE) ? "data1" : "data0",
+ (tmp & EPxSTATUS_SUSPEND) ? " suspend" : "",
+ (tmp & EPxSTATUS_FIFO_DISABLE) ? " disable" : "",
+ (tmp & EPxSTATUS_STAGE_ERROR) ? " ep0stat" : "");
+ if (seq_has_overflowed(m))
goto done;
if (list_empty(&ep->queue)) {
- if (seq_puts(m, "\t(nothing queued)\n") < 0)
+ seq_puts(m, "\t(nothing queued)\n");
+ if (seq_has_overflowed(m))
goto done;
continue;
}
@@ -1187,10 +1198,10 @@ static int udc_proc_read(struct seq_file *m, void *v)
} else
tmp = req->req.actual;
- if (seq_printf(m,
- "\treq %p len %u/%u buf %p\n",
- &req->req, tmp, req->req.length,
- req->req.buf) < 0)
+ seq_printf(m, "\treq %p len %u/%u buf %p\n",
+ &req->req, tmp, req->req.length,
+ req->req.buf);
+ if (seq_has_overflowed(m))
goto done;
}
}
diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c
index 27fd41333f71..3b6a7852822d 100644
--- a/drivers/usb/gadget/udc/lpc32xx_udc.c
+++ b/drivers/usb/gadget/udc/lpc32xx_udc.c
@@ -1803,23 +1803,14 @@ static int lpc32xx_ep_queue(struct usb_ep *_ep,
req = container_of(_req, struct lpc32xx_request, req);
ep = container_of(_ep, struct lpc32xx_ep, ep);
- if (!_req || !_req->complete || !_req->buf ||
+ if (!_ep || !_req || !_req->complete || !_req->buf ||
!list_empty(&req->queue))
return -EINVAL;
udc = ep->udc;
- if (!_ep) {
- dev_dbg(udc->dev, "invalid ep\n");
- return -EINVAL;
- }
-
-
- if ((!udc) || (!udc->driver) ||
- (udc->gadget.speed == USB_SPEED_UNKNOWN)) {
- dev_dbg(udc->dev, "invalid device\n");
- return -EINVAL;
- }
+ if (udc->gadget.speed == USB_SPEED_UNKNOWN)
+ return -EPIPE;
if (ep->lep) {
struct lpc32xx_usbd_dd_gad *dd;
diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c
index d2c0bf65e345..9871b90195ad 100644
--- a/drivers/usb/gadget/udc/net2280.c
+++ b/drivers/usb/gadget/udc/net2280.c
@@ -80,6 +80,13 @@ static const char *const ep_name[] = {
"ep-e", "ep-f", "ep-g", "ep-h",
};
+/* Endpoint names for usb3380 advance mode */
+static const char *const ep_name_adv[] = {
+ ep0name,
+ "ep1in", "ep2out", "ep3in", "ep4out",
+ "ep1out", "ep2in", "ep3out", "ep4in",
+};
+
/* mode 0 == ep-{a,b,c,d} 1K fifo each
* mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable
* mode 2 == ep-a 2K fifo, ep-{b,c} 1K each, ep-d unavailable
@@ -138,31 +145,44 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
u32 max, tmp;
unsigned long flags;
static const u32 ep_key[9] = { 1, 0, 1, 0, 1, 1, 0, 1, 0 };
+ int ret = 0;
ep = container_of(_ep, struct net2280_ep, ep);
if (!_ep || !desc || ep->desc || _ep->name == ep0name ||
- desc->bDescriptorType != USB_DT_ENDPOINT)
+ desc->bDescriptorType != USB_DT_ENDPOINT) {
+ pr_err("%s: failed at line=%d\n", __func__, __LINE__);
return -EINVAL;
+ }
dev = ep->dev;
- if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
- return -ESHUTDOWN;
+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
+ ret = -ESHUTDOWN;
+ goto print_err;
+ }
/* erratum 0119 workaround ties up an endpoint number */
- if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE)
- return -EDOM;
+ if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE) {
+ ret = -EDOM;
+ goto print_err;
+ }
if (dev->quirks & PLX_SUPERSPEED) {
- if ((desc->bEndpointAddress & 0x0f) >= 0x0c)
- return -EDOM;
+ if ((desc->bEndpointAddress & 0x0f) >= 0x0c) {
+ ret = -EDOM;
+ goto print_err;
+ }
ep->is_in = !!usb_endpoint_dir_in(desc);
- if (dev->enhanced_mode && ep->is_in && ep_key[ep->num])
- return -EINVAL;
+ if (dev->enhanced_mode && ep->is_in && ep_key[ep->num]) {
+ ret = -EINVAL;
+ goto print_err;
+ }
}
/* sanity check ep-e/ep-f since their fifos are small */
max = usb_endpoint_maxp(desc) & 0x1fff;
- if (ep->num > 4 && max > 64 && (dev->quirks & PLX_LEGACY))
- return -ERANGE;
+ if (ep->num > 4 && max > 64 && (dev->quirks & PLX_LEGACY)) {
+ ret = -ERANGE;
+ goto print_err;
+ }
spin_lock_irqsave(&dev->lock, flags);
_ep->maxpacket = max & 0x7ff;
@@ -192,7 +212,8 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
(dev->gadget.speed == USB_SPEED_HIGH && max != 512) ||
(dev->gadget.speed == USB_SPEED_FULL && max > 64)) {
spin_unlock_irqrestore(&dev->lock, flags);
- return -ERANGE;
+ ret = -ERANGE;
+ goto print_err;
}
}
ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC);
@@ -271,7 +292,11 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
/* pci writes may still be posted */
spin_unlock_irqrestore(&dev->lock, flags);
- return 0;
+ return ret;
+
+print_err:
+ dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, ret);
+ return ret;
}
static int handshake(u32 __iomem *ptr, u32 mask, u32 done, int usec)
@@ -426,9 +451,10 @@ static int net2280_disable(struct usb_ep *_ep)
unsigned long flags;
ep = container_of(_ep, struct net2280_ep, ep);
- if (!_ep || !ep->desc || _ep->name == ep0name)
+ if (!_ep || !ep->desc || _ep->name == ep0name) {
+ pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
return -EINVAL;
-
+ }
spin_lock_irqsave(&ep->dev->lock, flags);
nuke(ep);
@@ -458,8 +484,10 @@ static struct usb_request
struct net2280_ep *ep;
struct net2280_request *req;
- if (!_ep)
+ if (!_ep) {
+ pr_err("%s: Invalid ep\n", __func__);
return NULL;
+ }
ep = container_of(_ep, struct net2280_ep, ep);
req = kzalloc(sizeof(*req), gfp_flags);
@@ -491,8 +519,11 @@ static void net2280_free_request(struct usb_ep *_ep, struct usb_request *_req)
struct net2280_request *req;
ep = container_of(_ep, struct net2280_ep, ep);
- if (!_ep || !_req)
+ if (!_ep || !_req) {
+ dev_err(&ep->dev->pdev->dev, "%s: Inavlid ep=%p or req=%p\n",
+ __func__, _ep, _req);
return;
+ }
req = container_of(_req, struct net2280_request, req);
WARN_ON(!list_empty(&req->queue));
@@ -896,35 +927,44 @@ net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
struct net2280_ep *ep;
struct net2280 *dev;
unsigned long flags;
+ int ret = 0;
/* we always require a cpu-view buffer, so that we can
* always use pio (as fallback or whatever).
*/
- req = container_of(_req, struct net2280_request, req);
- if (!_req || !_req->complete || !_req->buf ||
- !list_empty(&req->queue))
- return -EINVAL;
- if (_req->length > (~0 & DMA_BYTE_COUNT_MASK))
- return -EDOM;
ep = container_of(_ep, struct net2280_ep, ep);
- if (!_ep || (!ep->desc && ep->num != 0))
+ if (!_ep || (!ep->desc && ep->num != 0)) {
+ pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
return -EINVAL;
+ }
+ req = container_of(_req, struct net2280_request, req);
+ if (!_req || !_req->complete || !_req->buf ||
+ !list_empty(&req->queue)) {
+ ret = -EINVAL;
+ goto print_err;
+ }
+ if (_req->length > (~0 & DMA_BYTE_COUNT_MASK)) {
+ ret = -EDOM;
+ goto print_err;
+ }
dev = ep->dev;
- if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
- return -ESHUTDOWN;
+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
+ ret = -ESHUTDOWN;
+ goto print_err;
+ }
/* FIXME implement PIO fallback for ZLPs with DMA */
- if (ep->dma && _req->length == 0)
- return -EOPNOTSUPP;
+ if (ep->dma && _req->length == 0) {
+ ret = -EOPNOTSUPP;
+ goto print_err;
+ }
/* set up dma mapping in case the caller didn't */
if (ep->dma) {
- int ret;
-
ret = usb_gadget_map_request(&dev->gadget, _req,
ep->is_in);
if (ret)
- return ret;
+ goto print_err;
}
ep_vdbg(dev, "%s queue req %p, len %d buf %p\n",
@@ -1013,7 +1053,11 @@ done:
spin_unlock_irqrestore(&dev->lock, flags);
/* pci writes may still be posted */
- return 0;
+ return ret;
+
+print_err:
+ dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, ret);
+ return ret;
}
static inline void
@@ -1134,8 +1178,11 @@ static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req)
int stopped;
ep = container_of(_ep, struct net2280_ep, ep);
- if (!_ep || (!ep->desc && ep->num != 0) || !_req)
+ if (!_ep || (!ep->desc && ep->num != 0) || !_req) {
+ pr_err("%s: Invalid ep=%p or ep->desc or req=%p\n",
+ __func__, _ep, _req);
return -EINVAL;
+ }
spin_lock_irqsave(&ep->dev->lock, flags);
stopped = ep->stopped;
@@ -1157,6 +1204,8 @@ static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req)
}
if (&req->req != _req) {
spin_unlock_irqrestore(&ep->dev->lock, flags);
+ dev_err(&ep->dev->pdev->dev, "%s: Request mismatch\n",
+ __func__);
return -EINVAL;
}
@@ -1214,20 +1263,28 @@ net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)
int retval = 0;
ep = container_of(_ep, struct net2280_ep, ep);
- if (!_ep || (!ep->desc && ep->num != 0))
+ if (!_ep || (!ep->desc && ep->num != 0)) {
+ pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
return -EINVAL;
- if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
- return -ESHUTDOWN;
+ }
+ if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) {
+ retval = -ESHUTDOWN;
+ goto print_err;
+ }
if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03)
- == USB_ENDPOINT_XFER_ISOC)
- return -EINVAL;
+ == USB_ENDPOINT_XFER_ISOC) {
+ retval = -EINVAL;
+ goto print_err;
+ }
spin_lock_irqsave(&ep->dev->lock, flags);
- if (!list_empty(&ep->queue))
+ if (!list_empty(&ep->queue)) {
retval = -EAGAIN;
- else if (ep->is_in && value && net2280_fifo_status(_ep) != 0)
+ goto print_unlock;
+ } else if (ep->is_in && value && net2280_fifo_status(_ep) != 0) {
retval = -EAGAIN;
- else {
+ goto print_unlock;
+ } else {
ep_vdbg(ep->dev, "%s %s %s\n", _ep->name,
value ? "set" : "clear",
wedged ? "wedge" : "halt");
@@ -1251,6 +1308,12 @@ net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)
spin_unlock_irqrestore(&ep->dev->lock, flags);
return retval;
+
+print_unlock:
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+print_err:
+ dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, retval);
+ return retval;
}
static int net2280_set_halt(struct usb_ep *_ep, int value)
@@ -1260,8 +1323,10 @@ static int net2280_set_halt(struct usb_ep *_ep, int value)
static int net2280_set_wedge(struct usb_ep *_ep)
{
- if (!_ep || _ep->name == ep0name)
+ if (!_ep || _ep->name == ep0name) {
+ pr_err("%s: Invalid ep=%p or ep0\n", __func__, _ep);
return -EINVAL;
+ }
return net2280_set_halt_and_wedge(_ep, 1, 1);
}
@@ -1271,14 +1336,22 @@ static int net2280_fifo_status(struct usb_ep *_ep)
u32 avail;
ep = container_of(_ep, struct net2280_ep, ep);
- if (!_ep || (!ep->desc && ep->num != 0))
+ if (!_ep || (!ep->desc && ep->num != 0)) {
+ pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
return -ENODEV;
- if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+ }
+ if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) {
+ dev_err(&ep->dev->pdev->dev,
+ "%s: Invalid driver=%p or speed=%d\n",
+ __func__, ep->dev->driver, ep->dev->gadget.speed);
return -ESHUTDOWN;
+ }
avail = readl(&ep->regs->ep_avail) & (BIT(12) - 1);
- if (avail > ep->fifo_size)
+ if (avail > ep->fifo_size) {
+ dev_err(&ep->dev->pdev->dev, "%s: Fifo overflow\n", __func__);
return -EOVERFLOW;
+ }
if (ep->is_in)
avail = ep->fifo_size - avail;
return avail;
@@ -1289,10 +1362,16 @@ static void net2280_fifo_flush(struct usb_ep *_ep)
struct net2280_ep *ep;
ep = container_of(_ep, struct net2280_ep, ep);
- if (!_ep || (!ep->desc && ep->num != 0))
+ if (!_ep || (!ep->desc && ep->num != 0)) {
+ pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
return;
- if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+ }
+ if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) {
+ dev_err(&ep->dev->pdev->dev,
+ "%s: Invalid driver=%p or speed=%d\n",
+ __func__, ep->dev->driver, ep->dev->gadget.speed);
return;
+ }
writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat);
(void) readl(&ep->regs->ep_rsp);
@@ -1977,7 +2056,7 @@ static void usb_reinit_338x(struct net2280 *dev)
for (i = 0; i < dev->n_ep; i++) {
struct net2280_ep *ep = &dev->ep[i];
- ep->ep.name = ep_name[i];
+ ep->ep.name = dev->enhanced_mode ? ep_name_adv[i] : ep_name[i];
ep->dev = dev;
ep->num = i;
@@ -1989,11 +2068,9 @@ static void usb_reinit_338x(struct net2280 *dev)
ep->regs = (struct net2280_ep_regs __iomem *)
(((void __iomem *)&dev->epregs[ne[i]]) +
ep_reg_addr[i]);
- ep->fiforegs = &dev->fiforegs[i];
} else {
ep->cfg = &dev->epregs[i];
ep->regs = &dev->epregs[i];
- ep->fiforegs = &dev->fiforegs[i];
}
ep->fifo_size = (i != 0) ? 2048 : 512;
@@ -2186,7 +2263,6 @@ static int net2280_start(struct usb_gadget *_gadget,
dev->ep[i].irqs = 0;
/* hook up the driver ... */
- dev->softconnect = 1;
driver->driver.bus = NULL;
dev->driver = driver;
@@ -3052,6 +3128,8 @@ next_endpoints:
BIT(PCI_RETRY_ABORT_INTERRUPT))
static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
+__releases(dev->lock)
+__acquires(dev->lock)
{
struct net2280_ep *ep;
u32 tmp, num, mask, scratch;
@@ -3373,8 +3451,6 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
u32 usbstat;
dev->usb_ext = (struct usb338x_usb_ext_regs __iomem *)
(base + 0x00b4);
- dev->fiforegs = (struct usb338x_fifo_regs __iomem *)
- (base + 0x0500);
dev->llregs = (struct usb338x_ll_regs __iomem *)
(base + 0x0700);
dev->ll_lfps_regs = (struct usb338x_ll_lfps_regs __iomem *)
diff --git a/drivers/usb/gadget/udc/net2280.h b/drivers/usb/gadget/udc/net2280.h
index ac8d5a20a378..4dff60d34f73 100644
--- a/drivers/usb/gadget/udc/net2280.h
+++ b/drivers/usb/gadget/udc/net2280.h
@@ -96,7 +96,6 @@ struct net2280_ep {
struct net2280_ep_regs __iomem *regs;
struct net2280_dma_regs __iomem *dma;
struct net2280_dma *dummy;
- struct usb338x_fifo_regs __iomem *fiforegs;
dma_addr_t td_dma; /* of dummy */
struct net2280 *dev;
unsigned long irqs;
@@ -181,7 +180,6 @@ struct net2280 {
struct net2280_dma_regs __iomem *dma;
struct net2280_dep_regs __iomem *dep;
struct net2280_ep_regs __iomem *epregs;
- struct usb338x_fifo_regs __iomem *fiforegs;
struct usb338x_ll_regs __iomem *llregs;
struct usb338x_ll_lfps_regs __iomem *ll_lfps_regs;
struct usb338x_ll_tsn_regs __iomem *ll_tsn_regs;
diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c
index 6a855fc9bd84..b51226abade6 100644
--- a/drivers/usb/gadget/udc/pxa27x_udc.c
+++ b/drivers/usb/gadget/udc/pxa27x_udc.c
@@ -93,50 +93,46 @@ static void handle_ep(struct pxa_ep *ep);
static int state_dbg_show(struct seq_file *s, void *p)
{
struct pxa_udc *udc = s->private;
- int pos = 0, ret;
u32 tmp;
- ret = -ENODEV;
if (!udc->driver)
- goto out;
+ return -ENODEV;
/* basic device status */
- pos += seq_printf(s, DRIVER_DESC "\n"
- "%s version: %s\nGadget driver: %s\n",
- driver_name, DRIVER_VERSION,
- udc->driver ? udc->driver->driver.name : "(none)");
+ seq_printf(s, DRIVER_DESC "\n"
+ "%s version: %s\n"
+ "Gadget driver: %s\n",
+ driver_name, DRIVER_VERSION,
+ udc->driver ? udc->driver->driver.name : "(none)");
tmp = udc_readl(udc, UDCCR);
- pos += seq_printf(s,
- "udccr=0x%0x(%s%s%s%s%s%s%s%s%s%s), "
- "con=%d,inter=%d,altinter=%d\n", tmp,
- (tmp & UDCCR_OEN) ? " oen":"",
- (tmp & UDCCR_AALTHNP) ? " aalthnp":"",
- (tmp & UDCCR_AHNP) ? " rem" : "",
- (tmp & UDCCR_BHNP) ? " rstir" : "",
- (tmp & UDCCR_DWRE) ? " dwre" : "",
- (tmp & UDCCR_SMAC) ? " smac" : "",
- (tmp & UDCCR_EMCE) ? " emce" : "",
- (tmp & UDCCR_UDR) ? " udr" : "",
- (tmp & UDCCR_UDA) ? " uda" : "",
- (tmp & UDCCR_UDE) ? " ude" : "",
- (tmp & UDCCR_ACN) >> UDCCR_ACN_S,
- (tmp & UDCCR_AIN) >> UDCCR_AIN_S,
- (tmp & UDCCR_AAISN) >> UDCCR_AAISN_S);
+ seq_printf(s,
+ "udccr=0x%0x(%s%s%s%s%s%s%s%s%s%s), con=%d,inter=%d,altinter=%d\n",
+ tmp,
+ (tmp & UDCCR_OEN) ? " oen":"",
+ (tmp & UDCCR_AALTHNP) ? " aalthnp":"",
+ (tmp & UDCCR_AHNP) ? " rem" : "",
+ (tmp & UDCCR_BHNP) ? " rstir" : "",
+ (tmp & UDCCR_DWRE) ? " dwre" : "",
+ (tmp & UDCCR_SMAC) ? " smac" : "",
+ (tmp & UDCCR_EMCE) ? " emce" : "",
+ (tmp & UDCCR_UDR) ? " udr" : "",
+ (tmp & UDCCR_UDA) ? " uda" : "",
+ (tmp & UDCCR_UDE) ? " ude" : "",
+ (tmp & UDCCR_ACN) >> UDCCR_ACN_S,
+ (tmp & UDCCR_AIN) >> UDCCR_AIN_S,
+ (tmp & UDCCR_AAISN) >> UDCCR_AAISN_S);
/* registers for device and ep0 */
- pos += seq_printf(s, "udcicr0=0x%08x udcicr1=0x%08x\n",
- udc_readl(udc, UDCICR0), udc_readl(udc, UDCICR1));
- pos += seq_printf(s, "udcisr0=0x%08x udcisr1=0x%08x\n",
- udc_readl(udc, UDCISR0), udc_readl(udc, UDCISR1));
- pos += seq_printf(s, "udcfnr=%d\n", udc_readl(udc, UDCFNR));
- pos += seq_printf(s, "irqs: reset=%lu, suspend=%lu, resume=%lu, "
- "reconfig=%lu\n",
- udc->stats.irqs_reset, udc->stats.irqs_suspend,
- udc->stats.irqs_resume, udc->stats.irqs_reconfig);
-
- ret = 0;
-out:
- return ret;
+ seq_printf(s, "udcicr0=0x%08x udcicr1=0x%08x\n",
+ udc_readl(udc, UDCICR0), udc_readl(udc, UDCICR1));
+ seq_printf(s, "udcisr0=0x%08x udcisr1=0x%08x\n",
+ udc_readl(udc, UDCISR0), udc_readl(udc, UDCISR1));
+ seq_printf(s, "udcfnr=%d\n", udc_readl(udc, UDCFNR));
+ seq_printf(s, "irqs: reset=%lu, suspend=%lu, resume=%lu, reconfig=%lu\n",
+ udc->stats.irqs_reset, udc->stats.irqs_suspend,
+ udc->stats.irqs_resume, udc->stats.irqs_reconfig);
+
+ return 0;
}
static int queues_dbg_show(struct seq_file *s, void *p)
@@ -144,75 +140,67 @@ static int queues_dbg_show(struct seq_file *s, void *p)
struct pxa_udc *udc = s->private;
struct pxa_ep *ep;
struct pxa27x_request *req;
- int pos = 0, i, maxpkt, ret;
+ int i, maxpkt;
- ret = -ENODEV;
if (!udc->driver)
- goto out;
+ return -ENODEV;
/* dump endpoint queues */
for (i = 0; i < NR_PXA_ENDPOINTS; i++) {
ep = &udc->pxa_ep[i];
maxpkt = ep->fifo_size;
- pos += seq_printf(s, "%-12s max_pkt=%d %s\n",
- EPNAME(ep), maxpkt, "pio");
+ seq_printf(s, "%-12s max_pkt=%d %s\n",
+ EPNAME(ep), maxpkt, "pio");
if (list_empty(&ep->queue)) {
- pos += seq_printf(s, "\t(nothing queued)\n");
+ seq_puts(s, "\t(nothing queued)\n");
continue;
}
list_for_each_entry(req, &ep->queue, queue) {
- pos += seq_printf(s, "\treq %p len %d/%d buf %p\n",
- &req->req, req->req.actual,
- req->req.length, req->req.buf);
+ seq_printf(s, "\treq %p len %d/%d buf %p\n",
+ &req->req, req->req.actual,
+ req->req.length, req->req.buf);
}
}
- ret = 0;
-out:
- return ret;
+ return 0;
}
static int eps_dbg_show(struct seq_file *s, void *p)
{
struct pxa_udc *udc = s->private;
struct pxa_ep *ep;
- int pos = 0, i, ret;
+ int i;
u32 tmp;
- ret = -ENODEV;
if (!udc->driver)
- goto out;
+ return -ENODEV;
ep = &udc->pxa_ep[0];
tmp = udc_ep_readl(ep, UDCCSR);
- pos += seq_printf(s, "udccsr0=0x%03x(%s%s%s%s%s%s%s)\n", tmp,
- (tmp & UDCCSR0_SA) ? " sa" : "",
- (tmp & UDCCSR0_RNE) ? " rne" : "",
- (tmp & UDCCSR0_FST) ? " fst" : "",
- (tmp & UDCCSR0_SST) ? " sst" : "",
- (tmp & UDCCSR0_DME) ? " dme" : "",
- (tmp & UDCCSR0_IPR) ? " ipr" : "",
- (tmp & UDCCSR0_OPC) ? " opc" : "");
+ seq_printf(s, "udccsr0=0x%03x(%s%s%s%s%s%s%s)\n",
+ tmp,
+ (tmp & UDCCSR0_SA) ? " sa" : "",
+ (tmp & UDCCSR0_RNE) ? " rne" : "",
+ (tmp & UDCCSR0_FST) ? " fst" : "",
+ (tmp & UDCCSR0_SST) ? " sst" : "",
+ (tmp & UDCCSR0_DME) ? " dme" : "",
+ (tmp & UDCCSR0_IPR) ? " ipr" : "",
+ (tmp & UDCCSR0_OPC) ? " opc" : "");
for (i = 0; i < NR_PXA_ENDPOINTS; i++) {
ep = &udc->pxa_ep[i];
tmp = i? udc_ep_readl(ep, UDCCR) : udc_readl(udc, UDCCR);
- pos += seq_printf(s, "%-12s: "
- "IN %lu(%lu reqs), OUT %lu(%lu reqs), "
- "irqs=%lu, udccr=0x%08x, udccsr=0x%03x, "
- "udcbcr=%d\n",
- EPNAME(ep),
- ep->stats.in_bytes, ep->stats.in_ops,
- ep->stats.out_bytes, ep->stats.out_ops,
- ep->stats.irqs,
- tmp, udc_ep_readl(ep, UDCCSR),
- udc_ep_readl(ep, UDCBCR));
+ seq_printf(s, "%-12s: IN %lu(%lu reqs), OUT %lu(%lu reqs), irqs=%lu, udccr=0x%08x, udccsr=0x%03x, udcbcr=%d\n",
+ EPNAME(ep),
+ ep->stats.in_bytes, ep->stats.in_ops,
+ ep->stats.out_bytes, ep->stats.out_ops,
+ ep->stats.irqs,
+ tmp, udc_ep_readl(ep, UDCCSR),
+ udc_ep_readl(ep, UDCBCR));
}
- ret = 0;
-out:
- return ret;
+ return 0;
}
static int eps_dbg_open(struct inode *inode, struct file *file)
@@ -2399,7 +2387,7 @@ static struct pxa_udc memory = {
};
#if defined(CONFIG_OF)
-static struct of_device_id udc_pxa_dt_ids[] = {
+static const struct of_device_id udc_pxa_dt_ids[] = {
{ .compatible = "marvell,pxa270-udc" },
{}
};
diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c
index 5a81cb086b99..d69c35558f68 100644
--- a/drivers/usb/gadget/udc/udc-core.c
+++ b/drivers/usb/gadget/udc/udc-core.c
@@ -35,6 +35,8 @@
* @dev - the child device to the actual controller
* @gadget - the gadget. For use by the class code
* @list - for use by the udc class driver
+ * @vbus - for udcs who care about vbus status, this value is real vbus status;
+ * for udcs who do not care about vbus status, this value is always true
*
* This represents the internal data structure which is used by the UDC-class
* to hold information about udc driver and gadget together.
@@ -44,6 +46,7 @@ struct usb_udc {
struct usb_gadget *gadget;
struct device dev;
struct list_head list;
+ bool vbus;
};
static struct class *udc_class;
@@ -128,21 +131,11 @@ 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);
+ struct usb_gadget *gadget = work_to_gadget(work);
+ struct usb_udc *udc = gadget->udc;
- sysfs_notify(&udc->dev.kobj, NULL, "state");
+ if (udc)
+ sysfs_notify(&udc->dev.kobj, NULL, "state");
}
void usb_gadget_set_state(struct usb_gadget *gadget,
@@ -155,6 +148,34 @@ EXPORT_SYMBOL_GPL(usb_gadget_set_state);
/* ------------------------------------------------------------------------- */
+static void usb_udc_connect_control(struct usb_udc *udc)
+{
+ if (udc->vbus)
+ usb_gadget_connect(udc->gadget);
+ else
+ usb_gadget_disconnect(udc->gadget);
+}
+
+/**
+ * usb_udc_vbus_handler - updates the udc core vbus status, and try to
+ * connect or disconnect gadget
+ * @gadget: The gadget which vbus change occurs
+ * @status: The vbus status
+ *
+ * The udc driver calls it when it wants to connect or disconnect gadget
+ * according to vbus status.
+ */
+void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status)
+{
+ struct usb_udc *udc = gadget->udc;
+
+ if (udc) {
+ udc->vbus = status;
+ usb_udc_connect_control(udc);
+ }
+}
+EXPORT_SYMBOL_GPL(usb_udc_vbus_handler);
+
/**
* usb_gadget_udc_reset - notifies the udc core that bus reset occurs
* @gadget: The gadget which bus reset occurs
@@ -278,6 +299,7 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
goto err3;
udc->gadget = gadget;
+ gadget->udc = udc;
mutex_lock(&udc_lock);
list_add_tail(&udc->list, &udc_list);
@@ -287,6 +309,7 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
goto err4;
usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
+ udc->vbus = true;
mutex_unlock(&udc_lock);
@@ -348,21 +371,14 @@ static void usb_gadget_remove_driver(struct usb_udc *udc)
*/
void usb_del_gadget_udc(struct usb_gadget *gadget)
{
- struct usb_udc *udc = NULL;
+ struct usb_udc *udc = gadget->udc;
- mutex_lock(&udc_lock);
- list_for_each_entry(udc, &udc_list, list)
- if (udc->gadget == gadget)
- goto found;
-
- dev_err(gadget->dev.parent, "gadget not registered.\n");
- mutex_unlock(&udc_lock);
-
- return;
+ if (!udc)
+ return;
-found:
dev_vdbg(gadget->dev.parent, "unregistering gadget\n");
+ mutex_lock(&udc_lock);
list_del(&udc->list);
mutex_unlock(&udc_lock);
@@ -397,7 +413,7 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri
driver->unbind(udc->gadget);
goto err1;
}
- usb_gadget_connect(udc->gadget);
+ usb_udc_connect_control(udc);
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
return 0;