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