|
Subject: Printer Gadget / "Device Class" Driver Newsgroups: gmane.linux.usb.devel Date: 2006-03-14 02:01:55 GMT (3 years, 16 weeks, 1 day, 2 hours and 44 minutes ago)
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
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-11 19:04:49.000000000 -0500
<at> <at> -0,0 +1,254 <at> <at>
+
+ 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 (craig <at> nadler.us).
+
+Any questions or comments about this driver should be sent to
+Craig Nadlers at craig <at> nadler.us .
+
+
+
+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 howto
+ 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 **********************************
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-03-04 17:22:05.000000000 -0500
<at> <at> -375,7 +375,7 <at> <at>
config USB_G_SERIAL
tristate "Serial Gadget (with CDC ACM support)"
help
- The Serial Gadget talks to the Linux-USB generic serial driver.
+ The Printer Gadget talks to the Linux-USB generic serial driver.
This driver supports a CDC-ACM module option, which can be used
to interoperate with MS-Windows hosts or with the Linux-USB
"cdc-acm" driver.
<at> <at> -387,6 +387,17 <at> <at>
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-03-04 17:22:05.000000000 -0500
<at> <at> -17,6 +17,8 <at> <at>
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
<at> <at> -27,4 +29,5 <at> <at>
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-11 18:41:01.000000000 -0500
<at> <at> -0,0 +1,1643 <at> <at>
+/*
+ * 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 1
+#define VERBOSE 1
+
+
+#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 03/11/2006"
+
+static const char shortname [] = "printer";
+static const char driver_desc [] = DRIVER_DESC;
+
+static const int g_printer_devno = MKDEV(180,200);
+static const int g_printer_status_devno = MKDEV(180,201);
+
+/*-------------------------------------------------------------------------*/
+
+struct printer_dev {
+ spinlock_t lock;
+ spinlock_t lock_printer_io;
+ struct usb_gadget *gadget;
+ struct usb_request req_copy;
+ 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;
+ u8 reset_printer;
+ 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;");
+
+
+/* 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
+#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 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);
+}
+
+
+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);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+
+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;
+ }
+
+ spin_unlock_irqrestore (&dev->lock, flags);
+
+ DEBUG (dev, "printer_open returned %x\n", ret);
+
+ 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);
+
+ DEBUG (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;
+ struct usb_request *current_rx_req;
+ size_t current_rx_bytes;
+ u8 *current_rx_buf;
+
+ if (len == 0)
+ return 0;
+
+ DEBUG (dev, "printer_read trying to read %d bytes\n", (int)len);
+
+ spin_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;
+
+ 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);
+
+ req->length = USB_BUFSIZE;
+ req->complete = rx_complete;
+
+ error = usb_ep_queue (dev->out_ep, req, GFP_ATOMIC);
+ if (error) {
+ DEBUG (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;
+
+ 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 free the req and buffer */
+ if (dev->reset_printer) {
+ printer_req_free (dev->out_ep, current_rx_req);
+ spin_unlock_irqrestore (&dev->lock, flags);
+ spin_unlock (&dev->lock_printer_io);
+ return 0;
+ }
+
+ if (size < current_rx_bytes) {
+ current_rx_bytes -= size;
+ current_rx_buf += size;
+ } else {
+ list_add (¤t_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);
+ spin_unlock (&dev->lock_printer_io);
+
+ DEBUG (dev, "printer_read returned %d bytes\n", (int)bytes_copied);
+
+ return bytes_copied;
+}
+
+
+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;
+ size_t bytes_copied = 0;
+ struct usb_request *req;
+
+ DEBUG (dev, "printer_write trying to send %d bytes\n", (int)len);
+
+ if (len == 0)
+ return 0;
+
+ spin_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;
+
+ while (likely (!list_empty (&dev->tx_reqs)) && len) {
+
+ /* 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++;
+ }
+
+ 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;
+
+ if (len > size)
+ req->zero = 0;
+ else
+ 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);
+ spin_unlock_irqrestore (&dev->lock, flags);
+ spin_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 free the req and buffer */
+ if (dev->reset_printer) {
+ printer_req_free (dev->in_ep, req);
+ spin_unlock_irqrestore (&dev->lock, flags);
+ spin_unlock (&dev->lock_printer_io);
+ return 0;
+ }
+
+ if (usb_ep_queue (dev->in_ep, req, GFP_ATOMIC)) {
+ list_add (&req->list, &dev->tx_reqs);
+ spin_unlock_irqrestore (&dev->lock, flags);
+ spin_unlock (&dev->lock_printer_io);
+ return 0;
+ }
+
+ list_add (&req->list, &dev->tx_reqs_active);
+
+ }
+
+ spin_unlock_irqrestore (&dev->lock, flags);
+ spin_unlock (&dev->lock_printer_io);
+
+ DEBUG (dev, "printer_write sent %d bytes\n", (int)bytes_copied);
+
+ return bytes_copied;
+}
+
+
+
+/* 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);
+
+ if (!dev->printer_status_cdev_open) {
+ dev->printer_status_cdev_open = 1;
+ fd->private_data = dev;
+ ret = 0;
+ }
+
+ spin_unlock_irqrestore (&dev->lock, flags);
+
+ DEBUG (dev, "printer_status_open returned %x\n", ret);
+
+ 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);
+
+ DEBUG (dev, "printer_status_close\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);
+
+ DEBUG (dev, "printer_status_read returned %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);
+
+ DEBUG (dev, "printer_status_write wrote %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
+};
+
+
+/*-------------------------------------------------------------------------*/
+
+
+static int
+set_printer_interface (struct printer_dev *dev)
+{
+ int result = 0;
+
+ 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:
+ /* 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)
+{
+ if (dev->interface < 0)
+ return;
+
+ DEBUG (dev, "%s\n", __FUNCTION__);
+
+ if (dev->in)
+ usb_ep_disable (dev->in_ep);
+
+ if (dev->out)
+ usb_ep_disable (dev->out_ep);
+
+ 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)
+{
+ 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
+set_interface (struct printer_dev *dev, unsigned number)
+{
+ 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;
+ }
+
+ /* Free the current interface */
+ switch (dev->interface) {
+ case PRINTER_INTERFACE:
+ printer_reset_interface (dev);
+ break;
+ }
+
+ switch (number) {
+ case PRINTER_INTERFACE:
+ result = set_printer_interface (dev);
+ if (result) {
+ printer_reset_interface (dev);
+ } else {
+ dev->interface = PRINTER_INTERFACE;
+ }
+ break;
+ default:
+ result = -EINVAL;
+ /* FALL THROUGH */
+ }
+
+ if (!result)
+ 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 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))
+ 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 (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);
+ 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 (likely (!(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");
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+
+/*
+ * 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);
+
+ DEBUG (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");
+ value = printer_set_config (dev, wValue);
+ 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;
+
+ value = set_interface (dev, PRINTER_INTERFACE);
+ 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>>8) != PRINTER_INTERFACE)
+ break;
+
+ req->buf = pnp_string;
+ value = (pnp_string[0]<<8)|pnp_string[1];
+ DEBUG (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;
+
+ printer_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;
+
+ DEBUG (dev, "%s\n", __FUNCTION__);
+
+ 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);
+ struct usb_request *req;
+
+
+ DEBUG (dev, "%s\n", __FUNCTION__);
+
+ /* Remove Character Device */
+ cdev_del(&dev->printer_cdev);
+ cdev_del(&dev->printer_status_cdev);
+
+ /* we've already been disconnected ... no i/o is active */
+ /* Free all memory for this driver. */
+ while (likely (!list_empty (&dev->tx_reqs_active))) {
+ req = container_of (dev->tx_reqs_active.next,
+ struct usb_request, list);
+ list_del (&req->list);
+ if (usb_ep_dequeue (dev->in_ep, req)) {
+ DEBUG (dev, "Failed to dequeue in req %p\n",
+ req);
+ }
+ printer_req_free (dev->in_ep, req);
+ }
+
+ while (likely (!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) {
+ printer_req_free (dev->out_ep, dev->current_rx_req);
+ }
+
+ while (likely (!list_empty (&dev->rx_reqs_active))) {
+ req = container_of (dev->rx_reqs_active.next,
+ struct usb_request, list);
+ list_del (&req->list);
+ if (usb_ep_dequeue (dev->out_ep, req)) {
+ DEBUG (dev, "Failed to dequeue in req %p\n",
+ req);
+ }
+ printer_req_free (dev->out_ep, req);
+ }
+
+ while (likely (!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 (likely (!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);
+ }
+
+ if (dev->req) {
+ INFO (dev,"printer_req_free ep0=%p buf=%p\n", dev->req, dev->req_copy.buf);
+ usb_ep_free_buffer (gadget->ep0, dev->req_copy.buf,
+ dev->req_copy.dma, dev->req_copy.length);
+ usb_ep_free_request (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;
+ u32 i;
+ struct usb_request *req;
+
+ dev = &usb_printer_gadget;
+
+ /*
+ * 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;
+ }
+
+
+ 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;
+ }
+
+ spin_lock_init (&dev->lock);
+ spin_lock_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);
+
+ 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);
+ memcpy (&dev->req_copy, dev->req, sizeof dev->req_copy);
+ INFO (dev,"printer_req_free ep0=%p buf=%p\n", dev->req, dev->req->buf);
+
+ if (!dev->req) {
+ status = -ENOMEM;
+ goto fail;
+ }
+
+ for (i=0; i < QLEN; i++) {
+ req = printer_req_alloc (dev->in_ep, USB_BUFSIZE, GFP_KERNEL);
+ if (!req) {
+ while (likely (!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 (likely (!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);
+ }
+
+ dev->req->complete = printer_setup_complete;
+
+ /* finish hookup to lower layer ... */
+ dev->gadget = gadget;
+ set_gadget_data (gadget, dev);
+ gadget->ep0->driver_data = dev;
+
+ 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 0;
+
+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;
+
+ status = register_chrdev_region (g_printer_devno, 2,
+ "USB printer (gadget)");
+ if (status) {
+ ERROR (dev, "register_chrdev_region %x\n", status);
+ return status;
+ }
+
+ status = usb_gadget_register_driver (&printer_driver);
+ if (status) {
+ unregister_chrdev_region (g_printer_devno, 2);
+ DEBUG (dev, "usb_gadget_register_driver %x\n", status);
+ }
+
+ return status;
+}
+module_init (init);
+
+static void __exit cleanup (void)
+{
+ int status;
+
+ spin_lock (&usb_printer_gadget.lock_printer_io);
+ unregister_chrdev_region (g_printer_devno, 2);
+
+ status = usb_gadget_unregister_driver (&printer_driver);
+ if (status) {
+ ERROR (dev, "usb_gadget_unregister_driver %x\n", status);
+ }
+ spin_unlock (&usb_printer_gadget.lock_printer_io);
+}
+module_exit (cleanup);
+
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 (craig <at> nadler.us).
Any questions or comments about this driver should be sent to
Craig Nadlers at craig <at> nadler.us .
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 howto
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 **********************************
|
|
|