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