xref: /openbmc/linux/drivers/usb/gadget/legacy/printer.c (revision 085617a1eb865c2987c05652bf82d35f500ac4b4)
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;
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 
279*085617a1SAndrzej Pietrasiewicz #define PNP_STRING_LEN			1024
280*085617a1SAndrzej Pietrasiewicz 
2818443f2d2SAndrzej Pietrasiewicz static char				product_desc [40] = DRIVER_DESC;
2828443f2d2SAndrzej Pietrasiewicz static char				serial_num [40] = "1";
283*085617a1SAndrzej Pietrasiewicz static char				pnp_string[PNP_STRING_LEN] =
2848443f2d2SAndrzej Pietrasiewicz 	"XXMFG:linux;MDL:g_printer;CLS:PRINTER;SN:1;";
2858443f2d2SAndrzej Pietrasiewicz 
2868443f2d2SAndrzej Pietrasiewicz /* static strings, in UTF-8 */
2878443f2d2SAndrzej Pietrasiewicz static struct usb_string		strings [] = {
2888443f2d2SAndrzej Pietrasiewicz 	[USB_GADGET_MANUFACTURER_IDX].s = "",
2898443f2d2SAndrzej Pietrasiewicz 	[USB_GADGET_PRODUCT_IDX].s = product_desc,
2908443f2d2SAndrzej Pietrasiewicz 	[USB_GADGET_SERIAL_IDX].s =	serial_num,
2918443f2d2SAndrzej Pietrasiewicz 	{  }		/* end of list */
2928443f2d2SAndrzej Pietrasiewicz };
2938443f2d2SAndrzej Pietrasiewicz 
2948443f2d2SAndrzej Pietrasiewicz static struct usb_gadget_strings	stringtab_dev = {
2958443f2d2SAndrzej Pietrasiewicz 	.language	= 0x0409,	/* en-us */
2968443f2d2SAndrzej Pietrasiewicz 	.strings	= strings,
2978443f2d2SAndrzej Pietrasiewicz };
2988443f2d2SAndrzej Pietrasiewicz 
2998443f2d2SAndrzej Pietrasiewicz static struct usb_gadget_strings *dev_strings[] = {
3008443f2d2SAndrzej Pietrasiewicz 	&stringtab_dev,
3018443f2d2SAndrzej Pietrasiewicz 	NULL,
3028443f2d2SAndrzej Pietrasiewicz };
3038443f2d2SAndrzej Pietrasiewicz 
3048443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
3058443f2d2SAndrzej Pietrasiewicz 
3068443f2d2SAndrzej Pietrasiewicz static struct usb_request *
3078443f2d2SAndrzej Pietrasiewicz printer_req_alloc(struct usb_ep *ep, unsigned len, gfp_t gfp_flags)
3088443f2d2SAndrzej Pietrasiewicz {
3098443f2d2SAndrzej Pietrasiewicz 	struct usb_request	*req;
3108443f2d2SAndrzej Pietrasiewicz 
3118443f2d2SAndrzej Pietrasiewicz 	req = usb_ep_alloc_request(ep, gfp_flags);
3128443f2d2SAndrzej Pietrasiewicz 
3138443f2d2SAndrzej Pietrasiewicz 	if (req != NULL) {
3148443f2d2SAndrzej Pietrasiewicz 		req->length = len;
3158443f2d2SAndrzej Pietrasiewicz 		req->buf = kmalloc(len, gfp_flags);
3168443f2d2SAndrzej Pietrasiewicz 		if (req->buf == NULL) {
3178443f2d2SAndrzej Pietrasiewicz 			usb_ep_free_request(ep, req);
3188443f2d2SAndrzej Pietrasiewicz 			return NULL;
3198443f2d2SAndrzej Pietrasiewicz 		}
3208443f2d2SAndrzej Pietrasiewicz 	}
3218443f2d2SAndrzej Pietrasiewicz 
3228443f2d2SAndrzej Pietrasiewicz 	return req;
3238443f2d2SAndrzej Pietrasiewicz }
3248443f2d2SAndrzej Pietrasiewicz 
3258443f2d2SAndrzej Pietrasiewicz static void
3268443f2d2SAndrzej Pietrasiewicz printer_req_free(struct usb_ep *ep, struct usb_request *req)
3278443f2d2SAndrzej Pietrasiewicz {
3288443f2d2SAndrzej Pietrasiewicz 	if (ep != NULL && req != NULL) {
3298443f2d2SAndrzej Pietrasiewicz 		kfree(req->buf);
3308443f2d2SAndrzej Pietrasiewicz 		usb_ep_free_request(ep, req);
3318443f2d2SAndrzej Pietrasiewicz 	}
3328443f2d2SAndrzej Pietrasiewicz }
3338443f2d2SAndrzej Pietrasiewicz 
3348443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
3358443f2d2SAndrzej Pietrasiewicz 
3368443f2d2SAndrzej Pietrasiewicz static void rx_complete(struct usb_ep *ep, struct usb_request *req)
3378443f2d2SAndrzej Pietrasiewicz {
3388443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev = ep->driver_data;
3398443f2d2SAndrzej Pietrasiewicz 	int			status = req->status;
3408443f2d2SAndrzej Pietrasiewicz 	unsigned long		flags;
3418443f2d2SAndrzej Pietrasiewicz 
3428443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
3438443f2d2SAndrzej Pietrasiewicz 
3448443f2d2SAndrzej Pietrasiewicz 	list_del_init(&req->list);	/* Remode from Active List */
3458443f2d2SAndrzej Pietrasiewicz 
3468443f2d2SAndrzej Pietrasiewicz 	switch (status) {
3478443f2d2SAndrzej Pietrasiewicz 
3488443f2d2SAndrzej Pietrasiewicz 	/* normal completion */
3498443f2d2SAndrzej Pietrasiewicz 	case 0:
3508443f2d2SAndrzej Pietrasiewicz 		if (req->actual > 0) {
3518443f2d2SAndrzej Pietrasiewicz 			list_add_tail(&req->list, &dev->rx_buffers);
3528443f2d2SAndrzej Pietrasiewicz 			DBG(dev, "G_Printer : rx length %d\n", req->actual);
3538443f2d2SAndrzej Pietrasiewicz 		} else {
3548443f2d2SAndrzej Pietrasiewicz 			list_add(&req->list, &dev->rx_reqs);
3558443f2d2SAndrzej Pietrasiewicz 		}
3568443f2d2SAndrzej Pietrasiewicz 		break;
3578443f2d2SAndrzej Pietrasiewicz 
3588443f2d2SAndrzej Pietrasiewicz 	/* software-driven interface shutdown */
3598443f2d2SAndrzej Pietrasiewicz 	case -ECONNRESET:		/* unlink */
3608443f2d2SAndrzej Pietrasiewicz 	case -ESHUTDOWN:		/* disconnect etc */
3618443f2d2SAndrzej Pietrasiewicz 		VDBG(dev, "rx shutdown, code %d\n", status);
3628443f2d2SAndrzej Pietrasiewicz 		list_add(&req->list, &dev->rx_reqs);
3638443f2d2SAndrzej Pietrasiewicz 		break;
3648443f2d2SAndrzej Pietrasiewicz 
3658443f2d2SAndrzej Pietrasiewicz 	/* for hardware automagic (such as pxa) */
3668443f2d2SAndrzej Pietrasiewicz 	case -ECONNABORTED:		/* endpoint reset */
3678443f2d2SAndrzej Pietrasiewicz 		DBG(dev, "rx %s reset\n", ep->name);
3688443f2d2SAndrzej Pietrasiewicz 		list_add(&req->list, &dev->rx_reqs);
3698443f2d2SAndrzej Pietrasiewicz 		break;
3708443f2d2SAndrzej Pietrasiewicz 
3718443f2d2SAndrzej Pietrasiewicz 	/* data overrun */
3728443f2d2SAndrzej Pietrasiewicz 	case -EOVERFLOW:
3738443f2d2SAndrzej Pietrasiewicz 		/* FALLTHROUGH */
3748443f2d2SAndrzej Pietrasiewicz 
3758443f2d2SAndrzej Pietrasiewicz 	default:
3768443f2d2SAndrzej Pietrasiewicz 		DBG(dev, "rx status %d\n", status);
3778443f2d2SAndrzej Pietrasiewicz 		list_add(&req->list, &dev->rx_reqs);
3788443f2d2SAndrzej Pietrasiewicz 		break;
3798443f2d2SAndrzej Pietrasiewicz 	}
3808443f2d2SAndrzej Pietrasiewicz 
3818443f2d2SAndrzej Pietrasiewicz 	wake_up_interruptible(&dev->rx_wait);
3828443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
3838443f2d2SAndrzej Pietrasiewicz }
3848443f2d2SAndrzej Pietrasiewicz 
3858443f2d2SAndrzej Pietrasiewicz static void tx_complete(struct usb_ep *ep, struct usb_request *req)
3868443f2d2SAndrzej Pietrasiewicz {
3878443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev = ep->driver_data;
3888443f2d2SAndrzej Pietrasiewicz 
3898443f2d2SAndrzej Pietrasiewicz 	switch (req->status) {
3908443f2d2SAndrzej Pietrasiewicz 	default:
3918443f2d2SAndrzej Pietrasiewicz 		VDBG(dev, "tx err %d\n", req->status);
3928443f2d2SAndrzej Pietrasiewicz 		/* FALLTHROUGH */
3938443f2d2SAndrzej Pietrasiewicz 	case -ECONNRESET:		/* unlink */
3948443f2d2SAndrzej Pietrasiewicz 	case -ESHUTDOWN:		/* disconnect etc */
3958443f2d2SAndrzej Pietrasiewicz 		break;
3968443f2d2SAndrzej Pietrasiewicz 	case 0:
3978443f2d2SAndrzej Pietrasiewicz 		break;
3988443f2d2SAndrzej Pietrasiewicz 	}
3998443f2d2SAndrzej Pietrasiewicz 
4008443f2d2SAndrzej Pietrasiewicz 	spin_lock(&dev->lock);
4018443f2d2SAndrzej Pietrasiewicz 	/* Take the request struct off the active list and put it on the
4028443f2d2SAndrzej Pietrasiewicz 	 * free list.
4038443f2d2SAndrzej Pietrasiewicz 	 */
4048443f2d2SAndrzej Pietrasiewicz 	list_del_init(&req->list);
4058443f2d2SAndrzej Pietrasiewicz 	list_add(&req->list, &dev->tx_reqs);
4068443f2d2SAndrzej Pietrasiewicz 	wake_up_interruptible(&dev->tx_wait);
4078443f2d2SAndrzej Pietrasiewicz 	if (likely(list_empty(&dev->tx_reqs_active)))
4088443f2d2SAndrzej Pietrasiewicz 		wake_up_interruptible(&dev->tx_flush_wait);
4098443f2d2SAndrzej Pietrasiewicz 
4108443f2d2SAndrzej Pietrasiewicz 	spin_unlock(&dev->lock);
4118443f2d2SAndrzej Pietrasiewicz }
4128443f2d2SAndrzej Pietrasiewicz 
4138443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
4148443f2d2SAndrzej Pietrasiewicz 
4158443f2d2SAndrzej Pietrasiewicz static int
4168443f2d2SAndrzej Pietrasiewicz printer_open(struct inode *inode, struct file *fd)
4178443f2d2SAndrzej Pietrasiewicz {
4188443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev;
4198443f2d2SAndrzej Pietrasiewicz 	unsigned long		flags;
4208443f2d2SAndrzej Pietrasiewicz 	int			ret = -EBUSY;
4218443f2d2SAndrzej Pietrasiewicz 
4228443f2d2SAndrzej Pietrasiewicz 	mutex_lock(&printer_mutex);
4238443f2d2SAndrzej Pietrasiewicz 	dev = container_of(inode->i_cdev, struct printer_dev, printer_cdev);
4248443f2d2SAndrzej Pietrasiewicz 
4258443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
4268443f2d2SAndrzej Pietrasiewicz 
4278443f2d2SAndrzej Pietrasiewicz 	if (!dev->printer_cdev_open) {
4288443f2d2SAndrzej Pietrasiewicz 		dev->printer_cdev_open = 1;
4298443f2d2SAndrzej Pietrasiewicz 		fd->private_data = dev;
4308443f2d2SAndrzej Pietrasiewicz 		ret = 0;
4318443f2d2SAndrzej Pietrasiewicz 		/* Change the printer status to show that it's on-line. */
4328443f2d2SAndrzej Pietrasiewicz 		dev->printer_status |= PRINTER_SELECTED;
4338443f2d2SAndrzej Pietrasiewicz 	}
4348443f2d2SAndrzej Pietrasiewicz 
4358443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
4368443f2d2SAndrzej Pietrasiewicz 
4378443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "printer_open returned %x\n", ret);
4388443f2d2SAndrzej Pietrasiewicz 	mutex_unlock(&printer_mutex);
4398443f2d2SAndrzej Pietrasiewicz 	return ret;
4408443f2d2SAndrzej Pietrasiewicz }
4418443f2d2SAndrzej Pietrasiewicz 
4428443f2d2SAndrzej Pietrasiewicz static int
4438443f2d2SAndrzej Pietrasiewicz printer_close(struct inode *inode, struct file *fd)
4448443f2d2SAndrzej Pietrasiewicz {
4458443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev = fd->private_data;
4468443f2d2SAndrzej Pietrasiewicz 	unsigned long		flags;
4478443f2d2SAndrzej Pietrasiewicz 
4488443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
4498443f2d2SAndrzej Pietrasiewicz 	dev->printer_cdev_open = 0;
4508443f2d2SAndrzej Pietrasiewicz 	fd->private_data = NULL;
4518443f2d2SAndrzej Pietrasiewicz 	/* Change printer status to show that the printer is off-line. */
4528443f2d2SAndrzej Pietrasiewicz 	dev->printer_status &= ~PRINTER_SELECTED;
4538443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
4548443f2d2SAndrzej Pietrasiewicz 
4558443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "printer_close\n");
4568443f2d2SAndrzej Pietrasiewicz 
4578443f2d2SAndrzej Pietrasiewicz 	return 0;
4588443f2d2SAndrzej Pietrasiewicz }
4598443f2d2SAndrzej Pietrasiewicz 
4608443f2d2SAndrzej Pietrasiewicz /* This function must be called with interrupts turned off. */
4618443f2d2SAndrzej Pietrasiewicz static void
4628443f2d2SAndrzej Pietrasiewicz setup_rx_reqs(struct printer_dev *dev)
4638443f2d2SAndrzej Pietrasiewicz {
4648443f2d2SAndrzej Pietrasiewicz 	struct usb_request              *req;
4658443f2d2SAndrzej Pietrasiewicz 
4668443f2d2SAndrzej Pietrasiewicz 	while (likely(!list_empty(&dev->rx_reqs))) {
4678443f2d2SAndrzej Pietrasiewicz 		int error;
4688443f2d2SAndrzej Pietrasiewicz 
4698443f2d2SAndrzej Pietrasiewicz 		req = container_of(dev->rx_reqs.next,
4708443f2d2SAndrzej Pietrasiewicz 				struct usb_request, list);
4718443f2d2SAndrzej Pietrasiewicz 		list_del_init(&req->list);
4728443f2d2SAndrzej Pietrasiewicz 
4738443f2d2SAndrzej Pietrasiewicz 		/* The USB Host sends us whatever amount of data it wants to
4748443f2d2SAndrzej Pietrasiewicz 		 * so we always set the length field to the full USB_BUFSIZE.
4758443f2d2SAndrzej Pietrasiewicz 		 * If the amount of data is more than the read() caller asked
4768443f2d2SAndrzej Pietrasiewicz 		 * for it will be stored in the request buffer until it is
4778443f2d2SAndrzej Pietrasiewicz 		 * asked for by read().
4788443f2d2SAndrzej Pietrasiewicz 		 */
4798443f2d2SAndrzej Pietrasiewicz 		req->length = USB_BUFSIZE;
4808443f2d2SAndrzej Pietrasiewicz 		req->complete = rx_complete;
4818443f2d2SAndrzej Pietrasiewicz 
4828443f2d2SAndrzej Pietrasiewicz 		/* here, we unlock, and only unlock, to avoid deadlock. */
4838443f2d2SAndrzej Pietrasiewicz 		spin_unlock(&dev->lock);
4848443f2d2SAndrzej Pietrasiewicz 		error = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC);
4858443f2d2SAndrzej Pietrasiewicz 		spin_lock(&dev->lock);
4868443f2d2SAndrzej Pietrasiewicz 		if (error) {
4878443f2d2SAndrzej Pietrasiewicz 			DBG(dev, "rx submit --> %d\n", error);
4888443f2d2SAndrzej Pietrasiewicz 			list_add(&req->list, &dev->rx_reqs);
4898443f2d2SAndrzej Pietrasiewicz 			break;
4908443f2d2SAndrzej Pietrasiewicz 		}
4918443f2d2SAndrzej Pietrasiewicz 		/* if the req is empty, then add it into dev->rx_reqs_active. */
4928443f2d2SAndrzej Pietrasiewicz 		else if (list_empty(&req->list)) {
4938443f2d2SAndrzej Pietrasiewicz 			list_add(&req->list, &dev->rx_reqs_active);
4948443f2d2SAndrzej Pietrasiewicz 		}
4958443f2d2SAndrzej Pietrasiewicz 	}
4968443f2d2SAndrzej Pietrasiewicz }
4978443f2d2SAndrzej Pietrasiewicz 
4988443f2d2SAndrzej Pietrasiewicz static ssize_t
4998443f2d2SAndrzej Pietrasiewicz printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
5008443f2d2SAndrzej Pietrasiewicz {
5018443f2d2SAndrzej Pietrasiewicz 	struct printer_dev		*dev = fd->private_data;
5028443f2d2SAndrzej Pietrasiewicz 	unsigned long			flags;
5038443f2d2SAndrzej Pietrasiewicz 	size_t				size;
5048443f2d2SAndrzej Pietrasiewicz 	size_t				bytes_copied;
5058443f2d2SAndrzej Pietrasiewicz 	struct usb_request		*req;
5068443f2d2SAndrzej Pietrasiewicz 	/* This is a pointer to the current USB rx request. */
5078443f2d2SAndrzej Pietrasiewicz 	struct usb_request		*current_rx_req;
5088443f2d2SAndrzej Pietrasiewicz 	/* This is the number of bytes in the current rx buffer. */
5098443f2d2SAndrzej Pietrasiewicz 	size_t				current_rx_bytes;
5108443f2d2SAndrzej Pietrasiewicz 	/* This is a pointer to the current rx buffer. */
5118443f2d2SAndrzej Pietrasiewicz 	u8				*current_rx_buf;
5128443f2d2SAndrzej Pietrasiewicz 
5138443f2d2SAndrzej Pietrasiewicz 	if (len == 0)
5148443f2d2SAndrzej Pietrasiewicz 		return -EINVAL;
5158443f2d2SAndrzej Pietrasiewicz 
5168443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "printer_read trying to read %d bytes\n", (int)len);
5178443f2d2SAndrzej Pietrasiewicz 
5188443f2d2SAndrzej Pietrasiewicz 	mutex_lock(&dev->lock_printer_io);
5198443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
5208443f2d2SAndrzej Pietrasiewicz 
5218443f2d2SAndrzej Pietrasiewicz 	/* We will use this flag later to check if a printer reset happened
5228443f2d2SAndrzej Pietrasiewicz 	 * after we turn interrupts back on.
5238443f2d2SAndrzej Pietrasiewicz 	 */
5248443f2d2SAndrzej Pietrasiewicz 	dev->reset_printer = 0;
5258443f2d2SAndrzej Pietrasiewicz 
5268443f2d2SAndrzej Pietrasiewicz 	setup_rx_reqs(dev);
5278443f2d2SAndrzej Pietrasiewicz 
5288443f2d2SAndrzej Pietrasiewicz 	bytes_copied = 0;
5298443f2d2SAndrzej Pietrasiewicz 	current_rx_req = dev->current_rx_req;
5308443f2d2SAndrzej Pietrasiewicz 	current_rx_bytes = dev->current_rx_bytes;
5318443f2d2SAndrzej Pietrasiewicz 	current_rx_buf = dev->current_rx_buf;
5328443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_req = NULL;
5338443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_bytes = 0;
5348443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_buf = NULL;
5358443f2d2SAndrzej Pietrasiewicz 
5368443f2d2SAndrzej Pietrasiewicz 	/* Check if there is any data in the read buffers. Please note that
5378443f2d2SAndrzej Pietrasiewicz 	 * current_rx_bytes is the number of bytes in the current rx buffer.
5388443f2d2SAndrzej Pietrasiewicz 	 * If it is zero then check if there are any other rx_buffers that
5398443f2d2SAndrzej Pietrasiewicz 	 * are on the completed list. We are only out of data if all rx
5408443f2d2SAndrzej Pietrasiewicz 	 * buffers are empty.
5418443f2d2SAndrzej Pietrasiewicz 	 */
5428443f2d2SAndrzej Pietrasiewicz 	if ((current_rx_bytes == 0) &&
5438443f2d2SAndrzej Pietrasiewicz 			(likely(list_empty(&dev->rx_buffers)))) {
5448443f2d2SAndrzej Pietrasiewicz 		/* Turn interrupts back on before sleeping. */
5458443f2d2SAndrzej Pietrasiewicz 		spin_unlock_irqrestore(&dev->lock, flags);
5468443f2d2SAndrzej Pietrasiewicz 
5478443f2d2SAndrzej Pietrasiewicz 		/*
5488443f2d2SAndrzej Pietrasiewicz 		 * If no data is available check if this is a NON-Blocking
5498443f2d2SAndrzej Pietrasiewicz 		 * call or not.
5508443f2d2SAndrzej Pietrasiewicz 		 */
5518443f2d2SAndrzej Pietrasiewicz 		if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) {
5528443f2d2SAndrzej Pietrasiewicz 			mutex_unlock(&dev->lock_printer_io);
5538443f2d2SAndrzej Pietrasiewicz 			return -EAGAIN;
5548443f2d2SAndrzej Pietrasiewicz 		}
5558443f2d2SAndrzej Pietrasiewicz 
5568443f2d2SAndrzej Pietrasiewicz 		/* Sleep until data is available */
5578443f2d2SAndrzej Pietrasiewicz 		wait_event_interruptible(dev->rx_wait,
5588443f2d2SAndrzej Pietrasiewicz 				(likely(!list_empty(&dev->rx_buffers))));
5598443f2d2SAndrzej Pietrasiewicz 		spin_lock_irqsave(&dev->lock, flags);
5608443f2d2SAndrzej Pietrasiewicz 	}
5618443f2d2SAndrzej Pietrasiewicz 
5628443f2d2SAndrzej Pietrasiewicz 	/* We have data to return then copy it to the caller's buffer.*/
5638443f2d2SAndrzej Pietrasiewicz 	while ((current_rx_bytes || likely(!list_empty(&dev->rx_buffers)))
5648443f2d2SAndrzej Pietrasiewicz 			&& len) {
5658443f2d2SAndrzej Pietrasiewicz 		if (current_rx_bytes == 0) {
5668443f2d2SAndrzej Pietrasiewicz 			req = container_of(dev->rx_buffers.next,
5678443f2d2SAndrzej Pietrasiewicz 					struct usb_request, list);
5688443f2d2SAndrzej Pietrasiewicz 			list_del_init(&req->list);
5698443f2d2SAndrzej Pietrasiewicz 
5708443f2d2SAndrzej Pietrasiewicz 			if (req->actual && req->buf) {
5718443f2d2SAndrzej Pietrasiewicz 				current_rx_req = req;
5728443f2d2SAndrzej Pietrasiewicz 				current_rx_bytes = req->actual;
5738443f2d2SAndrzej Pietrasiewicz 				current_rx_buf = req->buf;
5748443f2d2SAndrzej Pietrasiewicz 			} else {
5758443f2d2SAndrzej Pietrasiewicz 				list_add(&req->list, &dev->rx_reqs);
5768443f2d2SAndrzej Pietrasiewicz 				continue;
5778443f2d2SAndrzej Pietrasiewicz 			}
5788443f2d2SAndrzej Pietrasiewicz 		}
5798443f2d2SAndrzej Pietrasiewicz 
5808443f2d2SAndrzej Pietrasiewicz 		/* Don't leave irqs off while doing memory copies */
5818443f2d2SAndrzej Pietrasiewicz 		spin_unlock_irqrestore(&dev->lock, flags);
5828443f2d2SAndrzej Pietrasiewicz 
5838443f2d2SAndrzej Pietrasiewicz 		if (len > current_rx_bytes)
5848443f2d2SAndrzej Pietrasiewicz 			size = current_rx_bytes;
5858443f2d2SAndrzej Pietrasiewicz 		else
5868443f2d2SAndrzej Pietrasiewicz 			size = len;
5878443f2d2SAndrzej Pietrasiewicz 
5888443f2d2SAndrzej Pietrasiewicz 		size -= copy_to_user(buf, current_rx_buf, size);
5898443f2d2SAndrzej Pietrasiewicz 		bytes_copied += size;
5908443f2d2SAndrzej Pietrasiewicz 		len -= size;
5918443f2d2SAndrzej Pietrasiewicz 		buf += size;
5928443f2d2SAndrzej Pietrasiewicz 
5938443f2d2SAndrzej Pietrasiewicz 		spin_lock_irqsave(&dev->lock, flags);
5948443f2d2SAndrzej Pietrasiewicz 
5958443f2d2SAndrzej Pietrasiewicz 		/* We've disconnected or reset so return. */
5968443f2d2SAndrzej Pietrasiewicz 		if (dev->reset_printer) {
5978443f2d2SAndrzej Pietrasiewicz 			list_add(&current_rx_req->list, &dev->rx_reqs);
5988443f2d2SAndrzej Pietrasiewicz 			spin_unlock_irqrestore(&dev->lock, flags);
5998443f2d2SAndrzej Pietrasiewicz 			mutex_unlock(&dev->lock_printer_io);
6008443f2d2SAndrzej Pietrasiewicz 			return -EAGAIN;
6018443f2d2SAndrzej Pietrasiewicz 		}
6028443f2d2SAndrzej Pietrasiewicz 
6038443f2d2SAndrzej Pietrasiewicz 		/* If we not returning all the data left in this RX request
6048443f2d2SAndrzej Pietrasiewicz 		 * buffer then adjust the amount of data left in the buffer.
6058443f2d2SAndrzej Pietrasiewicz 		 * Othewise if we are done with this RX request buffer then
6068443f2d2SAndrzej Pietrasiewicz 		 * requeue it to get any incoming data from the USB host.
6078443f2d2SAndrzej Pietrasiewicz 		 */
6088443f2d2SAndrzej Pietrasiewicz 		if (size < current_rx_bytes) {
6098443f2d2SAndrzej Pietrasiewicz 			current_rx_bytes -= size;
6108443f2d2SAndrzej Pietrasiewicz 			current_rx_buf += size;
6118443f2d2SAndrzej Pietrasiewicz 		} else {
6128443f2d2SAndrzej Pietrasiewicz 			list_add(&current_rx_req->list, &dev->rx_reqs);
6138443f2d2SAndrzej Pietrasiewicz 			current_rx_bytes = 0;
6148443f2d2SAndrzej Pietrasiewicz 			current_rx_buf = NULL;
6158443f2d2SAndrzej Pietrasiewicz 			current_rx_req = NULL;
6168443f2d2SAndrzej Pietrasiewicz 		}
6178443f2d2SAndrzej Pietrasiewicz 	}
6188443f2d2SAndrzej Pietrasiewicz 
6198443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_req = current_rx_req;
6208443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_bytes = current_rx_bytes;
6218443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_buf = current_rx_buf;
6228443f2d2SAndrzej Pietrasiewicz 
6238443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
6248443f2d2SAndrzej Pietrasiewicz 	mutex_unlock(&dev->lock_printer_io);
6258443f2d2SAndrzej Pietrasiewicz 
6268443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "printer_read returned %d bytes\n", (int)bytes_copied);
6278443f2d2SAndrzej Pietrasiewicz 
6288443f2d2SAndrzej Pietrasiewicz 	if (bytes_copied)
6298443f2d2SAndrzej Pietrasiewicz 		return bytes_copied;
6308443f2d2SAndrzej Pietrasiewicz 	else
6318443f2d2SAndrzej Pietrasiewicz 		return -EAGAIN;
6328443f2d2SAndrzej Pietrasiewicz }
6338443f2d2SAndrzej Pietrasiewicz 
6348443f2d2SAndrzej Pietrasiewicz static ssize_t
6358443f2d2SAndrzej Pietrasiewicz printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
6368443f2d2SAndrzej Pietrasiewicz {
6378443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev = fd->private_data;
6388443f2d2SAndrzej Pietrasiewicz 	unsigned long		flags;
6398443f2d2SAndrzej Pietrasiewicz 	size_t			size;	/* Amount of data in a TX request. */
6408443f2d2SAndrzej Pietrasiewicz 	size_t			bytes_copied = 0;
6418443f2d2SAndrzej Pietrasiewicz 	struct usb_request	*req;
6428443f2d2SAndrzej Pietrasiewicz 
6438443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "printer_write trying to send %d bytes\n", (int)len);
6448443f2d2SAndrzej Pietrasiewicz 
6458443f2d2SAndrzej Pietrasiewicz 	if (len == 0)
6468443f2d2SAndrzej Pietrasiewicz 		return -EINVAL;
6478443f2d2SAndrzej Pietrasiewicz 
6488443f2d2SAndrzej Pietrasiewicz 	mutex_lock(&dev->lock_printer_io);
6498443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
6508443f2d2SAndrzej Pietrasiewicz 
6518443f2d2SAndrzej Pietrasiewicz 	/* Check if a printer reset happens while we have interrupts on */
6528443f2d2SAndrzej Pietrasiewicz 	dev->reset_printer = 0;
6538443f2d2SAndrzej Pietrasiewicz 
6548443f2d2SAndrzej Pietrasiewicz 	/* Check if there is any available write buffers */
6558443f2d2SAndrzej Pietrasiewicz 	if (likely(list_empty(&dev->tx_reqs))) {
6568443f2d2SAndrzej Pietrasiewicz 		/* Turn interrupts back on before sleeping. */
6578443f2d2SAndrzej Pietrasiewicz 		spin_unlock_irqrestore(&dev->lock, flags);
6588443f2d2SAndrzej Pietrasiewicz 
6598443f2d2SAndrzej Pietrasiewicz 		/*
6608443f2d2SAndrzej Pietrasiewicz 		 * If write buffers are available check if this is
6618443f2d2SAndrzej Pietrasiewicz 		 * a NON-Blocking call or not.
6628443f2d2SAndrzej Pietrasiewicz 		 */
6638443f2d2SAndrzej Pietrasiewicz 		if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) {
6648443f2d2SAndrzej Pietrasiewicz 			mutex_unlock(&dev->lock_printer_io);
6658443f2d2SAndrzej Pietrasiewicz 			return -EAGAIN;
6668443f2d2SAndrzej Pietrasiewicz 		}
6678443f2d2SAndrzej Pietrasiewicz 
6688443f2d2SAndrzej Pietrasiewicz 		/* Sleep until a write buffer is available */
6698443f2d2SAndrzej Pietrasiewicz 		wait_event_interruptible(dev->tx_wait,
6708443f2d2SAndrzej Pietrasiewicz 				(likely(!list_empty(&dev->tx_reqs))));
6718443f2d2SAndrzej Pietrasiewicz 		spin_lock_irqsave(&dev->lock, flags);
6728443f2d2SAndrzej Pietrasiewicz 	}
6738443f2d2SAndrzej Pietrasiewicz 
6748443f2d2SAndrzej Pietrasiewicz 	while (likely(!list_empty(&dev->tx_reqs)) && len) {
6758443f2d2SAndrzej Pietrasiewicz 
6768443f2d2SAndrzej Pietrasiewicz 		if (len > USB_BUFSIZE)
6778443f2d2SAndrzej Pietrasiewicz 			size = USB_BUFSIZE;
6788443f2d2SAndrzej Pietrasiewicz 		else
6798443f2d2SAndrzej Pietrasiewicz 			size = len;
6808443f2d2SAndrzej Pietrasiewicz 
6818443f2d2SAndrzej Pietrasiewicz 		req = container_of(dev->tx_reqs.next, struct usb_request,
6828443f2d2SAndrzej Pietrasiewicz 				list);
6838443f2d2SAndrzej Pietrasiewicz 		list_del_init(&req->list);
6848443f2d2SAndrzej Pietrasiewicz 
6858443f2d2SAndrzej Pietrasiewicz 		req->complete = tx_complete;
6868443f2d2SAndrzej Pietrasiewicz 		req->length = size;
6878443f2d2SAndrzej Pietrasiewicz 
6888443f2d2SAndrzej Pietrasiewicz 		/* Check if we need to send a zero length packet. */
6898443f2d2SAndrzej Pietrasiewicz 		if (len > size)
6908443f2d2SAndrzej Pietrasiewicz 			/* They will be more TX requests so no yet. */
6918443f2d2SAndrzej Pietrasiewicz 			req->zero = 0;
6928443f2d2SAndrzej Pietrasiewicz 		else
6938443f2d2SAndrzej Pietrasiewicz 			/* If the data amount is not a multple of the
6948443f2d2SAndrzej Pietrasiewicz 			 * maxpacket size then send a zero length packet.
6958443f2d2SAndrzej Pietrasiewicz 			 */
6968443f2d2SAndrzej Pietrasiewicz 			req->zero = ((len % dev->in_ep->maxpacket) == 0);
6978443f2d2SAndrzej Pietrasiewicz 
6988443f2d2SAndrzej Pietrasiewicz 		/* Don't leave irqs off while doing memory copies */
6998443f2d2SAndrzej Pietrasiewicz 		spin_unlock_irqrestore(&dev->lock, flags);
7008443f2d2SAndrzej Pietrasiewicz 
7018443f2d2SAndrzej Pietrasiewicz 		if (copy_from_user(req->buf, buf, size)) {
7028443f2d2SAndrzej Pietrasiewicz 			list_add(&req->list, &dev->tx_reqs);
7038443f2d2SAndrzej Pietrasiewicz 			mutex_unlock(&dev->lock_printer_io);
7048443f2d2SAndrzej Pietrasiewicz 			return bytes_copied;
7058443f2d2SAndrzej Pietrasiewicz 		}
7068443f2d2SAndrzej Pietrasiewicz 
7078443f2d2SAndrzej Pietrasiewicz 		bytes_copied += size;
7088443f2d2SAndrzej Pietrasiewicz 		len -= size;
7098443f2d2SAndrzej Pietrasiewicz 		buf += size;
7108443f2d2SAndrzej Pietrasiewicz 
7118443f2d2SAndrzej Pietrasiewicz 		spin_lock_irqsave(&dev->lock, flags);
7128443f2d2SAndrzej Pietrasiewicz 
7138443f2d2SAndrzej Pietrasiewicz 		/* We've disconnected or reset so free the req and buffer */
7148443f2d2SAndrzej Pietrasiewicz 		if (dev->reset_printer) {
7158443f2d2SAndrzej Pietrasiewicz 			list_add(&req->list, &dev->tx_reqs);
7168443f2d2SAndrzej Pietrasiewicz 			spin_unlock_irqrestore(&dev->lock, flags);
7178443f2d2SAndrzej Pietrasiewicz 			mutex_unlock(&dev->lock_printer_io);
7188443f2d2SAndrzej Pietrasiewicz 			return -EAGAIN;
7198443f2d2SAndrzej Pietrasiewicz 		}
7208443f2d2SAndrzej Pietrasiewicz 
7218443f2d2SAndrzej Pietrasiewicz 		if (usb_ep_queue(dev->in_ep, req, GFP_ATOMIC)) {
7228443f2d2SAndrzej Pietrasiewicz 			list_add(&req->list, &dev->tx_reqs);
7238443f2d2SAndrzej Pietrasiewicz 			spin_unlock_irqrestore(&dev->lock, flags);
7248443f2d2SAndrzej Pietrasiewicz 			mutex_unlock(&dev->lock_printer_io);
7258443f2d2SAndrzej Pietrasiewicz 			return -EAGAIN;
7268443f2d2SAndrzej Pietrasiewicz 		}
7278443f2d2SAndrzej Pietrasiewicz 
7288443f2d2SAndrzej Pietrasiewicz 		list_add(&req->list, &dev->tx_reqs_active);
7298443f2d2SAndrzej Pietrasiewicz 
7308443f2d2SAndrzej Pietrasiewicz 	}
7318443f2d2SAndrzej Pietrasiewicz 
7328443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
7338443f2d2SAndrzej Pietrasiewicz 	mutex_unlock(&dev->lock_printer_io);
7348443f2d2SAndrzej Pietrasiewicz 
7358443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "printer_write sent %d bytes\n", (int)bytes_copied);
7368443f2d2SAndrzej Pietrasiewicz 
7378443f2d2SAndrzej Pietrasiewicz 	if (bytes_copied) {
7388443f2d2SAndrzej Pietrasiewicz 		return bytes_copied;
7398443f2d2SAndrzej Pietrasiewicz 	} else {
7408443f2d2SAndrzej Pietrasiewicz 		return -EAGAIN;
7418443f2d2SAndrzej Pietrasiewicz 	}
7428443f2d2SAndrzej Pietrasiewicz }
7438443f2d2SAndrzej Pietrasiewicz 
7448443f2d2SAndrzej Pietrasiewicz static int
7458443f2d2SAndrzej Pietrasiewicz printer_fsync(struct file *fd, loff_t start, loff_t end, int datasync)
7468443f2d2SAndrzej Pietrasiewicz {
7478443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev = fd->private_data;
7488443f2d2SAndrzej Pietrasiewicz 	struct inode *inode = file_inode(fd);
7498443f2d2SAndrzej Pietrasiewicz 	unsigned long		flags;
7508443f2d2SAndrzej Pietrasiewicz 	int			tx_list_empty;
7518443f2d2SAndrzej Pietrasiewicz 
7528443f2d2SAndrzej Pietrasiewicz 	mutex_lock(&inode->i_mutex);
7538443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
7548443f2d2SAndrzej Pietrasiewicz 	tx_list_empty = (likely(list_empty(&dev->tx_reqs)));
7558443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
7568443f2d2SAndrzej Pietrasiewicz 
7578443f2d2SAndrzej Pietrasiewicz 	if (!tx_list_empty) {
7588443f2d2SAndrzej Pietrasiewicz 		/* Sleep until all data has been sent */
7598443f2d2SAndrzej Pietrasiewicz 		wait_event_interruptible(dev->tx_flush_wait,
7608443f2d2SAndrzej Pietrasiewicz 				(likely(list_empty(&dev->tx_reqs_active))));
7618443f2d2SAndrzej Pietrasiewicz 	}
7628443f2d2SAndrzej Pietrasiewicz 	mutex_unlock(&inode->i_mutex);
7638443f2d2SAndrzej Pietrasiewicz 
7648443f2d2SAndrzej Pietrasiewicz 	return 0;
7658443f2d2SAndrzej Pietrasiewicz }
7668443f2d2SAndrzej Pietrasiewicz 
7678443f2d2SAndrzej Pietrasiewicz static unsigned int
7688443f2d2SAndrzej Pietrasiewicz printer_poll(struct file *fd, poll_table *wait)
7698443f2d2SAndrzej Pietrasiewicz {
7708443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev = fd->private_data;
7718443f2d2SAndrzej Pietrasiewicz 	unsigned long		flags;
7728443f2d2SAndrzej Pietrasiewicz 	int			status = 0;
7738443f2d2SAndrzej Pietrasiewicz 
7748443f2d2SAndrzej Pietrasiewicz 	mutex_lock(&dev->lock_printer_io);
7758443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
7768443f2d2SAndrzej Pietrasiewicz 	setup_rx_reqs(dev);
7778443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
7788443f2d2SAndrzej Pietrasiewicz 	mutex_unlock(&dev->lock_printer_io);
7798443f2d2SAndrzej Pietrasiewicz 
7808443f2d2SAndrzej Pietrasiewicz 	poll_wait(fd, &dev->rx_wait, wait);
7818443f2d2SAndrzej Pietrasiewicz 	poll_wait(fd, &dev->tx_wait, wait);
7828443f2d2SAndrzej Pietrasiewicz 
7838443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
7848443f2d2SAndrzej Pietrasiewicz 	if (likely(!list_empty(&dev->tx_reqs)))
7858443f2d2SAndrzej Pietrasiewicz 		status |= POLLOUT | POLLWRNORM;
7868443f2d2SAndrzej Pietrasiewicz 
7878443f2d2SAndrzej Pietrasiewicz 	if (likely(dev->current_rx_bytes) ||
7888443f2d2SAndrzej Pietrasiewicz 			likely(!list_empty(&dev->rx_buffers)))
7898443f2d2SAndrzej Pietrasiewicz 		status |= POLLIN | POLLRDNORM;
7908443f2d2SAndrzej Pietrasiewicz 
7918443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
7928443f2d2SAndrzej Pietrasiewicz 
7938443f2d2SAndrzej Pietrasiewicz 	return status;
7948443f2d2SAndrzej Pietrasiewicz }
7958443f2d2SAndrzej Pietrasiewicz 
7968443f2d2SAndrzej Pietrasiewicz static long
7978443f2d2SAndrzej Pietrasiewicz printer_ioctl(struct file *fd, unsigned int code, unsigned long arg)
7988443f2d2SAndrzej Pietrasiewicz {
7998443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev = fd->private_data;
8008443f2d2SAndrzej Pietrasiewicz 	unsigned long		flags;
8018443f2d2SAndrzej Pietrasiewicz 	int			status = 0;
8028443f2d2SAndrzej Pietrasiewicz 
8038443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "printer_ioctl: cmd=0x%4.4x, arg=%lu\n", code, arg);
8048443f2d2SAndrzej Pietrasiewicz 
8058443f2d2SAndrzej Pietrasiewicz 	/* handle ioctls */
8068443f2d2SAndrzej Pietrasiewicz 
8078443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
8088443f2d2SAndrzej Pietrasiewicz 
8098443f2d2SAndrzej Pietrasiewicz 	switch (code) {
8108443f2d2SAndrzej Pietrasiewicz 	case GADGET_GET_PRINTER_STATUS:
8118443f2d2SAndrzej Pietrasiewicz 		status = (int)dev->printer_status;
8128443f2d2SAndrzej Pietrasiewicz 		break;
8138443f2d2SAndrzej Pietrasiewicz 	case GADGET_SET_PRINTER_STATUS:
8148443f2d2SAndrzej Pietrasiewicz 		dev->printer_status = (u8)arg;
8158443f2d2SAndrzej Pietrasiewicz 		break;
8168443f2d2SAndrzej Pietrasiewicz 	default:
8178443f2d2SAndrzej Pietrasiewicz 		/* could not handle ioctl */
8188443f2d2SAndrzej Pietrasiewicz 		DBG(dev, "printer_ioctl: ERROR cmd=0x%4.4xis not supported\n",
8198443f2d2SAndrzej Pietrasiewicz 				code);
8208443f2d2SAndrzej Pietrasiewicz 		status = -ENOTTY;
8218443f2d2SAndrzej Pietrasiewicz 	}
8228443f2d2SAndrzej Pietrasiewicz 
8238443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
8248443f2d2SAndrzej Pietrasiewicz 
8258443f2d2SAndrzej Pietrasiewicz 	return status;
8268443f2d2SAndrzej Pietrasiewicz }
8278443f2d2SAndrzej Pietrasiewicz 
8288443f2d2SAndrzej Pietrasiewicz /* used after endpoint configuration */
8298443f2d2SAndrzej Pietrasiewicz static const struct file_operations printer_io_operations = {
8308443f2d2SAndrzej Pietrasiewicz 	.owner =	THIS_MODULE,
8318443f2d2SAndrzej Pietrasiewicz 	.open =		printer_open,
8328443f2d2SAndrzej Pietrasiewicz 	.read =		printer_read,
8338443f2d2SAndrzej Pietrasiewicz 	.write =	printer_write,
8348443f2d2SAndrzej Pietrasiewicz 	.fsync =	printer_fsync,
8358443f2d2SAndrzej Pietrasiewicz 	.poll =		printer_poll,
8368443f2d2SAndrzej Pietrasiewicz 	.unlocked_ioctl = printer_ioctl,
8378443f2d2SAndrzej Pietrasiewicz 	.release =	printer_close,
8388443f2d2SAndrzej Pietrasiewicz 	.llseek =	noop_llseek,
8398443f2d2SAndrzej Pietrasiewicz };
8408443f2d2SAndrzej Pietrasiewicz 
8418443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
8428443f2d2SAndrzej Pietrasiewicz 
8438443f2d2SAndrzej Pietrasiewicz static int
8448443f2d2SAndrzej Pietrasiewicz set_printer_interface(struct printer_dev *dev)
8458443f2d2SAndrzej Pietrasiewicz {
8468443f2d2SAndrzej Pietrasiewicz 	int			result = 0;
8478443f2d2SAndrzej Pietrasiewicz 
84874df41b4SJorge Ramirez-Ortiz 	dev->in_ep->desc = ep_desc(dev->gadget, &fs_ep_in_desc, &hs_ep_in_desc,
84974df41b4SJorge Ramirez-Ortiz 				&ss_ep_in_desc);
8508443f2d2SAndrzej Pietrasiewicz 	dev->in_ep->driver_data = dev;
8518443f2d2SAndrzej Pietrasiewicz 
85274df41b4SJorge Ramirez-Ortiz 	dev->out_ep->desc = ep_desc(dev->gadget, &fs_ep_out_desc,
85374df41b4SJorge Ramirez-Ortiz 				    &hs_ep_out_desc, &ss_ep_out_desc);
8548443f2d2SAndrzej Pietrasiewicz 	dev->out_ep->driver_data = dev;
8558443f2d2SAndrzej Pietrasiewicz 
8568443f2d2SAndrzej Pietrasiewicz 	result = usb_ep_enable(dev->in_ep);
8578443f2d2SAndrzej Pietrasiewicz 	if (result != 0) {
8588443f2d2SAndrzej Pietrasiewicz 		DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result);
8598443f2d2SAndrzej Pietrasiewicz 		goto done;
8608443f2d2SAndrzej Pietrasiewicz 	}
8618443f2d2SAndrzej Pietrasiewicz 
8628443f2d2SAndrzej Pietrasiewicz 	result = usb_ep_enable(dev->out_ep);
8638443f2d2SAndrzej Pietrasiewicz 	if (result != 0) {
8648443f2d2SAndrzej Pietrasiewicz 		DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result);
8658443f2d2SAndrzej Pietrasiewicz 		goto done;
8668443f2d2SAndrzej Pietrasiewicz 	}
8678443f2d2SAndrzej Pietrasiewicz 
8688443f2d2SAndrzej Pietrasiewicz done:
8698443f2d2SAndrzej Pietrasiewicz 	/* on error, disable any endpoints  */
8708443f2d2SAndrzej Pietrasiewicz 	if (result != 0) {
8718443f2d2SAndrzej Pietrasiewicz 		(void) usb_ep_disable(dev->in_ep);
8728443f2d2SAndrzej Pietrasiewicz 		(void) usb_ep_disable(dev->out_ep);
8738443f2d2SAndrzej Pietrasiewicz 		dev->in_ep->desc = NULL;
8748443f2d2SAndrzej Pietrasiewicz 		dev->out_ep->desc = NULL;
8758443f2d2SAndrzej Pietrasiewicz 	}
8768443f2d2SAndrzej Pietrasiewicz 
8778443f2d2SAndrzej Pietrasiewicz 	/* caller is responsible for cleanup on error */
8788443f2d2SAndrzej Pietrasiewicz 	return result;
8798443f2d2SAndrzej Pietrasiewicz }
8808443f2d2SAndrzej Pietrasiewicz 
8818443f2d2SAndrzej Pietrasiewicz static void printer_reset_interface(struct printer_dev *dev)
8828443f2d2SAndrzej Pietrasiewicz {
8838443f2d2SAndrzej Pietrasiewicz 	if (dev->interface < 0)
8848443f2d2SAndrzej Pietrasiewicz 		return;
8858443f2d2SAndrzej Pietrasiewicz 
8868443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "%s\n", __func__);
8878443f2d2SAndrzej Pietrasiewicz 
8888443f2d2SAndrzej Pietrasiewicz 	if (dev->in_ep->desc)
8898443f2d2SAndrzej Pietrasiewicz 		usb_ep_disable(dev->in_ep);
8908443f2d2SAndrzej Pietrasiewicz 
8918443f2d2SAndrzej Pietrasiewicz 	if (dev->out_ep->desc)
8928443f2d2SAndrzej Pietrasiewicz 		usb_ep_disable(dev->out_ep);
8938443f2d2SAndrzej Pietrasiewicz 
8948443f2d2SAndrzej Pietrasiewicz 	dev->in_ep->desc = NULL;
8958443f2d2SAndrzej Pietrasiewicz 	dev->out_ep->desc = NULL;
8968443f2d2SAndrzej Pietrasiewicz 	dev->interface = -1;
8978443f2d2SAndrzej Pietrasiewicz }
8988443f2d2SAndrzej Pietrasiewicz 
8998443f2d2SAndrzej Pietrasiewicz /* Change our operational Interface. */
9008443f2d2SAndrzej Pietrasiewicz static int set_interface(struct printer_dev *dev, unsigned number)
9018443f2d2SAndrzej Pietrasiewicz {
9028443f2d2SAndrzej Pietrasiewicz 	int			result = 0;
9038443f2d2SAndrzej Pietrasiewicz 
9048443f2d2SAndrzej Pietrasiewicz 	/* Free the current interface */
9058443f2d2SAndrzej Pietrasiewicz 	printer_reset_interface(dev);
9068443f2d2SAndrzej Pietrasiewicz 
9078443f2d2SAndrzej Pietrasiewicz 	result = set_printer_interface(dev);
9088443f2d2SAndrzej Pietrasiewicz 	if (result)
9098443f2d2SAndrzej Pietrasiewicz 		printer_reset_interface(dev);
9108443f2d2SAndrzej Pietrasiewicz 	else
9118443f2d2SAndrzej Pietrasiewicz 		dev->interface = number;
9128443f2d2SAndrzej Pietrasiewicz 
9138443f2d2SAndrzej Pietrasiewicz 	if (!result)
9148443f2d2SAndrzej Pietrasiewicz 		INFO(dev, "Using interface %x\n", number);
9158443f2d2SAndrzej Pietrasiewicz 
9168443f2d2SAndrzej Pietrasiewicz 	return result;
9178443f2d2SAndrzej Pietrasiewicz }
9188443f2d2SAndrzej Pietrasiewicz 
9198443f2d2SAndrzej Pietrasiewicz static void printer_soft_reset(struct printer_dev *dev)
9208443f2d2SAndrzej Pietrasiewicz {
9218443f2d2SAndrzej Pietrasiewicz 	struct usb_request	*req;
9228443f2d2SAndrzej Pietrasiewicz 
9238443f2d2SAndrzej Pietrasiewicz 	INFO(dev, "Received Printer Reset Request\n");
9248443f2d2SAndrzej Pietrasiewicz 
9258443f2d2SAndrzej Pietrasiewicz 	if (usb_ep_disable(dev->in_ep))
9268443f2d2SAndrzej Pietrasiewicz 		DBG(dev, "Failed to disable USB in_ep\n");
9278443f2d2SAndrzej Pietrasiewicz 	if (usb_ep_disable(dev->out_ep))
9288443f2d2SAndrzej Pietrasiewicz 		DBG(dev, "Failed to disable USB out_ep\n");
9298443f2d2SAndrzej Pietrasiewicz 
9308443f2d2SAndrzej Pietrasiewicz 	if (dev->current_rx_req != NULL) {
9318443f2d2SAndrzej Pietrasiewicz 		list_add(&dev->current_rx_req->list, &dev->rx_reqs);
9328443f2d2SAndrzej Pietrasiewicz 		dev->current_rx_req = NULL;
9338443f2d2SAndrzej Pietrasiewicz 	}
9348443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_bytes = 0;
9358443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_buf = NULL;
9368443f2d2SAndrzej Pietrasiewicz 	dev->reset_printer = 1;
9378443f2d2SAndrzej Pietrasiewicz 
9388443f2d2SAndrzej Pietrasiewicz 	while (likely(!(list_empty(&dev->rx_buffers)))) {
9398443f2d2SAndrzej Pietrasiewicz 		req = container_of(dev->rx_buffers.next, struct usb_request,
9408443f2d2SAndrzej Pietrasiewicz 				list);
9418443f2d2SAndrzej Pietrasiewicz 		list_del_init(&req->list);
9428443f2d2SAndrzej Pietrasiewicz 		list_add(&req->list, &dev->rx_reqs);
9438443f2d2SAndrzej Pietrasiewicz 	}
9448443f2d2SAndrzej Pietrasiewicz 
9458443f2d2SAndrzej Pietrasiewicz 	while (likely(!(list_empty(&dev->rx_reqs_active)))) {
9468443f2d2SAndrzej Pietrasiewicz 		req = container_of(dev->rx_buffers.next, struct usb_request,
9478443f2d2SAndrzej Pietrasiewicz 				list);
9488443f2d2SAndrzej Pietrasiewicz 		list_del_init(&req->list);
9498443f2d2SAndrzej Pietrasiewicz 		list_add(&req->list, &dev->rx_reqs);
9508443f2d2SAndrzej Pietrasiewicz 	}
9518443f2d2SAndrzej Pietrasiewicz 
9528443f2d2SAndrzej Pietrasiewicz 	while (likely(!(list_empty(&dev->tx_reqs_active)))) {
9538443f2d2SAndrzej Pietrasiewicz 		req = container_of(dev->tx_reqs_active.next,
9548443f2d2SAndrzej Pietrasiewicz 				struct usb_request, list);
9558443f2d2SAndrzej Pietrasiewicz 		list_del_init(&req->list);
9568443f2d2SAndrzej Pietrasiewicz 		list_add(&req->list, &dev->tx_reqs);
9578443f2d2SAndrzej Pietrasiewicz 	}
9588443f2d2SAndrzej Pietrasiewicz 
9598443f2d2SAndrzej Pietrasiewicz 	if (usb_ep_enable(dev->in_ep))
9608443f2d2SAndrzej Pietrasiewicz 		DBG(dev, "Failed to enable USB in_ep\n");
9618443f2d2SAndrzej Pietrasiewicz 	if (usb_ep_enable(dev->out_ep))
9628443f2d2SAndrzej Pietrasiewicz 		DBG(dev, "Failed to enable USB out_ep\n");
9638443f2d2SAndrzej Pietrasiewicz 
9648443f2d2SAndrzej Pietrasiewicz 	wake_up_interruptible(&dev->rx_wait);
9658443f2d2SAndrzej Pietrasiewicz 	wake_up_interruptible(&dev->tx_wait);
9668443f2d2SAndrzej Pietrasiewicz 	wake_up_interruptible(&dev->tx_flush_wait);
9678443f2d2SAndrzej Pietrasiewicz }
9688443f2d2SAndrzej Pietrasiewicz 
9698443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
9708443f2d2SAndrzej Pietrasiewicz 
9718443f2d2SAndrzej Pietrasiewicz /*
9728443f2d2SAndrzej Pietrasiewicz  * The setup() callback implements all the ep0 functionality that's not
9738443f2d2SAndrzej Pietrasiewicz  * handled lower down.
9748443f2d2SAndrzej Pietrasiewicz  */
9758443f2d2SAndrzej Pietrasiewicz static int printer_func_setup(struct usb_function *f,
9768443f2d2SAndrzej Pietrasiewicz 		const struct usb_ctrlrequest *ctrl)
9778443f2d2SAndrzej Pietrasiewicz {
9788443f2d2SAndrzej Pietrasiewicz 	struct printer_dev *dev = container_of(f, struct printer_dev, function);
9798443f2d2SAndrzej Pietrasiewicz 	struct usb_composite_dev *cdev = f->config->cdev;
9808443f2d2SAndrzej Pietrasiewicz 	struct usb_request	*req = cdev->req;
9818443f2d2SAndrzej Pietrasiewicz 	int			value = -EOPNOTSUPP;
9828443f2d2SAndrzej Pietrasiewicz 	u16			wIndex = le16_to_cpu(ctrl->wIndex);
9838443f2d2SAndrzej Pietrasiewicz 	u16			wValue = le16_to_cpu(ctrl->wValue);
9848443f2d2SAndrzej Pietrasiewicz 	u16			wLength = le16_to_cpu(ctrl->wLength);
9858443f2d2SAndrzej Pietrasiewicz 
9868443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "ctrl req%02x.%02x v%04x i%04x l%d\n",
9878443f2d2SAndrzej Pietrasiewicz 		ctrl->bRequestType, ctrl->bRequest, wValue, wIndex, wLength);
9888443f2d2SAndrzej Pietrasiewicz 
9898443f2d2SAndrzej Pietrasiewicz 	switch (ctrl->bRequestType&USB_TYPE_MASK) {
9908443f2d2SAndrzej Pietrasiewicz 	case USB_TYPE_CLASS:
9918443f2d2SAndrzej Pietrasiewicz 		switch (ctrl->bRequest) {
9928443f2d2SAndrzej Pietrasiewicz 		case 0: /* Get the IEEE-1284 PNP String */
9938443f2d2SAndrzej Pietrasiewicz 			/* Only one printer interface is supported. */
9948443f2d2SAndrzej Pietrasiewicz 			if ((wIndex>>8) != dev->interface)
9958443f2d2SAndrzej Pietrasiewicz 				break;
9968443f2d2SAndrzej Pietrasiewicz 
9978443f2d2SAndrzej Pietrasiewicz 			value = (pnp_string[0]<<8)|pnp_string[1];
9988443f2d2SAndrzej Pietrasiewicz 			memcpy(req->buf, pnp_string, value);
9998443f2d2SAndrzej Pietrasiewicz 			DBG(dev, "1284 PNP String: %x %s\n", value,
10008443f2d2SAndrzej Pietrasiewicz 					&pnp_string[2]);
10018443f2d2SAndrzej Pietrasiewicz 			break;
10028443f2d2SAndrzej Pietrasiewicz 
10038443f2d2SAndrzej Pietrasiewicz 		case 1: /* Get Port Status */
10048443f2d2SAndrzej Pietrasiewicz 			/* Only one printer interface is supported. */
10058443f2d2SAndrzej Pietrasiewicz 			if (wIndex != dev->interface)
10068443f2d2SAndrzej Pietrasiewicz 				break;
10078443f2d2SAndrzej Pietrasiewicz 
10088443f2d2SAndrzej Pietrasiewicz 			*(u8 *)req->buf = dev->printer_status;
10098443f2d2SAndrzej Pietrasiewicz 			value = min(wLength, (u16) 1);
10108443f2d2SAndrzej Pietrasiewicz 			break;
10118443f2d2SAndrzej Pietrasiewicz 
10128443f2d2SAndrzej Pietrasiewicz 		case 2: /* Soft Reset */
10138443f2d2SAndrzej Pietrasiewicz 			/* Only one printer interface is supported. */
10148443f2d2SAndrzej Pietrasiewicz 			if (wIndex != dev->interface)
10158443f2d2SAndrzej Pietrasiewicz 				break;
10168443f2d2SAndrzej Pietrasiewicz 
10178443f2d2SAndrzej Pietrasiewicz 			printer_soft_reset(dev);
10188443f2d2SAndrzej Pietrasiewicz 
10198443f2d2SAndrzej Pietrasiewicz 			value = 0;
10208443f2d2SAndrzej Pietrasiewicz 			break;
10218443f2d2SAndrzej Pietrasiewicz 
10228443f2d2SAndrzej Pietrasiewicz 		default:
10238443f2d2SAndrzej Pietrasiewicz 			goto unknown;
10248443f2d2SAndrzej Pietrasiewicz 		}
10258443f2d2SAndrzej Pietrasiewicz 		break;
10268443f2d2SAndrzej Pietrasiewicz 
10278443f2d2SAndrzej Pietrasiewicz 	default:
10288443f2d2SAndrzej Pietrasiewicz unknown:
10298443f2d2SAndrzej Pietrasiewicz 		VDBG(dev,
10308443f2d2SAndrzej Pietrasiewicz 			"unknown ctrl req%02x.%02x v%04x i%04x l%d\n",
10318443f2d2SAndrzej Pietrasiewicz 			ctrl->bRequestType, ctrl->bRequest,
10328443f2d2SAndrzej Pietrasiewicz 			wValue, wIndex, wLength);
10338443f2d2SAndrzej Pietrasiewicz 		break;
10348443f2d2SAndrzej Pietrasiewicz 	}
10358443f2d2SAndrzej Pietrasiewicz 	/* host either stalls (value < 0) or reports success */
1036eb132ccbSAndrzej Pietrasiewicz 	if (value >= 0) {
1037eb132ccbSAndrzej Pietrasiewicz 		req->length = value;
1038eb132ccbSAndrzej Pietrasiewicz 		req->zero = value < wLength;
1039eb132ccbSAndrzej Pietrasiewicz 		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
1040eb132ccbSAndrzej Pietrasiewicz 		if (value < 0) {
1041eb132ccbSAndrzej Pietrasiewicz 			ERROR(dev, "%s:%d Error!\n", __func__, __LINE__);
1042eb132ccbSAndrzej Pietrasiewicz 			req->status = 0;
1043eb132ccbSAndrzej Pietrasiewicz 		}
1044eb132ccbSAndrzej Pietrasiewicz 	}
10458443f2d2SAndrzej Pietrasiewicz 	return value;
10468443f2d2SAndrzej Pietrasiewicz }
10478443f2d2SAndrzej Pietrasiewicz 
10488443f2d2SAndrzej Pietrasiewicz static int __init printer_func_bind(struct usb_configuration *c,
10498443f2d2SAndrzej Pietrasiewicz 		struct usb_function *f)
10508443f2d2SAndrzej Pietrasiewicz {
10514504b5a0SAndrzej Pietrasiewicz 	struct usb_gadget *gadget = c->cdev->gadget;
10528443f2d2SAndrzej Pietrasiewicz 	struct printer_dev *dev = container_of(f, struct printer_dev, function);
10534504b5a0SAndrzej Pietrasiewicz 	struct device *pdev;
10548443f2d2SAndrzej Pietrasiewicz 	struct usb_composite_dev *cdev = c->cdev;
10558443f2d2SAndrzej Pietrasiewicz 	struct usb_ep *in_ep;
10568443f2d2SAndrzej Pietrasiewicz 	struct usb_ep *out_ep = NULL;
10574504b5a0SAndrzej Pietrasiewicz 	struct usb_request *req;
10588443f2d2SAndrzej Pietrasiewicz 	int id;
10598443f2d2SAndrzej Pietrasiewicz 	int ret;
10604504b5a0SAndrzej Pietrasiewicz 	u32 i;
10618443f2d2SAndrzej Pietrasiewicz 
10628443f2d2SAndrzej Pietrasiewicz 	id = usb_interface_id(c, f);
10638443f2d2SAndrzej Pietrasiewicz 	if (id < 0)
10648443f2d2SAndrzej Pietrasiewicz 		return id;
10658443f2d2SAndrzej Pietrasiewicz 	intf_desc.bInterfaceNumber = id;
10668443f2d2SAndrzej Pietrasiewicz 
10674504b5a0SAndrzej Pietrasiewicz 	/* finish hookup to lower layer ... */
10684504b5a0SAndrzej Pietrasiewicz 	dev->gadget = gadget;
10694504b5a0SAndrzej Pietrasiewicz 
10708443f2d2SAndrzej Pietrasiewicz 	/* all we really need is bulk IN/OUT */
10718443f2d2SAndrzej Pietrasiewicz 	in_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_in_desc);
10728443f2d2SAndrzej Pietrasiewicz 	if (!in_ep) {
10738443f2d2SAndrzej Pietrasiewicz autoconf_fail:
10748443f2d2SAndrzej Pietrasiewicz 		dev_err(&cdev->gadget->dev, "can't autoconfigure on %s\n",
10758443f2d2SAndrzej Pietrasiewicz 			cdev->gadget->name);
10768443f2d2SAndrzej Pietrasiewicz 		return -ENODEV;
10778443f2d2SAndrzej Pietrasiewicz 	}
10788443f2d2SAndrzej Pietrasiewicz 	in_ep->driver_data = in_ep;	/* claim */
10798443f2d2SAndrzej Pietrasiewicz 
10808443f2d2SAndrzej Pietrasiewicz 	out_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_out_desc);
10818443f2d2SAndrzej Pietrasiewicz 	if (!out_ep)
10828443f2d2SAndrzej Pietrasiewicz 		goto autoconf_fail;
10838443f2d2SAndrzej Pietrasiewicz 	out_ep->driver_data = out_ep;	/* claim */
10848443f2d2SAndrzej Pietrasiewicz 
10858443f2d2SAndrzej Pietrasiewicz 	/* assumes that all endpoints are dual-speed */
10868443f2d2SAndrzej Pietrasiewicz 	hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
10878443f2d2SAndrzej Pietrasiewicz 	hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
108874df41b4SJorge Ramirez-Ortiz 	ss_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
108974df41b4SJorge Ramirez-Ortiz 	ss_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
10908443f2d2SAndrzej Pietrasiewicz 
10918443f2d2SAndrzej Pietrasiewicz 	ret = usb_assign_descriptors(f, fs_printer_function,
109274df41b4SJorge Ramirez-Ortiz 			hs_printer_function, ss_printer_function);
10938443f2d2SAndrzej Pietrasiewicz 	if (ret)
10948443f2d2SAndrzej Pietrasiewicz 		return ret;
10958443f2d2SAndrzej Pietrasiewicz 
10968443f2d2SAndrzej Pietrasiewicz 	dev->in_ep = in_ep;
10978443f2d2SAndrzej Pietrasiewicz 	dev->out_ep = out_ep;
10984504b5a0SAndrzej Pietrasiewicz 
10994504b5a0SAndrzej Pietrasiewicz 	ret = -ENOMEM;
11004504b5a0SAndrzej Pietrasiewicz 	for (i = 0; i < dev->q_len; i++) {
11014504b5a0SAndrzej Pietrasiewicz 		req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL);
11024504b5a0SAndrzej Pietrasiewicz 		if (!req)
11034504b5a0SAndrzej Pietrasiewicz 			goto fail_tx_reqs;
11044504b5a0SAndrzej Pietrasiewicz 		list_add(&req->list, &dev->tx_reqs);
11054504b5a0SAndrzej Pietrasiewicz 	}
11064504b5a0SAndrzej Pietrasiewicz 
11074504b5a0SAndrzej Pietrasiewicz 	for (i = 0; i < dev->q_len; i++) {
11084504b5a0SAndrzej Pietrasiewicz 		req = printer_req_alloc(dev->out_ep, USB_BUFSIZE, GFP_KERNEL);
11094504b5a0SAndrzej Pietrasiewicz 		if (!req)
11104504b5a0SAndrzej Pietrasiewicz 			goto fail_rx_reqs;
11114504b5a0SAndrzej Pietrasiewicz 		list_add(&req->list, &dev->rx_reqs);
11124504b5a0SAndrzej Pietrasiewicz 	}
11134504b5a0SAndrzej Pietrasiewicz 
11144504b5a0SAndrzej Pietrasiewicz 	/* Setup the sysfs files for the printer gadget. */
11154504b5a0SAndrzej Pietrasiewicz 	pdev = device_create(usb_gadget_class, NULL, g_printer_devno,
11164504b5a0SAndrzej Pietrasiewicz 				  NULL, "g_printer");
11174504b5a0SAndrzej Pietrasiewicz 	if (IS_ERR(pdev)) {
11184504b5a0SAndrzej Pietrasiewicz 		ERROR(dev, "Failed to create device: g_printer\n");
11194504b5a0SAndrzej Pietrasiewicz 		ret = PTR_ERR(pdev);
11204504b5a0SAndrzej Pietrasiewicz 		goto fail_rx_reqs;
11214504b5a0SAndrzej Pietrasiewicz 	}
11224504b5a0SAndrzej Pietrasiewicz 
11234504b5a0SAndrzej Pietrasiewicz 	/*
11244504b5a0SAndrzej Pietrasiewicz 	 * Register a character device as an interface to a user mode
11254504b5a0SAndrzej Pietrasiewicz 	 * program that handles the printer specific functionality.
11264504b5a0SAndrzej Pietrasiewicz 	 */
11274504b5a0SAndrzej Pietrasiewicz 	cdev_init(&dev->printer_cdev, &printer_io_operations);
11284504b5a0SAndrzej Pietrasiewicz 	dev->printer_cdev.owner = THIS_MODULE;
11294504b5a0SAndrzej Pietrasiewicz 	ret = cdev_add(&dev->printer_cdev, g_printer_devno, 1);
11304504b5a0SAndrzej Pietrasiewicz 	if (ret) {
11314504b5a0SAndrzej Pietrasiewicz 		ERROR(dev, "Failed to open char device\n");
11324504b5a0SAndrzej Pietrasiewicz 		goto fail_cdev_add;
11334504b5a0SAndrzej Pietrasiewicz 	}
11344504b5a0SAndrzej Pietrasiewicz 
11358443f2d2SAndrzej Pietrasiewicz 	return 0;
11364504b5a0SAndrzej Pietrasiewicz 
11374504b5a0SAndrzej Pietrasiewicz fail_cdev_add:
11384504b5a0SAndrzej Pietrasiewicz 	device_destroy(usb_gadget_class, g_printer_devno);
11394504b5a0SAndrzej Pietrasiewicz 
11404504b5a0SAndrzej Pietrasiewicz fail_rx_reqs:
11414504b5a0SAndrzej Pietrasiewicz 	while (!list_empty(&dev->rx_reqs)) {
11424504b5a0SAndrzej Pietrasiewicz 		req = container_of(dev->rx_reqs.next, struct usb_request, list);
11434504b5a0SAndrzej Pietrasiewicz 		list_del(&req->list);
11444504b5a0SAndrzej Pietrasiewicz 		printer_req_free(dev->out_ep, req);
11454504b5a0SAndrzej Pietrasiewicz 	}
11464504b5a0SAndrzej Pietrasiewicz 
11474504b5a0SAndrzej Pietrasiewicz fail_tx_reqs:
11484504b5a0SAndrzej Pietrasiewicz 	while (!list_empty(&dev->tx_reqs)) {
11494504b5a0SAndrzej Pietrasiewicz 		req = container_of(dev->tx_reqs.next, struct usb_request, list);
11504504b5a0SAndrzej Pietrasiewicz 		list_del(&req->list);
11514504b5a0SAndrzej Pietrasiewicz 		printer_req_free(dev->in_ep, req);
11524504b5a0SAndrzej Pietrasiewicz 	}
11534504b5a0SAndrzej Pietrasiewicz 
11544504b5a0SAndrzej Pietrasiewicz 	return ret;
11554504b5a0SAndrzej Pietrasiewicz 
11568443f2d2SAndrzej Pietrasiewicz }
11578443f2d2SAndrzej Pietrasiewicz 
11588443f2d2SAndrzej Pietrasiewicz static void printer_func_unbind(struct usb_configuration *c,
11598443f2d2SAndrzej Pietrasiewicz 		struct usb_function *f)
11608443f2d2SAndrzej Pietrasiewicz {
11618443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev;
11628443f2d2SAndrzej Pietrasiewicz 	struct usb_request	*req;
11638443f2d2SAndrzej Pietrasiewicz 
11648443f2d2SAndrzej Pietrasiewicz 	dev = &usb_printer_gadget;
11658443f2d2SAndrzej Pietrasiewicz 
11668443f2d2SAndrzej Pietrasiewicz 	device_destroy(usb_gadget_class, g_printer_devno);
11678443f2d2SAndrzej Pietrasiewicz 
11688443f2d2SAndrzej Pietrasiewicz 	/* Remove Character Device */
11698443f2d2SAndrzej Pietrasiewicz 	cdev_del(&dev->printer_cdev);
11708443f2d2SAndrzej Pietrasiewicz 
11718443f2d2SAndrzej Pietrasiewicz 	/* we must already have been disconnected ... no i/o may be active */
11728443f2d2SAndrzej Pietrasiewicz 	WARN_ON(!list_empty(&dev->tx_reqs_active));
11738443f2d2SAndrzej Pietrasiewicz 	WARN_ON(!list_empty(&dev->rx_reqs_active));
11748443f2d2SAndrzej Pietrasiewicz 
11758443f2d2SAndrzej Pietrasiewicz 	/* Free all memory for this driver. */
11768443f2d2SAndrzej Pietrasiewicz 	while (!list_empty(&dev->tx_reqs)) {
11778443f2d2SAndrzej Pietrasiewicz 		req = container_of(dev->tx_reqs.next, struct usb_request,
11788443f2d2SAndrzej Pietrasiewicz 				list);
11798443f2d2SAndrzej Pietrasiewicz 		list_del(&req->list);
11808443f2d2SAndrzej Pietrasiewicz 		printer_req_free(dev->in_ep, req);
11818443f2d2SAndrzej Pietrasiewicz 	}
11828443f2d2SAndrzej Pietrasiewicz 
11838443f2d2SAndrzej Pietrasiewicz 	if (dev->current_rx_req != NULL)
11848443f2d2SAndrzej Pietrasiewicz 		printer_req_free(dev->out_ep, dev->current_rx_req);
11858443f2d2SAndrzej Pietrasiewicz 
11868443f2d2SAndrzej Pietrasiewicz 	while (!list_empty(&dev->rx_reqs)) {
11878443f2d2SAndrzej Pietrasiewicz 		req = container_of(dev->rx_reqs.next,
11888443f2d2SAndrzej Pietrasiewicz 				struct usb_request, list);
11898443f2d2SAndrzej Pietrasiewicz 		list_del(&req->list);
11908443f2d2SAndrzej Pietrasiewicz 		printer_req_free(dev->out_ep, req);
11918443f2d2SAndrzej Pietrasiewicz 	}
11928443f2d2SAndrzej Pietrasiewicz 
11938443f2d2SAndrzej Pietrasiewicz 	while (!list_empty(&dev->rx_buffers)) {
11948443f2d2SAndrzej Pietrasiewicz 		req = container_of(dev->rx_buffers.next,
11958443f2d2SAndrzej Pietrasiewicz 				struct usb_request, list);
11968443f2d2SAndrzej Pietrasiewicz 		list_del(&req->list);
11978443f2d2SAndrzej Pietrasiewicz 		printer_req_free(dev->out_ep, req);
11988443f2d2SAndrzej Pietrasiewicz 	}
1199991cd262SAndrzej Pietrasiewicz 	usb_free_all_descriptors(f);
1200991cd262SAndrzej Pietrasiewicz }
1201991cd262SAndrzej Pietrasiewicz 
1202991cd262SAndrzej Pietrasiewicz static int printer_func_set_alt(struct usb_function *f,
1203991cd262SAndrzej Pietrasiewicz 		unsigned intf, unsigned alt)
1204991cd262SAndrzej Pietrasiewicz {
1205991cd262SAndrzej Pietrasiewicz 	struct printer_dev *dev = container_of(f, struct printer_dev, function);
1206991cd262SAndrzej Pietrasiewicz 	int ret = -ENOTSUPP;
1207991cd262SAndrzej Pietrasiewicz 
1208991cd262SAndrzej Pietrasiewicz 	if (!alt)
1209991cd262SAndrzej Pietrasiewicz 		ret = set_interface(dev, intf);
1210991cd262SAndrzej Pietrasiewicz 
1211991cd262SAndrzej Pietrasiewicz 	return ret;
1212991cd262SAndrzej Pietrasiewicz }
1213991cd262SAndrzej Pietrasiewicz 
1214991cd262SAndrzej Pietrasiewicz static void printer_func_disable(struct usb_function *f)
1215991cd262SAndrzej Pietrasiewicz {
1216991cd262SAndrzej Pietrasiewicz 	struct printer_dev *dev = container_of(f, struct printer_dev, function);
1217991cd262SAndrzej Pietrasiewicz 	unsigned long		flags;
1218991cd262SAndrzej Pietrasiewicz 
1219991cd262SAndrzej Pietrasiewicz 	DBG(dev, "%s\n", __func__);
1220991cd262SAndrzej Pietrasiewicz 
1221991cd262SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
1222991cd262SAndrzej Pietrasiewicz 	printer_reset_interface(dev);
1223991cd262SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
12248443f2d2SAndrzej Pietrasiewicz }
12258443f2d2SAndrzej Pietrasiewicz 
12268443f2d2SAndrzej Pietrasiewicz static struct usb_configuration printer_cfg_driver = {
12278443f2d2SAndrzej Pietrasiewicz 	.label			= "printer",
12288443f2d2SAndrzej Pietrasiewicz 	.bConfigurationValue	= 1,
12298443f2d2SAndrzej Pietrasiewicz 	.bmAttributes		= USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
12308443f2d2SAndrzej Pietrasiewicz };
12318443f2d2SAndrzej Pietrasiewicz 
1232ae2dd0deSAndrzej Pietrasiewicz static int f_printer_bind_config(struct usb_configuration *c, char *pnp_str,
1233ae2dd0deSAndrzej Pietrasiewicz 				 unsigned q_len)
12348443f2d2SAndrzej Pietrasiewicz {
12358443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev;
12368443f2d2SAndrzej Pietrasiewicz 	int			status = -ENOMEM;
12378443f2d2SAndrzej Pietrasiewicz 	size_t			len;
12388443f2d2SAndrzej Pietrasiewicz 
12398443f2d2SAndrzej Pietrasiewicz 	dev = &usb_printer_gadget;
12408443f2d2SAndrzej Pietrasiewicz 
12418443f2d2SAndrzej Pietrasiewicz 	dev->function.name = shortname;
12428443f2d2SAndrzej Pietrasiewicz 	dev->function.bind = printer_func_bind;
12438443f2d2SAndrzej Pietrasiewicz 	dev->function.setup = printer_func_setup;
12448443f2d2SAndrzej Pietrasiewicz 	dev->function.unbind = printer_func_unbind;
12458443f2d2SAndrzej Pietrasiewicz 	dev->function.set_alt = printer_func_set_alt;
12468443f2d2SAndrzej Pietrasiewicz 	dev->function.disable = printer_func_disable;
1247c69b8186SAndrzej Pietrasiewicz 	INIT_LIST_HEAD(&dev->tx_reqs);
1248c69b8186SAndrzej Pietrasiewicz 	INIT_LIST_HEAD(&dev->rx_reqs);
1249c69b8186SAndrzej Pietrasiewicz 	INIT_LIST_HEAD(&dev->rx_buffers);
12508443f2d2SAndrzej Pietrasiewicz 
1251ae2dd0deSAndrzej Pietrasiewicz 	if (pnp_str)
1252*085617a1SAndrzej Pietrasiewicz 		strlcpy(&pnp_string[2], pnp_str, PNP_STRING_LEN - 2);
12538443f2d2SAndrzej Pietrasiewicz 
12548443f2d2SAndrzej Pietrasiewicz 	len = strlen(pnp_string);
12558443f2d2SAndrzej Pietrasiewicz 	pnp_string[0] = (len >> 8) & 0xFF;
12568443f2d2SAndrzej Pietrasiewicz 	pnp_string[1] = len & 0xFF;
12578443f2d2SAndrzej Pietrasiewicz 
12588443f2d2SAndrzej Pietrasiewicz 	spin_lock_init(&dev->lock);
12598443f2d2SAndrzej Pietrasiewicz 	mutex_init(&dev->lock_printer_io);
12608443f2d2SAndrzej Pietrasiewicz 	INIT_LIST_HEAD(&dev->tx_reqs_active);
12618443f2d2SAndrzej Pietrasiewicz 	INIT_LIST_HEAD(&dev->rx_reqs_active);
12628443f2d2SAndrzej Pietrasiewicz 	init_waitqueue_head(&dev->rx_wait);
12638443f2d2SAndrzej Pietrasiewicz 	init_waitqueue_head(&dev->tx_wait);
12648443f2d2SAndrzej Pietrasiewicz 	init_waitqueue_head(&dev->tx_flush_wait);
12658443f2d2SAndrzej Pietrasiewicz 
12668443f2d2SAndrzej Pietrasiewicz 	dev->interface = -1;
12678443f2d2SAndrzej Pietrasiewicz 	dev->printer_cdev_open = 0;
12688443f2d2SAndrzej Pietrasiewicz 	dev->printer_status = PRINTER_NOT_ERROR;
12698443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_req = NULL;
12708443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_bytes = 0;
12718443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_buf = NULL;
1272cee5cbffSAndrzej Pietrasiewicz 	dev->q_len = q_len;
12738443f2d2SAndrzej Pietrasiewicz 
1274cee5cbffSAndrzej Pietrasiewicz 	status = usb_add_function(c, &dev->function);
1275cee5cbffSAndrzej Pietrasiewicz 	if (status)
1276cee5cbffSAndrzej Pietrasiewicz 		return status;
12778443f2d2SAndrzej Pietrasiewicz 	INFO(dev, "%s, version: " DRIVER_VERSION "\n", driver_desc);
12788443f2d2SAndrzej Pietrasiewicz 	return 0;
12798443f2d2SAndrzej Pietrasiewicz }
12808443f2d2SAndrzej Pietrasiewicz 
1281ae2dd0deSAndrzej Pietrasiewicz static int __init printer_do_config(struct usb_configuration *c)
1282ae2dd0deSAndrzej Pietrasiewicz {
1283ae2dd0deSAndrzej Pietrasiewicz 	struct usb_gadget	*gadget = c->cdev->gadget;
1284ae2dd0deSAndrzej Pietrasiewicz 
1285ae2dd0deSAndrzej Pietrasiewicz 	usb_ep_autoconfig_reset(gadget);
1286ae2dd0deSAndrzej Pietrasiewicz 
1287ae2dd0deSAndrzej Pietrasiewicz 	usb_gadget_set_selfpowered(gadget);
1288ae2dd0deSAndrzej Pietrasiewicz 
1289ae2dd0deSAndrzej Pietrasiewicz 	if (gadget_is_otg(gadget)) {
1290ae2dd0deSAndrzej Pietrasiewicz 		otg_descriptor.bmAttributes |= USB_OTG_HNP;
1291ae2dd0deSAndrzej Pietrasiewicz 		printer_cfg_driver.descriptors = otg_desc;
1292ae2dd0deSAndrzej Pietrasiewicz 		printer_cfg_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
1293ae2dd0deSAndrzej Pietrasiewicz 	}
1294ae2dd0deSAndrzej Pietrasiewicz 
1295ae2dd0deSAndrzej Pietrasiewicz 	return f_printer_bind_config(c, iPNPstring, QLEN);
1296ae2dd0deSAndrzej Pietrasiewicz 
1297ae2dd0deSAndrzej Pietrasiewicz }
1298ae2dd0deSAndrzej Pietrasiewicz 
12998443f2d2SAndrzej Pietrasiewicz static int __init printer_bind(struct usb_composite_dev *cdev)
13008443f2d2SAndrzej Pietrasiewicz {
13018443f2d2SAndrzej Pietrasiewicz 	int ret;
13028443f2d2SAndrzej Pietrasiewicz 
13038443f2d2SAndrzej Pietrasiewicz 	ret = usb_string_ids_tab(cdev, strings);
13048443f2d2SAndrzej Pietrasiewicz 	if (ret < 0)
13058443f2d2SAndrzej Pietrasiewicz 		return ret;
13068443f2d2SAndrzej Pietrasiewicz 	device_desc.iManufacturer = strings[USB_GADGET_MANUFACTURER_IDX].id;
13078443f2d2SAndrzej Pietrasiewicz 	device_desc.iProduct = strings[USB_GADGET_PRODUCT_IDX].id;
13088443f2d2SAndrzej Pietrasiewicz 	device_desc.iSerialNumber = strings[USB_GADGET_SERIAL_IDX].id;
13098443f2d2SAndrzej Pietrasiewicz 
1310406be2ccSAndrzej Pietrasiewicz 	ret = usb_add_config(cdev, &printer_cfg_driver, printer_do_config);
13118443f2d2SAndrzej Pietrasiewicz 	if (ret)
13128443f2d2SAndrzej Pietrasiewicz 		return ret;
13138443f2d2SAndrzej Pietrasiewicz 	usb_composite_overwrite_options(cdev, &coverwrite);
13148443f2d2SAndrzej Pietrasiewicz 	return ret;
13158443f2d2SAndrzej Pietrasiewicz }
13168443f2d2SAndrzej Pietrasiewicz 
13178443f2d2SAndrzej Pietrasiewicz static __refdata struct usb_composite_driver printer_driver = {
13188443f2d2SAndrzej Pietrasiewicz 	.name           = shortname,
13198443f2d2SAndrzej Pietrasiewicz 	.dev            = &device_desc,
13208443f2d2SAndrzej Pietrasiewicz 	.strings        = dev_strings,
132174df41b4SJorge Ramirez-Ortiz 	.max_speed      = USB_SPEED_SUPER,
13228443f2d2SAndrzej Pietrasiewicz 	.bind		= printer_bind,
13238443f2d2SAndrzej Pietrasiewicz };
13248443f2d2SAndrzej Pietrasiewicz 
13258443f2d2SAndrzej Pietrasiewicz static int __init
13268443f2d2SAndrzej Pietrasiewicz init(void)
13278443f2d2SAndrzej Pietrasiewicz {
13288443f2d2SAndrzej Pietrasiewicz 	int status;
13298443f2d2SAndrzej Pietrasiewicz 
13308443f2d2SAndrzej Pietrasiewicz 	usb_gadget_class = class_create(THIS_MODULE, "usb_printer_gadget");
13318443f2d2SAndrzej Pietrasiewicz 	if (IS_ERR(usb_gadget_class)) {
13328443f2d2SAndrzej Pietrasiewicz 		status = PTR_ERR(usb_gadget_class);
13338443f2d2SAndrzej Pietrasiewicz 		pr_err("unable to create usb_gadget class %d\n", status);
13348443f2d2SAndrzej Pietrasiewicz 		return status;
13358443f2d2SAndrzej Pietrasiewicz 	}
13368443f2d2SAndrzej Pietrasiewicz 
13378443f2d2SAndrzej Pietrasiewicz 	status = alloc_chrdev_region(&g_printer_devno, 0, 1,
13388443f2d2SAndrzej Pietrasiewicz 			"USB printer gadget");
13398443f2d2SAndrzej Pietrasiewicz 	if (status) {
13408443f2d2SAndrzej Pietrasiewicz 		pr_err("alloc_chrdev_region %d\n", status);
13418443f2d2SAndrzej Pietrasiewicz 		class_destroy(usb_gadget_class);
13428443f2d2SAndrzej Pietrasiewicz 		return status;
13438443f2d2SAndrzej Pietrasiewicz 	}
13448443f2d2SAndrzej Pietrasiewicz 
13458443f2d2SAndrzej Pietrasiewicz 	status = usb_composite_probe(&printer_driver);
13468443f2d2SAndrzej Pietrasiewicz 	if (status) {
13478443f2d2SAndrzej Pietrasiewicz 		class_destroy(usb_gadget_class);
13488443f2d2SAndrzej Pietrasiewicz 		unregister_chrdev_region(g_printer_devno, 1);
13498443f2d2SAndrzej Pietrasiewicz 		pr_err("usb_gadget_probe_driver %x\n", status);
13508443f2d2SAndrzej Pietrasiewicz 	}
13518443f2d2SAndrzej Pietrasiewicz 
13528443f2d2SAndrzej Pietrasiewicz 	return status;
13538443f2d2SAndrzej Pietrasiewicz }
13548443f2d2SAndrzej Pietrasiewicz module_init(init);
13558443f2d2SAndrzej Pietrasiewicz 
13568443f2d2SAndrzej Pietrasiewicz static void __exit
13578443f2d2SAndrzej Pietrasiewicz cleanup(void)
13588443f2d2SAndrzej Pietrasiewicz {
13598443f2d2SAndrzej Pietrasiewicz 	mutex_lock(&usb_printer_gadget.lock_printer_io);
13608443f2d2SAndrzej Pietrasiewicz 	usb_composite_unregister(&printer_driver);
13618443f2d2SAndrzej Pietrasiewicz 	unregister_chrdev_region(g_printer_devno, 1);
13628443f2d2SAndrzej Pietrasiewicz 	class_destroy(usb_gadget_class);
13638443f2d2SAndrzej Pietrasiewicz 	mutex_unlock(&usb_printer_gadget.lock_printer_io);
13648443f2d2SAndrzej Pietrasiewicz }
13658443f2d2SAndrzej Pietrasiewicz module_exit(cleanup);
13668443f2d2SAndrzej Pietrasiewicz 
13678443f2d2SAndrzej Pietrasiewicz MODULE_DESCRIPTION(DRIVER_DESC);
13688443f2d2SAndrzej Pietrasiewicz MODULE_AUTHOR("Craig Nadler");
13698443f2d2SAndrzej Pietrasiewicz MODULE_LICENSE("GPL");
1370