xref: /openbmc/linux/drivers/usb/gadget/legacy/printer.c (revision 6dd8c2e69521ec9d7b23a741294702f844353f1a)
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"
50d7239f4cSAndrzej Pietrasiewicz #define GET_DEVICE_ID		0
51d7239f4cSAndrzej Pietrasiewicz #define GET_PORT_STATUS		1
52d7239f4cSAndrzej Pietrasiewicz #define SOFT_RESET		2
538443f2d2SAndrzej Pietrasiewicz 
54*6dd8c2e6SAndrzej Pietrasiewicz #define PRINTER_MINORS		4
55*6dd8c2e6SAndrzej Pietrasiewicz 
568443f2d2SAndrzej Pietrasiewicz static const char shortname [] = "printer";
578443f2d2SAndrzej Pietrasiewicz static const char driver_desc [] = DRIVER_DESC;
588443f2d2SAndrzej Pietrasiewicz 
59*6dd8c2e6SAndrzej Pietrasiewicz static int major, minors;
608443f2d2SAndrzej Pietrasiewicz static struct class *usb_gadget_class;
618443f2d2SAndrzej Pietrasiewicz 
628443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
638443f2d2SAndrzej Pietrasiewicz 
648443f2d2SAndrzej Pietrasiewicz struct printer_dev {
658443f2d2SAndrzej Pietrasiewicz 	spinlock_t		lock;		/* lock this structure */
668443f2d2SAndrzej Pietrasiewicz 	/* lock buffer lists during read/write calls */
678443f2d2SAndrzej Pietrasiewicz 	struct mutex		lock_printer_io;
688443f2d2SAndrzej Pietrasiewicz 	struct usb_gadget	*gadget;
698443f2d2SAndrzej Pietrasiewicz 	s8			interface;
708443f2d2SAndrzej Pietrasiewicz 	struct usb_ep		*in_ep, *out_ep;
718443f2d2SAndrzej Pietrasiewicz 
728443f2d2SAndrzej Pietrasiewicz 	struct list_head	rx_reqs;	/* List of free RX structs */
738443f2d2SAndrzej Pietrasiewicz 	struct list_head	rx_reqs_active;	/* List of Active RX xfers */
748443f2d2SAndrzej Pietrasiewicz 	struct list_head	rx_buffers;	/* List of completed xfers */
758443f2d2SAndrzej Pietrasiewicz 	/* wait until there is data to be read. */
768443f2d2SAndrzej Pietrasiewicz 	wait_queue_head_t	rx_wait;
778443f2d2SAndrzej Pietrasiewicz 	struct list_head	tx_reqs;	/* List of free TX structs */
788443f2d2SAndrzej Pietrasiewicz 	struct list_head	tx_reqs_active; /* List of Active TX xfers */
798443f2d2SAndrzej Pietrasiewicz 	/* Wait until there are write buffers available to use. */
808443f2d2SAndrzej Pietrasiewicz 	wait_queue_head_t	tx_wait;
818443f2d2SAndrzej Pietrasiewicz 	/* Wait until all write buffers have been sent. */
828443f2d2SAndrzej Pietrasiewicz 	wait_queue_head_t	tx_flush_wait;
838443f2d2SAndrzej Pietrasiewicz 	struct usb_request	*current_rx_req;
848443f2d2SAndrzej Pietrasiewicz 	size_t			current_rx_bytes;
858443f2d2SAndrzej Pietrasiewicz 	u8			*current_rx_buf;
868443f2d2SAndrzej Pietrasiewicz 	u8			printer_status;
878443f2d2SAndrzej Pietrasiewicz 	u8			reset_printer;
88*6dd8c2e6SAndrzej Pietrasiewicz 	int			minor;
898443f2d2SAndrzej Pietrasiewicz 	struct cdev		printer_cdev;
908443f2d2SAndrzej Pietrasiewicz 	u8			printer_cdev_open;
918443f2d2SAndrzej Pietrasiewicz 	wait_queue_head_t	wait;
924504b5a0SAndrzej Pietrasiewicz 	unsigned		q_len;
935a84e6f6SAndrzej Pietrasiewicz 	char			*pnp_string;	/* We don't own memory! */
948443f2d2SAndrzej Pietrasiewicz 	struct usb_function	function;
958443f2d2SAndrzej Pietrasiewicz };
968443f2d2SAndrzej Pietrasiewicz 
97143d53e1SAndrzej Pietrasiewicz static inline struct printer_dev *func_to_printer(struct usb_function *f)
98143d53e1SAndrzej Pietrasiewicz {
99143d53e1SAndrzej Pietrasiewicz 	return container_of(f, struct printer_dev, function);
100143d53e1SAndrzej Pietrasiewicz }
101143d53e1SAndrzej Pietrasiewicz 
1028443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
1038443f2d2SAndrzej Pietrasiewicz 
1048443f2d2SAndrzej Pietrasiewicz /* DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!!
1058443f2d2SAndrzej Pietrasiewicz  * Instead:  allocate your own, using normal USB-IF procedures.
1068443f2d2SAndrzej Pietrasiewicz  */
1078443f2d2SAndrzej Pietrasiewicz 
1088443f2d2SAndrzej Pietrasiewicz /* Thanks to NetChip Technologies for donating this product ID.
1098443f2d2SAndrzej Pietrasiewicz  */
1108443f2d2SAndrzej Pietrasiewicz #define PRINTER_VENDOR_NUM	0x0525		/* NetChip */
1118443f2d2SAndrzej Pietrasiewicz #define PRINTER_PRODUCT_NUM	0xa4a8		/* Linux-USB Printer Gadget */
1128443f2d2SAndrzej Pietrasiewicz 
1138443f2d2SAndrzej Pietrasiewicz /* Some systems will want different product identifiers published in the
1148443f2d2SAndrzej Pietrasiewicz  * device descriptor, either numbers or strings or both.  These string
1158443f2d2SAndrzej Pietrasiewicz  * parameters are in UTF-8 (superset of ASCII's 7 bit characters).
1168443f2d2SAndrzej Pietrasiewicz  */
1178443f2d2SAndrzej Pietrasiewicz 
1188443f2d2SAndrzej Pietrasiewicz module_param_named(iSerialNum, coverwrite.serial_number, charp, S_IRUGO);
1198443f2d2SAndrzej Pietrasiewicz MODULE_PARM_DESC(iSerialNum, "1");
1208443f2d2SAndrzej Pietrasiewicz 
1218443f2d2SAndrzej Pietrasiewicz static char *iPNPstring;
1228443f2d2SAndrzej Pietrasiewicz module_param(iPNPstring, charp, S_IRUGO);
1238443f2d2SAndrzej Pietrasiewicz MODULE_PARM_DESC(iPNPstring, "MFG:linux;MDL:g_printer;CLS:PRINTER;SN:1;");
1248443f2d2SAndrzej Pietrasiewicz 
1258443f2d2SAndrzej Pietrasiewicz /* Number of requests to allocate per endpoint, not used for ep0. */
1268443f2d2SAndrzej Pietrasiewicz static unsigned qlen = 10;
1278443f2d2SAndrzej Pietrasiewicz module_param(qlen, uint, S_IRUGO|S_IWUSR);
1288443f2d2SAndrzej Pietrasiewicz 
1298443f2d2SAndrzej Pietrasiewicz #define QLEN	qlen
1308443f2d2SAndrzej Pietrasiewicz 
1318443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
1328443f2d2SAndrzej Pietrasiewicz 
1338443f2d2SAndrzej Pietrasiewicz /*
1348443f2d2SAndrzej Pietrasiewicz  * DESCRIPTORS ... most are static, but strings and (full) configuration
1358443f2d2SAndrzej Pietrasiewicz  * descriptors are built on demand.
1368443f2d2SAndrzej Pietrasiewicz  */
1378443f2d2SAndrzej Pietrasiewicz 
1388443f2d2SAndrzej Pietrasiewicz /* holds our biggest descriptor */
1398443f2d2SAndrzej Pietrasiewicz #define USB_DESC_BUFSIZE		256
1408443f2d2SAndrzej Pietrasiewicz #define USB_BUFSIZE			8192
1418443f2d2SAndrzej Pietrasiewicz 
1428443f2d2SAndrzej Pietrasiewicz static struct usb_device_descriptor device_desc = {
1438443f2d2SAndrzej Pietrasiewicz 	.bLength =		sizeof device_desc,
1448443f2d2SAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_DEVICE,
1458443f2d2SAndrzej Pietrasiewicz 	.bcdUSB =		cpu_to_le16(0x0200),
1468443f2d2SAndrzej Pietrasiewicz 	.bDeviceClass =		USB_CLASS_PER_INTERFACE,
1478443f2d2SAndrzej Pietrasiewicz 	.bDeviceSubClass =	0,
1488443f2d2SAndrzej Pietrasiewicz 	.bDeviceProtocol =	0,
1498443f2d2SAndrzej Pietrasiewicz 	.idVendor =		cpu_to_le16(PRINTER_VENDOR_NUM),
1508443f2d2SAndrzej Pietrasiewicz 	.idProduct =		cpu_to_le16(PRINTER_PRODUCT_NUM),
1518443f2d2SAndrzej Pietrasiewicz 	.bNumConfigurations =	1
1528443f2d2SAndrzej Pietrasiewicz };
1538443f2d2SAndrzej Pietrasiewicz 
1548443f2d2SAndrzej Pietrasiewicz static struct usb_interface_descriptor intf_desc = {
1558443f2d2SAndrzej Pietrasiewicz 	.bLength =		sizeof intf_desc,
1568443f2d2SAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_INTERFACE,
1578443f2d2SAndrzej Pietrasiewicz 	.bNumEndpoints =	2,
1588443f2d2SAndrzej Pietrasiewicz 	.bInterfaceClass =	USB_CLASS_PRINTER,
1598443f2d2SAndrzej Pietrasiewicz 	.bInterfaceSubClass =	1,	/* Printer Sub-Class */
1608443f2d2SAndrzej Pietrasiewicz 	.bInterfaceProtocol =	2,	/* Bi-Directional */
1618443f2d2SAndrzej Pietrasiewicz 	.iInterface =		0
1628443f2d2SAndrzej Pietrasiewicz };
1638443f2d2SAndrzej Pietrasiewicz 
1648443f2d2SAndrzej Pietrasiewicz static struct usb_endpoint_descriptor fs_ep_in_desc = {
1658443f2d2SAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
1668443f2d2SAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
1678443f2d2SAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_IN,
1688443f2d2SAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK
1698443f2d2SAndrzej Pietrasiewicz };
1708443f2d2SAndrzej Pietrasiewicz 
1718443f2d2SAndrzej Pietrasiewicz static struct usb_endpoint_descriptor fs_ep_out_desc = {
1728443f2d2SAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
1738443f2d2SAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
1748443f2d2SAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_OUT,
1758443f2d2SAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK
1768443f2d2SAndrzej Pietrasiewicz };
1778443f2d2SAndrzej Pietrasiewicz 
1788443f2d2SAndrzej Pietrasiewicz static struct usb_descriptor_header *fs_printer_function[] = {
1798443f2d2SAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &intf_desc,
1808443f2d2SAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &fs_ep_in_desc,
1818443f2d2SAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &fs_ep_out_desc,
1828443f2d2SAndrzej Pietrasiewicz 	NULL
1838443f2d2SAndrzej Pietrasiewicz };
1848443f2d2SAndrzej Pietrasiewicz 
1858443f2d2SAndrzej Pietrasiewicz /*
1868443f2d2SAndrzej Pietrasiewicz  * usb 2.0 devices need to expose both high speed and full speed
1878443f2d2SAndrzej Pietrasiewicz  * descriptors, unless they only run at full speed.
1888443f2d2SAndrzej Pietrasiewicz  */
1898443f2d2SAndrzej Pietrasiewicz 
1908443f2d2SAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hs_ep_in_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_endpoint_descriptor hs_ep_out_desc = {
1988443f2d2SAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
1998443f2d2SAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
2008443f2d2SAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
2018443f2d2SAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(512)
2028443f2d2SAndrzej Pietrasiewicz };
2038443f2d2SAndrzej Pietrasiewicz 
2048443f2d2SAndrzej Pietrasiewicz static struct usb_qualifier_descriptor dev_qualifier = {
2058443f2d2SAndrzej Pietrasiewicz 	.bLength =		sizeof dev_qualifier,
2068443f2d2SAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_DEVICE_QUALIFIER,
2078443f2d2SAndrzej Pietrasiewicz 	.bcdUSB =		cpu_to_le16(0x0200),
2088443f2d2SAndrzej Pietrasiewicz 	.bDeviceClass =		USB_CLASS_PRINTER,
2098443f2d2SAndrzej Pietrasiewicz 	.bNumConfigurations =	1
2108443f2d2SAndrzej Pietrasiewicz };
2118443f2d2SAndrzej Pietrasiewicz 
2128443f2d2SAndrzej Pietrasiewicz static struct usb_descriptor_header *hs_printer_function[] = {
2138443f2d2SAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &intf_desc,
2148443f2d2SAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &hs_ep_in_desc,
2158443f2d2SAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &hs_ep_out_desc,
2168443f2d2SAndrzej Pietrasiewicz 	NULL
2178443f2d2SAndrzej Pietrasiewicz };
2188443f2d2SAndrzej Pietrasiewicz 
21974df41b4SJorge Ramirez-Ortiz /*
22074df41b4SJorge Ramirez-Ortiz  * Added endpoint descriptors for 3.0 devices
22174df41b4SJorge Ramirez-Ortiz  */
22274df41b4SJorge Ramirez-Ortiz 
22374df41b4SJorge Ramirez-Ortiz static struct usb_endpoint_descriptor ss_ep_in_desc = {
22474df41b4SJorge Ramirez-Ortiz 	.bLength =              USB_DT_ENDPOINT_SIZE,
22574df41b4SJorge Ramirez-Ortiz 	.bDescriptorType =      USB_DT_ENDPOINT,
22674df41b4SJorge Ramirez-Ortiz 	.bmAttributes =         USB_ENDPOINT_XFER_BULK,
22774df41b4SJorge Ramirez-Ortiz 	.wMaxPacketSize =       cpu_to_le16(1024),
22874df41b4SJorge Ramirez-Ortiz };
22974df41b4SJorge Ramirez-Ortiz 
230ef24d749Skbuild test robot static struct usb_ss_ep_comp_descriptor ss_ep_in_comp_desc = {
23174df41b4SJorge Ramirez-Ortiz 	.bLength =              sizeof(ss_ep_in_comp_desc),
23274df41b4SJorge Ramirez-Ortiz 	.bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
23374df41b4SJorge Ramirez-Ortiz };
23474df41b4SJorge Ramirez-Ortiz 
23574df41b4SJorge Ramirez-Ortiz static struct usb_endpoint_descriptor ss_ep_out_desc = {
23674df41b4SJorge Ramirez-Ortiz 	.bLength =              USB_DT_ENDPOINT_SIZE,
23774df41b4SJorge Ramirez-Ortiz 	.bDescriptorType =      USB_DT_ENDPOINT,
23874df41b4SJorge Ramirez-Ortiz 	.bmAttributes =         USB_ENDPOINT_XFER_BULK,
23974df41b4SJorge Ramirez-Ortiz 	.wMaxPacketSize =       cpu_to_le16(1024),
24074df41b4SJorge Ramirez-Ortiz };
24174df41b4SJorge Ramirez-Ortiz 
242ef24d749Skbuild test robot static struct usb_ss_ep_comp_descriptor ss_ep_out_comp_desc = {
24374df41b4SJorge Ramirez-Ortiz 	.bLength =              sizeof(ss_ep_out_comp_desc),
24474df41b4SJorge Ramirez-Ortiz 	.bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
24574df41b4SJorge Ramirez-Ortiz };
24674df41b4SJorge Ramirez-Ortiz 
24774df41b4SJorge Ramirez-Ortiz static struct usb_descriptor_header *ss_printer_function[] = {
24874df41b4SJorge Ramirez-Ortiz 	(struct usb_descriptor_header *) &intf_desc,
24974df41b4SJorge Ramirez-Ortiz 	(struct usb_descriptor_header *) &ss_ep_in_desc,
25074df41b4SJorge Ramirez-Ortiz 	(struct usb_descriptor_header *) &ss_ep_in_comp_desc,
25174df41b4SJorge Ramirez-Ortiz 	(struct usb_descriptor_header *) &ss_ep_out_desc,
25274df41b4SJorge Ramirez-Ortiz 	(struct usb_descriptor_header *) &ss_ep_out_comp_desc,
25374df41b4SJorge Ramirez-Ortiz 	NULL
25474df41b4SJorge Ramirez-Ortiz };
25574df41b4SJorge Ramirez-Ortiz 
2568443f2d2SAndrzej Pietrasiewicz static struct usb_otg_descriptor otg_descriptor = {
2578443f2d2SAndrzej Pietrasiewicz 	.bLength =              sizeof otg_descriptor,
2588443f2d2SAndrzej Pietrasiewicz 	.bDescriptorType =      USB_DT_OTG,
2598443f2d2SAndrzej Pietrasiewicz 	.bmAttributes =         USB_OTG_SRP,
2608443f2d2SAndrzej Pietrasiewicz };
2618443f2d2SAndrzej Pietrasiewicz 
2628443f2d2SAndrzej Pietrasiewicz static const struct usb_descriptor_header *otg_desc[] = {
2638443f2d2SAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &otg_descriptor,
2648443f2d2SAndrzej Pietrasiewicz 	NULL,
2658443f2d2SAndrzej Pietrasiewicz };
2668443f2d2SAndrzej Pietrasiewicz 
2678443f2d2SAndrzej Pietrasiewicz /* maxpacket and other transfer characteristics vary by speed. */
26874df41b4SJorge Ramirez-Ortiz static inline struct usb_endpoint_descriptor *ep_desc(struct usb_gadget *gadget,
26974df41b4SJorge Ramirez-Ortiz 					struct usb_endpoint_descriptor *fs,
27074df41b4SJorge Ramirez-Ortiz 					struct usb_endpoint_descriptor *hs,
27174df41b4SJorge Ramirez-Ortiz 					struct usb_endpoint_descriptor *ss)
27274df41b4SJorge Ramirez-Ortiz {
27374df41b4SJorge Ramirez-Ortiz 	switch (gadget->speed) {
27474df41b4SJorge Ramirez-Ortiz 	case USB_SPEED_SUPER:
27574df41b4SJorge Ramirez-Ortiz 		return ss;
27674df41b4SJorge Ramirez-Ortiz 	case USB_SPEED_HIGH:
27774df41b4SJorge Ramirez-Ortiz 		return hs;
27874df41b4SJorge Ramirez-Ortiz 	default:
27974df41b4SJorge Ramirez-Ortiz 		return fs;
28074df41b4SJorge Ramirez-Ortiz 	}
28174df41b4SJorge Ramirez-Ortiz }
2828443f2d2SAndrzej Pietrasiewicz 
2838443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
2848443f2d2SAndrzej Pietrasiewicz 
2858443f2d2SAndrzej Pietrasiewicz /* descriptors that are built on-demand */
2868443f2d2SAndrzej Pietrasiewicz 
287085617a1SAndrzej Pietrasiewicz #define PNP_STRING_LEN			1024
288085617a1SAndrzej Pietrasiewicz 
2898443f2d2SAndrzej Pietrasiewicz static char				product_desc [40] = DRIVER_DESC;
2908443f2d2SAndrzej Pietrasiewicz static char				serial_num [40] = "1";
291085617a1SAndrzej Pietrasiewicz static char				pnp_string[PNP_STRING_LEN] =
2928443f2d2SAndrzej Pietrasiewicz 	"XXMFG:linux;MDL:g_printer;CLS:PRINTER;SN:1;";
2938443f2d2SAndrzej Pietrasiewicz 
2948443f2d2SAndrzej Pietrasiewicz /* static strings, in UTF-8 */
2958443f2d2SAndrzej Pietrasiewicz static struct usb_string		strings [] = {
2968443f2d2SAndrzej Pietrasiewicz 	[USB_GADGET_MANUFACTURER_IDX].s = "",
2978443f2d2SAndrzej Pietrasiewicz 	[USB_GADGET_PRODUCT_IDX].s = product_desc,
2988443f2d2SAndrzej Pietrasiewicz 	[USB_GADGET_SERIAL_IDX].s =	serial_num,
2998443f2d2SAndrzej Pietrasiewicz 	{  }		/* end of list */
3008443f2d2SAndrzej Pietrasiewicz };
3018443f2d2SAndrzej Pietrasiewicz 
3028443f2d2SAndrzej Pietrasiewicz static struct usb_gadget_strings	stringtab_dev = {
3038443f2d2SAndrzej Pietrasiewicz 	.language	= 0x0409,	/* en-us */
3048443f2d2SAndrzej Pietrasiewicz 	.strings	= strings,
3058443f2d2SAndrzej Pietrasiewicz };
3068443f2d2SAndrzej Pietrasiewicz 
3078443f2d2SAndrzej Pietrasiewicz static struct usb_gadget_strings *dev_strings[] = {
3088443f2d2SAndrzej Pietrasiewicz 	&stringtab_dev,
3098443f2d2SAndrzej Pietrasiewicz 	NULL,
3108443f2d2SAndrzej Pietrasiewicz };
3118443f2d2SAndrzej Pietrasiewicz 
3128443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
3138443f2d2SAndrzej Pietrasiewicz 
3148443f2d2SAndrzej Pietrasiewicz static struct usb_request *
3158443f2d2SAndrzej Pietrasiewicz printer_req_alloc(struct usb_ep *ep, unsigned len, gfp_t gfp_flags)
3168443f2d2SAndrzej Pietrasiewicz {
3178443f2d2SAndrzej Pietrasiewicz 	struct usb_request	*req;
3188443f2d2SAndrzej Pietrasiewicz 
3198443f2d2SAndrzej Pietrasiewicz 	req = usb_ep_alloc_request(ep, gfp_flags);
3208443f2d2SAndrzej Pietrasiewicz 
3218443f2d2SAndrzej Pietrasiewicz 	if (req != NULL) {
3228443f2d2SAndrzej Pietrasiewicz 		req->length = len;
3238443f2d2SAndrzej Pietrasiewicz 		req->buf = kmalloc(len, gfp_flags);
3248443f2d2SAndrzej Pietrasiewicz 		if (req->buf == NULL) {
3258443f2d2SAndrzej Pietrasiewicz 			usb_ep_free_request(ep, req);
3268443f2d2SAndrzej Pietrasiewicz 			return NULL;
3278443f2d2SAndrzej Pietrasiewicz 		}
3288443f2d2SAndrzej Pietrasiewicz 	}
3298443f2d2SAndrzej Pietrasiewicz 
3308443f2d2SAndrzej Pietrasiewicz 	return req;
3318443f2d2SAndrzej Pietrasiewicz }
3328443f2d2SAndrzej Pietrasiewicz 
3338443f2d2SAndrzej Pietrasiewicz static void
3348443f2d2SAndrzej Pietrasiewicz printer_req_free(struct usb_ep *ep, struct usb_request *req)
3358443f2d2SAndrzej Pietrasiewicz {
3368443f2d2SAndrzej Pietrasiewicz 	if (ep != NULL && req != NULL) {
3378443f2d2SAndrzej Pietrasiewicz 		kfree(req->buf);
3388443f2d2SAndrzej Pietrasiewicz 		usb_ep_free_request(ep, req);
3398443f2d2SAndrzej Pietrasiewicz 	}
3408443f2d2SAndrzej Pietrasiewicz }
3418443f2d2SAndrzej Pietrasiewicz 
3428443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
3438443f2d2SAndrzej Pietrasiewicz 
3448443f2d2SAndrzej Pietrasiewicz static void rx_complete(struct usb_ep *ep, struct usb_request *req)
3458443f2d2SAndrzej Pietrasiewicz {
3468443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev = ep->driver_data;
3478443f2d2SAndrzej Pietrasiewicz 	int			status = req->status;
3488443f2d2SAndrzej Pietrasiewicz 	unsigned long		flags;
3498443f2d2SAndrzej Pietrasiewicz 
3508443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
3518443f2d2SAndrzej Pietrasiewicz 
3528443f2d2SAndrzej Pietrasiewicz 	list_del_init(&req->list);	/* Remode from Active List */
3538443f2d2SAndrzej Pietrasiewicz 
3548443f2d2SAndrzej Pietrasiewicz 	switch (status) {
3558443f2d2SAndrzej Pietrasiewicz 
3568443f2d2SAndrzej Pietrasiewicz 	/* normal completion */
3578443f2d2SAndrzej Pietrasiewicz 	case 0:
3588443f2d2SAndrzej Pietrasiewicz 		if (req->actual > 0) {
3598443f2d2SAndrzej Pietrasiewicz 			list_add_tail(&req->list, &dev->rx_buffers);
3608443f2d2SAndrzej Pietrasiewicz 			DBG(dev, "G_Printer : rx length %d\n", req->actual);
3618443f2d2SAndrzej Pietrasiewicz 		} else {
3628443f2d2SAndrzej Pietrasiewicz 			list_add(&req->list, &dev->rx_reqs);
3638443f2d2SAndrzej Pietrasiewicz 		}
3648443f2d2SAndrzej Pietrasiewicz 		break;
3658443f2d2SAndrzej Pietrasiewicz 
3668443f2d2SAndrzej Pietrasiewicz 	/* software-driven interface shutdown */
3678443f2d2SAndrzej Pietrasiewicz 	case -ECONNRESET:		/* unlink */
3688443f2d2SAndrzej Pietrasiewicz 	case -ESHUTDOWN:		/* disconnect etc */
3698443f2d2SAndrzej Pietrasiewicz 		VDBG(dev, "rx shutdown, code %d\n", status);
3708443f2d2SAndrzej Pietrasiewicz 		list_add(&req->list, &dev->rx_reqs);
3718443f2d2SAndrzej Pietrasiewicz 		break;
3728443f2d2SAndrzej Pietrasiewicz 
3738443f2d2SAndrzej Pietrasiewicz 	/* for hardware automagic (such as pxa) */
3748443f2d2SAndrzej Pietrasiewicz 	case -ECONNABORTED:		/* endpoint reset */
3758443f2d2SAndrzej Pietrasiewicz 		DBG(dev, "rx %s reset\n", ep->name);
3768443f2d2SAndrzej Pietrasiewicz 		list_add(&req->list, &dev->rx_reqs);
3778443f2d2SAndrzej Pietrasiewicz 		break;
3788443f2d2SAndrzej Pietrasiewicz 
3798443f2d2SAndrzej Pietrasiewicz 	/* data overrun */
3808443f2d2SAndrzej Pietrasiewicz 	case -EOVERFLOW:
3818443f2d2SAndrzej Pietrasiewicz 		/* FALLTHROUGH */
3828443f2d2SAndrzej Pietrasiewicz 
3838443f2d2SAndrzej Pietrasiewicz 	default:
3848443f2d2SAndrzej Pietrasiewicz 		DBG(dev, "rx status %d\n", status);
3858443f2d2SAndrzej Pietrasiewicz 		list_add(&req->list, &dev->rx_reqs);
3868443f2d2SAndrzej Pietrasiewicz 		break;
3878443f2d2SAndrzej Pietrasiewicz 	}
3888443f2d2SAndrzej Pietrasiewicz 
3898443f2d2SAndrzej Pietrasiewicz 	wake_up_interruptible(&dev->rx_wait);
3908443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
3918443f2d2SAndrzej Pietrasiewicz }
3928443f2d2SAndrzej Pietrasiewicz 
3938443f2d2SAndrzej Pietrasiewicz static void tx_complete(struct usb_ep *ep, struct usb_request *req)
3948443f2d2SAndrzej Pietrasiewicz {
3958443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev = ep->driver_data;
3968443f2d2SAndrzej Pietrasiewicz 
3978443f2d2SAndrzej Pietrasiewicz 	switch (req->status) {
3988443f2d2SAndrzej Pietrasiewicz 	default:
3998443f2d2SAndrzej Pietrasiewicz 		VDBG(dev, "tx err %d\n", req->status);
4008443f2d2SAndrzej Pietrasiewicz 		/* FALLTHROUGH */
4018443f2d2SAndrzej Pietrasiewicz 	case -ECONNRESET:		/* unlink */
4028443f2d2SAndrzej Pietrasiewicz 	case -ESHUTDOWN:		/* disconnect etc */
4038443f2d2SAndrzej Pietrasiewicz 		break;
4048443f2d2SAndrzej Pietrasiewicz 	case 0:
4058443f2d2SAndrzej Pietrasiewicz 		break;
4068443f2d2SAndrzej Pietrasiewicz 	}
4078443f2d2SAndrzej Pietrasiewicz 
4088443f2d2SAndrzej Pietrasiewicz 	spin_lock(&dev->lock);
4098443f2d2SAndrzej Pietrasiewicz 	/* Take the request struct off the active list and put it on the
4108443f2d2SAndrzej Pietrasiewicz 	 * free list.
4118443f2d2SAndrzej Pietrasiewicz 	 */
4128443f2d2SAndrzej Pietrasiewicz 	list_del_init(&req->list);
4138443f2d2SAndrzej Pietrasiewicz 	list_add(&req->list, &dev->tx_reqs);
4148443f2d2SAndrzej Pietrasiewicz 	wake_up_interruptible(&dev->tx_wait);
4158443f2d2SAndrzej Pietrasiewicz 	if (likely(list_empty(&dev->tx_reqs_active)))
4168443f2d2SAndrzej Pietrasiewicz 		wake_up_interruptible(&dev->tx_flush_wait);
4178443f2d2SAndrzej Pietrasiewicz 
4188443f2d2SAndrzej Pietrasiewicz 	spin_unlock(&dev->lock);
4198443f2d2SAndrzej Pietrasiewicz }
4208443f2d2SAndrzej Pietrasiewicz 
4218443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
4228443f2d2SAndrzej Pietrasiewicz 
4238443f2d2SAndrzej Pietrasiewicz static int
4248443f2d2SAndrzej Pietrasiewicz printer_open(struct inode *inode, struct file *fd)
4258443f2d2SAndrzej Pietrasiewicz {
4268443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev;
4278443f2d2SAndrzej Pietrasiewicz 	unsigned long		flags;
4288443f2d2SAndrzej Pietrasiewicz 	int			ret = -EBUSY;
4298443f2d2SAndrzej Pietrasiewicz 
4308443f2d2SAndrzej Pietrasiewicz 	dev = container_of(inode->i_cdev, struct printer_dev, printer_cdev);
4318443f2d2SAndrzej Pietrasiewicz 
4328443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
4338443f2d2SAndrzej Pietrasiewicz 
4348443f2d2SAndrzej Pietrasiewicz 	if (!dev->printer_cdev_open) {
4358443f2d2SAndrzej Pietrasiewicz 		dev->printer_cdev_open = 1;
4368443f2d2SAndrzej Pietrasiewicz 		fd->private_data = dev;
4378443f2d2SAndrzej Pietrasiewicz 		ret = 0;
4388443f2d2SAndrzej Pietrasiewicz 		/* Change the printer status to show that it's on-line. */
4398443f2d2SAndrzej Pietrasiewicz 		dev->printer_status |= PRINTER_SELECTED;
4408443f2d2SAndrzej Pietrasiewicz 	}
4418443f2d2SAndrzej Pietrasiewicz 
4428443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
4438443f2d2SAndrzej Pietrasiewicz 
4448443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "printer_open returned %x\n", ret);
4458443f2d2SAndrzej Pietrasiewicz 	return ret;
4468443f2d2SAndrzej Pietrasiewicz }
4478443f2d2SAndrzej Pietrasiewicz 
4488443f2d2SAndrzej Pietrasiewicz static int
4498443f2d2SAndrzej Pietrasiewicz printer_close(struct inode *inode, struct file *fd)
4508443f2d2SAndrzej Pietrasiewicz {
4518443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev = fd->private_data;
4528443f2d2SAndrzej Pietrasiewicz 	unsigned long		flags;
4538443f2d2SAndrzej Pietrasiewicz 
4548443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
4558443f2d2SAndrzej Pietrasiewicz 	dev->printer_cdev_open = 0;
4568443f2d2SAndrzej Pietrasiewicz 	fd->private_data = NULL;
4578443f2d2SAndrzej Pietrasiewicz 	/* Change printer status to show that the printer is off-line. */
4588443f2d2SAndrzej Pietrasiewicz 	dev->printer_status &= ~PRINTER_SELECTED;
4598443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
4608443f2d2SAndrzej Pietrasiewicz 
4618443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "printer_close\n");
4628443f2d2SAndrzej Pietrasiewicz 
4638443f2d2SAndrzej Pietrasiewicz 	return 0;
4648443f2d2SAndrzej Pietrasiewicz }
4658443f2d2SAndrzej Pietrasiewicz 
4668443f2d2SAndrzej Pietrasiewicz /* This function must be called with interrupts turned off. */
4678443f2d2SAndrzej Pietrasiewicz static void
4688443f2d2SAndrzej Pietrasiewicz setup_rx_reqs(struct printer_dev *dev)
4698443f2d2SAndrzej Pietrasiewicz {
4708443f2d2SAndrzej Pietrasiewicz 	struct usb_request              *req;
4718443f2d2SAndrzej Pietrasiewicz 
4728443f2d2SAndrzej Pietrasiewicz 	while (likely(!list_empty(&dev->rx_reqs))) {
4738443f2d2SAndrzej Pietrasiewicz 		int error;
4748443f2d2SAndrzej Pietrasiewicz 
4758443f2d2SAndrzej Pietrasiewicz 		req = container_of(dev->rx_reqs.next,
4768443f2d2SAndrzej Pietrasiewicz 				struct usb_request, list);
4778443f2d2SAndrzej Pietrasiewicz 		list_del_init(&req->list);
4788443f2d2SAndrzej Pietrasiewicz 
4798443f2d2SAndrzej Pietrasiewicz 		/* The USB Host sends us whatever amount of data it wants to
4808443f2d2SAndrzej Pietrasiewicz 		 * so we always set the length field to the full USB_BUFSIZE.
4818443f2d2SAndrzej Pietrasiewicz 		 * If the amount of data is more than the read() caller asked
4828443f2d2SAndrzej Pietrasiewicz 		 * for it will be stored in the request buffer until it is
4838443f2d2SAndrzej Pietrasiewicz 		 * asked for by read().
4848443f2d2SAndrzej Pietrasiewicz 		 */
4858443f2d2SAndrzej Pietrasiewicz 		req->length = USB_BUFSIZE;
4868443f2d2SAndrzej Pietrasiewicz 		req->complete = rx_complete;
4878443f2d2SAndrzej Pietrasiewicz 
4888443f2d2SAndrzej Pietrasiewicz 		/* here, we unlock, and only unlock, to avoid deadlock. */
4898443f2d2SAndrzej Pietrasiewicz 		spin_unlock(&dev->lock);
4908443f2d2SAndrzej Pietrasiewicz 		error = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC);
4918443f2d2SAndrzej Pietrasiewicz 		spin_lock(&dev->lock);
4928443f2d2SAndrzej Pietrasiewicz 		if (error) {
4938443f2d2SAndrzej Pietrasiewicz 			DBG(dev, "rx submit --> %d\n", error);
4948443f2d2SAndrzej Pietrasiewicz 			list_add(&req->list, &dev->rx_reqs);
4958443f2d2SAndrzej Pietrasiewicz 			break;
4968443f2d2SAndrzej Pietrasiewicz 		}
4978443f2d2SAndrzej Pietrasiewicz 		/* if the req is empty, then add it into dev->rx_reqs_active. */
4988443f2d2SAndrzej Pietrasiewicz 		else if (list_empty(&req->list)) {
4998443f2d2SAndrzej Pietrasiewicz 			list_add(&req->list, &dev->rx_reqs_active);
5008443f2d2SAndrzej Pietrasiewicz 		}
5018443f2d2SAndrzej Pietrasiewicz 	}
5028443f2d2SAndrzej Pietrasiewicz }
5038443f2d2SAndrzej Pietrasiewicz 
5048443f2d2SAndrzej Pietrasiewicz static ssize_t
5058443f2d2SAndrzej Pietrasiewicz printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
5068443f2d2SAndrzej Pietrasiewicz {
5078443f2d2SAndrzej Pietrasiewicz 	struct printer_dev		*dev = fd->private_data;
5088443f2d2SAndrzej Pietrasiewicz 	unsigned long			flags;
5098443f2d2SAndrzej Pietrasiewicz 	size_t				size;
5108443f2d2SAndrzej Pietrasiewicz 	size_t				bytes_copied;
5118443f2d2SAndrzej Pietrasiewicz 	struct usb_request		*req;
5128443f2d2SAndrzej Pietrasiewicz 	/* This is a pointer to the current USB rx request. */
5138443f2d2SAndrzej Pietrasiewicz 	struct usb_request		*current_rx_req;
5148443f2d2SAndrzej Pietrasiewicz 	/* This is the number of bytes in the current rx buffer. */
5158443f2d2SAndrzej Pietrasiewicz 	size_t				current_rx_bytes;
5168443f2d2SAndrzej Pietrasiewicz 	/* This is a pointer to the current rx buffer. */
5178443f2d2SAndrzej Pietrasiewicz 	u8				*current_rx_buf;
5188443f2d2SAndrzej Pietrasiewicz 
5198443f2d2SAndrzej Pietrasiewicz 	if (len == 0)
5208443f2d2SAndrzej Pietrasiewicz 		return -EINVAL;
5218443f2d2SAndrzej Pietrasiewicz 
5228443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "printer_read trying to read %d bytes\n", (int)len);
5238443f2d2SAndrzej Pietrasiewicz 
5248443f2d2SAndrzej Pietrasiewicz 	mutex_lock(&dev->lock_printer_io);
5258443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
5268443f2d2SAndrzej Pietrasiewicz 
5278443f2d2SAndrzej Pietrasiewicz 	/* We will use this flag later to check if a printer reset happened
5288443f2d2SAndrzej Pietrasiewicz 	 * after we turn interrupts back on.
5298443f2d2SAndrzej Pietrasiewicz 	 */
5308443f2d2SAndrzej Pietrasiewicz 	dev->reset_printer = 0;
5318443f2d2SAndrzej Pietrasiewicz 
5328443f2d2SAndrzej Pietrasiewicz 	setup_rx_reqs(dev);
5338443f2d2SAndrzej Pietrasiewicz 
5348443f2d2SAndrzej Pietrasiewicz 	bytes_copied = 0;
5358443f2d2SAndrzej Pietrasiewicz 	current_rx_req = dev->current_rx_req;
5368443f2d2SAndrzej Pietrasiewicz 	current_rx_bytes = dev->current_rx_bytes;
5378443f2d2SAndrzej Pietrasiewicz 	current_rx_buf = dev->current_rx_buf;
5388443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_req = NULL;
5398443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_bytes = 0;
5408443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_buf = NULL;
5418443f2d2SAndrzej Pietrasiewicz 
5428443f2d2SAndrzej Pietrasiewicz 	/* Check if there is any data in the read buffers. Please note that
5438443f2d2SAndrzej Pietrasiewicz 	 * current_rx_bytes is the number of bytes in the current rx buffer.
5448443f2d2SAndrzej Pietrasiewicz 	 * If it is zero then check if there are any other rx_buffers that
5458443f2d2SAndrzej Pietrasiewicz 	 * are on the completed list. We are only out of data if all rx
5468443f2d2SAndrzej Pietrasiewicz 	 * buffers are empty.
5478443f2d2SAndrzej Pietrasiewicz 	 */
5488443f2d2SAndrzej Pietrasiewicz 	if ((current_rx_bytes == 0) &&
5498443f2d2SAndrzej Pietrasiewicz 			(likely(list_empty(&dev->rx_buffers)))) {
5508443f2d2SAndrzej Pietrasiewicz 		/* Turn interrupts back on before sleeping. */
5518443f2d2SAndrzej Pietrasiewicz 		spin_unlock_irqrestore(&dev->lock, flags);
5528443f2d2SAndrzej Pietrasiewicz 
5538443f2d2SAndrzej Pietrasiewicz 		/*
5548443f2d2SAndrzej Pietrasiewicz 		 * If no data is available check if this is a NON-Blocking
5558443f2d2SAndrzej Pietrasiewicz 		 * call or not.
5568443f2d2SAndrzej Pietrasiewicz 		 */
5578443f2d2SAndrzej Pietrasiewicz 		if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) {
5588443f2d2SAndrzej Pietrasiewicz 			mutex_unlock(&dev->lock_printer_io);
5598443f2d2SAndrzej Pietrasiewicz 			return -EAGAIN;
5608443f2d2SAndrzej Pietrasiewicz 		}
5618443f2d2SAndrzej Pietrasiewicz 
5628443f2d2SAndrzej Pietrasiewicz 		/* Sleep until data is available */
5638443f2d2SAndrzej Pietrasiewicz 		wait_event_interruptible(dev->rx_wait,
5648443f2d2SAndrzej Pietrasiewicz 				(likely(!list_empty(&dev->rx_buffers))));
5658443f2d2SAndrzej Pietrasiewicz 		spin_lock_irqsave(&dev->lock, flags);
5668443f2d2SAndrzej Pietrasiewicz 	}
5678443f2d2SAndrzej Pietrasiewicz 
5688443f2d2SAndrzej Pietrasiewicz 	/* We have data to return then copy it to the caller's buffer.*/
5698443f2d2SAndrzej Pietrasiewicz 	while ((current_rx_bytes || likely(!list_empty(&dev->rx_buffers)))
5708443f2d2SAndrzej Pietrasiewicz 			&& len) {
5718443f2d2SAndrzej Pietrasiewicz 		if (current_rx_bytes == 0) {
5728443f2d2SAndrzej Pietrasiewicz 			req = container_of(dev->rx_buffers.next,
5738443f2d2SAndrzej Pietrasiewicz 					struct usb_request, list);
5748443f2d2SAndrzej Pietrasiewicz 			list_del_init(&req->list);
5758443f2d2SAndrzej Pietrasiewicz 
5768443f2d2SAndrzej Pietrasiewicz 			if (req->actual && req->buf) {
5778443f2d2SAndrzej Pietrasiewicz 				current_rx_req = req;
5788443f2d2SAndrzej Pietrasiewicz 				current_rx_bytes = req->actual;
5798443f2d2SAndrzej Pietrasiewicz 				current_rx_buf = req->buf;
5808443f2d2SAndrzej Pietrasiewicz 			} else {
5818443f2d2SAndrzej Pietrasiewicz 				list_add(&req->list, &dev->rx_reqs);
5828443f2d2SAndrzej Pietrasiewicz 				continue;
5838443f2d2SAndrzej Pietrasiewicz 			}
5848443f2d2SAndrzej Pietrasiewicz 		}
5858443f2d2SAndrzej Pietrasiewicz 
5868443f2d2SAndrzej Pietrasiewicz 		/* Don't leave irqs off while doing memory copies */
5878443f2d2SAndrzej Pietrasiewicz 		spin_unlock_irqrestore(&dev->lock, flags);
5888443f2d2SAndrzej Pietrasiewicz 
5898443f2d2SAndrzej Pietrasiewicz 		if (len > current_rx_bytes)
5908443f2d2SAndrzej Pietrasiewicz 			size = current_rx_bytes;
5918443f2d2SAndrzej Pietrasiewicz 		else
5928443f2d2SAndrzej Pietrasiewicz 			size = len;
5938443f2d2SAndrzej Pietrasiewicz 
5948443f2d2SAndrzej Pietrasiewicz 		size -= copy_to_user(buf, current_rx_buf, size);
5958443f2d2SAndrzej Pietrasiewicz 		bytes_copied += size;
5968443f2d2SAndrzej Pietrasiewicz 		len -= size;
5978443f2d2SAndrzej Pietrasiewicz 		buf += size;
5988443f2d2SAndrzej Pietrasiewicz 
5998443f2d2SAndrzej Pietrasiewicz 		spin_lock_irqsave(&dev->lock, flags);
6008443f2d2SAndrzej Pietrasiewicz 
6018443f2d2SAndrzej Pietrasiewicz 		/* We've disconnected or reset so return. */
6028443f2d2SAndrzej Pietrasiewicz 		if (dev->reset_printer) {
6038443f2d2SAndrzej Pietrasiewicz 			list_add(&current_rx_req->list, &dev->rx_reqs);
6048443f2d2SAndrzej Pietrasiewicz 			spin_unlock_irqrestore(&dev->lock, flags);
6058443f2d2SAndrzej Pietrasiewicz 			mutex_unlock(&dev->lock_printer_io);
6068443f2d2SAndrzej Pietrasiewicz 			return -EAGAIN;
6078443f2d2SAndrzej Pietrasiewicz 		}
6088443f2d2SAndrzej Pietrasiewicz 
6098443f2d2SAndrzej Pietrasiewicz 		/* If we not returning all the data left in this RX request
6108443f2d2SAndrzej Pietrasiewicz 		 * buffer then adjust the amount of data left in the buffer.
6118443f2d2SAndrzej Pietrasiewicz 		 * Othewise if we are done with this RX request buffer then
6128443f2d2SAndrzej Pietrasiewicz 		 * requeue it to get any incoming data from the USB host.
6138443f2d2SAndrzej Pietrasiewicz 		 */
6148443f2d2SAndrzej Pietrasiewicz 		if (size < current_rx_bytes) {
6158443f2d2SAndrzej Pietrasiewicz 			current_rx_bytes -= size;
6168443f2d2SAndrzej Pietrasiewicz 			current_rx_buf += size;
6178443f2d2SAndrzej Pietrasiewicz 		} else {
6188443f2d2SAndrzej Pietrasiewicz 			list_add(&current_rx_req->list, &dev->rx_reqs);
6198443f2d2SAndrzej Pietrasiewicz 			current_rx_bytes = 0;
6208443f2d2SAndrzej Pietrasiewicz 			current_rx_buf = NULL;
6218443f2d2SAndrzej Pietrasiewicz 			current_rx_req = NULL;
6228443f2d2SAndrzej Pietrasiewicz 		}
6238443f2d2SAndrzej Pietrasiewicz 	}
6248443f2d2SAndrzej Pietrasiewicz 
6258443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_req = current_rx_req;
6268443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_bytes = current_rx_bytes;
6278443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_buf = current_rx_buf;
6288443f2d2SAndrzej Pietrasiewicz 
6298443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
6308443f2d2SAndrzej Pietrasiewicz 	mutex_unlock(&dev->lock_printer_io);
6318443f2d2SAndrzej Pietrasiewicz 
6328443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "printer_read returned %d bytes\n", (int)bytes_copied);
6338443f2d2SAndrzej Pietrasiewicz 
6348443f2d2SAndrzej Pietrasiewicz 	if (bytes_copied)
6358443f2d2SAndrzej Pietrasiewicz 		return bytes_copied;
6368443f2d2SAndrzej Pietrasiewicz 	else
6378443f2d2SAndrzej Pietrasiewicz 		return -EAGAIN;
6388443f2d2SAndrzej Pietrasiewicz }
6398443f2d2SAndrzej Pietrasiewicz 
6408443f2d2SAndrzej Pietrasiewicz static ssize_t
6418443f2d2SAndrzej Pietrasiewicz printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
6428443f2d2SAndrzej Pietrasiewicz {
6438443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev = fd->private_data;
6448443f2d2SAndrzej Pietrasiewicz 	unsigned long		flags;
6458443f2d2SAndrzej Pietrasiewicz 	size_t			size;	/* Amount of data in a TX request. */
6468443f2d2SAndrzej Pietrasiewicz 	size_t			bytes_copied = 0;
6478443f2d2SAndrzej Pietrasiewicz 	struct usb_request	*req;
6488443f2d2SAndrzej Pietrasiewicz 
6498443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "printer_write trying to send %d bytes\n", (int)len);
6508443f2d2SAndrzej Pietrasiewicz 
6518443f2d2SAndrzej Pietrasiewicz 	if (len == 0)
6528443f2d2SAndrzej Pietrasiewicz 		return -EINVAL;
6538443f2d2SAndrzej Pietrasiewicz 
6548443f2d2SAndrzej Pietrasiewicz 	mutex_lock(&dev->lock_printer_io);
6558443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
6568443f2d2SAndrzej Pietrasiewicz 
6578443f2d2SAndrzej Pietrasiewicz 	/* Check if a printer reset happens while we have interrupts on */
6588443f2d2SAndrzej Pietrasiewicz 	dev->reset_printer = 0;
6598443f2d2SAndrzej Pietrasiewicz 
6608443f2d2SAndrzej Pietrasiewicz 	/* Check if there is any available write buffers */
6618443f2d2SAndrzej Pietrasiewicz 	if (likely(list_empty(&dev->tx_reqs))) {
6628443f2d2SAndrzej Pietrasiewicz 		/* Turn interrupts back on before sleeping. */
6638443f2d2SAndrzej Pietrasiewicz 		spin_unlock_irqrestore(&dev->lock, flags);
6648443f2d2SAndrzej Pietrasiewicz 
6658443f2d2SAndrzej Pietrasiewicz 		/*
6668443f2d2SAndrzej Pietrasiewicz 		 * If write buffers are available check if this is
6678443f2d2SAndrzej Pietrasiewicz 		 * a NON-Blocking call or not.
6688443f2d2SAndrzej Pietrasiewicz 		 */
6698443f2d2SAndrzej Pietrasiewicz 		if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) {
6708443f2d2SAndrzej Pietrasiewicz 			mutex_unlock(&dev->lock_printer_io);
6718443f2d2SAndrzej Pietrasiewicz 			return -EAGAIN;
6728443f2d2SAndrzej Pietrasiewicz 		}
6738443f2d2SAndrzej Pietrasiewicz 
6748443f2d2SAndrzej Pietrasiewicz 		/* Sleep until a write buffer is available */
6758443f2d2SAndrzej Pietrasiewicz 		wait_event_interruptible(dev->tx_wait,
6768443f2d2SAndrzej Pietrasiewicz 				(likely(!list_empty(&dev->tx_reqs))));
6778443f2d2SAndrzej Pietrasiewicz 		spin_lock_irqsave(&dev->lock, flags);
6788443f2d2SAndrzej Pietrasiewicz 	}
6798443f2d2SAndrzej Pietrasiewicz 
6808443f2d2SAndrzej Pietrasiewicz 	while (likely(!list_empty(&dev->tx_reqs)) && len) {
6818443f2d2SAndrzej Pietrasiewicz 
6828443f2d2SAndrzej Pietrasiewicz 		if (len > USB_BUFSIZE)
6838443f2d2SAndrzej Pietrasiewicz 			size = USB_BUFSIZE;
6848443f2d2SAndrzej Pietrasiewicz 		else
6858443f2d2SAndrzej Pietrasiewicz 			size = len;
6868443f2d2SAndrzej Pietrasiewicz 
6878443f2d2SAndrzej Pietrasiewicz 		req = container_of(dev->tx_reqs.next, struct usb_request,
6888443f2d2SAndrzej Pietrasiewicz 				list);
6898443f2d2SAndrzej Pietrasiewicz 		list_del_init(&req->list);
6908443f2d2SAndrzej Pietrasiewicz 
6918443f2d2SAndrzej Pietrasiewicz 		req->complete = tx_complete;
6928443f2d2SAndrzej Pietrasiewicz 		req->length = size;
6938443f2d2SAndrzej Pietrasiewicz 
6948443f2d2SAndrzej Pietrasiewicz 		/* Check if we need to send a zero length packet. */
6958443f2d2SAndrzej Pietrasiewicz 		if (len > size)
6968443f2d2SAndrzej Pietrasiewicz 			/* They will be more TX requests so no yet. */
6978443f2d2SAndrzej Pietrasiewicz 			req->zero = 0;
6988443f2d2SAndrzej Pietrasiewicz 		else
6998443f2d2SAndrzej Pietrasiewicz 			/* If the data amount is not a multple of the
7008443f2d2SAndrzej Pietrasiewicz 			 * maxpacket size then send a zero length packet.
7018443f2d2SAndrzej Pietrasiewicz 			 */
7028443f2d2SAndrzej Pietrasiewicz 			req->zero = ((len % dev->in_ep->maxpacket) == 0);
7038443f2d2SAndrzej Pietrasiewicz 
7048443f2d2SAndrzej Pietrasiewicz 		/* Don't leave irqs off while doing memory copies */
7058443f2d2SAndrzej Pietrasiewicz 		spin_unlock_irqrestore(&dev->lock, flags);
7068443f2d2SAndrzej Pietrasiewicz 
7078443f2d2SAndrzej Pietrasiewicz 		if (copy_from_user(req->buf, buf, size)) {
7088443f2d2SAndrzej Pietrasiewicz 			list_add(&req->list, &dev->tx_reqs);
7098443f2d2SAndrzej Pietrasiewicz 			mutex_unlock(&dev->lock_printer_io);
7108443f2d2SAndrzej Pietrasiewicz 			return bytes_copied;
7118443f2d2SAndrzej Pietrasiewicz 		}
7128443f2d2SAndrzej Pietrasiewicz 
7138443f2d2SAndrzej Pietrasiewicz 		bytes_copied += size;
7148443f2d2SAndrzej Pietrasiewicz 		len -= size;
7158443f2d2SAndrzej Pietrasiewicz 		buf += size;
7168443f2d2SAndrzej Pietrasiewicz 
7178443f2d2SAndrzej Pietrasiewicz 		spin_lock_irqsave(&dev->lock, flags);
7188443f2d2SAndrzej Pietrasiewicz 
7198443f2d2SAndrzej Pietrasiewicz 		/* We've disconnected or reset so free the req and buffer */
7208443f2d2SAndrzej Pietrasiewicz 		if (dev->reset_printer) {
7218443f2d2SAndrzej Pietrasiewicz 			list_add(&req->list, &dev->tx_reqs);
7228443f2d2SAndrzej Pietrasiewicz 			spin_unlock_irqrestore(&dev->lock, flags);
7238443f2d2SAndrzej Pietrasiewicz 			mutex_unlock(&dev->lock_printer_io);
7248443f2d2SAndrzej Pietrasiewicz 			return -EAGAIN;
7258443f2d2SAndrzej Pietrasiewicz 		}
7268443f2d2SAndrzej Pietrasiewicz 
7278443f2d2SAndrzej Pietrasiewicz 		if (usb_ep_queue(dev->in_ep, req, GFP_ATOMIC)) {
7288443f2d2SAndrzej Pietrasiewicz 			list_add(&req->list, &dev->tx_reqs);
7298443f2d2SAndrzej Pietrasiewicz 			spin_unlock_irqrestore(&dev->lock, flags);
7308443f2d2SAndrzej Pietrasiewicz 			mutex_unlock(&dev->lock_printer_io);
7318443f2d2SAndrzej Pietrasiewicz 			return -EAGAIN;
7328443f2d2SAndrzej Pietrasiewicz 		}
7338443f2d2SAndrzej Pietrasiewicz 
7348443f2d2SAndrzej Pietrasiewicz 		list_add(&req->list, &dev->tx_reqs_active);
7358443f2d2SAndrzej Pietrasiewicz 
7368443f2d2SAndrzej Pietrasiewicz 	}
7378443f2d2SAndrzej Pietrasiewicz 
7388443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
7398443f2d2SAndrzej Pietrasiewicz 	mutex_unlock(&dev->lock_printer_io);
7408443f2d2SAndrzej Pietrasiewicz 
7418443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "printer_write sent %d bytes\n", (int)bytes_copied);
7428443f2d2SAndrzej Pietrasiewicz 
7438443f2d2SAndrzej Pietrasiewicz 	if (bytes_copied) {
7448443f2d2SAndrzej Pietrasiewicz 		return bytes_copied;
7458443f2d2SAndrzej Pietrasiewicz 	} else {
7468443f2d2SAndrzej Pietrasiewicz 		return -EAGAIN;
7478443f2d2SAndrzej Pietrasiewicz 	}
7488443f2d2SAndrzej Pietrasiewicz }
7498443f2d2SAndrzej Pietrasiewicz 
7508443f2d2SAndrzej Pietrasiewicz static int
7518443f2d2SAndrzej Pietrasiewicz printer_fsync(struct file *fd, loff_t start, loff_t end, int datasync)
7528443f2d2SAndrzej Pietrasiewicz {
7538443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev = fd->private_data;
7548443f2d2SAndrzej Pietrasiewicz 	struct inode *inode = file_inode(fd);
7558443f2d2SAndrzej Pietrasiewicz 	unsigned long		flags;
7568443f2d2SAndrzej Pietrasiewicz 	int			tx_list_empty;
7578443f2d2SAndrzej Pietrasiewicz 
7588443f2d2SAndrzej Pietrasiewicz 	mutex_lock(&inode->i_mutex);
7598443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
7608443f2d2SAndrzej Pietrasiewicz 	tx_list_empty = (likely(list_empty(&dev->tx_reqs)));
7618443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
7628443f2d2SAndrzej Pietrasiewicz 
7638443f2d2SAndrzej Pietrasiewicz 	if (!tx_list_empty) {
7648443f2d2SAndrzej Pietrasiewicz 		/* Sleep until all data has been sent */
7658443f2d2SAndrzej Pietrasiewicz 		wait_event_interruptible(dev->tx_flush_wait,
7668443f2d2SAndrzej Pietrasiewicz 				(likely(list_empty(&dev->tx_reqs_active))));
7678443f2d2SAndrzej Pietrasiewicz 	}
7688443f2d2SAndrzej Pietrasiewicz 	mutex_unlock(&inode->i_mutex);
7698443f2d2SAndrzej Pietrasiewicz 
7708443f2d2SAndrzej Pietrasiewicz 	return 0;
7718443f2d2SAndrzej Pietrasiewicz }
7728443f2d2SAndrzej Pietrasiewicz 
7738443f2d2SAndrzej Pietrasiewicz static unsigned int
7748443f2d2SAndrzej Pietrasiewicz printer_poll(struct file *fd, poll_table *wait)
7758443f2d2SAndrzej Pietrasiewicz {
7768443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev = fd->private_data;
7778443f2d2SAndrzej Pietrasiewicz 	unsigned long		flags;
7788443f2d2SAndrzej Pietrasiewicz 	int			status = 0;
7798443f2d2SAndrzej Pietrasiewicz 
7808443f2d2SAndrzej Pietrasiewicz 	mutex_lock(&dev->lock_printer_io);
7818443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
7828443f2d2SAndrzej Pietrasiewicz 	setup_rx_reqs(dev);
7838443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
7848443f2d2SAndrzej Pietrasiewicz 	mutex_unlock(&dev->lock_printer_io);
7858443f2d2SAndrzej Pietrasiewicz 
7868443f2d2SAndrzej Pietrasiewicz 	poll_wait(fd, &dev->rx_wait, wait);
7878443f2d2SAndrzej Pietrasiewicz 	poll_wait(fd, &dev->tx_wait, wait);
7888443f2d2SAndrzej Pietrasiewicz 
7898443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
7908443f2d2SAndrzej Pietrasiewicz 	if (likely(!list_empty(&dev->tx_reqs)))
7918443f2d2SAndrzej Pietrasiewicz 		status |= POLLOUT | POLLWRNORM;
7928443f2d2SAndrzej Pietrasiewicz 
7938443f2d2SAndrzej Pietrasiewicz 	if (likely(dev->current_rx_bytes) ||
7948443f2d2SAndrzej Pietrasiewicz 			likely(!list_empty(&dev->rx_buffers)))
7958443f2d2SAndrzej Pietrasiewicz 		status |= POLLIN | POLLRDNORM;
7968443f2d2SAndrzej Pietrasiewicz 
7978443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
7988443f2d2SAndrzej Pietrasiewicz 
7998443f2d2SAndrzej Pietrasiewicz 	return status;
8008443f2d2SAndrzej Pietrasiewicz }
8018443f2d2SAndrzej Pietrasiewicz 
8028443f2d2SAndrzej Pietrasiewicz static long
8038443f2d2SAndrzej Pietrasiewicz printer_ioctl(struct file *fd, unsigned int code, unsigned long arg)
8048443f2d2SAndrzej Pietrasiewicz {
8058443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev = fd->private_data;
8068443f2d2SAndrzej Pietrasiewicz 	unsigned long		flags;
8078443f2d2SAndrzej Pietrasiewicz 	int			status = 0;
8088443f2d2SAndrzej Pietrasiewicz 
8098443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "printer_ioctl: cmd=0x%4.4x, arg=%lu\n", code, arg);
8108443f2d2SAndrzej Pietrasiewicz 
8118443f2d2SAndrzej Pietrasiewicz 	/* handle ioctls */
8128443f2d2SAndrzej Pietrasiewicz 
8138443f2d2SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
8148443f2d2SAndrzej Pietrasiewicz 
8158443f2d2SAndrzej Pietrasiewicz 	switch (code) {
8168443f2d2SAndrzej Pietrasiewicz 	case GADGET_GET_PRINTER_STATUS:
8178443f2d2SAndrzej Pietrasiewicz 		status = (int)dev->printer_status;
8188443f2d2SAndrzej Pietrasiewicz 		break;
8198443f2d2SAndrzej Pietrasiewicz 	case GADGET_SET_PRINTER_STATUS:
8208443f2d2SAndrzej Pietrasiewicz 		dev->printer_status = (u8)arg;
8218443f2d2SAndrzej Pietrasiewicz 		break;
8228443f2d2SAndrzej Pietrasiewicz 	default:
8238443f2d2SAndrzej Pietrasiewicz 		/* could not handle ioctl */
8248443f2d2SAndrzej Pietrasiewicz 		DBG(dev, "printer_ioctl: ERROR cmd=0x%4.4xis not supported\n",
8258443f2d2SAndrzej Pietrasiewicz 				code);
8268443f2d2SAndrzej Pietrasiewicz 		status = -ENOTTY;
8278443f2d2SAndrzej Pietrasiewicz 	}
8288443f2d2SAndrzej Pietrasiewicz 
8298443f2d2SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
8308443f2d2SAndrzej Pietrasiewicz 
8318443f2d2SAndrzej Pietrasiewicz 	return status;
8328443f2d2SAndrzej Pietrasiewicz }
8338443f2d2SAndrzej Pietrasiewicz 
8348443f2d2SAndrzej Pietrasiewicz /* used after endpoint configuration */
8358443f2d2SAndrzej Pietrasiewicz static const struct file_operations printer_io_operations = {
8368443f2d2SAndrzej Pietrasiewicz 	.owner =	THIS_MODULE,
8378443f2d2SAndrzej Pietrasiewicz 	.open =		printer_open,
8388443f2d2SAndrzej Pietrasiewicz 	.read =		printer_read,
8398443f2d2SAndrzej Pietrasiewicz 	.write =	printer_write,
8408443f2d2SAndrzej Pietrasiewicz 	.fsync =	printer_fsync,
8418443f2d2SAndrzej Pietrasiewicz 	.poll =		printer_poll,
8428443f2d2SAndrzej Pietrasiewicz 	.unlocked_ioctl = printer_ioctl,
8438443f2d2SAndrzej Pietrasiewicz 	.release =	printer_close,
8448443f2d2SAndrzej Pietrasiewicz 	.llseek =	noop_llseek,
8458443f2d2SAndrzej Pietrasiewicz };
8468443f2d2SAndrzej Pietrasiewicz 
8478443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
8488443f2d2SAndrzej Pietrasiewicz 
8498443f2d2SAndrzej Pietrasiewicz static int
8508443f2d2SAndrzej Pietrasiewicz set_printer_interface(struct printer_dev *dev)
8518443f2d2SAndrzej Pietrasiewicz {
8528443f2d2SAndrzej Pietrasiewicz 	int			result = 0;
8538443f2d2SAndrzej Pietrasiewicz 
85474df41b4SJorge Ramirez-Ortiz 	dev->in_ep->desc = ep_desc(dev->gadget, &fs_ep_in_desc, &hs_ep_in_desc,
85574df41b4SJorge Ramirez-Ortiz 				&ss_ep_in_desc);
8568443f2d2SAndrzej Pietrasiewicz 	dev->in_ep->driver_data = dev;
8578443f2d2SAndrzej Pietrasiewicz 
85874df41b4SJorge Ramirez-Ortiz 	dev->out_ep->desc = ep_desc(dev->gadget, &fs_ep_out_desc,
85974df41b4SJorge Ramirez-Ortiz 				    &hs_ep_out_desc, &ss_ep_out_desc);
8608443f2d2SAndrzej Pietrasiewicz 	dev->out_ep->driver_data = dev;
8618443f2d2SAndrzej Pietrasiewicz 
8628443f2d2SAndrzej Pietrasiewicz 	result = usb_ep_enable(dev->in_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 	result = usb_ep_enable(dev->out_ep);
8698443f2d2SAndrzej Pietrasiewicz 	if (result != 0) {
8708443f2d2SAndrzej Pietrasiewicz 		DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result);
8718443f2d2SAndrzej Pietrasiewicz 		goto done;
8728443f2d2SAndrzej Pietrasiewicz 	}
8738443f2d2SAndrzej Pietrasiewicz 
8748443f2d2SAndrzej Pietrasiewicz done:
8758443f2d2SAndrzej Pietrasiewicz 	/* on error, disable any endpoints  */
8768443f2d2SAndrzej Pietrasiewicz 	if (result != 0) {
8778443f2d2SAndrzej Pietrasiewicz 		(void) usb_ep_disable(dev->in_ep);
8788443f2d2SAndrzej Pietrasiewicz 		(void) usb_ep_disable(dev->out_ep);
8798443f2d2SAndrzej Pietrasiewicz 		dev->in_ep->desc = NULL;
8808443f2d2SAndrzej Pietrasiewicz 		dev->out_ep->desc = NULL;
8818443f2d2SAndrzej Pietrasiewicz 	}
8828443f2d2SAndrzej Pietrasiewicz 
8838443f2d2SAndrzej Pietrasiewicz 	/* caller is responsible for cleanup on error */
8848443f2d2SAndrzej Pietrasiewicz 	return result;
8858443f2d2SAndrzej Pietrasiewicz }
8868443f2d2SAndrzej Pietrasiewicz 
8878443f2d2SAndrzej Pietrasiewicz static void printer_reset_interface(struct printer_dev *dev)
8888443f2d2SAndrzej Pietrasiewicz {
8898443f2d2SAndrzej Pietrasiewicz 	if (dev->interface < 0)
8908443f2d2SAndrzej Pietrasiewicz 		return;
8918443f2d2SAndrzej Pietrasiewicz 
8928443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "%s\n", __func__);
8938443f2d2SAndrzej Pietrasiewicz 
8948443f2d2SAndrzej Pietrasiewicz 	if (dev->in_ep->desc)
8958443f2d2SAndrzej Pietrasiewicz 		usb_ep_disable(dev->in_ep);
8968443f2d2SAndrzej Pietrasiewicz 
8978443f2d2SAndrzej Pietrasiewicz 	if (dev->out_ep->desc)
8988443f2d2SAndrzej Pietrasiewicz 		usb_ep_disable(dev->out_ep);
8998443f2d2SAndrzej Pietrasiewicz 
9008443f2d2SAndrzej Pietrasiewicz 	dev->in_ep->desc = NULL;
9018443f2d2SAndrzej Pietrasiewicz 	dev->out_ep->desc = NULL;
9028443f2d2SAndrzej Pietrasiewicz 	dev->interface = -1;
9038443f2d2SAndrzej Pietrasiewicz }
9048443f2d2SAndrzej Pietrasiewicz 
9058443f2d2SAndrzej Pietrasiewicz /* Change our operational Interface. */
9068443f2d2SAndrzej Pietrasiewicz static int set_interface(struct printer_dev *dev, unsigned number)
9078443f2d2SAndrzej Pietrasiewicz {
9088443f2d2SAndrzej Pietrasiewicz 	int			result = 0;
9098443f2d2SAndrzej Pietrasiewicz 
9108443f2d2SAndrzej Pietrasiewicz 	/* Free the current interface */
9118443f2d2SAndrzej Pietrasiewicz 	printer_reset_interface(dev);
9128443f2d2SAndrzej Pietrasiewicz 
9138443f2d2SAndrzej Pietrasiewicz 	result = set_printer_interface(dev);
9148443f2d2SAndrzej Pietrasiewicz 	if (result)
9158443f2d2SAndrzej Pietrasiewicz 		printer_reset_interface(dev);
9168443f2d2SAndrzej Pietrasiewicz 	else
9178443f2d2SAndrzej Pietrasiewicz 		dev->interface = number;
9188443f2d2SAndrzej Pietrasiewicz 
9198443f2d2SAndrzej Pietrasiewicz 	if (!result)
9208443f2d2SAndrzej Pietrasiewicz 		INFO(dev, "Using interface %x\n", number);
9218443f2d2SAndrzej Pietrasiewicz 
9228443f2d2SAndrzej Pietrasiewicz 	return result;
9238443f2d2SAndrzej Pietrasiewicz }
9248443f2d2SAndrzej Pietrasiewicz 
9258443f2d2SAndrzej Pietrasiewicz static void printer_soft_reset(struct printer_dev *dev)
9268443f2d2SAndrzej Pietrasiewicz {
9278443f2d2SAndrzej Pietrasiewicz 	struct usb_request	*req;
9288443f2d2SAndrzej Pietrasiewicz 
9298443f2d2SAndrzej Pietrasiewicz 	INFO(dev, "Received Printer Reset Request\n");
9308443f2d2SAndrzej Pietrasiewicz 
9318443f2d2SAndrzej Pietrasiewicz 	if (usb_ep_disable(dev->in_ep))
9328443f2d2SAndrzej Pietrasiewicz 		DBG(dev, "Failed to disable USB in_ep\n");
9338443f2d2SAndrzej Pietrasiewicz 	if (usb_ep_disable(dev->out_ep))
9348443f2d2SAndrzej Pietrasiewicz 		DBG(dev, "Failed to disable USB out_ep\n");
9358443f2d2SAndrzej Pietrasiewicz 
9368443f2d2SAndrzej Pietrasiewicz 	if (dev->current_rx_req != NULL) {
9378443f2d2SAndrzej Pietrasiewicz 		list_add(&dev->current_rx_req->list, &dev->rx_reqs);
9388443f2d2SAndrzej Pietrasiewicz 		dev->current_rx_req = NULL;
9398443f2d2SAndrzej Pietrasiewicz 	}
9408443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_bytes = 0;
9418443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_buf = NULL;
9428443f2d2SAndrzej Pietrasiewicz 	dev->reset_printer = 1;
9438443f2d2SAndrzej Pietrasiewicz 
9448443f2d2SAndrzej Pietrasiewicz 	while (likely(!(list_empty(&dev->rx_buffers)))) {
9458443f2d2SAndrzej Pietrasiewicz 		req = container_of(dev->rx_buffers.next, struct usb_request,
9468443f2d2SAndrzej Pietrasiewicz 				list);
9478443f2d2SAndrzej Pietrasiewicz 		list_del_init(&req->list);
9488443f2d2SAndrzej Pietrasiewicz 		list_add(&req->list, &dev->rx_reqs);
9498443f2d2SAndrzej Pietrasiewicz 	}
9508443f2d2SAndrzej Pietrasiewicz 
9518443f2d2SAndrzej Pietrasiewicz 	while (likely(!(list_empty(&dev->rx_reqs_active)))) {
9528443f2d2SAndrzej Pietrasiewicz 		req = container_of(dev->rx_buffers.next, struct usb_request,
9538443f2d2SAndrzej Pietrasiewicz 				list);
9548443f2d2SAndrzej Pietrasiewicz 		list_del_init(&req->list);
9558443f2d2SAndrzej Pietrasiewicz 		list_add(&req->list, &dev->rx_reqs);
9568443f2d2SAndrzej Pietrasiewicz 	}
9578443f2d2SAndrzej Pietrasiewicz 
9588443f2d2SAndrzej Pietrasiewicz 	while (likely(!(list_empty(&dev->tx_reqs_active)))) {
9598443f2d2SAndrzej Pietrasiewicz 		req = container_of(dev->tx_reqs_active.next,
9608443f2d2SAndrzej Pietrasiewicz 				struct usb_request, list);
9618443f2d2SAndrzej Pietrasiewicz 		list_del_init(&req->list);
9628443f2d2SAndrzej Pietrasiewicz 		list_add(&req->list, &dev->tx_reqs);
9638443f2d2SAndrzej Pietrasiewicz 	}
9648443f2d2SAndrzej Pietrasiewicz 
9658443f2d2SAndrzej Pietrasiewicz 	if (usb_ep_enable(dev->in_ep))
9668443f2d2SAndrzej Pietrasiewicz 		DBG(dev, "Failed to enable USB in_ep\n");
9678443f2d2SAndrzej Pietrasiewicz 	if (usb_ep_enable(dev->out_ep))
9688443f2d2SAndrzej Pietrasiewicz 		DBG(dev, "Failed to enable USB out_ep\n");
9698443f2d2SAndrzej Pietrasiewicz 
9708443f2d2SAndrzej Pietrasiewicz 	wake_up_interruptible(&dev->rx_wait);
9718443f2d2SAndrzej Pietrasiewicz 	wake_up_interruptible(&dev->tx_wait);
9728443f2d2SAndrzej Pietrasiewicz 	wake_up_interruptible(&dev->tx_flush_wait);
9738443f2d2SAndrzej Pietrasiewicz }
9748443f2d2SAndrzej Pietrasiewicz 
9758443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
9768443f2d2SAndrzej Pietrasiewicz 
977636bc0edSAndrzej Pietrasiewicz static bool gprinter_req_match(struct usb_function *f,
978636bc0edSAndrzej Pietrasiewicz 			       const struct usb_ctrlrequest *ctrl)
979636bc0edSAndrzej Pietrasiewicz {
980636bc0edSAndrzej Pietrasiewicz 	struct printer_dev	*dev = func_to_printer(f);
981636bc0edSAndrzej Pietrasiewicz 	u16			w_index = le16_to_cpu(ctrl->wIndex);
982636bc0edSAndrzej Pietrasiewicz 	u16			w_value = le16_to_cpu(ctrl->wValue);
983636bc0edSAndrzej Pietrasiewicz 	u16			w_length = le16_to_cpu(ctrl->wLength);
984636bc0edSAndrzej Pietrasiewicz 
985636bc0edSAndrzej Pietrasiewicz 	if ((ctrl->bRequestType & USB_RECIP_MASK) != USB_RECIP_INTERFACE ||
986636bc0edSAndrzej Pietrasiewicz 	    (ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS)
987636bc0edSAndrzej Pietrasiewicz 		return false;
988636bc0edSAndrzej Pietrasiewicz 
989636bc0edSAndrzej Pietrasiewicz 	switch (ctrl->bRequest) {
990636bc0edSAndrzej Pietrasiewicz 	case GET_DEVICE_ID:
991636bc0edSAndrzej Pietrasiewicz 		w_index >>= 8;
992636bc0edSAndrzej Pietrasiewicz 		if (w_length <= PNP_STRING_LEN &&
993636bc0edSAndrzej Pietrasiewicz 		    (USB_DIR_IN & ctrl->bRequestType))
994636bc0edSAndrzej Pietrasiewicz 			break;
995636bc0edSAndrzej Pietrasiewicz 		return false;
996636bc0edSAndrzej Pietrasiewicz 	case GET_PORT_STATUS:
997636bc0edSAndrzej Pietrasiewicz 		if (!w_value && w_length == 1 &&
998636bc0edSAndrzej Pietrasiewicz 		    (USB_DIR_IN & ctrl->bRequestType))
999636bc0edSAndrzej Pietrasiewicz 			break;
1000636bc0edSAndrzej Pietrasiewicz 		return false;
1001636bc0edSAndrzej Pietrasiewicz 	case SOFT_RESET:
1002636bc0edSAndrzej Pietrasiewicz 		if (!w_value && !w_length &&
1003636bc0edSAndrzej Pietrasiewicz 		   (USB_DIR_OUT & ctrl->bRequestType))
1004636bc0edSAndrzej Pietrasiewicz 			break;
1005636bc0edSAndrzej Pietrasiewicz 		/* fall through */
1006636bc0edSAndrzej Pietrasiewicz 	default:
1007636bc0edSAndrzej Pietrasiewicz 		return false;
1008636bc0edSAndrzej Pietrasiewicz 	}
1009636bc0edSAndrzej Pietrasiewicz 	return w_index == dev->interface;
1010636bc0edSAndrzej Pietrasiewicz }
1011636bc0edSAndrzej Pietrasiewicz 
10128443f2d2SAndrzej Pietrasiewicz /*
10138443f2d2SAndrzej Pietrasiewicz  * The setup() callback implements all the ep0 functionality that's not
10148443f2d2SAndrzej Pietrasiewicz  * handled lower down.
10158443f2d2SAndrzej Pietrasiewicz  */
10168443f2d2SAndrzej Pietrasiewicz static int printer_func_setup(struct usb_function *f,
10178443f2d2SAndrzej Pietrasiewicz 		const struct usb_ctrlrequest *ctrl)
10188443f2d2SAndrzej Pietrasiewicz {
1019143d53e1SAndrzej Pietrasiewicz 	struct printer_dev *dev = func_to_printer(f);
10208443f2d2SAndrzej Pietrasiewicz 	struct usb_composite_dev *cdev = f->config->cdev;
10218443f2d2SAndrzej Pietrasiewicz 	struct usb_request	*req = cdev->req;
10228443f2d2SAndrzej Pietrasiewicz 	int			value = -EOPNOTSUPP;
10238443f2d2SAndrzej Pietrasiewicz 	u16			wIndex = le16_to_cpu(ctrl->wIndex);
10248443f2d2SAndrzej Pietrasiewicz 	u16			wValue = le16_to_cpu(ctrl->wValue);
10258443f2d2SAndrzej Pietrasiewicz 	u16			wLength = le16_to_cpu(ctrl->wLength);
10268443f2d2SAndrzej Pietrasiewicz 
10278443f2d2SAndrzej Pietrasiewicz 	DBG(dev, "ctrl req%02x.%02x v%04x i%04x l%d\n",
10288443f2d2SAndrzej Pietrasiewicz 		ctrl->bRequestType, ctrl->bRequest, wValue, wIndex, wLength);
10298443f2d2SAndrzej Pietrasiewicz 
10308443f2d2SAndrzej Pietrasiewicz 	switch (ctrl->bRequestType&USB_TYPE_MASK) {
10318443f2d2SAndrzej Pietrasiewicz 	case USB_TYPE_CLASS:
10328443f2d2SAndrzej Pietrasiewicz 		switch (ctrl->bRequest) {
1033d7239f4cSAndrzej Pietrasiewicz 		case GET_DEVICE_ID: /* Get the IEEE-1284 PNP String */
10348443f2d2SAndrzej Pietrasiewicz 			/* Only one printer interface is supported. */
10358443f2d2SAndrzej Pietrasiewicz 			if ((wIndex>>8) != dev->interface)
10368443f2d2SAndrzej Pietrasiewicz 				break;
10378443f2d2SAndrzej Pietrasiewicz 
10385a84e6f6SAndrzej Pietrasiewicz 			value = (dev->pnp_string[0] << 8) | dev->pnp_string[1];
10395a84e6f6SAndrzej Pietrasiewicz 			memcpy(req->buf, dev->pnp_string, value);
10408443f2d2SAndrzej Pietrasiewicz 			DBG(dev, "1284 PNP String: %x %s\n", value,
10415a84e6f6SAndrzej Pietrasiewicz 					&dev->pnp_string[2]);
10428443f2d2SAndrzej Pietrasiewicz 			break;
10438443f2d2SAndrzej Pietrasiewicz 
1044d7239f4cSAndrzej Pietrasiewicz 		case GET_PORT_STATUS: /* Get Port Status */
10458443f2d2SAndrzej Pietrasiewicz 			/* Only one printer interface is supported. */
10468443f2d2SAndrzej Pietrasiewicz 			if (wIndex != dev->interface)
10478443f2d2SAndrzej Pietrasiewicz 				break;
10488443f2d2SAndrzej Pietrasiewicz 
10498443f2d2SAndrzej Pietrasiewicz 			*(u8 *)req->buf = dev->printer_status;
10508443f2d2SAndrzej Pietrasiewicz 			value = min(wLength, (u16) 1);
10518443f2d2SAndrzej Pietrasiewicz 			break;
10528443f2d2SAndrzej Pietrasiewicz 
1053d7239f4cSAndrzej Pietrasiewicz 		case SOFT_RESET: /* Soft Reset */
10548443f2d2SAndrzej Pietrasiewicz 			/* Only one printer interface is supported. */
10558443f2d2SAndrzej Pietrasiewicz 			if (wIndex != dev->interface)
10568443f2d2SAndrzej Pietrasiewicz 				break;
10578443f2d2SAndrzej Pietrasiewicz 
10588443f2d2SAndrzej Pietrasiewicz 			printer_soft_reset(dev);
10598443f2d2SAndrzej Pietrasiewicz 
10608443f2d2SAndrzej Pietrasiewicz 			value = 0;
10618443f2d2SAndrzej Pietrasiewicz 			break;
10628443f2d2SAndrzej Pietrasiewicz 
10638443f2d2SAndrzej Pietrasiewicz 		default:
10648443f2d2SAndrzej Pietrasiewicz 			goto unknown;
10658443f2d2SAndrzej Pietrasiewicz 		}
10668443f2d2SAndrzej Pietrasiewicz 		break;
10678443f2d2SAndrzej Pietrasiewicz 
10688443f2d2SAndrzej Pietrasiewicz 	default:
10698443f2d2SAndrzej Pietrasiewicz unknown:
10708443f2d2SAndrzej Pietrasiewicz 		VDBG(dev,
10718443f2d2SAndrzej Pietrasiewicz 			"unknown ctrl req%02x.%02x v%04x i%04x l%d\n",
10728443f2d2SAndrzej Pietrasiewicz 			ctrl->bRequestType, ctrl->bRequest,
10738443f2d2SAndrzej Pietrasiewicz 			wValue, wIndex, wLength);
10748443f2d2SAndrzej Pietrasiewicz 		break;
10758443f2d2SAndrzej Pietrasiewicz 	}
10768443f2d2SAndrzej Pietrasiewicz 	/* host either stalls (value < 0) or reports success */
1077eb132ccbSAndrzej Pietrasiewicz 	if (value >= 0) {
1078eb132ccbSAndrzej Pietrasiewicz 		req->length = value;
1079eb132ccbSAndrzej Pietrasiewicz 		req->zero = value < wLength;
1080eb132ccbSAndrzej Pietrasiewicz 		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
1081eb132ccbSAndrzej Pietrasiewicz 		if (value < 0) {
1082eb132ccbSAndrzej Pietrasiewicz 			ERROR(dev, "%s:%d Error!\n", __func__, __LINE__);
1083eb132ccbSAndrzej Pietrasiewicz 			req->status = 0;
1084eb132ccbSAndrzej Pietrasiewicz 		}
1085eb132ccbSAndrzej Pietrasiewicz 	}
10868443f2d2SAndrzej Pietrasiewicz 	return value;
10878443f2d2SAndrzej Pietrasiewicz }
10888443f2d2SAndrzej Pietrasiewicz 
10898443f2d2SAndrzej Pietrasiewicz static int __init printer_func_bind(struct usb_configuration *c,
10908443f2d2SAndrzej Pietrasiewicz 		struct usb_function *f)
10918443f2d2SAndrzej Pietrasiewicz {
10924504b5a0SAndrzej Pietrasiewicz 	struct usb_gadget *gadget = c->cdev->gadget;
1093143d53e1SAndrzej Pietrasiewicz 	struct printer_dev *dev = func_to_printer(f);
10944504b5a0SAndrzej Pietrasiewicz 	struct device *pdev;
10958443f2d2SAndrzej Pietrasiewicz 	struct usb_composite_dev *cdev = c->cdev;
10968443f2d2SAndrzej Pietrasiewicz 	struct usb_ep *in_ep;
10978443f2d2SAndrzej Pietrasiewicz 	struct usb_ep *out_ep = NULL;
10984504b5a0SAndrzej Pietrasiewicz 	struct usb_request *req;
1099*6dd8c2e6SAndrzej Pietrasiewicz 	dev_t devt;
11008443f2d2SAndrzej Pietrasiewicz 	int id;
11018443f2d2SAndrzej Pietrasiewicz 	int ret;
11024504b5a0SAndrzej Pietrasiewicz 	u32 i;
11038443f2d2SAndrzej Pietrasiewicz 
11048443f2d2SAndrzej Pietrasiewicz 	id = usb_interface_id(c, f);
11058443f2d2SAndrzej Pietrasiewicz 	if (id < 0)
11068443f2d2SAndrzej Pietrasiewicz 		return id;
11078443f2d2SAndrzej Pietrasiewicz 	intf_desc.bInterfaceNumber = id;
11088443f2d2SAndrzej Pietrasiewicz 
11094504b5a0SAndrzej Pietrasiewicz 	/* finish hookup to lower layer ... */
11104504b5a0SAndrzej Pietrasiewicz 	dev->gadget = gadget;
11114504b5a0SAndrzej Pietrasiewicz 
11128443f2d2SAndrzej Pietrasiewicz 	/* all we really need is bulk IN/OUT */
11138443f2d2SAndrzej Pietrasiewicz 	in_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_in_desc);
11148443f2d2SAndrzej Pietrasiewicz 	if (!in_ep) {
11158443f2d2SAndrzej Pietrasiewicz autoconf_fail:
11168443f2d2SAndrzej Pietrasiewicz 		dev_err(&cdev->gadget->dev, "can't autoconfigure on %s\n",
11178443f2d2SAndrzej Pietrasiewicz 			cdev->gadget->name);
11188443f2d2SAndrzej Pietrasiewicz 		return -ENODEV;
11198443f2d2SAndrzej Pietrasiewicz 	}
11208443f2d2SAndrzej Pietrasiewicz 	in_ep->driver_data = in_ep;	/* claim */
11218443f2d2SAndrzej Pietrasiewicz 
11228443f2d2SAndrzej Pietrasiewicz 	out_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_out_desc);
11238443f2d2SAndrzej Pietrasiewicz 	if (!out_ep)
11248443f2d2SAndrzej Pietrasiewicz 		goto autoconf_fail;
11258443f2d2SAndrzej Pietrasiewicz 	out_ep->driver_data = out_ep;	/* claim */
11268443f2d2SAndrzej Pietrasiewicz 
11278443f2d2SAndrzej Pietrasiewicz 	/* assumes that all endpoints are dual-speed */
11288443f2d2SAndrzej Pietrasiewicz 	hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
11298443f2d2SAndrzej Pietrasiewicz 	hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
113074df41b4SJorge Ramirez-Ortiz 	ss_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
113174df41b4SJorge Ramirez-Ortiz 	ss_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
11328443f2d2SAndrzej Pietrasiewicz 
11338443f2d2SAndrzej Pietrasiewicz 	ret = usb_assign_descriptors(f, fs_printer_function,
113474df41b4SJorge Ramirez-Ortiz 			hs_printer_function, ss_printer_function);
11358443f2d2SAndrzej Pietrasiewicz 	if (ret)
11368443f2d2SAndrzej Pietrasiewicz 		return ret;
11378443f2d2SAndrzej Pietrasiewicz 
11388443f2d2SAndrzej Pietrasiewicz 	dev->in_ep = in_ep;
11398443f2d2SAndrzej Pietrasiewicz 	dev->out_ep = out_ep;
11404504b5a0SAndrzej Pietrasiewicz 
11414504b5a0SAndrzej Pietrasiewicz 	ret = -ENOMEM;
11424504b5a0SAndrzej Pietrasiewicz 	for (i = 0; i < dev->q_len; i++) {
11434504b5a0SAndrzej Pietrasiewicz 		req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL);
11444504b5a0SAndrzej Pietrasiewicz 		if (!req)
11454504b5a0SAndrzej Pietrasiewicz 			goto fail_tx_reqs;
11464504b5a0SAndrzej Pietrasiewicz 		list_add(&req->list, &dev->tx_reqs);
11474504b5a0SAndrzej Pietrasiewicz 	}
11484504b5a0SAndrzej Pietrasiewicz 
11494504b5a0SAndrzej Pietrasiewicz 	for (i = 0; i < dev->q_len; i++) {
11504504b5a0SAndrzej Pietrasiewicz 		req = printer_req_alloc(dev->out_ep, USB_BUFSIZE, GFP_KERNEL);
11514504b5a0SAndrzej Pietrasiewicz 		if (!req)
11524504b5a0SAndrzej Pietrasiewicz 			goto fail_rx_reqs;
11534504b5a0SAndrzej Pietrasiewicz 		list_add(&req->list, &dev->rx_reqs);
11544504b5a0SAndrzej Pietrasiewicz 	}
11554504b5a0SAndrzej Pietrasiewicz 
11564504b5a0SAndrzej Pietrasiewicz 	/* Setup the sysfs files for the printer gadget. */
1157*6dd8c2e6SAndrzej Pietrasiewicz 	devt = MKDEV(major, dev->minor);
1158*6dd8c2e6SAndrzej Pietrasiewicz 	pdev = device_create(usb_gadget_class, NULL, devt,
1159*6dd8c2e6SAndrzej Pietrasiewicz 				  NULL, "g_printer%d", dev->minor);
11604504b5a0SAndrzej Pietrasiewicz 	if (IS_ERR(pdev)) {
11614504b5a0SAndrzej Pietrasiewicz 		ERROR(dev, "Failed to create device: g_printer\n");
11624504b5a0SAndrzej Pietrasiewicz 		ret = PTR_ERR(pdev);
11634504b5a0SAndrzej Pietrasiewicz 		goto fail_rx_reqs;
11644504b5a0SAndrzej Pietrasiewicz 	}
11654504b5a0SAndrzej Pietrasiewicz 
11664504b5a0SAndrzej Pietrasiewicz 	/*
11674504b5a0SAndrzej Pietrasiewicz 	 * Register a character device as an interface to a user mode
11684504b5a0SAndrzej Pietrasiewicz 	 * program that handles the printer specific functionality.
11694504b5a0SAndrzej Pietrasiewicz 	 */
11704504b5a0SAndrzej Pietrasiewicz 	cdev_init(&dev->printer_cdev, &printer_io_operations);
11714504b5a0SAndrzej Pietrasiewicz 	dev->printer_cdev.owner = THIS_MODULE;
1172*6dd8c2e6SAndrzej Pietrasiewicz 	ret = cdev_add(&dev->printer_cdev, devt, 1);
11734504b5a0SAndrzej Pietrasiewicz 	if (ret) {
11744504b5a0SAndrzej Pietrasiewicz 		ERROR(dev, "Failed to open char device\n");
11754504b5a0SAndrzej Pietrasiewicz 		goto fail_cdev_add;
11764504b5a0SAndrzej Pietrasiewicz 	}
11774504b5a0SAndrzej Pietrasiewicz 
11788443f2d2SAndrzej Pietrasiewicz 	return 0;
11794504b5a0SAndrzej Pietrasiewicz 
11804504b5a0SAndrzej Pietrasiewicz fail_cdev_add:
1181*6dd8c2e6SAndrzej Pietrasiewicz 	device_destroy(usb_gadget_class, devt);
11824504b5a0SAndrzej Pietrasiewicz 
11834504b5a0SAndrzej Pietrasiewicz fail_rx_reqs:
11844504b5a0SAndrzej Pietrasiewicz 	while (!list_empty(&dev->rx_reqs)) {
11854504b5a0SAndrzej Pietrasiewicz 		req = container_of(dev->rx_reqs.next, struct usb_request, list);
11864504b5a0SAndrzej Pietrasiewicz 		list_del(&req->list);
11874504b5a0SAndrzej Pietrasiewicz 		printer_req_free(dev->out_ep, req);
11884504b5a0SAndrzej Pietrasiewicz 	}
11894504b5a0SAndrzej Pietrasiewicz 
11904504b5a0SAndrzej Pietrasiewicz fail_tx_reqs:
11914504b5a0SAndrzej Pietrasiewicz 	while (!list_empty(&dev->tx_reqs)) {
11924504b5a0SAndrzej Pietrasiewicz 		req = container_of(dev->tx_reqs.next, struct usb_request, list);
11934504b5a0SAndrzej Pietrasiewicz 		list_del(&req->list);
11944504b5a0SAndrzej Pietrasiewicz 		printer_req_free(dev->in_ep, req);
11954504b5a0SAndrzej Pietrasiewicz 	}
11964504b5a0SAndrzej Pietrasiewicz 
11974504b5a0SAndrzej Pietrasiewicz 	return ret;
11984504b5a0SAndrzej Pietrasiewicz 
11998443f2d2SAndrzej Pietrasiewicz }
12008443f2d2SAndrzej Pietrasiewicz 
12018443f2d2SAndrzej Pietrasiewicz static void printer_func_unbind(struct usb_configuration *c,
12028443f2d2SAndrzej Pietrasiewicz 		struct usb_function *f)
12038443f2d2SAndrzej Pietrasiewicz {
12048443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev;
12058443f2d2SAndrzej Pietrasiewicz 	struct usb_request	*req;
12068443f2d2SAndrzej Pietrasiewicz 
1207143d53e1SAndrzej Pietrasiewicz 	dev = func_to_printer(f);
12088443f2d2SAndrzej Pietrasiewicz 
1209*6dd8c2e6SAndrzej Pietrasiewicz 	device_destroy(usb_gadget_class, MKDEV(major, dev->minor));
12108443f2d2SAndrzej Pietrasiewicz 
12118443f2d2SAndrzej Pietrasiewicz 	/* Remove Character Device */
12128443f2d2SAndrzej Pietrasiewicz 	cdev_del(&dev->printer_cdev);
12138443f2d2SAndrzej Pietrasiewicz 
12148443f2d2SAndrzej Pietrasiewicz 	/* we must already have been disconnected ... no i/o may be active */
12158443f2d2SAndrzej Pietrasiewicz 	WARN_ON(!list_empty(&dev->tx_reqs_active));
12168443f2d2SAndrzej Pietrasiewicz 	WARN_ON(!list_empty(&dev->rx_reqs_active));
12178443f2d2SAndrzej Pietrasiewicz 
12188443f2d2SAndrzej Pietrasiewicz 	/* Free all memory for this driver. */
12198443f2d2SAndrzej Pietrasiewicz 	while (!list_empty(&dev->tx_reqs)) {
12208443f2d2SAndrzej Pietrasiewicz 		req = container_of(dev->tx_reqs.next, struct usb_request,
12218443f2d2SAndrzej Pietrasiewicz 				list);
12228443f2d2SAndrzej Pietrasiewicz 		list_del(&req->list);
12238443f2d2SAndrzej Pietrasiewicz 		printer_req_free(dev->in_ep, req);
12248443f2d2SAndrzej Pietrasiewicz 	}
12258443f2d2SAndrzej Pietrasiewicz 
12268443f2d2SAndrzej Pietrasiewicz 	if (dev->current_rx_req != NULL)
12278443f2d2SAndrzej Pietrasiewicz 		printer_req_free(dev->out_ep, dev->current_rx_req);
12288443f2d2SAndrzej Pietrasiewicz 
12298443f2d2SAndrzej Pietrasiewicz 	while (!list_empty(&dev->rx_reqs)) {
12308443f2d2SAndrzej Pietrasiewicz 		req = container_of(dev->rx_reqs.next,
12318443f2d2SAndrzej Pietrasiewicz 				struct usb_request, list);
12328443f2d2SAndrzej Pietrasiewicz 		list_del(&req->list);
12338443f2d2SAndrzej Pietrasiewicz 		printer_req_free(dev->out_ep, req);
12348443f2d2SAndrzej Pietrasiewicz 	}
12358443f2d2SAndrzej Pietrasiewicz 
12368443f2d2SAndrzej Pietrasiewicz 	while (!list_empty(&dev->rx_buffers)) {
12378443f2d2SAndrzej Pietrasiewicz 		req = container_of(dev->rx_buffers.next,
12388443f2d2SAndrzej Pietrasiewicz 				struct usb_request, list);
12398443f2d2SAndrzej Pietrasiewicz 		list_del(&req->list);
12408443f2d2SAndrzej Pietrasiewicz 		printer_req_free(dev->out_ep, req);
12418443f2d2SAndrzej Pietrasiewicz 	}
1242991cd262SAndrzej Pietrasiewicz 	usb_free_all_descriptors(f);
1243*6dd8c2e6SAndrzej Pietrasiewicz 	kfree(dev);
1244991cd262SAndrzej Pietrasiewicz }
1245991cd262SAndrzej Pietrasiewicz 
1246991cd262SAndrzej Pietrasiewicz static int printer_func_set_alt(struct usb_function *f,
1247991cd262SAndrzej Pietrasiewicz 		unsigned intf, unsigned alt)
1248991cd262SAndrzej Pietrasiewicz {
1249143d53e1SAndrzej Pietrasiewicz 	struct printer_dev *dev = func_to_printer(f);
1250991cd262SAndrzej Pietrasiewicz 	int ret = -ENOTSUPP;
1251991cd262SAndrzej Pietrasiewicz 
1252991cd262SAndrzej Pietrasiewicz 	if (!alt)
1253991cd262SAndrzej Pietrasiewicz 		ret = set_interface(dev, intf);
1254991cd262SAndrzej Pietrasiewicz 
1255991cd262SAndrzej Pietrasiewicz 	return ret;
1256991cd262SAndrzej Pietrasiewicz }
1257991cd262SAndrzej Pietrasiewicz 
1258991cd262SAndrzej Pietrasiewicz static void printer_func_disable(struct usb_function *f)
1259991cd262SAndrzej Pietrasiewicz {
1260143d53e1SAndrzej Pietrasiewicz 	struct printer_dev *dev = func_to_printer(f);
1261991cd262SAndrzej Pietrasiewicz 	unsigned long		flags;
1262991cd262SAndrzej Pietrasiewicz 
1263991cd262SAndrzej Pietrasiewicz 	DBG(dev, "%s\n", __func__);
1264991cd262SAndrzej Pietrasiewicz 
1265991cd262SAndrzej Pietrasiewicz 	spin_lock_irqsave(&dev->lock, flags);
1266991cd262SAndrzej Pietrasiewicz 	printer_reset_interface(dev);
1267991cd262SAndrzej Pietrasiewicz 	spin_unlock_irqrestore(&dev->lock, flags);
12688443f2d2SAndrzej Pietrasiewicz }
12698443f2d2SAndrzej Pietrasiewicz 
12708443f2d2SAndrzej Pietrasiewicz static struct usb_configuration printer_cfg_driver = {
12718443f2d2SAndrzej Pietrasiewicz 	.label			= "printer",
12728443f2d2SAndrzej Pietrasiewicz 	.bConfigurationValue	= 1,
12738443f2d2SAndrzej Pietrasiewicz 	.bmAttributes		= USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
12748443f2d2SAndrzej Pietrasiewicz };
12758443f2d2SAndrzej Pietrasiewicz 
1276ae2dd0deSAndrzej Pietrasiewicz static int f_printer_bind_config(struct usb_configuration *c, char *pnp_str,
1277*6dd8c2e6SAndrzej Pietrasiewicz 				 char *pnp_string, unsigned q_len, int minor)
12788443f2d2SAndrzej Pietrasiewicz {
12798443f2d2SAndrzej Pietrasiewicz 	struct printer_dev	*dev;
12808443f2d2SAndrzej Pietrasiewicz 	int			status = -ENOMEM;
12818443f2d2SAndrzej Pietrasiewicz 	size_t			len;
12828443f2d2SAndrzej Pietrasiewicz 
1283*6dd8c2e6SAndrzej Pietrasiewicz 	if (minor >= minors)
1284*6dd8c2e6SAndrzej Pietrasiewicz 		return -ENOENT;
1285*6dd8c2e6SAndrzej Pietrasiewicz 
1286*6dd8c2e6SAndrzej Pietrasiewicz 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
1287*6dd8c2e6SAndrzej Pietrasiewicz 	if (!dev)
1288*6dd8c2e6SAndrzej Pietrasiewicz 		return -ENOMEM;
1289*6dd8c2e6SAndrzej Pietrasiewicz 
12905a84e6f6SAndrzej Pietrasiewicz 	dev->pnp_string = pnp_string;
1291*6dd8c2e6SAndrzej Pietrasiewicz 	dev->minor = minor;
12928443f2d2SAndrzej Pietrasiewicz 
12938443f2d2SAndrzej Pietrasiewicz 	dev->function.name = shortname;
12948443f2d2SAndrzej Pietrasiewicz 	dev->function.bind = printer_func_bind;
12958443f2d2SAndrzej Pietrasiewicz 	dev->function.setup = printer_func_setup;
12968443f2d2SAndrzej Pietrasiewicz 	dev->function.unbind = printer_func_unbind;
12978443f2d2SAndrzej Pietrasiewicz 	dev->function.set_alt = printer_func_set_alt;
12988443f2d2SAndrzej Pietrasiewicz 	dev->function.disable = printer_func_disable;
1299636bc0edSAndrzej Pietrasiewicz 	dev->function.req_match = gprinter_req_match;
1300c69b8186SAndrzej Pietrasiewicz 	INIT_LIST_HEAD(&dev->tx_reqs);
1301c69b8186SAndrzej Pietrasiewicz 	INIT_LIST_HEAD(&dev->rx_reqs);
1302c69b8186SAndrzej Pietrasiewicz 	INIT_LIST_HEAD(&dev->rx_buffers);
13038443f2d2SAndrzej Pietrasiewicz 
1304ae2dd0deSAndrzej Pietrasiewicz 	if (pnp_str)
13055a84e6f6SAndrzej Pietrasiewicz 		strlcpy(&dev->pnp_string[2], pnp_str, PNP_STRING_LEN - 2);
13068443f2d2SAndrzej Pietrasiewicz 
13078443f2d2SAndrzej Pietrasiewicz 	len = strlen(pnp_string);
13088443f2d2SAndrzej Pietrasiewicz 	pnp_string[0] = (len >> 8) & 0xFF;
13098443f2d2SAndrzej Pietrasiewicz 	pnp_string[1] = len & 0xFF;
13108443f2d2SAndrzej Pietrasiewicz 
13118443f2d2SAndrzej Pietrasiewicz 	spin_lock_init(&dev->lock);
13128443f2d2SAndrzej Pietrasiewicz 	mutex_init(&dev->lock_printer_io);
13138443f2d2SAndrzej Pietrasiewicz 	INIT_LIST_HEAD(&dev->tx_reqs_active);
13148443f2d2SAndrzej Pietrasiewicz 	INIT_LIST_HEAD(&dev->rx_reqs_active);
13158443f2d2SAndrzej Pietrasiewicz 	init_waitqueue_head(&dev->rx_wait);
13168443f2d2SAndrzej Pietrasiewicz 	init_waitqueue_head(&dev->tx_wait);
13178443f2d2SAndrzej Pietrasiewicz 	init_waitqueue_head(&dev->tx_flush_wait);
13188443f2d2SAndrzej Pietrasiewicz 
13198443f2d2SAndrzej Pietrasiewicz 	dev->interface = -1;
13208443f2d2SAndrzej Pietrasiewicz 	dev->printer_cdev_open = 0;
13218443f2d2SAndrzej Pietrasiewicz 	dev->printer_status = PRINTER_NOT_ERROR;
13228443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_req = NULL;
13238443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_bytes = 0;
13248443f2d2SAndrzej Pietrasiewicz 	dev->current_rx_buf = NULL;
1325cee5cbffSAndrzej Pietrasiewicz 	dev->q_len = q_len;
13268443f2d2SAndrzej Pietrasiewicz 
1327cee5cbffSAndrzej Pietrasiewicz 	status = usb_add_function(c, &dev->function);
1328*6dd8c2e6SAndrzej Pietrasiewicz 	if (status) {
1329*6dd8c2e6SAndrzej Pietrasiewicz 		kfree(dev);
1330cee5cbffSAndrzej Pietrasiewicz 		return status;
1331*6dd8c2e6SAndrzej Pietrasiewicz 	}
13328443f2d2SAndrzej Pietrasiewicz 	INFO(dev, "%s, version: " DRIVER_VERSION "\n", driver_desc);
13338443f2d2SAndrzej Pietrasiewicz 	return 0;
13348443f2d2SAndrzej Pietrasiewicz }
13358443f2d2SAndrzej Pietrasiewicz 
1336ae2dd0deSAndrzej Pietrasiewicz static int __init printer_do_config(struct usb_configuration *c)
1337ae2dd0deSAndrzej Pietrasiewicz {
1338ae2dd0deSAndrzej Pietrasiewicz 	struct usb_gadget	*gadget = c->cdev->gadget;
1339ae2dd0deSAndrzej Pietrasiewicz 
1340ae2dd0deSAndrzej Pietrasiewicz 	usb_ep_autoconfig_reset(gadget);
1341ae2dd0deSAndrzej Pietrasiewicz 
1342ae2dd0deSAndrzej Pietrasiewicz 	usb_gadget_set_selfpowered(gadget);
1343ae2dd0deSAndrzej Pietrasiewicz 
1344ae2dd0deSAndrzej Pietrasiewicz 	if (gadget_is_otg(gadget)) {
1345ae2dd0deSAndrzej Pietrasiewicz 		otg_descriptor.bmAttributes |= USB_OTG_HNP;
1346ae2dd0deSAndrzej Pietrasiewicz 		printer_cfg_driver.descriptors = otg_desc;
1347ae2dd0deSAndrzej Pietrasiewicz 		printer_cfg_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
1348ae2dd0deSAndrzej Pietrasiewicz 	}
1349ae2dd0deSAndrzej Pietrasiewicz 
1350*6dd8c2e6SAndrzej Pietrasiewicz 	return f_printer_bind_config(c, iPNPstring, pnp_string, QLEN, 0);
1351ae2dd0deSAndrzej Pietrasiewicz }
1352ae2dd0deSAndrzej Pietrasiewicz 
1353*6dd8c2e6SAndrzej Pietrasiewicz static int gprinter_setup(int count)
1354d82cd82eSAndrzej Pietrasiewicz {
1355d82cd82eSAndrzej Pietrasiewicz 	int status;
1356*6dd8c2e6SAndrzej Pietrasiewicz 	dev_t devt;
1357d82cd82eSAndrzej Pietrasiewicz 
1358d82cd82eSAndrzej Pietrasiewicz 	usb_gadget_class = class_create(THIS_MODULE, "usb_printer_gadget");
1359d82cd82eSAndrzej Pietrasiewicz 	if (IS_ERR(usb_gadget_class)) {
1360d82cd82eSAndrzej Pietrasiewicz 		status = PTR_ERR(usb_gadget_class);
1361*6dd8c2e6SAndrzej Pietrasiewicz 		usb_gadget_class = NULL;
1362d82cd82eSAndrzej Pietrasiewicz 		pr_err("unable to create usb_gadget class %d\n", status);
1363d82cd82eSAndrzej Pietrasiewicz 		return status;
1364d82cd82eSAndrzej Pietrasiewicz 	}
1365d82cd82eSAndrzej Pietrasiewicz 
1366*6dd8c2e6SAndrzej Pietrasiewicz 	status = alloc_chrdev_region(&devt, 0, count, "USB printer gadget");
1367d82cd82eSAndrzej Pietrasiewicz 	if (status) {
1368d82cd82eSAndrzej Pietrasiewicz 		pr_err("alloc_chrdev_region %d\n", status);
1369d82cd82eSAndrzej Pietrasiewicz 		class_destroy(usb_gadget_class);
1370*6dd8c2e6SAndrzej Pietrasiewicz 		usb_gadget_class = NULL;
1371*6dd8c2e6SAndrzej Pietrasiewicz 		return status;
1372d82cd82eSAndrzej Pietrasiewicz 	}
1373d82cd82eSAndrzej Pietrasiewicz 
1374*6dd8c2e6SAndrzej Pietrasiewicz 	major = MAJOR(devt);
1375*6dd8c2e6SAndrzej Pietrasiewicz 	minors = count;
1376*6dd8c2e6SAndrzej Pietrasiewicz 
1377d82cd82eSAndrzej Pietrasiewicz 	return status;
1378d82cd82eSAndrzej Pietrasiewicz }
1379d82cd82eSAndrzej Pietrasiewicz 
1380d82cd82eSAndrzej Pietrasiewicz static void gprinter_cleanup(void)
1381d82cd82eSAndrzej Pietrasiewicz {
1382*6dd8c2e6SAndrzej Pietrasiewicz 	if (major) {
1383*6dd8c2e6SAndrzej Pietrasiewicz 		unregister_chrdev_region(MKDEV(major, 0), minors);
1384*6dd8c2e6SAndrzej Pietrasiewicz 		major = minors = 0;
1385*6dd8c2e6SAndrzej Pietrasiewicz 	}
1386d82cd82eSAndrzej Pietrasiewicz 	class_destroy(usb_gadget_class);
1387*6dd8c2e6SAndrzej Pietrasiewicz 	usb_gadget_class = NULL;
1388d82cd82eSAndrzej Pietrasiewicz }
1389d82cd82eSAndrzej Pietrasiewicz 
13908443f2d2SAndrzej Pietrasiewicz static int __init printer_bind(struct usb_composite_dev *cdev)
13918443f2d2SAndrzej Pietrasiewicz {
13928443f2d2SAndrzej Pietrasiewicz 	int ret;
13938443f2d2SAndrzej Pietrasiewicz 
1394*6dd8c2e6SAndrzej Pietrasiewicz 	ret = gprinter_setup(PRINTER_MINORS);
1395a844715dSAndrzej Pietrasiewicz 	if (ret)
13968443f2d2SAndrzej Pietrasiewicz 		return ret;
1397a844715dSAndrzej Pietrasiewicz 
1398a844715dSAndrzej Pietrasiewicz 	ret = usb_string_ids_tab(cdev, strings);
1399a844715dSAndrzej Pietrasiewicz 	if (ret < 0) {
1400a844715dSAndrzej Pietrasiewicz 		gprinter_cleanup();
1401a844715dSAndrzej Pietrasiewicz 		return ret;
1402a844715dSAndrzej Pietrasiewicz 	}
14038443f2d2SAndrzej Pietrasiewicz 	device_desc.iManufacturer = strings[USB_GADGET_MANUFACTURER_IDX].id;
14048443f2d2SAndrzej Pietrasiewicz 	device_desc.iProduct = strings[USB_GADGET_PRODUCT_IDX].id;
14058443f2d2SAndrzej Pietrasiewicz 	device_desc.iSerialNumber = strings[USB_GADGET_SERIAL_IDX].id;
14068443f2d2SAndrzej Pietrasiewicz 
1407406be2ccSAndrzej Pietrasiewicz 	ret = usb_add_config(cdev, &printer_cfg_driver, printer_do_config);
1408a844715dSAndrzej Pietrasiewicz 	if (ret) {
1409a844715dSAndrzej Pietrasiewicz 		gprinter_cleanup();
14108443f2d2SAndrzej Pietrasiewicz 		return ret;
1411a844715dSAndrzej Pietrasiewicz 	}
14128443f2d2SAndrzej Pietrasiewicz 	usb_composite_overwrite_options(cdev, &coverwrite);
14138443f2d2SAndrzej Pietrasiewicz 	return ret;
14148443f2d2SAndrzej Pietrasiewicz }
14158443f2d2SAndrzej Pietrasiewicz 
1416a844715dSAndrzej Pietrasiewicz static int __exit printer_unbind(struct usb_composite_dev *cdev)
1417a844715dSAndrzej Pietrasiewicz {
1418a844715dSAndrzej Pietrasiewicz 	gprinter_cleanup();
1419a844715dSAndrzej Pietrasiewicz 	return 0;
1420a844715dSAndrzej Pietrasiewicz }
1421a844715dSAndrzej Pietrasiewicz 
14228443f2d2SAndrzej Pietrasiewicz static __refdata struct usb_composite_driver printer_driver = {
14238443f2d2SAndrzej Pietrasiewicz 	.name           = shortname,
14248443f2d2SAndrzej Pietrasiewicz 	.dev            = &device_desc,
14258443f2d2SAndrzej Pietrasiewicz 	.strings        = dev_strings,
142674df41b4SJorge Ramirez-Ortiz 	.max_speed      = USB_SPEED_SUPER,
14278443f2d2SAndrzej Pietrasiewicz 	.bind		= printer_bind,
1428a844715dSAndrzej Pietrasiewicz 	.unbind		= printer_unbind,
14298443f2d2SAndrzej Pietrasiewicz };
14308443f2d2SAndrzej Pietrasiewicz 
14318443f2d2SAndrzej Pietrasiewicz static int __init
14328443f2d2SAndrzej Pietrasiewicz init(void)
14338443f2d2SAndrzej Pietrasiewicz {
1434a844715dSAndrzej Pietrasiewicz 	return usb_composite_probe(&printer_driver);
14358443f2d2SAndrzej Pietrasiewicz }
14368443f2d2SAndrzej Pietrasiewicz module_init(init);
14378443f2d2SAndrzej Pietrasiewicz 
14388443f2d2SAndrzej Pietrasiewicz static void __exit
14398443f2d2SAndrzej Pietrasiewicz cleanup(void)
14408443f2d2SAndrzej Pietrasiewicz {
14418443f2d2SAndrzej Pietrasiewicz 	usb_composite_unregister(&printer_driver);
14428443f2d2SAndrzej Pietrasiewicz }
14438443f2d2SAndrzej Pietrasiewicz module_exit(cleanup);
14448443f2d2SAndrzej Pietrasiewicz 
14458443f2d2SAndrzej Pietrasiewicz MODULE_DESCRIPTION(DRIVER_DESC);
14468443f2d2SAndrzej Pietrasiewicz MODULE_AUTHOR("Craig Nadler");
14478443f2d2SAndrzej Pietrasiewicz MODULE_LICENSE("GPL");
1448