xref: /openbmc/linux/drivers/usb/gadget/legacy/printer.c (revision ae2dd0de57a3f6b12e30e5552033a492d6d206f7)
18443f2d2SAndrzej Pietrasiewicz /*
28443f2d2SAndrzej Pietrasiewicz  * printer.c -- Printer gadget driver
38443f2d2SAndrzej Pietrasiewicz  *
48443f2d2SAndrzej Pietrasiewicz  * Copyright (C) 2003-2005 David Brownell
58443f2d2SAndrzej Pietrasiewicz  * Copyright (C) 2006 Craig W. Nadler
68443f2d2SAndrzej Pietrasiewicz  *
78443f2d2SAndrzej Pietrasiewicz  * This program is free software; you can redistribute it and/or modify
88443f2d2SAndrzej Pietrasiewicz  * it under the terms of the GNU General Public License as published by
98443f2d2SAndrzej Pietrasiewicz  * the Free Software Foundation; either version 2 of the License, or
108443f2d2SAndrzej Pietrasiewicz  * (at your option) any later version.
118443f2d2SAndrzej Pietrasiewicz  */
128443f2d2SAndrzej Pietrasiewicz 
138443f2d2SAndrzej Pietrasiewicz #include <linux/module.h>
148443f2d2SAndrzej Pietrasiewicz #include <linux/kernel.h>
158443f2d2SAndrzej Pietrasiewicz #include <linux/delay.h>
168443f2d2SAndrzej Pietrasiewicz #include <linux/ioport.h>
178443f2d2SAndrzej Pietrasiewicz #include <linux/sched.h>
188443f2d2SAndrzej Pietrasiewicz #include <linux/slab.h>
198443f2d2SAndrzej Pietrasiewicz #include <linux/mutex.h>
208443f2d2SAndrzej Pietrasiewicz #include <linux/errno.h>
218443f2d2SAndrzej Pietrasiewicz #include <linux/init.h>
228443f2d2SAndrzej Pietrasiewicz #include <linux/timer.h>
238443f2d2SAndrzej Pietrasiewicz #include <linux/list.h>
248443f2d2SAndrzej Pietrasiewicz #include <linux/interrupt.h>
258443f2d2SAndrzej Pietrasiewicz #include <linux/device.h>
268443f2d2SAndrzej Pietrasiewicz #include <linux/moduleparam.h>
278443f2d2SAndrzej Pietrasiewicz #include <linux/fs.h>
288443f2d2SAndrzej Pietrasiewicz #include <linux/poll.h>
298443f2d2SAndrzej Pietrasiewicz #include <linux/types.h>
308443f2d2SAndrzej Pietrasiewicz #include <linux/ctype.h>
318443f2d2SAndrzej Pietrasiewicz #include <linux/cdev.h>
328443f2d2SAndrzej Pietrasiewicz 
338443f2d2SAndrzej Pietrasiewicz #include <asm/byteorder.h>
348443f2d2SAndrzej Pietrasiewicz #include <linux/io.h>
358443f2d2SAndrzej Pietrasiewicz #include <linux/irq.h>
368443f2d2SAndrzej Pietrasiewicz #include <linux/uaccess.h>
378443f2d2SAndrzej Pietrasiewicz #include <asm/unaligned.h>
388443f2d2SAndrzej Pietrasiewicz 
398443f2d2SAndrzej Pietrasiewicz #include <linux/usb/ch9.h>
408443f2d2SAndrzej Pietrasiewicz #include <linux/usb/composite.h>
418443f2d2SAndrzej Pietrasiewicz #include <linux/usb/gadget.h>
428443f2d2SAndrzej Pietrasiewicz #include <linux/usb/g_printer.h>
438443f2d2SAndrzej Pietrasiewicz 
448443f2d2SAndrzej Pietrasiewicz #include "gadget_chips.h"
458443f2d2SAndrzej Pietrasiewicz 
468443f2d2SAndrzej Pietrasiewicz USB_GADGET_COMPOSITE_OPTIONS();
478443f2d2SAndrzej Pietrasiewicz 
488443f2d2SAndrzej Pietrasiewicz #define DRIVER_DESC		"Printer Gadget"
498443f2d2SAndrzej Pietrasiewicz #define DRIVER_VERSION		"2007 OCT 06"
508443f2d2SAndrzej Pietrasiewicz 
518443f2d2SAndrzej Pietrasiewicz static DEFINE_MUTEX(printer_mutex);
528443f2d2SAndrzej Pietrasiewicz static const char shortname [] = "printer";
538443f2d2SAndrzej Pietrasiewicz static const char driver_desc [] = DRIVER_DESC;
548443f2d2SAndrzej Pietrasiewicz 
558443f2d2SAndrzej Pietrasiewicz static dev_t g_printer_devno;
568443f2d2SAndrzej Pietrasiewicz 
578443f2d2SAndrzej Pietrasiewicz static struct class *usb_gadget_class;
588443f2d2SAndrzej Pietrasiewicz 
598443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
608443f2d2SAndrzej Pietrasiewicz 
618443f2d2SAndrzej Pietrasiewicz struct printer_dev {
628443f2d2SAndrzej Pietrasiewicz 	spinlock_t		lock;		/* lock this structure */
638443f2d2SAndrzej Pietrasiewicz 	/* lock buffer lists during read/write calls */
648443f2d2SAndrzej Pietrasiewicz 	struct mutex		lock_printer_io;
658443f2d2SAndrzej Pietrasiewicz 	struct usb_gadget	*gadget;
668443f2d2SAndrzej Pietrasiewicz 	s8			interface;
678443f2d2SAndrzej Pietrasiewicz 	struct usb_ep		*in_ep, *out_ep;
688443f2d2SAndrzej Pietrasiewicz 
698443f2d2SAndrzej Pietrasiewicz 	struct list_head	rx_reqs;	/* List of free RX structs */
708443f2d2SAndrzej Pietrasiewicz 	struct list_head	rx_reqs_active;	/* List of Active RX xfers */
718443f2d2SAndrzej Pietrasiewicz 	struct list_head	rx_buffers;	/* List of completed xfers */
728443f2d2SAndrzej Pietrasiewicz 	/* wait until there is data to be read. */
738443f2d2SAndrzej Pietrasiewicz 	wait_queue_head_t	rx_wait;
748443f2d2SAndrzej Pietrasiewicz 	struct list_head	tx_reqs;	/* List of free TX structs */
758443f2d2SAndrzej Pietrasiewicz 	struct list_head	tx_reqs_active; /* List of Active TX xfers */
768443f2d2SAndrzej Pietrasiewicz 	/* Wait until there are write buffers available to use. */
778443f2d2SAndrzej Pietrasiewicz 	wait_queue_head_t	tx_wait;
788443f2d2SAndrzej Pietrasiewicz 	/* Wait until all write buffers have been sent. */
798443f2d2SAndrzej Pietrasiewicz 	wait_queue_head_t	tx_flush_wait;
808443f2d2SAndrzej Pietrasiewicz 	struct usb_request	*current_rx_req;
818443f2d2SAndrzej Pietrasiewicz 	size_t			current_rx_bytes;
828443f2d2SAndrzej Pietrasiewicz 	u8			*current_rx_buf;
838443f2d2SAndrzej Pietrasiewicz 	u8			printer_status;
848443f2d2SAndrzej Pietrasiewicz 	u8			reset_printer;
858443f2d2SAndrzej Pietrasiewicz 	struct cdev		printer_cdev;
868443f2d2SAndrzej Pietrasiewicz 	u8			printer_cdev_open;
878443f2d2SAndrzej Pietrasiewicz 	wait_queue_head_t	wait;
888443f2d2SAndrzej Pietrasiewicz 	struct usb_function	function;
898443f2d2SAndrzej Pietrasiewicz };
908443f2d2SAndrzej Pietrasiewicz 
918443f2d2SAndrzej Pietrasiewicz static struct printer_dev usb_printer_gadget;
928443f2d2SAndrzej Pietrasiewicz 
938443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
948443f2d2SAndrzej Pietrasiewicz 
958443f2d2SAndrzej Pietrasiewicz /* DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!!
968443f2d2SAndrzej Pietrasiewicz  * Instead:  allocate your own, using normal USB-IF procedures.
978443f2d2SAndrzej Pietrasiewicz  */
988443f2d2SAndrzej Pietrasiewicz 
998443f2d2SAndrzej Pietrasiewicz /* Thanks to NetChip Technologies for donating this product ID.
1008443f2d2SAndrzej Pietrasiewicz  */
1018443f2d2SAndrzej Pietrasiewicz #define PRINTER_VENDOR_NUM	0x0525		/* NetChip */
1028443f2d2SAndrzej Pietrasiewicz #define PRINTER_PRODUCT_NUM	0xa4a8		/* Linux-USB Printer Gadget */
1038443f2d2SAndrzej Pietrasiewicz 
1048443f2d2SAndrzej Pietrasiewicz /* Some systems will want different product identifiers published in the
1058443f2d2SAndrzej Pietrasiewicz  * device descriptor, either numbers or strings or both.  These string
1068443f2d2SAndrzej Pietrasiewicz  * parameters are in UTF-8 (superset of ASCII's 7 bit characters).
1078443f2d2SAndrzej Pietrasiewicz  */
1088443f2d2SAndrzej Pietrasiewicz 
1098443f2d2SAndrzej Pietrasiewicz module_param_named(iSerialNum, coverwrite.serial_number, charp, S_IRUGO);
1108443f2d2SAndrzej Pietrasiewicz MODULE_PARM_DESC(iSerialNum, "1");
1118443f2d2SAndrzej Pietrasiewicz 
1128443f2d2SAndrzej Pietrasiewicz static char *iPNPstring;
1138443f2d2SAndrzej Pietrasiewicz module_param(iPNPstring, charp, S_IRUGO);
1148443f2d2SAndrzej Pietrasiewicz MODULE_PARM_DESC(iPNPstring, "MFG:linux;MDL:g_printer;CLS:PRINTER;SN:1;");
1158443f2d2SAndrzej Pietrasiewicz 
1168443f2d2SAndrzej Pietrasiewicz /* Number of requests to allocate per endpoint, not used for ep0. */
1178443f2d2SAndrzej Pietrasiewicz static unsigned qlen = 10;
1188443f2d2SAndrzej Pietrasiewicz module_param(qlen, uint, S_IRUGO|S_IWUSR);
1198443f2d2SAndrzej Pietrasiewicz 
1208443f2d2SAndrzej Pietrasiewicz #define QLEN	qlen
1218443f2d2SAndrzej Pietrasiewicz 
1228443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
1238443f2d2SAndrzej Pietrasiewicz 
1248443f2d2SAndrzej Pietrasiewicz /*
1258443f2d2SAndrzej Pietrasiewicz  * DESCRIPTORS ... most are static, but strings and (full) configuration
1268443f2d2SAndrzej Pietrasiewicz  * descriptors are built on demand.
1278443f2d2SAndrzej Pietrasiewicz  */
1288443f2d2SAndrzej Pietrasiewicz 
1298443f2d2SAndrzej Pietrasiewicz /* holds our biggest descriptor */
1308443f2d2SAndrzej Pietrasiewicz #define USB_DESC_BUFSIZE		256
1318443f2d2SAndrzej Pietrasiewicz #define USB_BUFSIZE			8192
1328443f2d2SAndrzej Pietrasiewicz 
1338443f2d2SAndrzej Pietrasiewicz static struct usb_device_descriptor device_desc = {
1348443f2d2SAndrzej Pietrasiewicz 	.bLength =		sizeof device_desc,
1358443f2d2SAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_DEVICE,
1368443f2d2SAndrzej Pietrasiewicz 	.bcdUSB =		cpu_to_le16(0x0200),
1378443f2d2SAndrzej Pietrasiewicz 	.bDeviceClass =		USB_CLASS_PER_INTERFACE,
1388443f2d2SAndrzej Pietrasiewicz 	.bDeviceSubClass =	0,
1398443f2d2SAndrzej Pietrasiewicz 	.bDeviceProtocol =	0,
1408443f2d2SAndrzej Pietrasiewicz 	.idVendor =		cpu_to_le16(PRINTER_VENDOR_NUM),
1418443f2d2SAndrzej Pietrasiewicz 	.idProduct =		cpu_to_le16(PRINTER_PRODUCT_NUM),
1428443f2d2SAndrzej Pietrasiewicz 	.bNumConfigurations =	1
1438443f2d2SAndrzej Pietrasiewicz };
1448443f2d2SAndrzej Pietrasiewicz 
1458443f2d2SAndrzej Pietrasiewicz static struct usb_interface_descriptor intf_desc = {
1468443f2d2SAndrzej Pietrasiewicz 	.bLength =		sizeof intf_desc,
1478443f2d2SAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_INTERFACE,
1488443f2d2SAndrzej Pietrasiewicz 	.bNumEndpoints =	2,
1498443f2d2SAndrzej Pietrasiewicz 	.bInterfaceClass =	USB_CLASS_PRINTER,
1508443f2d2SAndrzej Pietrasiewicz 	.bInterfaceSubClass =	1,	/* Printer Sub-Class */
1518443f2d2SAndrzej Pietrasiewicz 	.bInterfaceProtocol =	2,	/* Bi-Directional */
1528443f2d2SAndrzej Pietrasiewicz 	.iInterface =		0
1538443f2d2SAndrzej Pietrasiewicz };
1548443f2d2SAndrzej Pietrasiewicz 
1558443f2d2SAndrzej Pietrasiewicz static struct usb_endpoint_descriptor fs_ep_in_desc = {
1568443f2d2SAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
1578443f2d2SAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
1588443f2d2SAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_IN,
1598443f2d2SAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK
1608443f2d2SAndrzej Pietrasiewicz };
1618443f2d2SAndrzej Pietrasiewicz 
1628443f2d2SAndrzej Pietrasiewicz static struct usb_endpoint_descriptor fs_ep_out_desc = {
1638443f2d2SAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
1648443f2d2SAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
1658443f2d2SAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_OUT,
1668443f2d2SAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK
1678443f2d2SAndrzej Pietrasiewicz };
1688443f2d2SAndrzej Pietrasiewicz 
1698443f2d2SAndrzej Pietrasiewicz static struct usb_descriptor_header *fs_printer_function[] = {
1708443f2d2SAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &intf_desc,
1718443f2d2SAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &fs_ep_in_desc,
1728443f2d2SAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &fs_ep_out_desc,
1738443f2d2SAndrzej Pietrasiewicz 	NULL
1748443f2d2SAndrzej Pietrasiewicz };
1758443f2d2SAndrzej Pietrasiewicz 
1768443f2d2SAndrzej Pietrasiewicz /*
1778443f2d2SAndrzej Pietrasiewicz  * usb 2.0 devices need to expose both high speed and full speed
1788443f2d2SAndrzej Pietrasiewicz  * descriptors, unless they only run at full speed.
1798443f2d2SAndrzej Pietrasiewicz  */
1808443f2d2SAndrzej Pietrasiewicz 
1818443f2d2SAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hs_ep_in_desc = {
1828443f2d2SAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
1838443f2d2SAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
1848443f2d2SAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
1858443f2d2SAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(512)
1868443f2d2SAndrzej Pietrasiewicz };
1878443f2d2SAndrzej Pietrasiewicz 
1888443f2d2SAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hs_ep_out_desc = {
1898443f2d2SAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
1908443f2d2SAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
1918443f2d2SAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
1928443f2d2SAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(512)
1938443f2d2SAndrzej Pietrasiewicz };
1948443f2d2SAndrzej Pietrasiewicz 
1958443f2d2SAndrzej Pietrasiewicz static struct usb_qualifier_descriptor dev_qualifier = {
1968443f2d2SAndrzej Pietrasiewicz 	.bLength =		sizeof dev_qualifier,
1978443f2d2SAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_DEVICE_QUALIFIER,
1988443f2d2SAndrzej Pietrasiewicz 	.bcdUSB =		cpu_to_le16(0x0200),
1998443f2d2SAndrzej Pietrasiewicz 	.bDeviceClass =		USB_CLASS_PRINTER,
2008443f2d2SAndrzej Pietrasiewicz 	.bNumConfigurations =	1
2018443f2d2SAndrzej Pietrasiewicz };
2028443f2d2SAndrzej Pietrasiewicz 
2038443f2d2SAndrzej Pietrasiewicz static struct usb_descriptor_header *hs_printer_function[] = {
2048443f2d2SAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &intf_desc,
2058443f2d2SAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &hs_ep_in_desc,
2068443f2d2SAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &hs_ep_out_desc,
2078443f2d2SAndrzej Pietrasiewicz 	NULL
2088443f2d2SAndrzej Pietrasiewicz };
2098443f2d2SAndrzej Pietrasiewicz 
21074df41b4SJorge Ramirez-Ortiz /*
21174df41b4SJorge Ramirez-Ortiz  * Added endpoint descriptors for 3.0 devices
21274df41b4SJorge Ramirez-Ortiz  */
21374df41b4SJorge Ramirez-Ortiz 
21474df41b4SJorge Ramirez-Ortiz static struct usb_endpoint_descriptor ss_ep_in_desc = {
21574df41b4SJorge Ramirez-Ortiz 	.bLength =              USB_DT_ENDPOINT_SIZE,
21674df41b4SJorge Ramirez-Ortiz 	.bDescriptorType =      USB_DT_ENDPOINT,
21774df41b4SJorge Ramirez-Ortiz 	.bmAttributes =         USB_ENDPOINT_XFER_BULK,
21874df41b4SJorge Ramirez-Ortiz 	.wMaxPacketSize =       cpu_to_le16(1024),
21974df41b4SJorge Ramirez-Ortiz };
22074df41b4SJorge Ramirez-Ortiz 
221ef24d749Skbuild test robot static struct usb_ss_ep_comp_descriptor ss_ep_in_comp_desc = {
22274df41b4SJorge Ramirez-Ortiz 	.bLength =              sizeof(ss_ep_in_comp_desc),
22374df41b4SJorge Ramirez-Ortiz 	.bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
22474df41b4SJorge Ramirez-Ortiz };
22574df41b4SJorge Ramirez-Ortiz 
22674df41b4SJorge Ramirez-Ortiz static struct usb_endpoint_descriptor ss_ep_out_desc = {
22774df41b4SJorge Ramirez-Ortiz 	.bLength =              USB_DT_ENDPOINT_SIZE,
22874df41b4SJorge Ramirez-Ortiz 	.bDescriptorType =      USB_DT_ENDPOINT,
22974df41b4SJorge Ramirez-Ortiz 	.bmAttributes =         USB_ENDPOINT_XFER_BULK,
23074df41b4SJorge Ramirez-Ortiz 	.wMaxPacketSize =       cpu_to_le16(1024),
23174df41b4SJorge Ramirez-Ortiz };
23274df41b4SJorge Ramirez-Ortiz 
233ef24d749Skbuild test robot static struct usb_ss_ep_comp_descriptor ss_ep_out_comp_desc = {
23474df41b4SJorge Ramirez-Ortiz 	.bLength =              sizeof(ss_ep_out_comp_desc),
23574df41b4SJorge Ramirez-Ortiz 	.bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
23674df41b4SJorge Ramirez-Ortiz };
23774df41b4SJorge Ramirez-Ortiz 
23874df41b4SJorge Ramirez-Ortiz static struct usb_descriptor_header *ss_printer_function[] = {
23974df41b4SJorge Ramirez-Ortiz 	(struct usb_descriptor_header *) &intf_desc,
24074df41b4SJorge Ramirez-Ortiz 	(struct usb_descriptor_header *) &ss_ep_in_desc,
24174df41b4SJorge Ramirez-Ortiz 	(struct usb_descriptor_header *) &ss_ep_in_comp_desc,
24274df41b4SJorge Ramirez-Ortiz 	(struct usb_descriptor_header *) &ss_ep_out_desc,
24374df41b4SJorge Ramirez-Ortiz 	(struct usb_descriptor_header *) &ss_ep_out_comp_desc,
24474df41b4SJorge Ramirez-Ortiz 	NULL
24574df41b4SJorge Ramirez-Ortiz };
24674df41b4SJorge Ramirez-Ortiz 
2478443f2d2SAndrzej Pietrasiewicz static struct usb_otg_descriptor otg_descriptor = {
2488443f2d2SAndrzej Pietrasiewicz 	.bLength =              sizeof otg_descriptor,
2498443f2d2SAndrzej Pietrasiewicz 	.bDescriptorType =      USB_DT_OTG,
2508443f2d2SAndrzej Pietrasiewicz 	.bmAttributes =         USB_OTG_SRP,
2518443f2d2SAndrzej Pietrasiewicz };
2528443f2d2SAndrzej Pietrasiewicz 
2538443f2d2SAndrzej Pietrasiewicz static const struct usb_descriptor_header *otg_desc[] = {
2548443f2d2SAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &otg_descriptor,
2558443f2d2SAndrzej Pietrasiewicz 	NULL,
2568443f2d2SAndrzej Pietrasiewicz };
2578443f2d2SAndrzej Pietrasiewicz 
2588443f2d2SAndrzej Pietrasiewicz /* maxpacket and other transfer characteristics vary by speed. */
25974df41b4SJorge Ramirez-Ortiz static inline struct usb_endpoint_descriptor *ep_desc(struct usb_gadget *gadget,
26074df41b4SJorge Ramirez-Ortiz 					struct usb_endpoint_descriptor *fs,
26174df41b4SJorge Ramirez-Ortiz 					struct usb_endpoint_descriptor *hs,
26274df41b4SJorge Ramirez-Ortiz 					struct usb_endpoint_descriptor *ss)
26374df41b4SJorge Ramirez-Ortiz {
26474df41b4SJorge Ramirez-Ortiz 	switch (gadget->speed) {
26574df41b4SJorge Ramirez-Ortiz 	case USB_SPEED_SUPER:
26674df41b4SJorge Ramirez-Ortiz 		return ss;
26774df41b4SJorge Ramirez-Ortiz 	case USB_SPEED_HIGH:
26874df41b4SJorge Ramirez-Ortiz 		return hs;
26974df41b4SJorge Ramirez-Ortiz 	default:
27074df41b4SJorge Ramirez-Ortiz 		return fs;
27174df41b4SJorge Ramirez-Ortiz 	}
27274df41b4SJorge Ramirez-Ortiz }
2738443f2d2SAndrzej Pietrasiewicz 
2748443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
2758443f2d2SAndrzej Pietrasiewicz 
2768443f2d2SAndrzej Pietrasiewicz /* descriptors that are built on-demand */
2778443f2d2SAndrzej Pietrasiewicz 
2788443f2d2SAndrzej Pietrasiewicz static char				product_desc [40] = DRIVER_DESC;
2798443f2d2SAndrzej Pietrasiewicz static char				serial_num [40] = "1";
2808443f2d2SAndrzej Pietrasiewicz static char				pnp_string [1024] =
2818443f2d2SAndrzej Pietrasiewicz 	"XXMFG:linux;MDL:g_printer;CLS:PRINTER;SN:1;";
2828443f2d2SAndrzej Pietrasiewicz 
2838443f2d2SAndrzej Pietrasiewicz /* static strings, in UTF-8 */
2848443f2d2SAndrzej Pietrasiewicz static struct usb_string		strings [] = {
2858443f2d2SAndrzej Pietrasiewicz 	[USB_GADGET_MANUFACTURER_IDX].s = "",
2868443f2d2SAndrzej Pietrasiewicz 	[USB_GADGET_PRODUCT_IDX].s = product_desc,
2878443f2d2SAndrzej Pietrasiewicz 	[USB_GADGET_SERIAL_IDX].s =	serial_num,
2888443f2d2SAndrzej Pietrasiewicz 	{  }		/* end of list */
2898443f2d2SAndrzej Pietrasiewicz };
2908443f2d2SAndrzej Pietrasiewicz 
2918443f2d2SAndrzej Pietrasiewicz static struct usb_gadget_strings	stringtab_dev = {
2928443f2d2SAndrzej Pietrasiewicz 	.language	= 0x0409,	/* en-us */
2938443f2d2SAndrzej Pietrasiewicz 	.strings	= strings,
2948443f2d2SAndrzej Pietrasiewicz };
2958443f2d2SAndrzej Pietrasiewicz 
2968443f2d2SAndrzej Pietrasiewicz static struct usb_gadget_strings *dev_strings[] = {
2978443f2d2SAndrzej Pietrasiewicz 	&stringtab_dev,
2988443f2d2SAndrzej Pietrasiewicz 	NULL,
2998443f2d2SAndrzej Pietrasiewicz };
3008443f2d2SAndrzej Pietrasiewicz 
3018443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
3028443f2d2SAndrzej Pietrasiewicz 
3038443f2d2SAndrzej Pietrasiewicz static struct usb_request *
3048443f2d2SAndrzej Pietrasiewicz printer_req_alloc(struct usb_ep *ep, unsigned len, gfp_t gfp_flags)
3058443f2d2SAndrzej Pietrasiewicz {
3068443f2d2SAndrzej Pietrasiewicz 	struct usb_request	*req;
3078443f2d2SAndrzej Pietrasiewicz 
3088443f2d2SAndrzej Pietrasiewicz 	req = usb_ep_alloc_request(ep, gfp_flags);
3098443f2d2SAndrzej Pietrasiewicz 
3108443f2d2SAndrzej Pietrasiewicz 	if (req != NULL) {
3118443f2d2SAndrzej Pietrasiewicz 		req->length = len;
3128443f2d2SAndrzej Pietrasiewicz 		req->buf = kmalloc(len, gfp_flags);
3138443f2d2SAndrzej Pietrasiewicz 		if (req->buf == NULL) {
3148443f2d2SAndrzej Pietrasiewicz 			usb_ep_free_request(ep, req);
3158443f2d2SAndrzej Pietrasiewicz 			return NULL;
3168443f2d2SAndrzej Pietrasiewicz 		}
3178443f2d2SAndrzej Pietrasiewicz 	}
3188443f2d2SAndrzej Pietrasiewicz 
3198443f2d2SAndrzej Pietrasiewicz 	return req;
3208443f2d2SAndrzej Pietrasiewicz }
3218443f2d2SAndrzej Pietrasiewicz 
3228443f2d2SAndrzej Pietrasiewicz static void
3238443f2d2SAndrzej Pietrasiewicz printer_req_free(struct usb_ep *ep, struct usb_request *req)
3248443f2d2SAndrzej Pietrasiewicz {
3258443f2d2SAndrzej Pietrasiewicz 	if (ep != NULL && req != NULL) {
3268443f2d2SAndrzej Pietrasiewicz 		kfree(req->buf);
3278443f2d2SAndrzej Pietrasiewicz 		usb_ep_free_request(ep, req);
3288443f2d2SAndrzej Pietrasiewicz 	}
3298443f2d2SAndrzej Pietrasiewicz }
3308443f2d2SAndrzej Pietrasiewicz 
3318443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
3328443f2d2SAndrzej Pietrasiewicz 
3338443f2d2SAndrzej Pietrasiewicz static void rx_complete(struct usb_ep *ep, struct usb_request *req)
3348443f2d2SAndrzej Pietrasiewicz {
3358443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev = ep->driver_data;
3368443f2d2SAndrzej Pietrasiewicz 	int			status = req->status;
3378443f2d2SAndrzej Pietrasiewicz 	unsigned long		flags;
3388443f2d2SAndrzej Pietrasiewicz 
3398443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
3408443f2d2SAndrzej Pietrasiewicz 
3418443f2d2SAndrzej Pietrasiewicz 	list_del_init(&req->list);	/* Remode from Active List */
3428443f2d2SAndrzej Pietrasiewicz 
3438443f2d2SAndrzej Pietrasiewicz 	switch (status) {
3448443f2d2SAndrzej Pietrasiewicz 
3458443f2d2SAndrzej Pietrasiewicz 	/* normal completion */
3468443f2d2SAndrzej Pietrasiewicz 	case 0:
3478443f2d2SAndrzej Pietrasiewicz 		if (req->actual > 0) {
3488443f2d2SAndrzej Pietrasiewicz 			list_add_tail(&req->list, &dev->rx_buffers);
3498443f2d2SAndrzej Pietrasiewicz 			DBG(dev, "G_Printer : rx length %d\n", req->actual);
3508443f2d2SAndrzej Pietrasiewicz 		} else {
3518443f2d2SAndrzej Pietrasiewicz 			list_add(&req->list, &dev->rx_reqs);
3528443f2d2SAndrzej Pietrasiewicz 		}
3538443f2d2SAndrzej Pietrasiewicz 		break;
3548443f2d2SAndrzej Pietrasiewicz 
3558443f2d2SAndrzej Pietrasiewicz 	/* software-driven interface shutdown */
3568443f2d2SAndrzej Pietrasiewicz 	case -ECONNRESET:		/* unlink */
3578443f2d2SAndrzej Pietrasiewicz 	case -ESHUTDOWN:		/* disconnect etc */
3588443f2d2SAndrzej Pietrasiewicz 		VDBG(dev, "rx shutdown, code %d\n", status);
3598443f2d2SAndrzej Pietrasiewicz 		list_add(&req->list, &dev->rx_reqs);
3608443f2d2SAndrzej Pietrasiewicz 		break;
3618443f2d2SAndrzej Pietrasiewicz 
3628443f2d2SAndrzej Pietrasiewicz 	/* for hardware automagic (such as pxa) */
3638443f2d2SAndrzej Pietrasiewicz 	case -ECONNABORTED:		/* endpoint reset */
3648443f2d2SAndrzej Pietrasiewicz 		DBG(dev, "rx %s reset\n", ep->name);
3658443f2d2SAndrzej Pietrasiewicz 		list_add(&req->list, &dev->rx_reqs);
3668443f2d2SAndrzej Pietrasiewicz 		break;
3678443f2d2SAndrzej Pietrasiewicz 
3688443f2d2SAndrzej Pietrasiewicz 	/* data overrun */
3698443f2d2SAndrzej Pietrasiewicz 	case -EOVERFLOW:
3708443f2d2SAndrzej Pietrasiewicz 		/* FALLTHROUGH */
3718443f2d2SAndrzej Pietrasiewicz 
3728443f2d2SAndrzej Pietrasiewicz 	default:
3738443f2d2SAndrzej Pietrasiewicz 		DBG(dev, "rx status %d\n", status);
3748443f2d2SAndrzej Pietrasiewicz 		list_add(&req->list, &dev->rx_reqs);
3758443f2d2SAndrzej Pietrasiewicz 		break;
3768443f2d2SAndrzej Pietrasiewicz 	}
3778443f2d2SAndrzej Pietrasiewicz 
3788443f2d2SAndrzej Pietrasiewicz 	wake_up_interruptible(&dev->rx_wait);
3798443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
3808443f2d2SAndrzej Pietrasiewicz }
3818443f2d2SAndrzej Pietrasiewicz 
3828443f2d2SAndrzej Pietrasiewicz static void tx_complete(struct usb_ep *ep, struct usb_request *req)
3838443f2d2SAndrzej Pietrasiewicz {
3848443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev = ep->driver_data;
3858443f2d2SAndrzej Pietrasiewicz 
3868443f2d2SAndrzej Pietrasiewicz 	switch (req->status) {
3878443f2d2SAndrzej Pietrasiewicz 	default:
3888443f2d2SAndrzej Pietrasiewicz 		VDBG(dev, "tx err %d\n", req->status);
3898443f2d2SAndrzej Pietrasiewicz 		/* FALLTHROUGH */
3908443f2d2SAndrzej Pietrasiewicz 	case -ECONNRESET:		/* unlink */
3918443f2d2SAndrzej Pietrasiewicz 	case -ESHUTDOWN:		/* disconnect etc */
3928443f2d2SAndrzej Pietrasiewicz 		break;
3938443f2d2SAndrzej Pietrasiewicz 	case 0:
3948443f2d2SAndrzej Pietrasiewicz 		break;
3958443f2d2SAndrzej Pietrasiewicz 	}
3968443f2d2SAndrzej Pietrasiewicz 
3978443f2d2SAndrzej Pietrasiewicz 	spin_lock(&dev->lock);
3988443f2d2SAndrzej Pietrasiewicz 	/* Take the request struct off the active list and put it on the
3998443f2d2SAndrzej Pietrasiewicz 	 * free list.
4008443f2d2SAndrzej Pietrasiewicz 	 */
4018443f2d2SAndrzej Pietrasiewicz 	list_del_init(&req->list);
4028443f2d2SAndrzej Pietrasiewicz 	list_add(&req->list, &dev->tx_reqs);
4038443f2d2SAndrzej Pietrasiewicz 	wake_up_interruptible(&dev->tx_wait);
4048443f2d2SAndrzej Pietrasiewicz 	if (likely(list_empty(&dev->tx_reqs_active)))
4058443f2d2SAndrzej Pietrasiewicz 		wake_up_interruptible(&dev->tx_flush_wait);
4068443f2d2SAndrzej Pietrasiewicz 
4078443f2d2SAndrzej Pietrasiewicz 	spin_unlock(&dev->lock);
4088443f2d2SAndrzej Pietrasiewicz }
4098443f2d2SAndrzej Pietrasiewicz 
4108443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
4118443f2d2SAndrzej Pietrasiewicz 
4128443f2d2SAndrzej Pietrasiewicz static int
4138443f2d2SAndrzej Pietrasiewicz printer_open(struct inode *inode, struct file *fd)
4148443f2d2SAndrzej Pietrasiewicz {
4158443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev;
4168443f2d2SAndrzej Pietrasiewicz 	unsigned long		flags;
4178443f2d2SAndrzej Pietrasiewicz 	int			ret = -EBUSY;
4188443f2d2SAndrzej Pietrasiewicz 
4198443f2d2SAndrzej Pietrasiewicz 	mutex_lock(&printer_mutex);
4208443f2d2SAndrzej Pietrasiewicz 	dev = container_of(inode->i_cdev, struct printer_dev, printer_cdev);
4218443f2d2SAndrzej Pietrasiewicz 
4228443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
4238443f2d2SAndrzej Pietrasiewicz 
4248443f2d2SAndrzej Pietrasiewicz 	if (!dev->printer_cdev_open) {
4258443f2d2SAndrzej Pietrasiewicz 		dev->printer_cdev_open = 1;
4268443f2d2SAndrzej Pietrasiewicz 		fd->private_data = dev;
4278443f2d2SAndrzej Pietrasiewicz 		ret = 0;
4288443f2d2SAndrzej Pietrasiewicz 		/* Change the printer status to show that it's on-line. */
4298443f2d2SAndrzej Pietrasiewicz 		dev->printer_status |= PRINTER_SELECTED;
4308443f2d2SAndrzej Pietrasiewicz 	}
4318443f2d2SAndrzej Pietrasiewicz 
4328443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
4338443f2d2SAndrzej Pietrasiewicz 
4348443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "printer_open returned %x\n", ret);
4358443f2d2SAndrzej Pietrasiewicz 	mutex_unlock(&printer_mutex);
4368443f2d2SAndrzej Pietrasiewicz 	return ret;
4378443f2d2SAndrzej Pietrasiewicz }
4388443f2d2SAndrzej Pietrasiewicz 
4398443f2d2SAndrzej Pietrasiewicz static int
4408443f2d2SAndrzej Pietrasiewicz printer_close(struct inode *inode, struct file *fd)
4418443f2d2SAndrzej Pietrasiewicz {
4428443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev = fd->private_data;
4438443f2d2SAndrzej Pietrasiewicz 	unsigned long		flags;
4448443f2d2SAndrzej Pietrasiewicz 
4458443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
4468443f2d2SAndrzej Pietrasiewicz 	dev->printer_cdev_open = 0;
4478443f2d2SAndrzej Pietrasiewicz 	fd->private_data = NULL;
4488443f2d2SAndrzej Pietrasiewicz 	/* Change printer status to show that the printer is off-line. */
4498443f2d2SAndrzej Pietrasiewicz 	dev->printer_status &= ~PRINTER_SELECTED;
4508443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
4518443f2d2SAndrzej Pietrasiewicz 
4528443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "printer_close\n");
4538443f2d2SAndrzej Pietrasiewicz 
4548443f2d2SAndrzej Pietrasiewicz 	return 0;
4558443f2d2SAndrzej Pietrasiewicz }
4568443f2d2SAndrzej Pietrasiewicz 
4578443f2d2SAndrzej Pietrasiewicz /* This function must be called with interrupts turned off. */
4588443f2d2SAndrzej Pietrasiewicz static void
4598443f2d2SAndrzej Pietrasiewicz setup_rx_reqs(struct printer_dev *dev)
4608443f2d2SAndrzej Pietrasiewicz {
4618443f2d2SAndrzej Pietrasiewicz 	struct usb_request              *req;
4628443f2d2SAndrzej Pietrasiewicz 
4638443f2d2SAndrzej Pietrasiewicz 	while (likely(!list_empty(&dev->rx_reqs))) {
4648443f2d2SAndrzej Pietrasiewicz 		int error;
4658443f2d2SAndrzej Pietrasiewicz 
4668443f2d2SAndrzej Pietrasiewicz 		req = container_of(dev->rx_reqs.next,
4678443f2d2SAndrzej Pietrasiewicz 				struct usb_request, list);
4688443f2d2SAndrzej Pietrasiewicz 		list_del_init(&req->list);
4698443f2d2SAndrzej Pietrasiewicz 
4708443f2d2SAndrzej Pietrasiewicz 		/* The USB Host sends us whatever amount of data it wants to
4718443f2d2SAndrzej Pietrasiewicz 		 * so we always set the length field to the full USB_BUFSIZE.
4728443f2d2SAndrzej Pietrasiewicz 		 * If the amount of data is more than the read() caller asked
4738443f2d2SAndrzej Pietrasiewicz 		 * for it will be stored in the request buffer until it is
4748443f2d2SAndrzej Pietrasiewicz 		 * asked for by read().
4758443f2d2SAndrzej Pietrasiewicz 		 */
4768443f2d2SAndrzej Pietrasiewicz 		req->length = USB_BUFSIZE;
4778443f2d2SAndrzej Pietrasiewicz 		req->complete = rx_complete;
4788443f2d2SAndrzej Pietrasiewicz 
4798443f2d2SAndrzej Pietrasiewicz 		/* here, we unlock, and only unlock, to avoid deadlock. */
4808443f2d2SAndrzej Pietrasiewicz 		spin_unlock(&dev->lock);
4818443f2d2SAndrzej Pietrasiewicz 		error = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC);
4828443f2d2SAndrzej Pietrasiewicz 		spin_lock(&dev->lock);
4838443f2d2SAndrzej Pietrasiewicz 		if (error) {
4848443f2d2SAndrzej Pietrasiewicz 			DBG(dev, "rx submit --> %d\n", error);
4858443f2d2SAndrzej Pietrasiewicz 			list_add(&req->list, &dev->rx_reqs);
4868443f2d2SAndrzej Pietrasiewicz 			break;
4878443f2d2SAndrzej Pietrasiewicz 		}
4888443f2d2SAndrzej Pietrasiewicz 		/* if the req is empty, then add it into dev->rx_reqs_active. */
4898443f2d2SAndrzej Pietrasiewicz 		else if (list_empty(&req->list)) {
4908443f2d2SAndrzej Pietrasiewicz 			list_add(&req->list, &dev->rx_reqs_active);
4918443f2d2SAndrzej Pietrasiewicz 		}
4928443f2d2SAndrzej Pietrasiewicz 	}
4938443f2d2SAndrzej Pietrasiewicz }
4948443f2d2SAndrzej Pietrasiewicz 
4958443f2d2SAndrzej Pietrasiewicz static ssize_t
4968443f2d2SAndrzej Pietrasiewicz printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
4978443f2d2SAndrzej Pietrasiewicz {
4988443f2d2SAndrzej Pietrasiewicz 	struct printer_dev		*dev = fd->private_data;
4998443f2d2SAndrzej Pietrasiewicz 	unsigned long			flags;
5008443f2d2SAndrzej Pietrasiewicz 	size_t				size;
5018443f2d2SAndrzej Pietrasiewicz 	size_t				bytes_copied;
5028443f2d2SAndrzej Pietrasiewicz 	struct usb_request		*req;
5038443f2d2SAndrzej Pietrasiewicz 	/* This is a pointer to the current USB rx request. */
5048443f2d2SAndrzej Pietrasiewicz 	struct usb_request		*current_rx_req;
5058443f2d2SAndrzej Pietrasiewicz 	/* This is the number of bytes in the current rx buffer. */
5068443f2d2SAndrzej Pietrasiewicz 	size_t				current_rx_bytes;
5078443f2d2SAndrzej Pietrasiewicz 	/* This is a pointer to the current rx buffer. */
5088443f2d2SAndrzej Pietrasiewicz 	u8				*current_rx_buf;
5098443f2d2SAndrzej Pietrasiewicz 
5108443f2d2SAndrzej Pietrasiewicz 	if (len == 0)
5118443f2d2SAndrzej Pietrasiewicz 		return -EINVAL;
5128443f2d2SAndrzej Pietrasiewicz 
5138443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "printer_read trying to read %d bytes\n", (int)len);
5148443f2d2SAndrzej Pietrasiewicz 
5158443f2d2SAndrzej Pietrasiewicz 	mutex_lock(&dev->lock_printer_io);
5168443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
5178443f2d2SAndrzej Pietrasiewicz 
5188443f2d2SAndrzej Pietrasiewicz 	/* We will use this flag later to check if a printer reset happened
5198443f2d2SAndrzej Pietrasiewicz 	 * after we turn interrupts back on.
5208443f2d2SAndrzej Pietrasiewicz 	 */
5218443f2d2SAndrzej Pietrasiewicz 	dev->reset_printer = 0;
5228443f2d2SAndrzej Pietrasiewicz 
5238443f2d2SAndrzej Pietrasiewicz 	setup_rx_reqs(dev);
5248443f2d2SAndrzej Pietrasiewicz 
5258443f2d2SAndrzej Pietrasiewicz 	bytes_copied = 0;
5268443f2d2SAndrzej Pietrasiewicz 	current_rx_req = dev->current_rx_req;
5278443f2d2SAndrzej Pietrasiewicz 	current_rx_bytes = dev->current_rx_bytes;
5288443f2d2SAndrzej Pietrasiewicz 	current_rx_buf = dev->current_rx_buf;
5298443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_req = NULL;
5308443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_bytes = 0;
5318443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_buf = NULL;
5328443f2d2SAndrzej Pietrasiewicz 
5338443f2d2SAndrzej Pietrasiewicz 	/* Check if there is any data in the read buffers. Please note that
5348443f2d2SAndrzej Pietrasiewicz 	 * current_rx_bytes is the number of bytes in the current rx buffer.
5358443f2d2SAndrzej Pietrasiewicz 	 * If it is zero then check if there are any other rx_buffers that
5368443f2d2SAndrzej Pietrasiewicz 	 * are on the completed list. We are only out of data if all rx
5378443f2d2SAndrzej Pietrasiewicz 	 * buffers are empty.
5388443f2d2SAndrzej Pietrasiewicz 	 */
5398443f2d2SAndrzej Pietrasiewicz 	if ((current_rx_bytes == 0) &&
5408443f2d2SAndrzej Pietrasiewicz 			(likely(list_empty(&dev->rx_buffers)))) {
5418443f2d2SAndrzej Pietrasiewicz 		/* Turn interrupts back on before sleeping. */
5428443f2d2SAndrzej Pietrasiewicz 		spin_unlock_irqrestore(&dev->lock, flags);
5438443f2d2SAndrzej Pietrasiewicz 
5448443f2d2SAndrzej Pietrasiewicz 		/*
5458443f2d2SAndrzej Pietrasiewicz 		 * If no data is available check if this is a NON-Blocking
5468443f2d2SAndrzej Pietrasiewicz 		 * call or not.
5478443f2d2SAndrzej Pietrasiewicz 		 */
5488443f2d2SAndrzej Pietrasiewicz 		if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) {
5498443f2d2SAndrzej Pietrasiewicz 			mutex_unlock(&dev->lock_printer_io);
5508443f2d2SAndrzej Pietrasiewicz 			return -EAGAIN;
5518443f2d2SAndrzej Pietrasiewicz 		}
5528443f2d2SAndrzej Pietrasiewicz 
5538443f2d2SAndrzej Pietrasiewicz 		/* Sleep until data is available */
5548443f2d2SAndrzej Pietrasiewicz 		wait_event_interruptible(dev->rx_wait,
5558443f2d2SAndrzej Pietrasiewicz 				(likely(!list_empty(&dev->rx_buffers))));
5568443f2d2SAndrzej Pietrasiewicz 		spin_lock_irqsave(&dev->lock, flags);
5578443f2d2SAndrzej Pietrasiewicz 	}
5588443f2d2SAndrzej Pietrasiewicz 
5598443f2d2SAndrzej Pietrasiewicz 	/* We have data to return then copy it to the caller's buffer.*/
5608443f2d2SAndrzej Pietrasiewicz 	while ((current_rx_bytes || likely(!list_empty(&dev->rx_buffers)))
5618443f2d2SAndrzej Pietrasiewicz 			&& len) {
5628443f2d2SAndrzej Pietrasiewicz 		if (current_rx_bytes == 0) {
5638443f2d2SAndrzej Pietrasiewicz 			req = container_of(dev->rx_buffers.next,
5648443f2d2SAndrzej Pietrasiewicz 					struct usb_request, list);
5658443f2d2SAndrzej Pietrasiewicz 			list_del_init(&req->list);
5668443f2d2SAndrzej Pietrasiewicz 
5678443f2d2SAndrzej Pietrasiewicz 			if (req->actual && req->buf) {
5688443f2d2SAndrzej Pietrasiewicz 				current_rx_req = req;
5698443f2d2SAndrzej Pietrasiewicz 				current_rx_bytes = req->actual;
5708443f2d2SAndrzej Pietrasiewicz 				current_rx_buf = req->buf;
5718443f2d2SAndrzej Pietrasiewicz 			} else {
5728443f2d2SAndrzej Pietrasiewicz 				list_add(&req->list, &dev->rx_reqs);
5738443f2d2SAndrzej Pietrasiewicz 				continue;
5748443f2d2SAndrzej Pietrasiewicz 			}
5758443f2d2SAndrzej Pietrasiewicz 		}
5768443f2d2SAndrzej Pietrasiewicz 
5778443f2d2SAndrzej Pietrasiewicz 		/* Don't leave irqs off while doing memory copies */
5788443f2d2SAndrzej Pietrasiewicz 		spin_unlock_irqrestore(&dev->lock, flags);
5798443f2d2SAndrzej Pietrasiewicz 
5808443f2d2SAndrzej Pietrasiewicz 		if (len > current_rx_bytes)
5818443f2d2SAndrzej Pietrasiewicz 			size = current_rx_bytes;
5828443f2d2SAndrzej Pietrasiewicz 		else
5838443f2d2SAndrzej Pietrasiewicz 			size = len;
5848443f2d2SAndrzej Pietrasiewicz 
5858443f2d2SAndrzej Pietrasiewicz 		size -= copy_to_user(buf, current_rx_buf, size);
5868443f2d2SAndrzej Pietrasiewicz 		bytes_copied += size;
5878443f2d2SAndrzej Pietrasiewicz 		len -= size;
5888443f2d2SAndrzej Pietrasiewicz 		buf += size;
5898443f2d2SAndrzej Pietrasiewicz 
5908443f2d2SAndrzej Pietrasiewicz 		spin_lock_irqsave(&dev->lock, flags);
5918443f2d2SAndrzej Pietrasiewicz 
5928443f2d2SAndrzej Pietrasiewicz 		/* We've disconnected or reset so return. */
5938443f2d2SAndrzej Pietrasiewicz 		if (dev->reset_printer) {
5948443f2d2SAndrzej Pietrasiewicz 			list_add(&current_rx_req->list, &dev->rx_reqs);
5958443f2d2SAndrzej Pietrasiewicz 			spin_unlock_irqrestore(&dev->lock, flags);
5968443f2d2SAndrzej Pietrasiewicz 			mutex_unlock(&dev->lock_printer_io);
5978443f2d2SAndrzej Pietrasiewicz 			return -EAGAIN;
5988443f2d2SAndrzej Pietrasiewicz 		}
5998443f2d2SAndrzej Pietrasiewicz 
6008443f2d2SAndrzej Pietrasiewicz 		/* If we not returning all the data left in this RX request
6018443f2d2SAndrzej Pietrasiewicz 		 * buffer then adjust the amount of data left in the buffer.
6028443f2d2SAndrzej Pietrasiewicz 		 * Othewise if we are done with this RX request buffer then
6038443f2d2SAndrzej Pietrasiewicz 		 * requeue it to get any incoming data from the USB host.
6048443f2d2SAndrzej Pietrasiewicz 		 */
6058443f2d2SAndrzej Pietrasiewicz 		if (size < current_rx_bytes) {
6068443f2d2SAndrzej Pietrasiewicz 			current_rx_bytes -= size;
6078443f2d2SAndrzej Pietrasiewicz 			current_rx_buf += size;
6088443f2d2SAndrzej Pietrasiewicz 		} else {
6098443f2d2SAndrzej Pietrasiewicz 			list_add(&current_rx_req->list, &dev->rx_reqs);
6108443f2d2SAndrzej Pietrasiewicz 			current_rx_bytes = 0;
6118443f2d2SAndrzej Pietrasiewicz 			current_rx_buf = NULL;
6128443f2d2SAndrzej Pietrasiewicz 			current_rx_req = NULL;
6138443f2d2SAndrzej Pietrasiewicz 		}
6148443f2d2SAndrzej Pietrasiewicz 	}
6158443f2d2SAndrzej Pietrasiewicz 
6168443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_req = current_rx_req;
6178443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_bytes = current_rx_bytes;
6188443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_buf = current_rx_buf;
6198443f2d2SAndrzej Pietrasiewicz 
6208443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
6218443f2d2SAndrzej Pietrasiewicz 	mutex_unlock(&dev->lock_printer_io);
6228443f2d2SAndrzej Pietrasiewicz 
6238443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "printer_read returned %d bytes\n", (int)bytes_copied);
6248443f2d2SAndrzej Pietrasiewicz 
6258443f2d2SAndrzej Pietrasiewicz 	if (bytes_copied)
6268443f2d2SAndrzej Pietrasiewicz 		return bytes_copied;
6278443f2d2SAndrzej Pietrasiewicz 	else
6288443f2d2SAndrzej Pietrasiewicz 		return -EAGAIN;
6298443f2d2SAndrzej Pietrasiewicz }
6308443f2d2SAndrzej Pietrasiewicz 
6318443f2d2SAndrzej Pietrasiewicz static ssize_t
6328443f2d2SAndrzej Pietrasiewicz printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
6338443f2d2SAndrzej Pietrasiewicz {
6348443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev = fd->private_data;
6358443f2d2SAndrzej Pietrasiewicz 	unsigned long		flags;
6368443f2d2SAndrzej Pietrasiewicz 	size_t			size;	/* Amount of data in a TX request. */
6378443f2d2SAndrzej Pietrasiewicz 	size_t			bytes_copied = 0;
6388443f2d2SAndrzej Pietrasiewicz 	struct usb_request	*req;
6398443f2d2SAndrzej Pietrasiewicz 
6408443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "printer_write trying to send %d bytes\n", (int)len);
6418443f2d2SAndrzej Pietrasiewicz 
6428443f2d2SAndrzej Pietrasiewicz 	if (len == 0)
6438443f2d2SAndrzej Pietrasiewicz 		return -EINVAL;
6448443f2d2SAndrzej Pietrasiewicz 
6458443f2d2SAndrzej Pietrasiewicz 	mutex_lock(&dev->lock_printer_io);
6468443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
6478443f2d2SAndrzej Pietrasiewicz 
6488443f2d2SAndrzej Pietrasiewicz 	/* Check if a printer reset happens while we have interrupts on */
6498443f2d2SAndrzej Pietrasiewicz 	dev->reset_printer = 0;
6508443f2d2SAndrzej Pietrasiewicz 
6518443f2d2SAndrzej Pietrasiewicz 	/* Check if there is any available write buffers */
6528443f2d2SAndrzej Pietrasiewicz 	if (likely(list_empty(&dev->tx_reqs))) {
6538443f2d2SAndrzej Pietrasiewicz 		/* Turn interrupts back on before sleeping. */
6548443f2d2SAndrzej Pietrasiewicz 		spin_unlock_irqrestore(&dev->lock, flags);
6558443f2d2SAndrzej Pietrasiewicz 
6568443f2d2SAndrzej Pietrasiewicz 		/*
6578443f2d2SAndrzej Pietrasiewicz 		 * If write buffers are available check if this is
6588443f2d2SAndrzej Pietrasiewicz 		 * a NON-Blocking call or not.
6598443f2d2SAndrzej Pietrasiewicz 		 */
6608443f2d2SAndrzej Pietrasiewicz 		if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) {
6618443f2d2SAndrzej Pietrasiewicz 			mutex_unlock(&dev->lock_printer_io);
6628443f2d2SAndrzej Pietrasiewicz 			return -EAGAIN;
6638443f2d2SAndrzej Pietrasiewicz 		}
6648443f2d2SAndrzej Pietrasiewicz 
6658443f2d2SAndrzej Pietrasiewicz 		/* Sleep until a write buffer is available */
6668443f2d2SAndrzej Pietrasiewicz 		wait_event_interruptible(dev->tx_wait,
6678443f2d2SAndrzej Pietrasiewicz 				(likely(!list_empty(&dev->tx_reqs))));
6688443f2d2SAndrzej Pietrasiewicz 		spin_lock_irqsave(&dev->lock, flags);
6698443f2d2SAndrzej Pietrasiewicz 	}
6708443f2d2SAndrzej Pietrasiewicz 
6718443f2d2SAndrzej Pietrasiewicz 	while (likely(!list_empty(&dev->tx_reqs)) && len) {
6728443f2d2SAndrzej Pietrasiewicz 
6738443f2d2SAndrzej Pietrasiewicz 		if (len > USB_BUFSIZE)
6748443f2d2SAndrzej Pietrasiewicz 			size = USB_BUFSIZE;
6758443f2d2SAndrzej Pietrasiewicz 		else
6768443f2d2SAndrzej Pietrasiewicz 			size = len;
6778443f2d2SAndrzej Pietrasiewicz 
6788443f2d2SAndrzej Pietrasiewicz 		req = container_of(dev->tx_reqs.next, struct usb_request,
6798443f2d2SAndrzej Pietrasiewicz 				list);
6808443f2d2SAndrzej Pietrasiewicz 		list_del_init(&req->list);
6818443f2d2SAndrzej Pietrasiewicz 
6828443f2d2SAndrzej Pietrasiewicz 		req->complete = tx_complete;
6838443f2d2SAndrzej Pietrasiewicz 		req->length = size;
6848443f2d2SAndrzej Pietrasiewicz 
6858443f2d2SAndrzej Pietrasiewicz 		/* Check if we need to send a zero length packet. */
6868443f2d2SAndrzej Pietrasiewicz 		if (len > size)
6878443f2d2SAndrzej Pietrasiewicz 			/* They will be more TX requests so no yet. */
6888443f2d2SAndrzej Pietrasiewicz 			req->zero = 0;
6898443f2d2SAndrzej Pietrasiewicz 		else
6908443f2d2SAndrzej Pietrasiewicz 			/* If the data amount is not a multple of the
6918443f2d2SAndrzej Pietrasiewicz 			 * maxpacket size then send a zero length packet.
6928443f2d2SAndrzej Pietrasiewicz 			 */
6938443f2d2SAndrzej Pietrasiewicz 			req->zero = ((len % dev->in_ep->maxpacket) == 0);
6948443f2d2SAndrzej Pietrasiewicz 
6958443f2d2SAndrzej Pietrasiewicz 		/* Don't leave irqs off while doing memory copies */
6968443f2d2SAndrzej Pietrasiewicz 		spin_unlock_irqrestore(&dev->lock, flags);
6978443f2d2SAndrzej Pietrasiewicz 
6988443f2d2SAndrzej Pietrasiewicz 		if (copy_from_user(req->buf, buf, size)) {
6998443f2d2SAndrzej Pietrasiewicz 			list_add(&req->list, &dev->tx_reqs);
7008443f2d2SAndrzej Pietrasiewicz 			mutex_unlock(&dev->lock_printer_io);
7018443f2d2SAndrzej Pietrasiewicz 			return bytes_copied;
7028443f2d2SAndrzej Pietrasiewicz 		}
7038443f2d2SAndrzej Pietrasiewicz 
7048443f2d2SAndrzej Pietrasiewicz 		bytes_copied += size;
7058443f2d2SAndrzej Pietrasiewicz 		len -= size;
7068443f2d2SAndrzej Pietrasiewicz 		buf += size;
7078443f2d2SAndrzej Pietrasiewicz 
7088443f2d2SAndrzej Pietrasiewicz 		spin_lock_irqsave(&dev->lock, flags);
7098443f2d2SAndrzej Pietrasiewicz 
7108443f2d2SAndrzej Pietrasiewicz 		/* We've disconnected or reset so free the req and buffer */
7118443f2d2SAndrzej Pietrasiewicz 		if (dev->reset_printer) {
7128443f2d2SAndrzej Pietrasiewicz 			list_add(&req->list, &dev->tx_reqs);
7138443f2d2SAndrzej Pietrasiewicz 			spin_unlock_irqrestore(&dev->lock, flags);
7148443f2d2SAndrzej Pietrasiewicz 			mutex_unlock(&dev->lock_printer_io);
7158443f2d2SAndrzej Pietrasiewicz 			return -EAGAIN;
7168443f2d2SAndrzej Pietrasiewicz 		}
7178443f2d2SAndrzej Pietrasiewicz 
7188443f2d2SAndrzej Pietrasiewicz 		if (usb_ep_queue(dev->in_ep, req, GFP_ATOMIC)) {
7198443f2d2SAndrzej Pietrasiewicz 			list_add(&req->list, &dev->tx_reqs);
7208443f2d2SAndrzej Pietrasiewicz 			spin_unlock_irqrestore(&dev->lock, flags);
7218443f2d2SAndrzej Pietrasiewicz 			mutex_unlock(&dev->lock_printer_io);
7228443f2d2SAndrzej Pietrasiewicz 			return -EAGAIN;
7238443f2d2SAndrzej Pietrasiewicz 		}
7248443f2d2SAndrzej Pietrasiewicz 
7258443f2d2SAndrzej Pietrasiewicz 		list_add(&req->list, &dev->tx_reqs_active);
7268443f2d2SAndrzej Pietrasiewicz 
7278443f2d2SAndrzej Pietrasiewicz 	}
7288443f2d2SAndrzej Pietrasiewicz 
7298443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
7308443f2d2SAndrzej Pietrasiewicz 	mutex_unlock(&dev->lock_printer_io);
7318443f2d2SAndrzej Pietrasiewicz 
7328443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "printer_write sent %d bytes\n", (int)bytes_copied);
7338443f2d2SAndrzej Pietrasiewicz 
7348443f2d2SAndrzej Pietrasiewicz 	if (bytes_copied) {
7358443f2d2SAndrzej Pietrasiewicz 		return bytes_copied;
7368443f2d2SAndrzej Pietrasiewicz 	} else {
7378443f2d2SAndrzej Pietrasiewicz 		return -EAGAIN;
7388443f2d2SAndrzej Pietrasiewicz 	}
7398443f2d2SAndrzej Pietrasiewicz }
7408443f2d2SAndrzej Pietrasiewicz 
7418443f2d2SAndrzej Pietrasiewicz static int
7428443f2d2SAndrzej Pietrasiewicz printer_fsync(struct file *fd, loff_t start, loff_t end, int datasync)
7438443f2d2SAndrzej Pietrasiewicz {
7448443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev = fd->private_data;
7458443f2d2SAndrzej Pietrasiewicz 	struct inode *inode = file_inode(fd);
7468443f2d2SAndrzej Pietrasiewicz 	unsigned long		flags;
7478443f2d2SAndrzej Pietrasiewicz 	int			tx_list_empty;
7488443f2d2SAndrzej Pietrasiewicz 
7498443f2d2SAndrzej Pietrasiewicz 	mutex_lock(&inode->i_mutex);
7508443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
7518443f2d2SAndrzej Pietrasiewicz 	tx_list_empty = (likely(list_empty(&dev->tx_reqs)));
7528443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
7538443f2d2SAndrzej Pietrasiewicz 
7548443f2d2SAndrzej Pietrasiewicz 	if (!tx_list_empty) {
7558443f2d2SAndrzej Pietrasiewicz 		/* Sleep until all data has been sent */
7568443f2d2SAndrzej Pietrasiewicz 		wait_event_interruptible(dev->tx_flush_wait,
7578443f2d2SAndrzej Pietrasiewicz 				(likely(list_empty(&dev->tx_reqs_active))));
7588443f2d2SAndrzej Pietrasiewicz 	}
7598443f2d2SAndrzej Pietrasiewicz 	mutex_unlock(&inode->i_mutex);
7608443f2d2SAndrzej Pietrasiewicz 
7618443f2d2SAndrzej Pietrasiewicz 	return 0;
7628443f2d2SAndrzej Pietrasiewicz }
7638443f2d2SAndrzej Pietrasiewicz 
7648443f2d2SAndrzej Pietrasiewicz static unsigned int
7658443f2d2SAndrzej Pietrasiewicz printer_poll(struct file *fd, poll_table *wait)
7668443f2d2SAndrzej Pietrasiewicz {
7678443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev = fd->private_data;
7688443f2d2SAndrzej Pietrasiewicz 	unsigned long		flags;
7698443f2d2SAndrzej Pietrasiewicz 	int			status = 0;
7708443f2d2SAndrzej Pietrasiewicz 
7718443f2d2SAndrzej Pietrasiewicz 	mutex_lock(&dev->lock_printer_io);
7728443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
7738443f2d2SAndrzej Pietrasiewicz 	setup_rx_reqs(dev);
7748443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
7758443f2d2SAndrzej Pietrasiewicz 	mutex_unlock(&dev->lock_printer_io);
7768443f2d2SAndrzej Pietrasiewicz 
7778443f2d2SAndrzej Pietrasiewicz 	poll_wait(fd, &dev->rx_wait, wait);
7788443f2d2SAndrzej Pietrasiewicz 	poll_wait(fd, &dev->tx_wait, wait);
7798443f2d2SAndrzej Pietrasiewicz 
7808443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
7818443f2d2SAndrzej Pietrasiewicz 	if (likely(!list_empty(&dev->tx_reqs)))
7828443f2d2SAndrzej Pietrasiewicz 		status |= POLLOUT | POLLWRNORM;
7838443f2d2SAndrzej Pietrasiewicz 
7848443f2d2SAndrzej Pietrasiewicz 	if (likely(dev->current_rx_bytes) ||
7858443f2d2SAndrzej Pietrasiewicz 			likely(!list_empty(&dev->rx_buffers)))
7868443f2d2SAndrzej Pietrasiewicz 		status |= POLLIN | POLLRDNORM;
7878443f2d2SAndrzej Pietrasiewicz 
7888443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
7898443f2d2SAndrzej Pietrasiewicz 
7908443f2d2SAndrzej Pietrasiewicz 	return status;
7918443f2d2SAndrzej Pietrasiewicz }
7928443f2d2SAndrzej Pietrasiewicz 
7938443f2d2SAndrzej Pietrasiewicz static long
7948443f2d2SAndrzej Pietrasiewicz printer_ioctl(struct file *fd, unsigned int code, unsigned long arg)
7958443f2d2SAndrzej Pietrasiewicz {
7968443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev = fd->private_data;
7978443f2d2SAndrzej Pietrasiewicz 	unsigned long		flags;
7988443f2d2SAndrzej Pietrasiewicz 	int			status = 0;
7998443f2d2SAndrzej Pietrasiewicz 
8008443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "printer_ioctl: cmd=0x%4.4x, arg=%lu\n", code, arg);
8018443f2d2SAndrzej Pietrasiewicz 
8028443f2d2SAndrzej Pietrasiewicz 	/* handle ioctls */
8038443f2d2SAndrzej Pietrasiewicz 
8048443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
8058443f2d2SAndrzej Pietrasiewicz 
8068443f2d2SAndrzej Pietrasiewicz 	switch (code) {
8078443f2d2SAndrzej Pietrasiewicz 	case GADGET_GET_PRINTER_STATUS:
8088443f2d2SAndrzej Pietrasiewicz 		status = (int)dev->printer_status;
8098443f2d2SAndrzej Pietrasiewicz 		break;
8108443f2d2SAndrzej Pietrasiewicz 	case GADGET_SET_PRINTER_STATUS:
8118443f2d2SAndrzej Pietrasiewicz 		dev->printer_status = (u8)arg;
8128443f2d2SAndrzej Pietrasiewicz 		break;
8138443f2d2SAndrzej Pietrasiewicz 	default:
8148443f2d2SAndrzej Pietrasiewicz 		/* could not handle ioctl */
8158443f2d2SAndrzej Pietrasiewicz 		DBG(dev, "printer_ioctl: ERROR cmd=0x%4.4xis not supported\n",
8168443f2d2SAndrzej Pietrasiewicz 				code);
8178443f2d2SAndrzej Pietrasiewicz 		status = -ENOTTY;
8188443f2d2SAndrzej Pietrasiewicz 	}
8198443f2d2SAndrzej Pietrasiewicz 
8208443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
8218443f2d2SAndrzej Pietrasiewicz 
8228443f2d2SAndrzej Pietrasiewicz 	return status;
8238443f2d2SAndrzej Pietrasiewicz }
8248443f2d2SAndrzej Pietrasiewicz 
8258443f2d2SAndrzej Pietrasiewicz /* used after endpoint configuration */
8268443f2d2SAndrzej Pietrasiewicz static const struct file_operations printer_io_operations = {
8278443f2d2SAndrzej Pietrasiewicz 	.owner =	THIS_MODULE,
8288443f2d2SAndrzej Pietrasiewicz 	.open =		printer_open,
8298443f2d2SAndrzej Pietrasiewicz 	.read =		printer_read,
8308443f2d2SAndrzej Pietrasiewicz 	.write =	printer_write,
8318443f2d2SAndrzej Pietrasiewicz 	.fsync =	printer_fsync,
8328443f2d2SAndrzej Pietrasiewicz 	.poll =		printer_poll,
8338443f2d2SAndrzej Pietrasiewicz 	.unlocked_ioctl = printer_ioctl,
8348443f2d2SAndrzej Pietrasiewicz 	.release =	printer_close,
8358443f2d2SAndrzej Pietrasiewicz 	.llseek =	noop_llseek,
8368443f2d2SAndrzej Pietrasiewicz };
8378443f2d2SAndrzej Pietrasiewicz 
8388443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
8398443f2d2SAndrzej Pietrasiewicz 
8408443f2d2SAndrzej Pietrasiewicz static int
8418443f2d2SAndrzej Pietrasiewicz set_printer_interface(struct printer_dev *dev)
8428443f2d2SAndrzej Pietrasiewicz {
8438443f2d2SAndrzej Pietrasiewicz 	int			result = 0;
8448443f2d2SAndrzej Pietrasiewicz 
84574df41b4SJorge Ramirez-Ortiz 	dev->in_ep->desc = ep_desc(dev->gadget, &fs_ep_in_desc, &hs_ep_in_desc,
84674df41b4SJorge Ramirez-Ortiz 				&ss_ep_in_desc);
8478443f2d2SAndrzej Pietrasiewicz 	dev->in_ep->driver_data = dev;
8488443f2d2SAndrzej Pietrasiewicz 
84974df41b4SJorge Ramirez-Ortiz 	dev->out_ep->desc = ep_desc(dev->gadget, &fs_ep_out_desc,
85074df41b4SJorge Ramirez-Ortiz 				    &hs_ep_out_desc, &ss_ep_out_desc);
8518443f2d2SAndrzej Pietrasiewicz 	dev->out_ep->driver_data = dev;
8528443f2d2SAndrzej Pietrasiewicz 
8538443f2d2SAndrzej Pietrasiewicz 	result = usb_ep_enable(dev->in_ep);
8548443f2d2SAndrzej Pietrasiewicz 	if (result != 0) {
8558443f2d2SAndrzej Pietrasiewicz 		DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result);
8568443f2d2SAndrzej Pietrasiewicz 		goto done;
8578443f2d2SAndrzej Pietrasiewicz 	}
8588443f2d2SAndrzej Pietrasiewicz 
8598443f2d2SAndrzej Pietrasiewicz 	result = usb_ep_enable(dev->out_ep);
8608443f2d2SAndrzej Pietrasiewicz 	if (result != 0) {
8618443f2d2SAndrzej Pietrasiewicz 		DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result);
8628443f2d2SAndrzej Pietrasiewicz 		goto done;
8638443f2d2SAndrzej Pietrasiewicz 	}
8648443f2d2SAndrzej Pietrasiewicz 
8658443f2d2SAndrzej Pietrasiewicz done:
8668443f2d2SAndrzej Pietrasiewicz 	/* on error, disable any endpoints  */
8678443f2d2SAndrzej Pietrasiewicz 	if (result != 0) {
8688443f2d2SAndrzej Pietrasiewicz 		(void) usb_ep_disable(dev->in_ep);
8698443f2d2SAndrzej Pietrasiewicz 		(void) usb_ep_disable(dev->out_ep);
8708443f2d2SAndrzej Pietrasiewicz 		dev->in_ep->desc = NULL;
8718443f2d2SAndrzej Pietrasiewicz 		dev->out_ep->desc = NULL;
8728443f2d2SAndrzej Pietrasiewicz 	}
8738443f2d2SAndrzej Pietrasiewicz 
8748443f2d2SAndrzej Pietrasiewicz 	/* caller is responsible for cleanup on error */
8758443f2d2SAndrzej Pietrasiewicz 	return result;
8768443f2d2SAndrzej Pietrasiewicz }
8778443f2d2SAndrzej Pietrasiewicz 
8788443f2d2SAndrzej Pietrasiewicz static void printer_reset_interface(struct printer_dev *dev)
8798443f2d2SAndrzej Pietrasiewicz {
8808443f2d2SAndrzej Pietrasiewicz 	if (dev->interface < 0)
8818443f2d2SAndrzej Pietrasiewicz 		return;
8828443f2d2SAndrzej Pietrasiewicz 
8838443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "%s\n", __func__);
8848443f2d2SAndrzej Pietrasiewicz 
8858443f2d2SAndrzej Pietrasiewicz 	if (dev->in_ep->desc)
8868443f2d2SAndrzej Pietrasiewicz 		usb_ep_disable(dev->in_ep);
8878443f2d2SAndrzej Pietrasiewicz 
8888443f2d2SAndrzej Pietrasiewicz 	if (dev->out_ep->desc)
8898443f2d2SAndrzej Pietrasiewicz 		usb_ep_disable(dev->out_ep);
8908443f2d2SAndrzej Pietrasiewicz 
8918443f2d2SAndrzej Pietrasiewicz 	dev->in_ep->desc = NULL;
8928443f2d2SAndrzej Pietrasiewicz 	dev->out_ep->desc = NULL;
8938443f2d2SAndrzej Pietrasiewicz 	dev->interface = -1;
8948443f2d2SAndrzej Pietrasiewicz }
8958443f2d2SAndrzej Pietrasiewicz 
8968443f2d2SAndrzej Pietrasiewicz /* Change our operational Interface. */
8978443f2d2SAndrzej Pietrasiewicz static int set_interface(struct printer_dev *dev, unsigned number)
8988443f2d2SAndrzej Pietrasiewicz {
8998443f2d2SAndrzej Pietrasiewicz 	int			result = 0;
9008443f2d2SAndrzej Pietrasiewicz 
9018443f2d2SAndrzej Pietrasiewicz 	/* Free the current interface */
9028443f2d2SAndrzej Pietrasiewicz 	printer_reset_interface(dev);
9038443f2d2SAndrzej Pietrasiewicz 
9048443f2d2SAndrzej Pietrasiewicz 	result = set_printer_interface(dev);
9058443f2d2SAndrzej Pietrasiewicz 	if (result)
9068443f2d2SAndrzej Pietrasiewicz 		printer_reset_interface(dev);
9078443f2d2SAndrzej Pietrasiewicz 	else
9088443f2d2SAndrzej Pietrasiewicz 		dev->interface = number;
9098443f2d2SAndrzej Pietrasiewicz 
9108443f2d2SAndrzej Pietrasiewicz 	if (!result)
9118443f2d2SAndrzej Pietrasiewicz 		INFO(dev, "Using interface %x\n", number);
9128443f2d2SAndrzej Pietrasiewicz 
9138443f2d2SAndrzej Pietrasiewicz 	return result;
9148443f2d2SAndrzej Pietrasiewicz }
9158443f2d2SAndrzej Pietrasiewicz 
9168443f2d2SAndrzej Pietrasiewicz static void printer_soft_reset(struct printer_dev *dev)
9178443f2d2SAndrzej Pietrasiewicz {
9188443f2d2SAndrzej Pietrasiewicz 	struct usb_request	*req;
9198443f2d2SAndrzej Pietrasiewicz 
9208443f2d2SAndrzej Pietrasiewicz 	INFO(dev, "Received Printer Reset Request\n");
9218443f2d2SAndrzej Pietrasiewicz 
9228443f2d2SAndrzej Pietrasiewicz 	if (usb_ep_disable(dev->in_ep))
9238443f2d2SAndrzej Pietrasiewicz 		DBG(dev, "Failed to disable USB in_ep\n");
9248443f2d2SAndrzej Pietrasiewicz 	if (usb_ep_disable(dev->out_ep))
9258443f2d2SAndrzej Pietrasiewicz 		DBG(dev, "Failed to disable USB out_ep\n");
9268443f2d2SAndrzej Pietrasiewicz 
9278443f2d2SAndrzej Pietrasiewicz 	if (dev->current_rx_req != NULL) {
9288443f2d2SAndrzej Pietrasiewicz 		list_add(&dev->current_rx_req->list, &dev->rx_reqs);
9298443f2d2SAndrzej Pietrasiewicz 		dev->current_rx_req = NULL;
9308443f2d2SAndrzej Pietrasiewicz 	}
9318443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_bytes = 0;
9328443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_buf = NULL;
9338443f2d2SAndrzej Pietrasiewicz 	dev->reset_printer = 1;
9348443f2d2SAndrzej Pietrasiewicz 
9358443f2d2SAndrzej Pietrasiewicz 	while (likely(!(list_empty(&dev->rx_buffers)))) {
9368443f2d2SAndrzej Pietrasiewicz 		req = container_of(dev->rx_buffers.next, struct usb_request,
9378443f2d2SAndrzej Pietrasiewicz 				list);
9388443f2d2SAndrzej Pietrasiewicz 		list_del_init(&req->list);
9398443f2d2SAndrzej Pietrasiewicz 		list_add(&req->list, &dev->rx_reqs);
9408443f2d2SAndrzej Pietrasiewicz 	}
9418443f2d2SAndrzej Pietrasiewicz 
9428443f2d2SAndrzej Pietrasiewicz 	while (likely(!(list_empty(&dev->rx_reqs_active)))) {
9438443f2d2SAndrzej Pietrasiewicz 		req = container_of(dev->rx_buffers.next, struct usb_request,
9448443f2d2SAndrzej Pietrasiewicz 				list);
9458443f2d2SAndrzej Pietrasiewicz 		list_del_init(&req->list);
9468443f2d2SAndrzej Pietrasiewicz 		list_add(&req->list, &dev->rx_reqs);
9478443f2d2SAndrzej Pietrasiewicz 	}
9488443f2d2SAndrzej Pietrasiewicz 
9498443f2d2SAndrzej Pietrasiewicz 	while (likely(!(list_empty(&dev->tx_reqs_active)))) {
9508443f2d2SAndrzej Pietrasiewicz 		req = container_of(dev->tx_reqs_active.next,
9518443f2d2SAndrzej Pietrasiewicz 				struct usb_request, list);
9528443f2d2SAndrzej Pietrasiewicz 		list_del_init(&req->list);
9538443f2d2SAndrzej Pietrasiewicz 		list_add(&req->list, &dev->tx_reqs);
9548443f2d2SAndrzej Pietrasiewicz 	}
9558443f2d2SAndrzej Pietrasiewicz 
9568443f2d2SAndrzej Pietrasiewicz 	if (usb_ep_enable(dev->in_ep))
9578443f2d2SAndrzej Pietrasiewicz 		DBG(dev, "Failed to enable USB in_ep\n");
9588443f2d2SAndrzej Pietrasiewicz 	if (usb_ep_enable(dev->out_ep))
9598443f2d2SAndrzej Pietrasiewicz 		DBG(dev, "Failed to enable USB out_ep\n");
9608443f2d2SAndrzej Pietrasiewicz 
9618443f2d2SAndrzej Pietrasiewicz 	wake_up_interruptible(&dev->rx_wait);
9628443f2d2SAndrzej Pietrasiewicz 	wake_up_interruptible(&dev->tx_wait);
9638443f2d2SAndrzej Pietrasiewicz 	wake_up_interruptible(&dev->tx_flush_wait);
9648443f2d2SAndrzej Pietrasiewicz }
9658443f2d2SAndrzej Pietrasiewicz 
9668443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
9678443f2d2SAndrzej Pietrasiewicz 
9688443f2d2SAndrzej Pietrasiewicz /*
9698443f2d2SAndrzej Pietrasiewicz  * The setup() callback implements all the ep0 functionality that's not
9708443f2d2SAndrzej Pietrasiewicz  * handled lower down.
9718443f2d2SAndrzej Pietrasiewicz  */
9728443f2d2SAndrzej Pietrasiewicz static int printer_func_setup(struct usb_function *f,
9738443f2d2SAndrzej Pietrasiewicz 		const struct usb_ctrlrequest *ctrl)
9748443f2d2SAndrzej Pietrasiewicz {
9758443f2d2SAndrzej Pietrasiewicz 	struct printer_dev *dev = container_of(f, struct printer_dev, function);
9768443f2d2SAndrzej Pietrasiewicz 	struct usb_composite_dev *cdev = f->config->cdev;
9778443f2d2SAndrzej Pietrasiewicz 	struct usb_request	*req = cdev->req;
9788443f2d2SAndrzej Pietrasiewicz 	int			value = -EOPNOTSUPP;
9798443f2d2SAndrzej Pietrasiewicz 	u16			wIndex = le16_to_cpu(ctrl->wIndex);
9808443f2d2SAndrzej Pietrasiewicz 	u16			wValue = le16_to_cpu(ctrl->wValue);
9818443f2d2SAndrzej Pietrasiewicz 	u16			wLength = le16_to_cpu(ctrl->wLength);
9828443f2d2SAndrzej Pietrasiewicz 
9838443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "ctrl req%02x.%02x v%04x i%04x l%d\n",
9848443f2d2SAndrzej Pietrasiewicz 		ctrl->bRequestType, ctrl->bRequest, wValue, wIndex, wLength);
9858443f2d2SAndrzej Pietrasiewicz 
9868443f2d2SAndrzej Pietrasiewicz 	switch (ctrl->bRequestType&USB_TYPE_MASK) {
9878443f2d2SAndrzej Pietrasiewicz 	case USB_TYPE_CLASS:
9888443f2d2SAndrzej Pietrasiewicz 		switch (ctrl->bRequest) {
9898443f2d2SAndrzej Pietrasiewicz 		case 0: /* Get the IEEE-1284 PNP String */
9908443f2d2SAndrzej Pietrasiewicz 			/* Only one printer interface is supported. */
9918443f2d2SAndrzej Pietrasiewicz 			if ((wIndex>>8) != dev->interface)
9928443f2d2SAndrzej Pietrasiewicz 				break;
9938443f2d2SAndrzej Pietrasiewicz 
9948443f2d2SAndrzej Pietrasiewicz 			value = (pnp_string[0]<<8)|pnp_string[1];
9958443f2d2SAndrzej Pietrasiewicz 			memcpy(req->buf, pnp_string, value);
9968443f2d2SAndrzej Pietrasiewicz 			DBG(dev, "1284 PNP String: %x %s\n", value,
9978443f2d2SAndrzej Pietrasiewicz 					&pnp_string[2]);
9988443f2d2SAndrzej Pietrasiewicz 			break;
9998443f2d2SAndrzej Pietrasiewicz 
10008443f2d2SAndrzej Pietrasiewicz 		case 1: /* Get Port Status */
10018443f2d2SAndrzej Pietrasiewicz 			/* Only one printer interface is supported. */
10028443f2d2SAndrzej Pietrasiewicz 			if (wIndex != dev->interface)
10038443f2d2SAndrzej Pietrasiewicz 				break;
10048443f2d2SAndrzej Pietrasiewicz 
10058443f2d2SAndrzej Pietrasiewicz 			*(u8 *)req->buf = dev->printer_status;
10068443f2d2SAndrzej Pietrasiewicz 			value = min(wLength, (u16) 1);
10078443f2d2SAndrzej Pietrasiewicz 			break;
10088443f2d2SAndrzej Pietrasiewicz 
10098443f2d2SAndrzej Pietrasiewicz 		case 2: /* Soft Reset */
10108443f2d2SAndrzej Pietrasiewicz 			/* Only one printer interface is supported. */
10118443f2d2SAndrzej Pietrasiewicz 			if (wIndex != dev->interface)
10128443f2d2SAndrzej Pietrasiewicz 				break;
10138443f2d2SAndrzej Pietrasiewicz 
10148443f2d2SAndrzej Pietrasiewicz 			printer_soft_reset(dev);
10158443f2d2SAndrzej Pietrasiewicz 
10168443f2d2SAndrzej Pietrasiewicz 			value = 0;
10178443f2d2SAndrzej Pietrasiewicz 			break;
10188443f2d2SAndrzej Pietrasiewicz 
10198443f2d2SAndrzej Pietrasiewicz 		default:
10208443f2d2SAndrzej Pietrasiewicz 			goto unknown;
10218443f2d2SAndrzej Pietrasiewicz 		}
10228443f2d2SAndrzej Pietrasiewicz 		break;
10238443f2d2SAndrzej Pietrasiewicz 
10248443f2d2SAndrzej Pietrasiewicz 	default:
10258443f2d2SAndrzej Pietrasiewicz unknown:
10268443f2d2SAndrzej Pietrasiewicz 		VDBG(dev,
10278443f2d2SAndrzej Pietrasiewicz 			"unknown ctrl req%02x.%02x v%04x i%04x l%d\n",
10288443f2d2SAndrzej Pietrasiewicz 			ctrl->bRequestType, ctrl->bRequest,
10298443f2d2SAndrzej Pietrasiewicz 			wValue, wIndex, wLength);
10308443f2d2SAndrzej Pietrasiewicz 		break;
10318443f2d2SAndrzej Pietrasiewicz 	}
10328443f2d2SAndrzej Pietrasiewicz 	/* host either stalls (value < 0) or reports success */
1033eb132ccbSAndrzej Pietrasiewicz 	if (value >= 0) {
1034eb132ccbSAndrzej Pietrasiewicz 		req->length = value;
1035eb132ccbSAndrzej Pietrasiewicz 		req->zero = value < wLength;
1036eb132ccbSAndrzej Pietrasiewicz 		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
1037eb132ccbSAndrzej Pietrasiewicz 		if (value < 0) {
1038eb132ccbSAndrzej Pietrasiewicz 			ERROR(dev, "%s:%d Error!\n", __func__, __LINE__);
1039eb132ccbSAndrzej Pietrasiewicz 			req->status = 0;
1040eb132ccbSAndrzej Pietrasiewicz 		}
1041eb132ccbSAndrzej Pietrasiewicz 	}
10428443f2d2SAndrzej Pietrasiewicz 	return value;
10438443f2d2SAndrzej Pietrasiewicz }
10448443f2d2SAndrzej Pietrasiewicz 
10458443f2d2SAndrzej Pietrasiewicz static int __init printer_func_bind(struct usb_configuration *c,
10468443f2d2SAndrzej Pietrasiewicz 		struct usb_function *f)
10478443f2d2SAndrzej Pietrasiewicz {
10488443f2d2SAndrzej Pietrasiewicz 	struct printer_dev *dev = container_of(f, struct printer_dev, function);
10498443f2d2SAndrzej Pietrasiewicz 	struct usb_composite_dev *cdev = c->cdev;
10508443f2d2SAndrzej Pietrasiewicz 	struct usb_ep *in_ep;
10518443f2d2SAndrzej Pietrasiewicz 	struct usb_ep *out_ep = NULL;
10528443f2d2SAndrzej Pietrasiewicz 	int id;
10538443f2d2SAndrzej Pietrasiewicz 	int ret;
10548443f2d2SAndrzej Pietrasiewicz 
10558443f2d2SAndrzej Pietrasiewicz 	id = usb_interface_id(c, f);
10568443f2d2SAndrzej Pietrasiewicz 	if (id < 0)
10578443f2d2SAndrzej Pietrasiewicz 		return id;
10588443f2d2SAndrzej Pietrasiewicz 	intf_desc.bInterfaceNumber = id;
10598443f2d2SAndrzej Pietrasiewicz 
10608443f2d2SAndrzej Pietrasiewicz 	/* all we really need is bulk IN/OUT */
10618443f2d2SAndrzej Pietrasiewicz 	in_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_in_desc);
10628443f2d2SAndrzej Pietrasiewicz 	if (!in_ep) {
10638443f2d2SAndrzej Pietrasiewicz autoconf_fail:
10648443f2d2SAndrzej Pietrasiewicz 		dev_err(&cdev->gadget->dev, "can't autoconfigure on %s\n",
10658443f2d2SAndrzej Pietrasiewicz 			cdev->gadget->name);
10668443f2d2SAndrzej Pietrasiewicz 		return -ENODEV;
10678443f2d2SAndrzej Pietrasiewicz 	}
10688443f2d2SAndrzej Pietrasiewicz 	in_ep->driver_data = in_ep;	/* claim */
10698443f2d2SAndrzej Pietrasiewicz 
10708443f2d2SAndrzej Pietrasiewicz 	out_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_out_desc);
10718443f2d2SAndrzej Pietrasiewicz 	if (!out_ep)
10728443f2d2SAndrzej Pietrasiewicz 		goto autoconf_fail;
10738443f2d2SAndrzej Pietrasiewicz 	out_ep->driver_data = out_ep;	/* claim */
10748443f2d2SAndrzej Pietrasiewicz 
10758443f2d2SAndrzej Pietrasiewicz 	/* assumes that all endpoints are dual-speed */
10768443f2d2SAndrzej Pietrasiewicz 	hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
10778443f2d2SAndrzej Pietrasiewicz 	hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
107874df41b4SJorge Ramirez-Ortiz 	ss_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
107974df41b4SJorge Ramirez-Ortiz 	ss_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
10808443f2d2SAndrzej Pietrasiewicz 
10818443f2d2SAndrzej Pietrasiewicz 	ret = usb_assign_descriptors(f, fs_printer_function,
108274df41b4SJorge Ramirez-Ortiz 			hs_printer_function, ss_printer_function);
10838443f2d2SAndrzej Pietrasiewicz 	if (ret)
10848443f2d2SAndrzej Pietrasiewicz 		return ret;
10858443f2d2SAndrzej Pietrasiewicz 
10868443f2d2SAndrzej Pietrasiewicz 	dev->in_ep = in_ep;
10878443f2d2SAndrzej Pietrasiewicz 	dev->out_ep = out_ep;
10888443f2d2SAndrzej Pietrasiewicz 	return 0;
10898443f2d2SAndrzej Pietrasiewicz }
10908443f2d2SAndrzej Pietrasiewicz 
10918443f2d2SAndrzej Pietrasiewicz static void printer_func_unbind(struct usb_configuration *c,
10928443f2d2SAndrzej Pietrasiewicz 		struct usb_function *f)
10938443f2d2SAndrzej Pietrasiewicz {
10948443f2d2SAndrzej Pietrasiewicz 	usb_free_all_descriptors(f);
10958443f2d2SAndrzej Pietrasiewicz }
10968443f2d2SAndrzej Pietrasiewicz 
10978443f2d2SAndrzej Pietrasiewicz static int printer_func_set_alt(struct usb_function *f,
10988443f2d2SAndrzej Pietrasiewicz 		unsigned intf, unsigned alt)
10998443f2d2SAndrzej Pietrasiewicz {
11008443f2d2SAndrzej Pietrasiewicz 	struct printer_dev *dev = container_of(f, struct printer_dev, function);
11018443f2d2SAndrzej Pietrasiewicz 	int ret = -ENOTSUPP;
11028443f2d2SAndrzej Pietrasiewicz 
11038443f2d2SAndrzej Pietrasiewicz 	if (!alt)
11048443f2d2SAndrzej Pietrasiewicz 		ret = set_interface(dev, intf);
11058443f2d2SAndrzej Pietrasiewicz 
11068443f2d2SAndrzej Pietrasiewicz 	return ret;
11078443f2d2SAndrzej Pietrasiewicz }
11088443f2d2SAndrzej Pietrasiewicz 
11098443f2d2SAndrzej Pietrasiewicz static void printer_func_disable(struct usb_function *f)
11108443f2d2SAndrzej Pietrasiewicz {
11118443f2d2SAndrzej Pietrasiewicz 	struct printer_dev *dev = container_of(f, struct printer_dev, function);
11128443f2d2SAndrzej Pietrasiewicz 	unsigned long		flags;
11138443f2d2SAndrzej Pietrasiewicz 
11148443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "%s\n", __func__);
11158443f2d2SAndrzej Pietrasiewicz 
11168443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
11178443f2d2SAndrzej Pietrasiewicz 	printer_reset_interface(dev);
11188443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
11198443f2d2SAndrzej Pietrasiewicz }
11208443f2d2SAndrzej Pietrasiewicz 
11218443f2d2SAndrzej Pietrasiewicz static void printer_cfg_unbind(struct usb_configuration *c)
11228443f2d2SAndrzej Pietrasiewicz {
11238443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev;
11248443f2d2SAndrzej Pietrasiewicz 	struct usb_request	*req;
11258443f2d2SAndrzej Pietrasiewicz 
11268443f2d2SAndrzej Pietrasiewicz 	dev = &usb_printer_gadget;
11278443f2d2SAndrzej Pietrasiewicz 
11288443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "%s\n", __func__);
11298443f2d2SAndrzej Pietrasiewicz 
11308443f2d2SAndrzej Pietrasiewicz 	/* Remove sysfs files */
11318443f2d2SAndrzej Pietrasiewicz 	device_destroy(usb_gadget_class, g_printer_devno);
11328443f2d2SAndrzej Pietrasiewicz 
11338443f2d2SAndrzej Pietrasiewicz 	/* Remove Character Device */
11348443f2d2SAndrzej Pietrasiewicz 	cdev_del(&dev->printer_cdev);
11358443f2d2SAndrzej Pietrasiewicz 
11368443f2d2SAndrzej Pietrasiewicz 	/* we must already have been disconnected ... no i/o may be active */
11378443f2d2SAndrzej Pietrasiewicz 	WARN_ON(!list_empty(&dev->tx_reqs_active));
11388443f2d2SAndrzej Pietrasiewicz 	WARN_ON(!list_empty(&dev->rx_reqs_active));
11398443f2d2SAndrzej Pietrasiewicz 
11408443f2d2SAndrzej Pietrasiewicz 	/* Free all memory for this driver. */
11418443f2d2SAndrzej Pietrasiewicz 	while (!list_empty(&dev->tx_reqs)) {
11428443f2d2SAndrzej Pietrasiewicz 		req = container_of(dev->tx_reqs.next, struct usb_request,
11438443f2d2SAndrzej Pietrasiewicz 				list);
11448443f2d2SAndrzej Pietrasiewicz 		list_del(&req->list);
11458443f2d2SAndrzej Pietrasiewicz 		printer_req_free(dev->in_ep, req);
11468443f2d2SAndrzej Pietrasiewicz 	}
11478443f2d2SAndrzej Pietrasiewicz 
11488443f2d2SAndrzej Pietrasiewicz 	if (dev->current_rx_req != NULL)
11498443f2d2SAndrzej Pietrasiewicz 		printer_req_free(dev->out_ep, dev->current_rx_req);
11508443f2d2SAndrzej Pietrasiewicz 
11518443f2d2SAndrzej Pietrasiewicz 	while (!list_empty(&dev->rx_reqs)) {
11528443f2d2SAndrzej Pietrasiewicz 		req = container_of(dev->rx_reqs.next,
11538443f2d2SAndrzej Pietrasiewicz 				struct usb_request, list);
11548443f2d2SAndrzej Pietrasiewicz 		list_del(&req->list);
11558443f2d2SAndrzej Pietrasiewicz 		printer_req_free(dev->out_ep, req);
11568443f2d2SAndrzej Pietrasiewicz 	}
11578443f2d2SAndrzej Pietrasiewicz 
11588443f2d2SAndrzej Pietrasiewicz 	while (!list_empty(&dev->rx_buffers)) {
11598443f2d2SAndrzej Pietrasiewicz 		req = container_of(dev->rx_buffers.next,
11608443f2d2SAndrzej Pietrasiewicz 				struct usb_request, list);
11618443f2d2SAndrzej Pietrasiewicz 		list_del(&req->list);
11628443f2d2SAndrzej Pietrasiewicz 		printer_req_free(dev->out_ep, req);
11638443f2d2SAndrzej Pietrasiewicz 	}
11648443f2d2SAndrzej Pietrasiewicz }
11658443f2d2SAndrzej Pietrasiewicz 
11668443f2d2SAndrzej Pietrasiewicz static struct usb_configuration printer_cfg_driver = {
11678443f2d2SAndrzej Pietrasiewicz 	.label			= "printer",
11688443f2d2SAndrzej Pietrasiewicz 	.unbind			= printer_cfg_unbind,
11698443f2d2SAndrzej Pietrasiewicz 	.bConfigurationValue	= 1,
11708443f2d2SAndrzej Pietrasiewicz 	.bmAttributes		= USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
11718443f2d2SAndrzej Pietrasiewicz };
11728443f2d2SAndrzej Pietrasiewicz 
1173*ae2dd0deSAndrzej Pietrasiewicz static int f_printer_bind_config(struct usb_configuration *c, char *pnp_str,
1174*ae2dd0deSAndrzej Pietrasiewicz 				 unsigned q_len)
11758443f2d2SAndrzej Pietrasiewicz {
11768443f2d2SAndrzej Pietrasiewicz 	struct usb_gadget	*gadget = c->cdev->gadget;
11778443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev;
117844ecccedSAndrzej Pietrasiewicz 	struct device		*pdev;
11798443f2d2SAndrzej Pietrasiewicz 	int			status = -ENOMEM;
11808443f2d2SAndrzej Pietrasiewicz 	size_t			len;
11818443f2d2SAndrzej Pietrasiewicz 	u32			i;
11828443f2d2SAndrzej Pietrasiewicz 	struct usb_request	*req;
11838443f2d2SAndrzej Pietrasiewicz 
11848443f2d2SAndrzej Pietrasiewicz 	dev = &usb_printer_gadget;
11858443f2d2SAndrzej Pietrasiewicz 
11868443f2d2SAndrzej Pietrasiewicz 	dev->function.name = shortname;
11878443f2d2SAndrzej Pietrasiewicz 	dev->function.bind = printer_func_bind;
11888443f2d2SAndrzej Pietrasiewicz 	dev->function.setup = printer_func_setup;
11898443f2d2SAndrzej Pietrasiewicz 	dev->function.unbind = printer_func_unbind;
11908443f2d2SAndrzej Pietrasiewicz 	dev->function.set_alt = printer_func_set_alt;
11918443f2d2SAndrzej Pietrasiewicz 	dev->function.disable = printer_func_disable;
1192c69b8186SAndrzej Pietrasiewicz 	INIT_LIST_HEAD(&dev->tx_reqs);
1193c69b8186SAndrzej Pietrasiewicz 	INIT_LIST_HEAD(&dev->rx_reqs);
1194c69b8186SAndrzej Pietrasiewicz 	INIT_LIST_HEAD(&dev->rx_buffers);
11958443f2d2SAndrzej Pietrasiewicz 
11968443f2d2SAndrzej Pietrasiewicz 	status = usb_add_function(c, &dev->function);
11978443f2d2SAndrzej Pietrasiewicz 	if (status)
11988443f2d2SAndrzej Pietrasiewicz 		return status;
11998443f2d2SAndrzej Pietrasiewicz 
12008443f2d2SAndrzej Pietrasiewicz 	/* Setup the sysfs files for the printer gadget. */
120144ecccedSAndrzej Pietrasiewicz 	pdev = device_create(usb_gadget_class, NULL, g_printer_devno,
12028443f2d2SAndrzej Pietrasiewicz 				  NULL, "g_printer");
120344ecccedSAndrzej Pietrasiewicz 	if (IS_ERR(pdev)) {
12048443f2d2SAndrzej Pietrasiewicz 		ERROR(dev, "Failed to create device: g_printer\n");
120544ecccedSAndrzej Pietrasiewicz 		status = PTR_ERR(pdev);
12068443f2d2SAndrzej Pietrasiewicz 		goto fail;
12078443f2d2SAndrzej Pietrasiewicz 	}
12088443f2d2SAndrzej Pietrasiewicz 
12098443f2d2SAndrzej Pietrasiewicz 	/*
12108443f2d2SAndrzej Pietrasiewicz 	 * Register a character device as an interface to a user mode
12118443f2d2SAndrzej Pietrasiewicz 	 * program that handles the printer specific functionality.
12128443f2d2SAndrzej Pietrasiewicz 	 */
12138443f2d2SAndrzej Pietrasiewicz 	cdev_init(&dev->printer_cdev, &printer_io_operations);
12148443f2d2SAndrzej Pietrasiewicz 	dev->printer_cdev.owner = THIS_MODULE;
12158443f2d2SAndrzej Pietrasiewicz 	status = cdev_add(&dev->printer_cdev, g_printer_devno, 1);
12168443f2d2SAndrzej Pietrasiewicz 	if (status) {
12178443f2d2SAndrzej Pietrasiewicz 		ERROR(dev, "Failed to open char device\n");
12188443f2d2SAndrzej Pietrasiewicz 		goto fail;
12198443f2d2SAndrzej Pietrasiewicz 	}
12208443f2d2SAndrzej Pietrasiewicz 
1221*ae2dd0deSAndrzej Pietrasiewicz 	if (pnp_str)
1222*ae2dd0deSAndrzej Pietrasiewicz 		strlcpy(&pnp_string[2], pnp_str, sizeof(pnp_string) - 2);
12238443f2d2SAndrzej Pietrasiewicz 
12248443f2d2SAndrzej Pietrasiewicz 	len = strlen(pnp_string);
12258443f2d2SAndrzej Pietrasiewicz 	pnp_string[0] = (len >> 8) & 0xFF;
12268443f2d2SAndrzej Pietrasiewicz 	pnp_string[1] = len & 0xFF;
12278443f2d2SAndrzej Pietrasiewicz 
12288443f2d2SAndrzej Pietrasiewicz 	spin_lock_init(&dev->lock);
12298443f2d2SAndrzej Pietrasiewicz 	mutex_init(&dev->lock_printer_io);
12308443f2d2SAndrzej Pietrasiewicz 	INIT_LIST_HEAD(&dev->tx_reqs_active);
12318443f2d2SAndrzej Pietrasiewicz 	INIT_LIST_HEAD(&dev->rx_reqs_active);
12328443f2d2SAndrzej Pietrasiewicz 	init_waitqueue_head(&dev->rx_wait);
12338443f2d2SAndrzej Pietrasiewicz 	init_waitqueue_head(&dev->tx_wait);
12348443f2d2SAndrzej Pietrasiewicz 	init_waitqueue_head(&dev->tx_flush_wait);
12358443f2d2SAndrzej Pietrasiewicz 
12368443f2d2SAndrzej Pietrasiewicz 	dev->interface = -1;
12378443f2d2SAndrzej Pietrasiewicz 	dev->printer_cdev_open = 0;
12388443f2d2SAndrzej Pietrasiewicz 	dev->printer_status = PRINTER_NOT_ERROR;
12398443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_req = NULL;
12408443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_bytes = 0;
12418443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_buf = NULL;
12428443f2d2SAndrzej Pietrasiewicz 
124344b31652SAndrzej Pietrasiewicz 	status = -ENOMEM;
1244*ae2dd0deSAndrzej Pietrasiewicz 	for (i = 0; i < q_len; i++) {
12458443f2d2SAndrzej Pietrasiewicz 		req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL);
124644b31652SAndrzej Pietrasiewicz 		if (!req)
124744b31652SAndrzej Pietrasiewicz 			goto fail;
12488443f2d2SAndrzej Pietrasiewicz 		list_add(&req->list, &dev->tx_reqs);
12498443f2d2SAndrzej Pietrasiewicz 	}
12508443f2d2SAndrzej Pietrasiewicz 
1251*ae2dd0deSAndrzej Pietrasiewicz 	for (i = 0; i < q_len; i++) {
12528443f2d2SAndrzej Pietrasiewicz 		req = printer_req_alloc(dev->out_ep, USB_BUFSIZE, GFP_KERNEL);
125344b31652SAndrzej Pietrasiewicz 		if (!req)
125444b31652SAndrzej Pietrasiewicz 			goto fail;
12558443f2d2SAndrzej Pietrasiewicz 		list_add(&req->list, &dev->rx_reqs);
12568443f2d2SAndrzej Pietrasiewicz 	}
12578443f2d2SAndrzej Pietrasiewicz 
12588443f2d2SAndrzej Pietrasiewicz 	/* finish hookup to lower layer ... */
12598443f2d2SAndrzej Pietrasiewicz 	dev->gadget = gadget;
12608443f2d2SAndrzej Pietrasiewicz 
12618443f2d2SAndrzej Pietrasiewicz 	INFO(dev, "%s, version: " DRIVER_VERSION "\n", driver_desc);
12628443f2d2SAndrzej Pietrasiewicz 	return 0;
12638443f2d2SAndrzej Pietrasiewicz 
12648443f2d2SAndrzej Pietrasiewicz fail:
12658443f2d2SAndrzej Pietrasiewicz 	printer_cfg_unbind(c);
1266f5bda003SAndrzej Pietrasiewicz 	usb_remove_function(c, &dev->function);
12678443f2d2SAndrzej Pietrasiewicz 	return status;
12688443f2d2SAndrzej Pietrasiewicz }
12698443f2d2SAndrzej Pietrasiewicz 
1270*ae2dd0deSAndrzej Pietrasiewicz static int __init printer_do_config(struct usb_configuration *c)
1271*ae2dd0deSAndrzej Pietrasiewicz {
1272*ae2dd0deSAndrzej Pietrasiewicz 	struct usb_gadget	*gadget = c->cdev->gadget;
1273*ae2dd0deSAndrzej Pietrasiewicz 
1274*ae2dd0deSAndrzej Pietrasiewicz 	usb_ep_autoconfig_reset(gadget);
1275*ae2dd0deSAndrzej Pietrasiewicz 
1276*ae2dd0deSAndrzej Pietrasiewicz 	usb_gadget_set_selfpowered(gadget);
1277*ae2dd0deSAndrzej Pietrasiewicz 
1278*ae2dd0deSAndrzej Pietrasiewicz 	if (gadget_is_otg(gadget)) {
1279*ae2dd0deSAndrzej Pietrasiewicz 		otg_descriptor.bmAttributes |= USB_OTG_HNP;
1280*ae2dd0deSAndrzej Pietrasiewicz 		printer_cfg_driver.descriptors = otg_desc;
1281*ae2dd0deSAndrzej Pietrasiewicz 		printer_cfg_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
1282*ae2dd0deSAndrzej Pietrasiewicz 	}
1283*ae2dd0deSAndrzej Pietrasiewicz 
1284*ae2dd0deSAndrzej Pietrasiewicz 	return f_printer_bind_config(c, iPNPstring, QLEN);
1285*ae2dd0deSAndrzej Pietrasiewicz 
1286*ae2dd0deSAndrzej Pietrasiewicz }
1287*ae2dd0deSAndrzej Pietrasiewicz 
12888443f2d2SAndrzej Pietrasiewicz static int __init printer_bind(struct usb_composite_dev *cdev)
12898443f2d2SAndrzej Pietrasiewicz {
12908443f2d2SAndrzej Pietrasiewicz 	int ret;
12918443f2d2SAndrzej Pietrasiewicz 
12928443f2d2SAndrzej Pietrasiewicz 	ret = usb_string_ids_tab(cdev, strings);
12938443f2d2SAndrzej Pietrasiewicz 	if (ret < 0)
12948443f2d2SAndrzej Pietrasiewicz 		return ret;
12958443f2d2SAndrzej Pietrasiewicz 	device_desc.iManufacturer = strings[USB_GADGET_MANUFACTURER_IDX].id;
12968443f2d2SAndrzej Pietrasiewicz 	device_desc.iProduct = strings[USB_GADGET_PRODUCT_IDX].id;
12978443f2d2SAndrzej Pietrasiewicz 	device_desc.iSerialNumber = strings[USB_GADGET_SERIAL_IDX].id;
12988443f2d2SAndrzej Pietrasiewicz 
1299406be2ccSAndrzej Pietrasiewicz 	ret = usb_add_config(cdev, &printer_cfg_driver, printer_do_config);
13008443f2d2SAndrzej Pietrasiewicz 	if (ret)
13018443f2d2SAndrzej Pietrasiewicz 		return ret;
13028443f2d2SAndrzej Pietrasiewicz 	usb_composite_overwrite_options(cdev, &coverwrite);
13038443f2d2SAndrzej Pietrasiewicz 	return ret;
13048443f2d2SAndrzej Pietrasiewicz }
13058443f2d2SAndrzej Pietrasiewicz 
13068443f2d2SAndrzej Pietrasiewicz static __refdata struct usb_composite_driver printer_driver = {
13078443f2d2SAndrzej Pietrasiewicz 	.name           = shortname,
13088443f2d2SAndrzej Pietrasiewicz 	.dev            = &device_desc,
13098443f2d2SAndrzej Pietrasiewicz 	.strings        = dev_strings,
131074df41b4SJorge Ramirez-Ortiz 	.max_speed      = USB_SPEED_SUPER,
13118443f2d2SAndrzej Pietrasiewicz 	.bind		= printer_bind,
13128443f2d2SAndrzej Pietrasiewicz };
13138443f2d2SAndrzej Pietrasiewicz 
13148443f2d2SAndrzej Pietrasiewicz static int __init
13158443f2d2SAndrzej Pietrasiewicz init(void)
13168443f2d2SAndrzej Pietrasiewicz {
13178443f2d2SAndrzej Pietrasiewicz 	int status;
13188443f2d2SAndrzej Pietrasiewicz 
13198443f2d2SAndrzej Pietrasiewicz 	usb_gadget_class = class_create(THIS_MODULE, "usb_printer_gadget");
13208443f2d2SAndrzej Pietrasiewicz 	if (IS_ERR(usb_gadget_class)) {
13218443f2d2SAndrzej Pietrasiewicz 		status = PTR_ERR(usb_gadget_class);
13228443f2d2SAndrzej Pietrasiewicz 		pr_err("unable to create usb_gadget class %d\n", status);
13238443f2d2SAndrzej Pietrasiewicz 		return status;
13248443f2d2SAndrzej Pietrasiewicz 	}
13258443f2d2SAndrzej Pietrasiewicz 
13268443f2d2SAndrzej Pietrasiewicz 	status = alloc_chrdev_region(&g_printer_devno, 0, 1,
13278443f2d2SAndrzej Pietrasiewicz 			"USB printer gadget");
13288443f2d2SAndrzej Pietrasiewicz 	if (status) {
13298443f2d2SAndrzej Pietrasiewicz 		pr_err("alloc_chrdev_region %d\n", status);
13308443f2d2SAndrzej Pietrasiewicz 		class_destroy(usb_gadget_class);
13318443f2d2SAndrzej Pietrasiewicz 		return status;
13328443f2d2SAndrzej Pietrasiewicz 	}
13338443f2d2SAndrzej Pietrasiewicz 
13348443f2d2SAndrzej Pietrasiewicz 	status = usb_composite_probe(&printer_driver);
13358443f2d2SAndrzej Pietrasiewicz 	if (status) {
13368443f2d2SAndrzej Pietrasiewicz 		class_destroy(usb_gadget_class);
13378443f2d2SAndrzej Pietrasiewicz 		unregister_chrdev_region(g_printer_devno, 1);
13388443f2d2SAndrzej Pietrasiewicz 		pr_err("usb_gadget_probe_driver %x\n", status);
13398443f2d2SAndrzej Pietrasiewicz 	}
13408443f2d2SAndrzej Pietrasiewicz 
13418443f2d2SAndrzej Pietrasiewicz 	return status;
13428443f2d2SAndrzej Pietrasiewicz }
13438443f2d2SAndrzej Pietrasiewicz module_init(init);
13448443f2d2SAndrzej Pietrasiewicz 
13458443f2d2SAndrzej Pietrasiewicz static void __exit
13468443f2d2SAndrzej Pietrasiewicz cleanup(void)
13478443f2d2SAndrzej Pietrasiewicz {
13488443f2d2SAndrzej Pietrasiewicz 	mutex_lock(&usb_printer_gadget.lock_printer_io);
13498443f2d2SAndrzej Pietrasiewicz 	usb_composite_unregister(&printer_driver);
13508443f2d2SAndrzej Pietrasiewicz 	unregister_chrdev_region(g_printer_devno, 1);
13518443f2d2SAndrzej Pietrasiewicz 	class_destroy(usb_gadget_class);
13528443f2d2SAndrzej Pietrasiewicz 	mutex_unlock(&usb_printer_gadget.lock_printer_io);
13538443f2d2SAndrzej Pietrasiewicz }
13548443f2d2SAndrzej Pietrasiewicz module_exit(cleanup);
13558443f2d2SAndrzej Pietrasiewicz 
13568443f2d2SAndrzej Pietrasiewicz MODULE_DESCRIPTION(DRIVER_DESC);
13578443f2d2SAndrzej Pietrasiewicz MODULE_AUTHOR("Craig Nadler");
13588443f2d2SAndrzej Pietrasiewicz MODULE_LICENSE("GPL");
1359