Craig W. Nadler wrote: >Craig W. Nadler wrote: > > > >> I have a Beta version of a Printer Gadget driver that I'd like to >>submit. The patch file included contains all the changes, the main C file >>printer.c and the documentation file gadget_printer.txt are also attached >>so that they're viewable without applying the patch. >> The documentation includes example code for reading and writing >>printer data or the printer status from a user mode program. This model >>of having the USB device class driver in kernel mode and the PDL (page >>description language) renderer in user mode is used by some PDL firmware >>vendors. >> If you have any comments on the driver or documentation please let >>me know. >> >>Best Regards, >> >>Craig Nadler >> >> >> >> >> >I forgot to include the document in the patch file, a new patch file is >attached. I also fixed a grammar error in the document. > >Best Regards, > >Craig Nadler > > >
Sorry, I just spotted another typo and turned off debug messages by default.
diff -Nru a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig --- a/drivers/usb/gadget/Kconfig 2006-01-02 22:21:10.000000000 -0500 +++ b/drivers/usb/gadget/Kconfig 2006-02-27 16:01:04.000000000 -0500 @@ -387,6 +387,17 @@ which includes instructions and a "driver info file" needed to make MS-Windows work with this driver. +config USB_G_PRINTER + tristate "Printer Gadget" + help + The Printer Gadget channels data between it's USB printer interface + and a device file. A user space program can be used to read and + write this device file to receive or send printer data. If you'd + perfer to handle the printer data in Kernel space there is a + compile time option for using an API instead of device files. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_printer". # put drivers that need isochronous transfer support (for audio # or video class gadget drivers), or specific hardware, here. diff -Nru a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile --- a/drivers/usb/gadget/Makefile 2006-01-02 22:21:10.000000000 -0500 +++ b/drivers/usb/gadget/Makefile 2006-02-27 16:01:09.000000000 -0500 @@ -17,6 +17,8 @@ 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 @@ -27,4 +29,5 @@ 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 diff -Nru a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c --- a/drivers/usb/gadget/printer.c 1969-12-31 19:00:00.000000000 -0500 +++ b/drivers/usb/gadget/printer.c 2006-03-03 01:33:32.000000000 -0500 @@ -0,0 +1,1659 @@ +/* + * 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 + */ + + +#define DEBUG 0 +#define VERBOSE 0 + + +#include <linux/config.h> +#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/types.h> +#include <linux/ctype.h> +#include <linux/cdev.h> + +#include <asm/byteorder.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/unaligned.h> + +#include <linux/usb_ch9.h> +#include <linux/usb_gadget.h> + +#include "gadget_chips.h" + + + +#define PRINTER_NOT_ERROR 0x08 +#define PRINTER_SELECTED 0x10 +#define PRINTER_PAPER_EMPTY 0x20 + +#define DRIVER_DESC "Printer Gadget" +#define DRIVER_VERSION "0.1 02/18/2006" + +static const char shortname [] = "printer"; +static const char driver_desc [] = DRIVER_DESC; + +#define USE_DEVICE_FILE +#ifdef USE_DEVICE_FILE +static const int g_printer_devno = MKDEV(180,200); +static const int g_printer_status_devno = MKDEV(180,201); +#endif /* USE_DEVICE_FILE */ + +/*-------------------------------------------------------------------------*/ + +struct printer_dev { + spinlock_t lock; + 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; + struct list_head rx_reqs_active; + struct list_head rx_buffers; + struct list_head tx_reqs; + struct list_head tx_reqs_active; + struct usb_request *current_rx_req; + size_t current_rx_bytes; + u8 *current_rx_buf; + u8 printer_status; + unsigned zlp:1; + unsigned suspended:1; + struct cdev printer_cdev; + u8 printer_cdev_open; + struct cdev printer_status_cdev; + u8 printer_status_cdev_open; +}; + +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;"); + + +#define DEFAULT_QLEN 2 /* double buffering by default */ + +#ifdef CONFIG_USB_GADGET_DUALSPEED + +static unsigned qmult = 5; +module_param (qmult, uint, S_IRUGO|S_IWUSR); + +/* for dual-speed hardware, use deeper queues at highspeed */ +#define qlen(gadget) \ + (DEFAULT_QLEN*((gadget->speed == USB_SPEED_HIGH) ? qmult : 1)) +#define DEVSPEED USB_SPEED_HIGH + +#else /* full speed (low speed doesn't do bulk) */ + +#define qlen(gadget) DEFAULT_QLEN +#define DEVSPEED USB_SPEED_FULL + +#endif + + +/*-------------------------------------------------------------------------*/ + + +#define xprintk(d,level,fmt,args...) \ + printk(level "%s: " fmt , DRIVER_DESC , ## args) + +#ifdef DEBUG +#undef DEBUG +#define DEBUG(dev,fmt,args...) \ + xprintk(dev , KERN_DEBUG , fmt , ## args) +#else +#define DEBUG(dev,fmt,args...) \ + do { } while (0) +#endif /* DEBUG */ + +#ifdef VERBOSE +#define VDEBUG(dev,fmt,args...) \ + xprintk(dev , KERN_DEBUG , fmt , ## args) +#else +#define VDEBUG(dev,fmt,args...) \ + xprintk(dev , KERN_DEBUG , fmt , ## args) +#endif /* DEBUG */ + +#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 size, gfp_t gfp_flags) +{ + struct usb_request *req; + + req = usb_ep_alloc_request (ep, gfp_flags); + if (!req) + return NULL; + + req->buf = usb_ep_alloc_buffer (ep, size, &req->dma, gfp_flags); + if (!req->buf) { + usb_ep_free_request (ep, req); + req = NULL; + } + req->length = size; + + return req; +} + + +static void +printer_req_free (struct usb_ep *ep, struct usb_request *req) +{ + usb_ep_free_buffer (ep, req->buf, req->dma, req->length); + usb_ep_free_request (ep, req); +} + + +static int prealloc (struct list_head *list, struct usb_ep *ep, + unsigned n, gfp_t gfp_flags) +{ + unsigned i; + struct usb_request *req; + + if (!n) + return -ENOMEM; + + /* queue/recycle up to N requests */ + i = n; + list_for_each_entry (req, list, list) { + if (i-- == 0) + goto extra; + } + while (i--) { + req = printer_req_alloc (ep, USB_BUFSIZE, gfp_flags); + if (!req) + return list_empty (list) ? -ENOMEM : 0; + list_add (&req->list, list); + } + return 0; + +extra: + /* free extras */ + for (;;) { + struct list_head *next; + + next = req->list.next; + list_del (&req->list); + usb_ep_free_request (ep, req); + + if (next == list) + break; + + req = container_of (next, struct usb_request, list); + } + return 0; +} + +static int alloc_requests (struct printer_dev *dev, unsigned n, + gfp_t gfp_flags) +{ + int status; + + status = prealloc (&dev->tx_reqs, dev->in_ep, n, gfp_flags); + if (status < 0) + goto fail; + status = prealloc (&dev->rx_reqs, dev->out_ep, n, gfp_flags); + if (status < 0) + goto fail; + return 0; +fail: + DEBUG (dev, "can't alloc requests\n"); + return status; +} + + +/*-------------------------------------------------------------------------*/ + + +static void rx_fill (struct printer_dev *dev, gfp_t gfp_flags); +static int rx_submit (struct printer_dev *dev, struct usb_request *req, + gfp_t gfp_flags); + +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); + DEBUG (dev, "G_Printer : rx length %d\n", req->actual); + break; + + /* software-driven interface shutdown */ + case -ECONNRESET: // unlink + case -ESHUTDOWN: // disconnect etc + VDEBUG (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 + DEBUG (dev, "rx %s reset\n", ep->name); + list_add (&req->list, &dev->rx_reqs); + break; + + /* data overrun */ + case -EOVERFLOW: + // FALLTHROUGH + + default: + DEBUG (dev, "rx status %d\n", status); + list_add (&req->list, &dev->rx_reqs); + break; + } + spin_unlock_irqrestore (&dev->lock, flags); + + rx_fill (dev, GFP_ATOMIC); +} + + +static void rx_fill (struct printer_dev *dev, gfp_t gfp_flags) +{ + struct usb_request *req; + unsigned long flags; + + spin_lock_irqsave (&dev->lock, flags); + while (!list_empty (&dev->rx_reqs)) { + req = container_of (dev->rx_reqs.next, + struct usb_request, list); + list_del_init (&req->list); + spin_unlock_irqrestore (&dev->lock, flags); + + if (rx_submit (dev, req, gfp_flags) < 0) { + return; + } + + spin_lock_irqsave (&dev->lock, flags); + } + spin_unlock_irqrestore (&dev->lock, flags); +} + + +static int +rx_submit (struct printer_dev *dev, struct usb_request *req, gfp_t gfp_flags) +{ + int retval = -ENOMEM; + size_t size = USB_BUFSIZE; + + req->length = size; + req->complete = rx_complete; + + retval = usb_ep_queue (dev->out_ep, req, gfp_flags); + if (retval) { + DEBUG (dev, "rx submit --> %d\n", retval); + spin_lock (&dev->lock); + list_add (&req->list, &dev->rx_reqs); + spin_unlock (&dev->lock); + } else { + spin_lock (&dev->lock); + list_add (&req->list, &dev->rx_reqs_active); + spin_unlock (&dev->lock); + } + return retval; +} + + +static void tx_complete (struct usb_ep *ep, struct usb_request *req) +{ + struct printer_dev *dev = ep->driver_data; + + switch (req->status) { + default: + VDEBUG (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); + spin_unlock (&dev->lock); +} + + +/*-------------------------------------------------------------------------*/ + + +#ifdef USE_DEVICE_FILE + +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; + ret = 0; + } + + spin_unlock_irqrestore (&dev->lock, flags); + + return ret; +} + + +static int +printer_close (struct inode *inode, struct file *fd) +{ + struct printer_dev *dev; + unsigned long flags; + + dev = container_of (inode->i_cdev, struct printer_dev, printer_cdev); + + spin_lock_irqsave (&dev->lock, flags); + + dev->printer_cdev_open = 0; + + spin_unlock_irqrestore (&dev->lock, flags); + + 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; +#else /* USE_DEVICE_FILE */ +ssize_t +printer_read (char *buf, size_t len) +{ + struct printer_dev *dev = &usb_printer_gadget; +#endif /* USE_DEVICE_FILE */ + unsigned long flags; + size_t size; + size_t bytes_copied; + struct usb_request *req; + + if (len == 0) { + return 0; + } + + spin_lock_irqsave (&dev->lock, flags); + + if (list_empty (&dev->rx_reqs_active)) + rx_fill (dev, GFP_ATOMIC); + + bytes_copied = 0; + while (!(list_empty (&dev->rx_buffers)) && len) { + req = container_of (dev->rx_buffers.next, struct usb_request, + list); + list_del_init (&req->list); + + if (req->actual) { + dev->current_rx_req = req; + dev->current_rx_bytes = req->actual; + dev->current_rx_buf = req->buf; + } else { + list_add (&req->list, &dev->rx_reqs); + spin_unlock_irqrestore (&dev->lock, flags); + return 0; + } + + if (len > dev->current_rx_bytes) + size = dev->current_rx_bytes; + else + size = len; + +#ifdef USE_DEVICE_FILE + size -= copy_to_user (buf, dev->current_rx_buf, size); +#else /* USE_DEVICE_FILE */ + memcpy(buf, dev->current_rx_buf, size); +#endif /* USE_DEVICE_FILE */ + bytes_copied += size; + len -= size; + buf += size; + + if (size < dev->current_rx_bytes) { + dev->current_rx_bytes -= size; + dev->current_rx_buf += size; + } else { + dev->current_rx_bytes = 0; + list_add (&dev->current_rx_req->list, &dev->rx_reqs); + } + } + + spin_unlock_irqrestore (&dev->lock, flags); + + return bytes_copied; +} + + +#ifdef USE_DEVICE_FILE +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; +#else /* USE_DEVICE_FILE */ +ssize_t +printer_write (const char *buf, size_t len) +{ + struct printer_dev *dev = &usb_printer_gadget; +#endif /* USE_DEVICE_FILE */ + unsigned long flags; + struct usb_request *req; + + if (len > USB_BUFSIZE) { + len = USB_BUFSIZE; + } + + if (len == 0) + return 0; + + /* Some hardware doesn't like to write zlps. Hopefully the + * extra data byte won't cause any problems. + */ + if (len < USB_BUFSIZE && !dev->zlp && + (len % dev->in_ep->maxpacket) == 0) { + len++; + } + + spin_lock_irqsave (&dev->lock, flags); + + if (list_empty (&dev->tx_reqs)) { + spin_unlock_irqrestore (&dev->lock, flags); + return 0; + } + + req = container_of (dev->tx_reqs.next, struct usb_request, list); + list_del_init (&req->list); + + spin_unlock_irqrestore (&dev->lock, flags); + + req->complete = tx_complete; + req->length = len; + req->zero = ((len % dev->in_ep->maxpacket) == 0); + +#ifdef USE_DEVICE_FILE + if (copy_from_user (req->buf, buf, len)) { + list_add (&req->list, &dev->tx_reqs); + spin_unlock_irqrestore (&dev->lock, flags); + return 0; + } +#else /* USE_DEVICE_FILE */ + memcpy (req->buf, buf, len); +#endif /* USE_DEVICE_FILE */ + + if (usb_ep_queue (dev->out_ep, req, GFP_ATOMIC)) { + list_add (&req->list, &dev->tx_reqs); + spin_unlock_irqrestore (&dev->lock, flags); + return 0; + } + + list_add (&req->list, &dev->tx_reqs_active); + spin_unlock_irqrestore (&dev->lock, flags); + + return len; +} + + +#ifdef USE_DEVICE_FILE + +/* used after endpoint configuration */ +static struct file_operations printer_io_operations = { + .owner = THIS_MODULE, + .open = printer_open, + .read = printer_read, + .write = printer_write, + .release = printer_close +}; + + + + +static int +printer_status_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_status_cdev); + + spin_lock_irqsave (&dev->lock, flags); + + fd->private_data = dev; + + if (!dev->printer_status_cdev_open) { + dev->printer_status_cdev_open = 1; + ret = 0; + } + + spin_unlock_irqrestore (&dev->lock, flags); + + if (ret == 0) + INFO (dev, "opened printer status file\n"); + + return ret; +} + + +static int +printer_status_close (struct inode *inode, struct file *fd) +{ + struct printer_dev *dev; + unsigned long flags; + + dev = container_of (inode->i_cdev, struct printer_dev, + printer_status_cdev); + + spin_lock_irqsave (&dev->lock, flags); + + dev->printer_status_cdev_open = 0; + + spin_unlock_irqrestore (&dev->lock, flags); + + INFO (dev, "closed printer status file\n"); + + return 0; +} + + +static ssize_t +printer_status_read (struct file *fd, char __user *buf, size_t len, + loff_t *ptr) +{ + struct printer_dev *dev; + unsigned long flags; + int ret; + + if (len <= 0) + return 0; + + dev = fd->private_data; + + spin_lock_irqsave (&dev->lock, flags); + + ret = !(copy_to_user (buf, &dev->printer_status, 1)); + + spin_unlock_irqrestore (&dev->lock, flags); + + INFO (dev, "read printer status %x\n", dev->printer_status); + + return ret; +} + + +static ssize_t +printer_status_write (struct file *fd, const char __user *buf, size_t len, + loff_t *ptr) +{ + struct printer_dev *dev; + unsigned long flags; + int ret; + + if (len <= 0) + return 0; + + dev = fd->private_data; + + spin_lock_irqsave (&dev->lock, flags); + + ret = !(copy_from_user (&dev->printer_status, buf, 1)); + + spin_unlock_irqrestore (&dev->lock, flags); + + INFO (dev, "wrote printer status %x\n", dev->printer_status); + + return ret; +} + + + +/* used after endpoint configuration */ +static struct file_operations printer_status_operations = { + .owner = THIS_MODULE, + .open = printer_status_open, + .read = printer_status_read, + .write = printer_status_write, + .release = printer_status_close +}; + + +#else /* USE_DEVICE_FILE */ + + +void set_printer_error_status (u8 val) +{ + if (val) + usb_printer_gadget.printer_status &= ~PRINTER_NOT_ERROR; + else + usb_printer_gadget.printer_status |= PRINTER_NOT_ERROR; +} + + +void set_printer_paperout_status (u8 val) +{ + if (val) + usb_printer_gadget.printer_status |= PRINTER_PAPER_EMPTY; + else + usb_printer_gadget.printer_status &= ~PRINTER_PAPER_EMPTY; +} + + +void set_printer_select_status (u8 val) +{ + if (val) + usb_printer_gadget.printer_status |= PRINTER_SELECTED; + else + usb_printer_gadget.printer_status &= ~PRINTER_SELECTED; +} + + +#endif /* USE_DEVICE_FILE */ + + +/*-------------------------------------------------------------------------*/ + + +static int +set_printer_interface (struct printer_dev *dev, gfp_t gfp_flags) +{ + int result = 0; + struct usb_gadget *gadget = dev->gadget; + + dev->in = ep_desc (dev->gadget, &hs_ep_in_desc, &fs_ep_in_desc); + dev->in_ep->driver_data = dev; + + dev->out = ep_desc (dev->gadget, &hs_ep_out_desc, &fs_ep_out_desc); + dev->out_ep->driver_data = dev; + + result = usb_ep_enable (dev->in_ep, dev->in); + if (result != 0) { + DEBUG(dev, "enable %s --> %d\n", + dev->in_ep->name, result); + goto done; + } + + result = usb_ep_enable (dev->out_ep, dev->out); + if (result != 0) { + DEBUG (dev, "enable %s --> %d\n", + dev->in_ep->name, result); + goto done; + } + + +done: + if (result == 0) { + result = alloc_requests (dev, qlen (gadget), gfp_flags); + } + + /* on error, disable any endpoints */ + if (result < 0) { + (void) usb_ep_disable (dev->in_ep); + (void) usb_ep_disable (dev->out_ep); + dev->in = NULL; + dev->out = NULL; + } + + /* caller is responsible for cleanup on error */ + return result; +} + + +static void printer_reset_interface (struct printer_dev *dev) +{ + struct usb_request *req; + + if (dev->interface < 0) + return; + + DEBUG (dev, "%s\n", __FUNCTION__); + /* disable endpoints, forcing (synchronous) completion of + * pending i/o. then free the requests. + */ + if (dev->in) { + usb_ep_disable (dev->in_ep); + while (likely (!list_empty (&dev->tx_reqs))) { + req = container_of (dev->tx_reqs.next, + struct usb_request, list); + list_del (&req->list); + usb_ep_free_request (dev->in_ep, req); + } + } + if (dev->out) { + usb_ep_disable (dev->out_ep); + while (likely (!list_empty (&dev->rx_reqs))) { + req = container_of (dev->rx_reqs.next, + struct usb_request, list); + list_del (&req->list); + usb_ep_free_request (dev->out_ep, req); + } + } + + dev->interface = -1; +} + + +/* change our operational config. must agree with the code + * that returns config descriptors, and altsetting code. + */ +static int +printer_set_config (struct printer_dev *dev, unsigned number, gfp_t gfp_flags) +{ + int result = 0; + struct usb_gadget *gadget = dev->gadget; + + if (gadget_is_sa1100 (gadget) + && dev->config) { + /* tx fifo is full, but we can't clear it...*/ + INFO (dev, "can't change configurations\n"); + return -ESPIPE; + } + + switch (number) { + case DEV_CONFIG_VALUE: + result = 0; + break; + default: + result = -EINVAL; + /* FALL THROUGH */ + case 0: + break; + } + + if (result) { + usb_gadget_vbus_draw(dev->gadget, + dev->gadget->is_otg ? 8 : 100); + } else { + char *speed; + unsigned power; + + power = 2 * config_desc.bMaxPower; + usb_gadget_vbus_draw(dev->gadget, power); + + switch (gadget->speed) { + case USB_SPEED_FULL: speed = "full"; break; +#ifdef CONFIG_USB_GADGET_DUALSPEED + case USB_SPEED_HIGH: speed = "high"; break; +#endif + default: speed = "?"; break; + } + + dev->config = number; + INFO (dev, "%s speed config #%d: %d mA, %s\n", + speed, number, power, driver_desc); + } + return result; +} + + +static int +config_buf (enum usb_device_speed speed, + u8 *buf, u8 type, + unsigned index, int is_otg) +{ + int len; + const struct usb_descriptor_header **function; +#ifdef CONFIG_USB_GADGET_DUALSPEED + int hs = (speed == USB_SPEED_HIGH); + + if (type == USB_DT_OTHER_SPEED_CONFIG) + hs = !hs; + + if (hs) { + function = hs_printer_function; + } else { + function = fs_printer_function; + } +#else + function = fs_printer_function; +#endif + + if (index >= device_desc.bNumConfigurations) + return -EINVAL; + + /* for now, don't advertise srp-only devices */ + if (!is_otg) + function++; + + len = usb_gadget_config_buf (&config_desc, buf, USB_DESC_BUFSIZE, + function); + if (len < 0) + return len; + ((struct usb_config_descriptor *) buf)->bDescriptorType = type; + return len; +} + + +/* Change our operational Interface. */ +static int +printer_set_interface (struct printer_dev *dev, unsigned number, + gfp_t gfp_flags) +{ + int result = 0; + + if (gadget_is_sa1100 (dev->gadget) + && dev->interface < 0) { + /* tx fifo is full, but we can't clear it...*/ + INFO (dev, "can't change interfaces\n"); + return -ESPIPE; + } + printer_reset_interface (dev); + + switch (number) { + case PRINTER_INTERFACE: + result = set_printer_interface (dev, gfp_flags); + break; + default: + result = -EINVAL; + /* FALL THROUGH */ + } + + if (result) { + if (number) + printer_reset_interface (dev); + } else { + INFO (dev, "Using interface %x\n", number); + } + + return result; +} + + +static void printer_setup_complete (struct usb_ep *ep, + struct usb_request *req) +{ + if (req->status || req->actual != req->length) + DEBUG ((struct printer_dev *) ep->driver_data, + "setup complete --> %d, %d/%d\n", + req->status, req->actual, req->length); +} + + +static void soft_reset (struct printer_dev *dev) +{ + unsigned long flags; + struct usb_request *req; + + spin_lock_irqsave (&dev->lock, flags); + + if (usb_ep_disable (dev->in_ep)) + DEBUG (dev, "Failed to disable USB in_ep\n"); + if (usb_ep_disable (dev->out_ep)) + DEBUG (dev, "Failed to disable USB out_ep\n"); + + if (usb_ep_dequeue (dev->in_ep, dev->req)) + DEBUG (dev, "Failed to dequeue ctrl req %p\n", dev->req); + + 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; + + while (!(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 (!(list_empty (&dev->rx_reqs_active))) { + req = container_of (dev->rx_buffers.next, struct usb_request, + list); + if (usb_ep_dequeue (dev->in_ep, req)) + DEBUG (dev, "Failed to dequeue in req %p\n",req); + + list_del_init (&req->list); + list_add (&req->list, &dev->rx_reqs); + } + + while (!(list_empty (&dev->tx_reqs_active))) { + req = container_of (dev->tx_reqs_active.next, + struct usb_request, list); + if (usb_ep_dequeue (dev->out_ep, req)) + DEBUG (dev, "Failed to dequeue in req %p\n",req); + + list_del_init (&req->list); + list_add (&req->list, &dev->tx_reqs); + } + + if (usb_ep_enable (dev->in_ep, dev->in)) + DEBUG (dev, "Failed to enable USB in_ep\n"); + if (usb_ep_enable (dev->out_ep, dev->out)) + DEBUG (dev, "Failed to enable USB out_ep\n"); + + spin_unlock_irqrestore (&dev->lock, flags); +} + + +/*-------------------------------------------------------------------------*/ + + +/* + * The setup() callback implements all the ep0 functionality that's not + * handled lower down. + */ +static int +printer_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) +{ + struct printer_dev *dev = get_gadget_data (gadget); + struct usb_request *req = dev->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); + + INFO (dev, "ctrl req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, wValue, wIndex, wLength); + + req->complete = printer_setup_complete; + + switch (ctrl->bRequestType&USB_TYPE_MASK) { + + case USB_TYPE_STANDARD: + switch (ctrl->bRequest) { + + case USB_REQ_GET_DESCRIPTOR: + if (ctrl->bRequestType != USB_DIR_IN) + break; + switch (wValue >> 8) { + + case USB_DT_DEVICE: + value = min (wLength, + (u16) sizeof device_desc); + memcpy (req->buf, &device_desc, value); + break; +#ifdef CONFIG_USB_GADGET_DUALSPEED + case USB_DT_DEVICE_QUALIFIER: + if (!gadget->is_dualspeed) + break; + value = min (wLength, + (u16) sizeof dev_qualifier); + memcpy (req->buf, &dev_qualifier, value); + break; + + case USB_DT_OTHER_SPEED_CONFIG: + if (!gadget->is_dualspeed) + break; + // FALLTHROUGH +#endif /* CONFIG_USB_GADGET_DUALSPEED */ + case USB_DT_CONFIG: + value = config_buf (gadget->speed, req->buf, + wValue >> 8, + wValue & 0xff, + gadget->is_otg); + if (value >= 0) + value = min (wLength, (u16) value); + break; + + case USB_DT_STRING: + value = usb_gadget_get_string (&stringtab, + wValue & 0xff, req->buf); + if (value >= 0) + value = min (wLength, (u16) value); + break; + } + break; + + case USB_REQ_SET_CONFIGURATION: + if (ctrl->bRequestType != 0) + break; + if (gadget->a_hnp_support) + DEBUG(dev,"HNP available\n"); + else if (gadget->a_alt_hnp_support) + DEBUG(dev,"HNP needs a different root port\n"); + spin_lock (&dev->lock); + value = printer_set_config (dev, wValue, GFP_ATOMIC); + spin_unlock (&dev->lock); + break; + case USB_REQ_GET_CONFIGURATION: + if (ctrl->bRequestType != USB_DIR_IN) + break; + *(u8 *)req->buf = dev->config; + value = min (wLength, (u16) 1); + break; + + case USB_REQ_SET_INTERFACE: + if (ctrl->bRequestType != USB_RECIP_INTERFACE || + !dev->config) + break; + + spin_lock (&dev->lock); + value = printer_set_interface (dev, + PRINTER_INTERFACE, GFP_ATOMIC); + spin_unlock (&dev->lock); + break; + case USB_REQ_GET_INTERFACE: + if (ctrl->bRequestType != + (USB_DIR_IN|USB_RECIP_INTERFACE) + || !dev->config) + break; + + *(u8 *)req->buf = dev->interface; + value = min (wLength, (u16) 1); + break; + + default: + VDEBUG (dev, + "unknown ctrl req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + wValue, wIndex, wLength); + } + break; + + case USB_TYPE_CLASS: + switch (ctrl->bRequest) { + case 0: /* Get the IEEE-1284 PNP String */ + /* Only one printer interface is supported. */ + if (wIndex != PRINTER_INTERFACE) + break; + + req->buf = pnp_string; + value = (pnp_string[0]<<8)|pnp_string[1]; + INFO (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 != PRINTER_INTERFACE) + break; + + req->buf = &dev->printer_status; + value = min (wLength, (u16) 1); + break; + + case 2: /* Soft Reset */ + /* Only one printer interface is supported. */ + if (wIndex != PRINTER_INTERFACE) + break; + + soft_reset(dev); + + value = 0; + break; + + default: + VDEBUG (dev, + "unknown ctrl req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + wValue, wIndex, wLength); + } + break; + + default: + VDEBUG (dev, + "unknown ctrl req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + wValue, wIndex, wLength); + break; + } + + /* respond with data transfer before status phase? */ + if (value >= 0) { + req->length = value; + req->zero = value < wLength + && (value % gadget->ep0->maxpacket) == 0; + value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC); + if (value < 0) { + DEBUG (dev, "ep_queue --> %d\n", value); + req->status = 0; + printer_setup_complete (gadget->ep0, req); + } + } + + /* host either stalls (value < 0) or reports success */ + return value; +} + + +static void +printer_disconnect (struct usb_gadget *gadget) +{ + struct printer_dev *dev = get_gadget_data (gadget); + unsigned long flags; + + spin_lock_irqsave (&dev->lock, flags); + printer_reset_interface (dev); + spin_unlock_irqrestore (&dev->lock, flags); +} + + +static void +printer_unbind (struct usb_gadget *gadget) +{ + struct printer_dev *dev = get_gadget_data (gadget); + + DEBUG (dev, "unbind\n"); + + /* Remove Character Device */ + cdev_del(&dev->printer_cdev); + cdev_del(&dev->printer_status_cdev); + + /* we've already been disconnected ... no i/o is active */ + if (dev->req) { + printer_req_free (gadget->ep0, dev->req); + dev->req = NULL; + } + + set_gadget_data (gadget, NULL); +} + +static int __init +printer_bind (struct usb_gadget *gadget) +{ + struct printer_dev *dev; + u8 zlp = 1; + struct usb_ep *in_ep, *out_ep; + int status = -ENOMEM; + int gcnum; + size_t len; + + if (gadget_is_sa1100 (gadget)) { + /* hardware can't write zlps */ + zlp = 0; + } + + gcnum = usb_gadget_controller_number (gadget); + if (gcnum >= 0) { + device_desc.bcdDevice = cpu_to_le16 (0x0200 + gcnum); + } else { + dev_warn (&gadget->dev, "controller '%s' not recognized\n", + gadget->name); + /* unrecognized, but safe unless bulk is REALLY quirky */ + device_desc.bcdDevice = + __constant_cpu_to_le16(0xFFFF); + } + snprintf (manufacturer, sizeof manufacturer, "%s %s/%s", + system_utsname.sysname, system_utsname.release, + gadget->name); + + device_desc.idVendor = + __constant_cpu_to_le16(PRINTER_VENDOR_NUM); + device_desc.idProduct = + __constant_cpu_to_le16(PRINTER_PRODUCT_NUM); + + /* support optional vendor/distro customization */ + if (idVendor) { + if (!idProduct) { + dev_err (&gadget->dev, "idVendor needs idProduct!\n"); + return -ENODEV; + } + device_desc.idVendor = cpu_to_le16(idVendor); + device_desc.idProduct = cpu_to_le16(idProduct); + if (bcdDevice) + device_desc.bcdDevice = cpu_to_le16(bcdDevice); + } + if (iManufacturer) + strlcpy (manufacturer, iManufacturer, sizeof manufacturer); + if (iProduct) + strlcpy (product_desc, iProduct, sizeof product_desc); + if (iSerialNum) + strlcpy (serial_num, iSerialNum, sizeof serial_num); + 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; + + /* all we really need is bulk IN/OUT */ + usb_ep_autoconfig_reset (gadget); + in_ep = usb_ep_autoconfig (gadget, &fs_ep_in_desc); + if (!in_ep) { +autoconf_fail: + dev_err (&gadget->dev, + "can't autoconfigure on %s\n", + gadget->name); + return -ENODEV; + } + in_ep->driver_data = in_ep; /* claim */ + + out_ep = usb_ep_autoconfig (gadget, &fs_ep_out_desc); + if (!out_ep) + goto autoconf_fail; + out_ep->driver_data = out_ep; /* claim */ + +#ifdef CONFIG_USB_GADGET_DUALSPEED + /* assumes ep0 uses the same value for both speeds ... */ + dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0; + + /* and 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; +#endif /* DUALSPEED */ + + device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; + usb_gadget_set_selfpowered (gadget); + + if (gadget->is_otg) { + otg_desc.bmAttributes |= USB_OTG_HNP, + config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + config_desc.bMaxPower = 4; + } + + dev = &usb_printer_gadget; + + spin_lock_init (&dev->lock); + INIT_LIST_HEAD (&dev->tx_reqs); + INIT_LIST_HEAD (&dev->rx_reqs); + INIT_LIST_HEAD (&dev->rx_reqs_active); + INIT_LIST_HEAD (&dev->rx_buffers); + + dev->config = 0; + dev->interface = -1; + dev->printer_cdev_open = 0; + dev->printer_status_cdev_open = 0; + dev->zlp = zlp; + dev->printer_status = PRINTER_NOT_ERROR|PRINTER_SELECTED; + dev->current_rx_req = NULL; + dev->current_rx_bytes = 0; + dev->current_rx_buf = NULL; + + dev->in_ep = in_ep; + dev->out_ep = out_ep; + + /* preallocate control message data and buffer */ + dev->req = printer_req_alloc (gadget->ep0, USB_DESC_BUFSIZE, + GFP_KERNEL); + if (!dev->req) + goto fail; + + dev->req->complete = printer_setup_complete; + + /* finish hookup to lower layer ... */ + dev->gadget = gadget; + set_gadget_data (gadget, dev); + gadget->ep0->driver_data = dev; + +#ifdef USE_DEVICE_FILE + /* + * 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; + } + + cdev_init(&dev->printer_status_cdev, &printer_status_operations); + dev->printer_status_cdev.owner = THIS_MODULE; + status = cdev_add (&dev->printer_status_cdev, + g_printer_status_devno, 1); + if (status) { + ERROR (dev, "Failed to open char device\n"); + goto fail; + } + +#endif /* USE_DEVICE_FILE */ + + + INFO (dev, "%s, version: " DRIVER_VERSION "\n", driver_desc); + INFO (dev, "using %s, OUT %s IN %s\n", gadget->name, out_ep->name, + in_ep->name); + + return status; + +fail: + printer_unbind (gadget); + return status; +} + + +static void +printer_suspend (struct usb_gadget *gadget) +{ + struct printer_dev *dev = get_gadget_data (gadget); + + DEBUG (dev, "suspend\n"); + dev->suspended = 1; +} + +static void +printer_resume (struct usb_gadget *gadget) +{ + struct printer_dev *dev = get_gadget_data (gadget); + + DEBUG (dev, "resume\n"); + dev->suspended = 0; +} + +/*-------------------------------------------------------------------------*/ + +static struct usb_gadget_driver printer_driver = { + .speed = DEVSPEED, + + .function = (char *) driver_desc, + .bind = printer_bind, + .unbind = printer_unbind, + + .setup = printer_setup, + .disconnect = printer_disconnect, + + .suspend = printer_suspend, + .resume = printer_resume, + + .driver = { + .name = (char *) shortname, + .owner = THIS_MODULE, + }, +}; + +MODULE_DESCRIPTION (DRIVER_DESC); +MODULE_AUTHOR ("Craig Nadler, David Brownell"); +MODULE_LICENSE ("GPL"); + + +static int __init init (void) +{ + int status; + +#ifdef USE_DEVICE_FILE + status = register_chrdev_region (g_printer_devno, 2, + "USB printer (gadget)"); + if (status) { + ERROR (dev, "register_chrdev_region %x\n", status); + return status; + } +#endif /* USE_DEVICE_FILE */ + + status = usb_gadget_register_driver (&printer_driver); + if (status) { + DEBUG (dev, "usb_gadget_register_driver %x\n", status); + } + + return status; +} +module_init (init); + +static void __exit cleanup (void) +{ + int status; + +#ifdef USE_DEVICE_FILE + unregister_chrdev_region (g_printer_devno, 2); +#endif /* USE_DEVICE_FILE */ + + status = usb_gadget_unregister_driver (&printer_driver); + if (status) { + ERROR (dev, "usb_gadget_unregister_driver %x\n", status); + } +} +module_exit (cleanup); + diff -Nru a/Documentation/usb/gadget_printer.txt b/Documentation/usb/gadget_printer.txt --- a/Documentation/usb/gadget_printer.txt 1969-12-31 19:00:00.000000000 -0500 +++ b/Documentation/usb/gadget_printer.txt 2006-03-15 01:40:48.000000000 -0500 @@ -0,0 +1,254 @@ + + Linux USB Printer Gadget Driver v0.1 + 03/11/2006 + + +License and Disclaimer +---------------------- +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. + +This document and the gadget printer driver are +Copyright (C) 2006 by Craig Nadlers ([EMAIL PROTECTED]). + +Any questions or comments about this driver should be sent to +Craig Nadlers at [EMAIL PROTECTED] . + + + +GENERAL +======= + +This driver is 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 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. +All the read and write calls for this printer gadget driver are non-blocking. +If there is no room to write more data or no more data to read the system calls +with return 0. + + + + +HOWTO USE THIS DRIVER +===================== + +To create the device files for the USB printer gadget + +mknod /dev/glp0 c 180 200 +mknod /dev/glp0_status c 180 201 + + +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. + + + + + +EXAMPLE CODE +============ + + The following is an example of how to read data from the printer gadget +from a user space application. + + +********************************* CUT HERE ********************************** +#include <stdio.h> +#include <fcntl.h> + +#define PRINTER_DATA_FILE "/dev/glp0" +#define BUF_SIZE 512 + +main () +{ + int fd = open(PRINTER_DATA_FILE, O_RDWR); + + if (fd < 0) { + printf("Error %d opening %s\n", fd, PRINTER_DATA_FILE); + close (fd); + return; + } + while (1) { + static char buf[BUF_SIZE]; + int bytes_read; + + bytes_read = read (fd, buf, BUF_SIZE); + if (bytes_read) { + fwrite (buf, 1, bytes_read, stdout); + fflush (stdout); + } else { + usleep (10000); + } + } + + close(fd); +} +********************************* CUT HERE ********************************** + + + + + + The following is an example of how to write data to the printer gadget +from a user space application. + +********************************* CUT HERE ********************************** +#include <stdio.h> +#include <fcntl.h> + +#define PRINTER_DATA_FILE "/dev/glp0" +#define BUF_SIZE 512 + +main () +{ + int fd = open(PRINTER_DATA_FILE, O_RDWR); + + if (fd < 0) { + printf("Error %d opening %s\n", fd, PRINTER_DATA_FILE); + close (fd); + return; + } + while (1) { + static char buf[BUF_SIZE]; + int bytes_read = fread (buf, 1, BUF_SIZE, stdin); + + if (!bytes_read) break; + + while (bytes_read) { + bytes_read -= write (fd, buf, bytes_read); + } + } + + close(fd); +} +********************************* CUT HERE ********************************** + + + + + + The following is an example of how to get the printer status from a user +space application. See the USB Printer Class Spec. for the get printer status +device request. + +********************************* CUT HERE ********************************** +#include <stdio.h> +#include <fcntl.h> + +#define PRINTER_DATA_FILE "/dev/glp0_status" + +main () +{ + char buf; + int bytes_read; + int fd = open(PRINTER_DATA_FILE, O_RDWR); + + if (fd < 0) { + printf("Error %d opening %s\n", fd, PRINTER_DATA_FILE); + close (fd); + return; + } + + bytes_read = read (fd, &buf, 1); + if (bytes_read == 1) { + fwrite (&buf, 1, 1, stdout); + fflush (stdout); + } + + close(fd); +} +********************************* CUT HERE ********************************** + + + + + + The following is an example of how to set the printer status from a user +space application. See the USB Printer Class Spec. for the get printer status +device request. + +********************************* CUT HERE ********************************** +#include <stdio.h> +#include <fcntl.h> + +#define PRINTER_DATA_FILE "/dev/glp0_status" + +main () +{ + char buf; + int bytes_read; + int fd = open(PRINTER_DATA_FILE, O_RDWR); + + if (fd < 0) { + printf("Error %d opening %s\n", fd, PRINTER_DATA_FILE); + close (fd); + return; + } + + bytes_read = fread (&buf, 1, 1, stdin); + if (bytes_read == 1) + write (fd, &buf, 1); + + close(fd); +} +********************************* CUT HERE **********************************