aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig W. Nadler <craig@nadler.us>2007-11-11 15:00:15 -0800
committerGreg Kroah-Hartman <gregkh@suse.de>2008-02-01 14:34:49 -0800
commit25a010c8c1a5f0cc2e2794adf969e2df2ad1f0b6 (patch)
tree1d2e477e44a2a8122696711bfd2b9aad35a265bf
parent676d3aa16f66d94bf5654781b77d1e070c8b0514 (diff)
USB: add Printer Gadget Driver
G_PRINTER: Adds a USB printer gadget driver for use in printer firmware. This adds a USB printer gadget driver for use in printer firmware. The printer gadget 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 to receive or send printer data. It can use ioctl calls to the device file to get or set printer status. Signed-off-by: Craig W. Nadler <craig@nadler.us> Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--Documentation/usb/gadget_printer.txt510
-rw-r--r--drivers/usb/gadget/Kconfig14
-rw-r--r--drivers/usb/gadget/Makefile3
-rw-r--r--drivers/usb/gadget/printer.c1592
-rw-r--r--include/linux/usb/Kbuild1
-rw-r--r--include/linux/usb/g_printer.h31
-rw-r--r--include/linux/usb/gadgetfs.h5
7 files changed, 2156 insertions, 0 deletions
diff --git a/Documentation/usb/gadget_printer.txt b/Documentation/usb/gadget_printer.txt
new file mode 100644
index 00000000000..ad995bf0db4
--- /dev/null
+++ b/Documentation/usb/gadget_printer.txt
@@ -0,0 +1,510 @@
+
+ Linux USB Printer Gadget Driver
+ 06/04/2007
+
+ Copyright (C) 2007 Craig W. Nadler <craig@nadler.us>
+
+
+
+GENERAL
+=======
+
+This driver may be used if you are writing printer firmware using Linux as
+the embedded OS. This driver has nothing to do with using a printer with
+your Linux host system.
+
+You will need a USB device controller and a Linux driver for it that accepts
+a gadget / "device class" driver using the Linux USB Gadget API. After the
+USB device controller driver is loaded then load the printer gadget driver.
+This will present a printer interface to the USB Host that your USB Device
+port is connected to.
+
+This driver is structured for printer firmware that runs in user mode. The
+user mode printer firmware will read and write data from the kernel mode
+printer gadget driver using a device file. The printer returns a printer status
+byte when the USB HOST sends a device request to get the printer status. The
+user space firmware can read or write this status byte using a device file
+/dev/g_printer . Both blocking and non-blocking read/write calls are supported.
+
+
+
+
+HOWTO USE THIS DRIVER
+=====================
+
+To load the USB device controller driver and the printer gadget driver. The
+following example uses the Netchip 2280 USB device controller driver:
+
+modprobe net2280
+modprobe g_printer
+
+
+The follow command line parameter can be used when loading the printer gadget
+(ex: modprobe g_printer idVendor=0x0525 idProduct=0xa4a8 ):
+
+idVendor - This is the Vendor ID used in the device descriptor. The default is
+ the Netchip vendor id 0x0525. YOU MUST CHANGE TO YOUR OWN VENDOR ID
+ BEFORE RELEASING A PRODUCT. If you plan to release a product and don't
+ already have a Vendor ID please see www.usb.org for details on how to
+ get one.
+
+idProduct - This is the Product ID used in the device descriptor. The default
+ is 0xa4a8, you should change this to an ID that's not used by any of
+ your other USB products if you have any. It would be a good idea to
+ start numbering your products starting with say 0x0001.
+
+bcdDevice - This is the version number of your product. It would be a good idea
+ to put your firmware version here.
+
+iManufacturer - A string containing the name of the Vendor.
+
+iProduct - A string containing the Product Name.
+
+iSerialNum - A string containing the Serial Number. This should be changed for
+ each unit of your product.
+
+iPNPstring - The PNP ID string used for this printer. You will want to set
+ either on the command line or hard code the PNP ID string used for
+ your printer product.
+
+qlen - The number of 8k buffers to use per endpoint. The default is 10, you
+ should tune this for your product. You may also want to tune the
+ size of each buffer for your product.
+
+
+
+
+USING THE EXAMPLE CODE
+======================
+
+This example code talks to stdout, instead of a print engine.
+
+To compile the test code below:
+
+1) save it to a file called prn_example.c
+2) compile the code with the follow command:
+ gcc prn_example.c -o prn_example
+
+
+
+To read printer data from the host to stdout:
+
+ # prn_example -read_data
+
+
+To write printer data from a file (data_file) to the host:
+
+ # cat data_file | prn_example -write_data
+
+
+To get the current printer status for the gadget driver:
+
+ # prn_example -get_status
+
+ Printer status is:
+ Printer is NOT Selected
+ Paper is Out
+ Printer OK
+
+
+To set printer to Selected/On-line:
+
+ # prn_example -selected
+
+
+To set printer to Not Selected/Off-line:
+
+ # prn_example -not_selected
+
+
+To set paper status to paper out:
+
+ # prn_example -paper_out
+
+
+To set paper status to paper loaded:
+
+ # prn_example -paper_loaded
+
+
+To set error status to printer OK:
+
+ # prn_example -no_error
+
+
+To set error status to ERROR:
+
+ # prn_example -error
+
+
+
+
+EXAMPLE CODE
+============
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <linux/poll.h>
+#include <sys/ioctl.h>
+#include <linux/usb/g_printer.h>
+
+#define PRINTER_FILE "/dev/g_printer"
+#define BUF_SIZE 512
+
+
+/*
+ * 'usage()' - Show program usage.
+ */
+
+static void
+usage(const char *option) /* I - Option string or NULL */
+{
+ if (option) {
+ fprintf(stderr,"prn_example: Unknown option \"%s\"!\n",
+ option);
+ }
+
+ fputs("\n", stderr);
+ fputs("Usage: prn_example -[options]\n", stderr);
+ fputs("Options:\n", stderr);
+ fputs("\n", stderr);
+ fputs("-get_status Get the current printer status.\n", stderr);
+ fputs("-selected Set the selected status to selected.\n", stderr);
+ fputs("-not_selected Set the selected status to NOT selected.\n",
+ stderr);
+ fputs("-error Set the error status to error.\n", stderr);
+ fputs("-no_error Set the error status to NO error.\n", stderr);
+ fputs("-paper_out Set the paper status to paper out.\n", stderr);
+ fputs("-paper_loaded Set the paper status to paper loaded.\n",
+ stderr);
+ fputs("-read_data Read printer data from driver.\n", stderr);
+ fputs("-write_data Write printer sata to driver.\n", stderr);
+ fputs("-NB_read_data (Non-Blocking) Read printer data from driver.\n",
+ stderr);
+ fputs("\n\n", stderr);
+
+ exit(1);
+}
+
+
+static int
+read_printer_data()
+{
+ struct pollfd fd[1];
+
+ /* Open device file for printer gadget. */
+ fd[0].fd = open(PRINTER_FILE, O_RDWR);
+ if (fd[0].fd < 0) {
+ printf("Error %d opening %s\n", fd[0].fd, PRINTER_FILE);
+ close(fd[0].fd);
+ return(-1);
+ }
+
+ fd[0].events = POLLIN | POLLRDNORM;
+
+ while (1) {
+ static char buf[BUF_SIZE];
+ int bytes_read;
+ int retval;
+
+ /* Wait for up to 1 second for data. */
+ retval = poll(fd, 1, 1000);
+
+ if (retval && (fd[0].revents & POLLRDNORM)) {
+
+ /* Read data from printer gadget driver. */
+ bytes_read = read(fd[0].fd, buf, BUF_SIZE);
+
+ if (bytes_read < 0) {
+ printf("Error %d reading from %s\n",
+ fd[0].fd, PRINTER_FILE);
+ close(fd[0].fd);
+ return(-1);
+ } else if (bytes_read > 0) {
+ /* Write data to standard OUTPUT (stdout). */
+ fwrite(buf, 1, bytes_read, stdout);
+ fflush(stdout);
+ }
+
+ }
+
+ }
+
+ /* Close the device file. */
+ close(fd[0].fd);
+
+ return 0;
+}
+
+
+static int
+write_printer_data()
+{
+ struct pollfd fd[1];
+
+ /* Open device file for printer gadget. */
+ fd[0].fd = open (PRINTER_FILE, O_RDWR);
+ if (fd[0].fd < 0) {
+ printf("Error %d opening %s\n", fd[0].fd, PRINTER_FILE);
+ close(fd[0].fd);
+ return(-1);
+ }
+
+ fd[0].events = POLLOUT | POLLWRNORM;
+
+ while (1) {
+ int retval;
+ static char buf[BUF_SIZE];
+ /* Read data from standard INPUT (stdin). */
+ int bytes_read = fread(buf, 1, BUF_SIZE, stdin);
+
+ if (!bytes_read) {
+ break;
+ }
+
+ while (bytes_read) {
+
+ /* Wait for up to 1 second to sent data. */
+ retval = poll(fd, 1, 1000);
+
+ /* Write data to printer gadget driver. */
+ if (retval && (fd[0].revents & POLLWRNORM)) {
+ retval = write(fd[0].fd, buf, bytes_read);
+ if (retval < 0) {
+ printf("Error %d writing to %s\n",
+ fd[0].fd,
+ PRINTER_FILE);
+ close(fd[0].fd);
+ return(-1);
+ } else {
+ bytes_read -= retval;
+ }
+
+ }
+
+ }
+
+ }
+
+ /* Wait until the data has been sent. */
+ fsync(fd[0].fd);
+
+ /* Close the device file. */
+ close(fd[0].fd);
+
+ return 0;
+}
+
+
+static int
+read_NB_printer_data()
+{
+ int fd;
+ static char buf[BUF_SIZE];
+ int bytes_read;
+
+ /* Open device file for printer gadget. */
+ fd = open(PRINTER_FILE, O_RDWR|O_NONBLOCK);
+ if (fd < 0) {
+ printf("Error %d opening %s\n", fd, PRINTER_FILE);
+ close(fd);
+ return(-1);
+ }
+
+ while (1) {
+ /* Read data from printer gadget driver. */
+ bytes_read = read(fd, buf, BUF_SIZE);
+ if (bytes_read <= 0) {
+ break;
+ }
+
+ /* Write data to standard OUTPUT (stdout). */
+ fwrite(buf, 1, bytes_read, stdout);
+ fflush(stdout);
+ }
+
+ /* Close the device file. */
+ close(fd);
+
+ return 0;
+}
+
+
+static int
+get_printer_status()
+{
+ int retval;
+ int fd;
+
+ /* Open device file for printer gadget. */
+ fd = open(PRINTER_FILE, O_RDWR);
+ if (fd < 0) {
+ printf("Error %d opening %s\n", fd, PRINTER_FILE);
+ close(fd);
+ return(-1);
+ }
+
+ /* Make the IOCTL call. */
+ retval = ioctl(fd, GADGET_GET_PRINTER_STATUS);
+ if (retval < 0) {
+ fprintf(stderr, "ERROR: Failed to set printer status\n");
+ return(-1);
+ }
+
+ /* Close the device file. */
+ close(fd);
+
+ return(retval);
+}
+
+
+static int
+set_printer_status(unsigned char buf, int clear_printer_status_bit)
+{
+ int retval;
+ int fd;
+
+ retval = get_printer_status();
+ if (retval < 0) {
+ fprintf(stderr, "ERROR: Failed to get printer status\n");
+ return(-1);
+ }
+
+ /* Open device file for printer gadget. */
+ fd = open(PRINTER_FILE, O_RDWR);
+
+ if (fd < 0) {
+ printf("Error %d opening %s\n", fd, PRINTER_FILE);
+ close(fd);
+ return(-1);
+ }
+
+ if (clear_printer_status_bit) {
+ retval &= ~buf;
+ } else {
+ retval |= buf;
+ }
+
+ /* Make the IOCTL call. */
+ if (ioctl(fd, GADGET_SET_PRINTER_STATUS, (unsigned char)retval)) {
+ fprintf(stderr, "ERROR: Failed to set printer status\n");
+ return(-1);
+ }
+
+ /* Close the device file. */
+ close(fd);
+
+ return 0;
+}
+
+
+static int
+display_printer_status()
+{
+ char printer_status;
+
+ printer_status = get_printer_status();
+ if (printer_status < 0) {
+ fprintf(stderr, "ERROR: Failed to get printer status\n");
+ return(-1);
+ }
+
+ printf("Printer status is:\n");
+ if (printer_status & PRINTER_SELECTED) {
+ printf(" Printer is Selected\n");
+ } else {
+ printf(" Printer is NOT Selected\n");
+ }
+ if (printer_status & PRINTER_PAPER_EMPTY) {
+ printf(" Paper is Out\n");
+ } else {
+ printf(" Paper is Loaded\n");
+ }
+ if (printer_status & PRINTER_NOT_ERROR) {
+ printf(" Printer OK\n");
+ } else {
+ printf(" Printer ERROR\n");
+ }
+
+ return(0);
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ int i; /* Looping var */
+ int retval = 0;
+
+ /* No Args */
+ if (argc == 1) {
+ usage(0);
+ exit(0);
+ }
+
+ for (i = 1; i < argc && !retval; i ++) {
+
+ if (argv[i][0] != '-') {
+ continue;
+ }
+
+ if (!strcmp(argv[i], "-get_status")) {
+ if (display_printer_status()) {
+ retval = 1;
+ }
+
+ } else if (!strcmp(argv[i], "-paper_loaded")) {
+ if (set_printer_status(PRINTER_PAPER_EMPTY, 1)) {
+ retval = 1;
+ }
+
+ } else if (!strcmp(argv[i], "-paper_out")) {
+ if (set_printer_status(PRINTER_PAPER_EMPTY, 0)) {
+ retval = 1;
+ }
+
+ } else if (!strcmp(argv[i], "-selected")) {
+ if (set_printer_status(PRINTER_SELECTED, 0)) {
+ retval = 1;
+ }
+
+ } else if (!strcmp(argv[i], "-not_selected")) {
+ if (set_printer_status(PRINTER_SELECTED, 1)) {
+ retval = 1;
+ }
+
+ } else if (!strcmp(argv[i], "-error")) {
+ if (set_printer_status(PRINTER_NOT_ERROR, 1)) {
+ retval = 1;
+ }
+
+ } else if (!strcmp(argv[i], "-no_error")) {
+ if (set_printer_status(PRINTER_NOT_ERROR, 0)) {
+ retval = 1;
+ }
+
+ } else if (!strcmp(argv[i], "-read_data")) {
+ if (read_printer_data()) {
+ retval = 1;
+ }
+
+ } else if (!strcmp(argv[i], "-write_data")) {
+ if (write_printer_data()) {
+ retval = 1;
+ }
+
+ } else if (!strcmp(argv[i], "-NB_read_data")) {
+ if (read_NB_printer_data()) {
+ retval = 1;
+ }
+
+ } else {
+ usage(argv[i]);
+ retval = 1;
+ }
+ }
+
+ exit(retval);
+}
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index e706882a8e6..8a88f24270b 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -548,6 +548,20 @@ config USB_MIDI_GADGET
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "g_midi".
+config USB_G_PRINTER
+ tristate "Printer Gadget"
+ help
+ The Printer Gadget 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 to
+ receive or send printer data. It can use ioctl calls to
+ the device file to get or set printer status.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_printer".
+
+ For more information, see Documentation/usb/gadget_printer.txt
+ which includes sample code for accessing the device file.
# put drivers that need isochronous transfer support (for audio
# or video class gadget drivers), or specific hardware, here.
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 904e57bf611..c3aab80b6c7 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -28,6 +28,8 @@ g_midi-objs := gmidi.o usbstring.o config.o epautoconf.o
gadgetfs-objs := inode.o
g_file_storage-objs := file_storage.o usbstring.o config.o \
epautoconf.o
+g_printer-objs := printer.o usbstring.o config.o \
+ epautoconf.o
ifeq ($(CONFIG_USB_ETH_RNDIS),y)
g_ether-objs += rndis.o
@@ -38,5 +40,6 @@ obj-$(CONFIG_USB_ETH) += g_ether.o
obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o
obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o
obj-$(CONFIG_USB_G_SERIAL) += g_serial.o
+obj-$(CONFIG_USB_G_PRINTER) += g_printer.o
obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o
diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c
new file mode 100644
index 00000000000..9fdabc8fcac
--- /dev/null
+++ b/drivers/usb/gadget/printer.c
@@ -0,0 +1,1592 @@
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#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/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/utsname.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 <asm/system.h>
+#include <linux/uaccess.h>
+#include <asm/unaligned.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/g_printer.h>
+
+#include "gadget_chips.h"
+
+#define DRIVER_DESC "Printer Gadget"
+#define DRIVER_VERSION "2007 OCT 06"
+
+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 */
+ spinlock_t lock_printer_io;
+ struct usb_gadget *gadget;
+ struct usb_request *req; /* for control responses */
+ u8 config;
+ s8 interface;
+ struct usb_ep *in_ep, *out_ep;
+ const struct usb_endpoint_descriptor
+ *in, *out;
+ 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 class_device *printer_class_dev;
+ struct cdev printer_cdev;
+ struct device *pdev;
+ u8 printer_cdev_open;
+ wait_queue_head_t wait;
+};
+
+static struct printer_dev usb_printer_gadget;
+
+/*-------------------------------------------------------------------------*/
+
+/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
+ * Instead: allocate your own, using normal USB-IF procedures.
+ */
+
+/* Thanks to NetChip Technologies for donating this product ID.
+ */
+#define PRINTER_VENDOR_NUM 0x0525 /* NetChip */
+#define PRINTER_PRODUCT_NUM 0xa4a8 /* Linux-USB Printer Gadget */
+
+/* Some systems will want different product identifers published in the
+ * device descriptor, either numbers or strings or both. These string
+ * parameters are in UTF-8 (superset of ASCII's 7 bit characters).
+ */
+
+static ushort __initdata idVendor;
+module_param(idVendor, ushort, S_IRUGO);
+MODULE_PARM_DESC(idVendor, "USB Vendor ID");
+
+static ushort __initdata idProduct;
+module_param(idProduct, ushort, S_IRUGO);
+MODULE_PARM_DESC(idProduct, "USB Product ID");
+
+static ushort __initdata bcdDevice;
+module_param(bcdDevice, ushort, S_IRUGO);
+MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)");
+
+static char *__initdata iManufacturer;
+module_param(iManufacturer, charp, S_IRUGO);
+MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string");
+
+static char *__initdata iProduct;
+module_param(iProduct, charp, S_IRUGO);
+MODULE_PARM_DESC(iProduct, "USB Product string");
+
+static char *__initdata iSerialNum;
+module_param(iSerialNum, charp, S_IRUGO);
+MODULE_PARM_DESC(iSerialNum, "1");
+
+static char *__initdata iPNPstring;
+module_param(iPNPstring, charp, S_IRUGO);
+MODULE_PARM_DESC(iPNPstring, "MFG:linux;MDL:g_printer;CLS:PRINTER;SN:1;");
+
+/* Number of requests to allocate per endpoint, not used for ep0. */
+static unsigned qlen = 10;
+module_param(qlen, uint, S_IRUGO|S_IWUSR);
+
+#define QLEN qlen
+
+#ifdef CONFIG_USB_GADGET_DUALSPEED
+#define DEVSPEED USB_SPEED_HIGH
+#else /* full speed (low speed doesn't do bulk) */
+#define DEVSPEED USB_SPEED_FULL
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+#define xprintk(d, level, fmt, args...) \
+ printk(level "%s: " fmt, DRIVER_DESC, ## args)
+
+#ifdef DEBUG
+#define DBG(dev, fmt, args...) \
+ xprintk(dev, KERN_DEBUG, fmt, ## args)
+#else
+#define DBG(dev, fmt, args...) \
+ do { } while (0)
+#endif /* DEBUG */
+
+#ifdef VERBOSE
+#define VDBG(dev, fmt, args...) \
+ xprintk(dev, KERN_DEBUG, fmt, ## args)
+#else
+#define VDBG(dev, fmt, args...) \
+ do { } while (0)
+#endif /* VERBOSE */
+
+#define ERROR(dev, fmt, args...) \
+ xprintk(dev, KERN_ERR, fmt, ## args)
+#define WARN(dev, fmt, args...) \
+ xprintk(dev, KERN_WARNING, fmt, ## args)
+#define INFO(dev, fmt, args...) \
+ xprintk(dev, KERN_INFO, fmt, ## args)
+
+/*-------------------------------------------------------------------------*/
+
+/* USB DRIVER HOOKUP (to the hardware driver, below us), mostly
+ * ep0 implementation: descriptors, config management, setup().
+ * also optional class-specific notification interrupt transfer.
+ */
+
+/*
+ * DESCRIPTORS ... most are static, but strings and (full) configuration
+ * descriptors are built on demand.
+ */
+
+#define STRING_MANUFACTURER 1
+#define STRING_PRODUCT 2
+#define STRING_SERIALNUM 3
+
+/* holds our biggest descriptor */
+#define USB_DESC_BUFSIZE 256
+#define USB_BUFSIZE 8192
+
+/* This device advertises one configuration. */
+#define DEV_CONFIG_VALUE 1
+#define PRINTER_INTERFACE 0
+
+static struct usb_device_descriptor device_desc = {
+ .bLength = sizeof device_desc,
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_PER_INTERFACE,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+ .idVendor = __constant_cpu_to_le16(PRINTER_VENDOR_NUM),
+ .idProduct = __constant_cpu_to_le16(PRINTER_PRODUCT_NUM),
+ .iManufacturer = STRING_MANUFACTURER,
+ .iProduct = STRING_PRODUCT,
+ .iSerialNumber = STRING_SERIALNUM,
+ .bNumConfigurations = 1
+};
+
+static struct usb_otg_descriptor otg_desc = {
+ .bLength = sizeof otg_desc,
+ .bDescriptorType = USB_DT_OTG,
+ .bmAttributes = USB_OTG_SRP
+};
+
+static struct usb_config_descriptor config_desc = {
+ .bLength = sizeof config_desc,
+ .bDescriptorType = USB_DT_CONFIG,
+
+ /* compute wTotalLength on the fly */
+ .bNumInterfaces = 1,
+ .bConfigurationValue = DEV_CONFIG_VALUE,
+ .iConfiguration = 0,
+ .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+ .bMaxPower = 1 /* Self-Powered */
+};
+
+static struct usb_interface_descriptor intf_desc = {
+ .bLength = sizeof intf_desc,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = PRINTER_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 const struct usb_descriptor_header *fs_printer_function [11] = {
+ (struct usb_descriptor_header *) &otg_desc,
+ (struct usb_descriptor_header *) &intf_desc,
+ (struct usb_descriptor_header *) &fs_ep_in_desc,
+ (struct usb_descriptor_header *) &fs_ep_out_desc,
+ NULL
+};
+
+#ifdef CONFIG_USB_GADGET_DUALSPEED
+
+/*
+ * 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 = __constant_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 = __constant_cpu_to_le16(512)
+};
+
+static struct usb_qualifier_descriptor dev_qualifier = {
+ .bLength = sizeof dev_qualifier,
+ .bDescriptorType = USB_DT_DEVICE_QUALIFIER,
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_PRINTER,
+ .bNumConfigurations = 1
+};
+
+static const struct usb_descriptor_header *hs_printer_function [11] = {
+ (struct usb_descriptor_header *) &otg_desc,
+ (struct usb_descriptor_header *) &intf_desc,
+ (struct usb_descriptor_header *) &hs_ep_in_desc,
+ (struct usb_descriptor_header *) &hs_ep_out_desc,
+ NULL
+};
+
+/* maxpacket and other transfer characteristics vary by speed. */
+#define ep_desc(g, hs, fs) (((g)->speed == USB_SPEED_HIGH)?(hs):(fs))
+
+#else
+
+/* if there's no high speed support, maxpacket doesn't change. */
+#define ep_desc(g, hs, fs) (((void)(g)), (fs))
+
+#endif /* !CONFIG_USB_GADGET_DUALSPEED */
+
+/*-------------------------------------------------------------------------*/
+
+/* descriptors that are built on-demand */
+
+static char manufacturer [50];
+static char product_desc [40] = DRIVER_DESC;
+static char serial_num [40] = "1";
+static char pnp_string [1024] =
+ "XXMFG:linux;MDL:g_printer;CLS:PRINTER;SN:1;";
+
+/* static strings, in UTF-8 */
+static struct usb_string strings [] = {
+ { STRING_MANUFACTURER, manufacturer, },
+ { STRING_PRODUCT, product_desc, },
+ { STRING_SERIALNUM, serial_num, },
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab = {
+ .language = 0x0409, /* en-us */
+ .strings = strings,
+};
+
+/*-------------------------------------------------------------------------*/
+
+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:
+ list_add_tail(&req->list, &dev->rx_buffers);
+ wake_up_interruptible(&dev->rx_wait);
+ DBG(dev, "G_Printer : rx length %d\n", req->actual);
+ 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;
+ }
+ 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;
+}
+
+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);
+
+ spin_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;
+
+ 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;
+
+ error = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC);
+ if (error) {
+ DBG(dev, "rx submit --> %d\n", error);
+ list_add(&req->list, &dev->rx_reqs);
+ break;
+ } else {
+ list_add(&req->list, &dev->rx_reqs_active);
+ }
+ }
+
+ 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_u