xref: /OK3568_Linux_fs/kernel/drivers/usb/serial/cypress_m8.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * USB Cypress M8 driver
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * 	Copyright (C) 2004
6*4882a593Smuzhiyun  * 	    Lonnie Mendez (dignome@gmail.com)
7*4882a593Smuzhiyun  *	Copyright (C) 2003,2004
8*4882a593Smuzhiyun  *	    Neil Whelchel (koyama@firstlight.net)
9*4882a593Smuzhiyun  *
10*4882a593Smuzhiyun  * See Documentation/usb/usb-serial.rst for more information on using this
11*4882a593Smuzhiyun  * driver
12*4882a593Smuzhiyun  *
13*4882a593Smuzhiyun  * See http://geocities.com/i0xox0i for information on this driver and the
14*4882a593Smuzhiyun  * earthmate usb device.
15*4882a593Smuzhiyun  */
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun /* Thanks to Neil Whelchel for writing the first cypress m8 implementation
18*4882a593Smuzhiyun    for linux. */
19*4882a593Smuzhiyun /* Thanks to cypress for providing references for the hid reports. */
20*4882a593Smuzhiyun /* Thanks to Jiang Zhang for providing links and for general help. */
21*4882a593Smuzhiyun /* Code originates and was built up from ftdi_sio, belkin, pl2303 and others.*/
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun #include <linux/kernel.h>
25*4882a593Smuzhiyun #include <linux/errno.h>
26*4882a593Smuzhiyun #include <linux/slab.h>
27*4882a593Smuzhiyun #include <linux/tty.h>
28*4882a593Smuzhiyun #include <linux/tty_driver.h>
29*4882a593Smuzhiyun #include <linux/tty_flip.h>
30*4882a593Smuzhiyun #include <linux/module.h>
31*4882a593Smuzhiyun #include <linux/moduleparam.h>
32*4882a593Smuzhiyun #include <linux/spinlock.h>
33*4882a593Smuzhiyun #include <linux/usb.h>
34*4882a593Smuzhiyun #include <linux/usb/serial.h>
35*4882a593Smuzhiyun #include <linux/serial.h>
36*4882a593Smuzhiyun #include <linux/kfifo.h>
37*4882a593Smuzhiyun #include <linux/delay.h>
38*4882a593Smuzhiyun #include <linux/uaccess.h>
39*4882a593Smuzhiyun #include <asm/unaligned.h>
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun #include "cypress_m8.h"
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun static bool stats;
45*4882a593Smuzhiyun static int interval;
46*4882a593Smuzhiyun static bool unstable_bauds;
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun #define DRIVER_AUTHOR "Lonnie Mendez <dignome@gmail.com>, Neil Whelchel <koyama@firstlight.net>"
49*4882a593Smuzhiyun #define DRIVER_DESC "Cypress USB to Serial Driver"
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun /* write buffer size defines */
52*4882a593Smuzhiyun #define CYPRESS_BUF_SIZE	1024
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun static const struct usb_device_id id_table_earthmate[] = {
55*4882a593Smuzhiyun 	{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB) },
56*4882a593Smuzhiyun 	{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB_LT20) },
57*4882a593Smuzhiyun 	{ }						/* Terminating entry */
58*4882a593Smuzhiyun };
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun static const struct usb_device_id id_table_cyphidcomrs232[] = {
61*4882a593Smuzhiyun 	{ USB_DEVICE(VENDOR_ID_CYPRESS, PRODUCT_ID_CYPHIDCOM) },
62*4882a593Smuzhiyun 	{ USB_DEVICE(VENDOR_ID_SAI, PRODUCT_ID_CYPHIDCOM) },
63*4882a593Smuzhiyun 	{ USB_DEVICE(VENDOR_ID_POWERCOM, PRODUCT_ID_UPS) },
64*4882a593Smuzhiyun 	{ USB_DEVICE(VENDOR_ID_FRWD, PRODUCT_ID_CYPHIDCOM_FRWD) },
65*4882a593Smuzhiyun 	{ }						/* Terminating entry */
66*4882a593Smuzhiyun };
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun static const struct usb_device_id id_table_nokiaca42v2[] = {
69*4882a593Smuzhiyun 	{ USB_DEVICE(VENDOR_ID_DAZZLE, PRODUCT_ID_CA42) },
70*4882a593Smuzhiyun 	{ }						/* Terminating entry */
71*4882a593Smuzhiyun };
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun static const struct usb_device_id id_table_combined[] = {
74*4882a593Smuzhiyun 	{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB) },
75*4882a593Smuzhiyun 	{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB_LT20) },
76*4882a593Smuzhiyun 	{ USB_DEVICE(VENDOR_ID_CYPRESS, PRODUCT_ID_CYPHIDCOM) },
77*4882a593Smuzhiyun 	{ USB_DEVICE(VENDOR_ID_SAI, PRODUCT_ID_CYPHIDCOM) },
78*4882a593Smuzhiyun 	{ USB_DEVICE(VENDOR_ID_POWERCOM, PRODUCT_ID_UPS) },
79*4882a593Smuzhiyun 	{ USB_DEVICE(VENDOR_ID_FRWD, PRODUCT_ID_CYPHIDCOM_FRWD) },
80*4882a593Smuzhiyun 	{ USB_DEVICE(VENDOR_ID_DAZZLE, PRODUCT_ID_CA42) },
81*4882a593Smuzhiyun 	{ }						/* Terminating entry */
82*4882a593Smuzhiyun };
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun MODULE_DEVICE_TABLE(usb, id_table_combined);
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun enum packet_format {
87*4882a593Smuzhiyun 	packet_format_1,  /* b0:status, b1:payload count */
88*4882a593Smuzhiyun 	packet_format_2   /* b0[7:3]:status, b0[2:0]:payload count */
89*4882a593Smuzhiyun };
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun struct cypress_private {
92*4882a593Smuzhiyun 	spinlock_t lock;		   /* private lock */
93*4882a593Smuzhiyun 	int chiptype;			   /* identifier of device, for quirks/etc */
94*4882a593Smuzhiyun 	int bytes_in;			   /* used for statistics */
95*4882a593Smuzhiyun 	int bytes_out;			   /* used for statistics */
96*4882a593Smuzhiyun 	int cmd_count;			   /* used for statistics */
97*4882a593Smuzhiyun 	int cmd_ctrl;			   /* always set this to 1 before issuing a command */
98*4882a593Smuzhiyun 	struct kfifo write_fifo;	   /* write fifo */
99*4882a593Smuzhiyun 	int write_urb_in_use;		   /* write urb in use indicator */
100*4882a593Smuzhiyun 	int write_urb_interval;            /* interval to use for write urb */
101*4882a593Smuzhiyun 	int read_urb_interval;             /* interval to use for read urb */
102*4882a593Smuzhiyun 	int comm_is_ok;                    /* true if communication is (still) ok */
103*4882a593Smuzhiyun 	__u8 line_control;	   	   /* holds dtr / rts value */
104*4882a593Smuzhiyun 	__u8 current_status;	   	   /* received from last read - info on dsr,cts,cd,ri,etc */
105*4882a593Smuzhiyun 	__u8 current_config;	   	   /* stores the current configuration byte */
106*4882a593Smuzhiyun 	__u8 rx_flags;			   /* throttling - used from whiteheat/ftdi_sio */
107*4882a593Smuzhiyun 	enum packet_format pkt_fmt;	   /* format to use for packet send / receive */
108*4882a593Smuzhiyun 	int get_cfg_unsafe;		   /* If true, the CYPRESS_GET_CONFIG is unsafe */
109*4882a593Smuzhiyun 	int baud_rate;			   /* stores current baud rate in
110*4882a593Smuzhiyun 					      integer form */
111*4882a593Smuzhiyun 	char prev_status;		   /* used for TIOCMIWAIT */
112*4882a593Smuzhiyun };
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun /* function prototypes for the Cypress USB to serial device */
115*4882a593Smuzhiyun static int  cypress_earthmate_port_probe(struct usb_serial_port *port);
116*4882a593Smuzhiyun static int  cypress_hidcom_port_probe(struct usb_serial_port *port);
117*4882a593Smuzhiyun static int  cypress_ca42v2_port_probe(struct usb_serial_port *port);
118*4882a593Smuzhiyun static int  cypress_port_remove(struct usb_serial_port *port);
119*4882a593Smuzhiyun static int  cypress_open(struct tty_struct *tty, struct usb_serial_port *port);
120*4882a593Smuzhiyun static void cypress_close(struct usb_serial_port *port);
121*4882a593Smuzhiyun static void cypress_dtr_rts(struct usb_serial_port *port, int on);
122*4882a593Smuzhiyun static int  cypress_write(struct tty_struct *tty, struct usb_serial_port *port,
123*4882a593Smuzhiyun 			const unsigned char *buf, int count);
124*4882a593Smuzhiyun static void cypress_send(struct usb_serial_port *port);
125*4882a593Smuzhiyun static int  cypress_write_room(struct tty_struct *tty);
126*4882a593Smuzhiyun static void cypress_earthmate_init_termios(struct tty_struct *tty);
127*4882a593Smuzhiyun static void cypress_set_termios(struct tty_struct *tty,
128*4882a593Smuzhiyun 			struct usb_serial_port *port, struct ktermios *old);
129*4882a593Smuzhiyun static int  cypress_tiocmget(struct tty_struct *tty);
130*4882a593Smuzhiyun static int  cypress_tiocmset(struct tty_struct *tty,
131*4882a593Smuzhiyun 			unsigned int set, unsigned int clear);
132*4882a593Smuzhiyun static int  cypress_chars_in_buffer(struct tty_struct *tty);
133*4882a593Smuzhiyun static void cypress_throttle(struct tty_struct *tty);
134*4882a593Smuzhiyun static void cypress_unthrottle(struct tty_struct *tty);
135*4882a593Smuzhiyun static void cypress_set_dead(struct usb_serial_port *port);
136*4882a593Smuzhiyun static void cypress_read_int_callback(struct urb *urb);
137*4882a593Smuzhiyun static void cypress_write_int_callback(struct urb *urb);
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun static struct usb_serial_driver cypress_earthmate_device = {
140*4882a593Smuzhiyun 	.driver = {
141*4882a593Smuzhiyun 		.owner =		THIS_MODULE,
142*4882a593Smuzhiyun 		.name =			"earthmate",
143*4882a593Smuzhiyun 	},
144*4882a593Smuzhiyun 	.description =			"DeLorme Earthmate USB",
145*4882a593Smuzhiyun 	.id_table =			id_table_earthmate,
146*4882a593Smuzhiyun 	.num_ports =			1,
147*4882a593Smuzhiyun 	.port_probe =			cypress_earthmate_port_probe,
148*4882a593Smuzhiyun 	.port_remove =			cypress_port_remove,
149*4882a593Smuzhiyun 	.open =				cypress_open,
150*4882a593Smuzhiyun 	.close =			cypress_close,
151*4882a593Smuzhiyun 	.dtr_rts =			cypress_dtr_rts,
152*4882a593Smuzhiyun 	.write =			cypress_write,
153*4882a593Smuzhiyun 	.write_room =			cypress_write_room,
154*4882a593Smuzhiyun 	.init_termios =			cypress_earthmate_init_termios,
155*4882a593Smuzhiyun 	.set_termios =			cypress_set_termios,
156*4882a593Smuzhiyun 	.tiocmget =			cypress_tiocmget,
157*4882a593Smuzhiyun 	.tiocmset =			cypress_tiocmset,
158*4882a593Smuzhiyun 	.tiocmiwait =			usb_serial_generic_tiocmiwait,
159*4882a593Smuzhiyun 	.chars_in_buffer =		cypress_chars_in_buffer,
160*4882a593Smuzhiyun 	.throttle =		 	cypress_throttle,
161*4882a593Smuzhiyun 	.unthrottle =			cypress_unthrottle,
162*4882a593Smuzhiyun 	.read_int_callback =		cypress_read_int_callback,
163*4882a593Smuzhiyun 	.write_int_callback =		cypress_write_int_callback,
164*4882a593Smuzhiyun };
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun static struct usb_serial_driver cypress_hidcom_device = {
167*4882a593Smuzhiyun 	.driver = {
168*4882a593Smuzhiyun 		.owner =		THIS_MODULE,
169*4882a593Smuzhiyun 		.name =			"cyphidcom",
170*4882a593Smuzhiyun 	},
171*4882a593Smuzhiyun 	.description =			"HID->COM RS232 Adapter",
172*4882a593Smuzhiyun 	.id_table =			id_table_cyphidcomrs232,
173*4882a593Smuzhiyun 	.num_ports =			1,
174*4882a593Smuzhiyun 	.port_probe =			cypress_hidcom_port_probe,
175*4882a593Smuzhiyun 	.port_remove =			cypress_port_remove,
176*4882a593Smuzhiyun 	.open =				cypress_open,
177*4882a593Smuzhiyun 	.close =			cypress_close,
178*4882a593Smuzhiyun 	.dtr_rts =			cypress_dtr_rts,
179*4882a593Smuzhiyun 	.write =			cypress_write,
180*4882a593Smuzhiyun 	.write_room =			cypress_write_room,
181*4882a593Smuzhiyun 	.set_termios =			cypress_set_termios,
182*4882a593Smuzhiyun 	.tiocmget =			cypress_tiocmget,
183*4882a593Smuzhiyun 	.tiocmset =			cypress_tiocmset,
184*4882a593Smuzhiyun 	.tiocmiwait =			usb_serial_generic_tiocmiwait,
185*4882a593Smuzhiyun 	.chars_in_buffer =		cypress_chars_in_buffer,
186*4882a593Smuzhiyun 	.throttle =			cypress_throttle,
187*4882a593Smuzhiyun 	.unthrottle =			cypress_unthrottle,
188*4882a593Smuzhiyun 	.read_int_callback =		cypress_read_int_callback,
189*4882a593Smuzhiyun 	.write_int_callback =		cypress_write_int_callback,
190*4882a593Smuzhiyun };
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun static struct usb_serial_driver cypress_ca42v2_device = {
193*4882a593Smuzhiyun 	.driver = {
194*4882a593Smuzhiyun 		.owner =		THIS_MODULE,
195*4882a593Smuzhiyun 		.name =			"nokiaca42v2",
196*4882a593Smuzhiyun 	},
197*4882a593Smuzhiyun 	.description =			"Nokia CA-42 V2 Adapter",
198*4882a593Smuzhiyun 	.id_table =			id_table_nokiaca42v2,
199*4882a593Smuzhiyun 	.num_ports =			1,
200*4882a593Smuzhiyun 	.port_probe =			cypress_ca42v2_port_probe,
201*4882a593Smuzhiyun 	.port_remove =			cypress_port_remove,
202*4882a593Smuzhiyun 	.open =				cypress_open,
203*4882a593Smuzhiyun 	.close =			cypress_close,
204*4882a593Smuzhiyun 	.dtr_rts =			cypress_dtr_rts,
205*4882a593Smuzhiyun 	.write =			cypress_write,
206*4882a593Smuzhiyun 	.write_room =			cypress_write_room,
207*4882a593Smuzhiyun 	.set_termios =			cypress_set_termios,
208*4882a593Smuzhiyun 	.tiocmget =			cypress_tiocmget,
209*4882a593Smuzhiyun 	.tiocmset =			cypress_tiocmset,
210*4882a593Smuzhiyun 	.tiocmiwait =			usb_serial_generic_tiocmiwait,
211*4882a593Smuzhiyun 	.chars_in_buffer =		cypress_chars_in_buffer,
212*4882a593Smuzhiyun 	.throttle =			cypress_throttle,
213*4882a593Smuzhiyun 	.unthrottle =			cypress_unthrottle,
214*4882a593Smuzhiyun 	.read_int_callback =		cypress_read_int_callback,
215*4882a593Smuzhiyun 	.write_int_callback =		cypress_write_int_callback,
216*4882a593Smuzhiyun };
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun static struct usb_serial_driver * const serial_drivers[] = {
219*4882a593Smuzhiyun 	&cypress_earthmate_device, &cypress_hidcom_device,
220*4882a593Smuzhiyun 	&cypress_ca42v2_device, NULL
221*4882a593Smuzhiyun };
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun /*****************************************************************************
224*4882a593Smuzhiyun  * Cypress serial helper functions
225*4882a593Smuzhiyun  *****************************************************************************/
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun /* FRWD Dongle hidcom needs to skip reset and speed checks */
is_frwd(struct usb_device * dev)228*4882a593Smuzhiyun static inline bool is_frwd(struct usb_device *dev)
229*4882a593Smuzhiyun {
230*4882a593Smuzhiyun 	return ((le16_to_cpu(dev->descriptor.idVendor) == VENDOR_ID_FRWD) &&
231*4882a593Smuzhiyun 		(le16_to_cpu(dev->descriptor.idProduct) == PRODUCT_ID_CYPHIDCOM_FRWD));
232*4882a593Smuzhiyun }
233*4882a593Smuzhiyun 
analyze_baud_rate(struct usb_serial_port * port,speed_t new_rate)234*4882a593Smuzhiyun static int analyze_baud_rate(struct usb_serial_port *port, speed_t new_rate)
235*4882a593Smuzhiyun {
236*4882a593Smuzhiyun 	struct cypress_private *priv;
237*4882a593Smuzhiyun 	priv = usb_get_serial_port_data(port);
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 	if (unstable_bauds)
240*4882a593Smuzhiyun 		return new_rate;
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun 	/* FRWD Dongle uses 115200 bps */
243*4882a593Smuzhiyun 	if (is_frwd(port->serial->dev))
244*4882a593Smuzhiyun 		return new_rate;
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun 	/*
247*4882a593Smuzhiyun 	 * The general purpose firmware for the Cypress M8 allows for
248*4882a593Smuzhiyun 	 * a maximum speed of 57600bps (I have no idea whether DeLorme
249*4882a593Smuzhiyun 	 * chose to use the general purpose firmware or not), if you
250*4882a593Smuzhiyun 	 * need to modify this speed setting for your own project
251*4882a593Smuzhiyun 	 * please add your own chiptype and modify the code likewise.
252*4882a593Smuzhiyun 	 * The Cypress HID->COM device will work successfully up to
253*4882a593Smuzhiyun 	 * 115200bps (but the actual throughput is around 3kBps).
254*4882a593Smuzhiyun 	 */
255*4882a593Smuzhiyun 	if (port->serial->dev->speed == USB_SPEED_LOW) {
256*4882a593Smuzhiyun 		/*
257*4882a593Smuzhiyun 		 * Mike Isely <isely@pobox.com> 2-Feb-2008: The
258*4882a593Smuzhiyun 		 * Cypress app note that describes this mechanism
259*4882a593Smuzhiyun 		 * states the the low-speed part can't handle more
260*4882a593Smuzhiyun 		 * than 800 bytes/sec, in which case 4800 baud is the
261*4882a593Smuzhiyun 		 * safest speed for a part like that.
262*4882a593Smuzhiyun 		 */
263*4882a593Smuzhiyun 		if (new_rate > 4800) {
264*4882a593Smuzhiyun 			dev_dbg(&port->dev,
265*4882a593Smuzhiyun 				"%s - failed setting baud rate, device incapable speed %d\n",
266*4882a593Smuzhiyun 				__func__, new_rate);
267*4882a593Smuzhiyun 			return -1;
268*4882a593Smuzhiyun 		}
269*4882a593Smuzhiyun 	}
270*4882a593Smuzhiyun 	switch (priv->chiptype) {
271*4882a593Smuzhiyun 	case CT_EARTHMATE:
272*4882a593Smuzhiyun 		if (new_rate <= 600) {
273*4882a593Smuzhiyun 			/* 300 and 600 baud rates are supported under
274*4882a593Smuzhiyun 			 * the generic firmware, but are not used with
275*4882a593Smuzhiyun 			 * NMEA and SiRF protocols */
276*4882a593Smuzhiyun 			dev_dbg(&port->dev,
277*4882a593Smuzhiyun 				"%s - failed setting baud rate, unsupported speed of %d on Earthmate GPS\n",
278*4882a593Smuzhiyun 				__func__, new_rate);
279*4882a593Smuzhiyun 			return -1;
280*4882a593Smuzhiyun 		}
281*4882a593Smuzhiyun 		break;
282*4882a593Smuzhiyun 	default:
283*4882a593Smuzhiyun 		break;
284*4882a593Smuzhiyun 	}
285*4882a593Smuzhiyun 	return new_rate;
286*4882a593Smuzhiyun }
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun 
289*4882a593Smuzhiyun /* This function can either set or retrieve the current serial line settings */
cypress_serial_control(struct tty_struct * tty,struct usb_serial_port * port,speed_t baud_rate,int data_bits,int stop_bits,int parity_enable,int parity_type,int reset,int cypress_request_type)290*4882a593Smuzhiyun static int cypress_serial_control(struct tty_struct *tty,
291*4882a593Smuzhiyun 	struct usb_serial_port *port, speed_t baud_rate, int data_bits,
292*4882a593Smuzhiyun 	int stop_bits, int parity_enable, int parity_type, int reset,
293*4882a593Smuzhiyun 	int cypress_request_type)
294*4882a593Smuzhiyun {
295*4882a593Smuzhiyun 	int new_baudrate = 0, retval = 0, tries = 0;
296*4882a593Smuzhiyun 	struct cypress_private *priv;
297*4882a593Smuzhiyun 	struct device *dev = &port->dev;
298*4882a593Smuzhiyun 	u8 *feature_buffer;
299*4882a593Smuzhiyun 	const unsigned int feature_len = 5;
300*4882a593Smuzhiyun 	unsigned long flags;
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun 	priv = usb_get_serial_port_data(port);
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun 	if (!priv->comm_is_ok)
305*4882a593Smuzhiyun 		return -ENODEV;
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 	feature_buffer = kcalloc(feature_len, sizeof(u8), GFP_KERNEL);
308*4882a593Smuzhiyun 	if (!feature_buffer)
309*4882a593Smuzhiyun 		return -ENOMEM;
310*4882a593Smuzhiyun 
311*4882a593Smuzhiyun 	switch (cypress_request_type) {
312*4882a593Smuzhiyun 	case CYPRESS_SET_CONFIG:
313*4882a593Smuzhiyun 		/* 0 means 'Hang up' so doesn't change the true bit rate */
314*4882a593Smuzhiyun 		new_baudrate = priv->baud_rate;
315*4882a593Smuzhiyun 		if (baud_rate && baud_rate != priv->baud_rate) {
316*4882a593Smuzhiyun 			dev_dbg(dev, "%s - baud rate is changing\n", __func__);
317*4882a593Smuzhiyun 			retval = analyze_baud_rate(port, baud_rate);
318*4882a593Smuzhiyun 			if (retval >= 0) {
319*4882a593Smuzhiyun 				new_baudrate = retval;
320*4882a593Smuzhiyun 				dev_dbg(dev, "%s - New baud rate set to %d\n",
321*4882a593Smuzhiyun 					__func__, new_baudrate);
322*4882a593Smuzhiyun 			}
323*4882a593Smuzhiyun 		}
324*4882a593Smuzhiyun 		dev_dbg(dev, "%s - baud rate is being sent as %d\n", __func__,
325*4882a593Smuzhiyun 			new_baudrate);
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun 		/* fill the feature_buffer with new configuration */
328*4882a593Smuzhiyun 		put_unaligned_le32(new_baudrate, feature_buffer);
329*4882a593Smuzhiyun 		feature_buffer[4] |= data_bits;   /* assign data bits in 2 bit space ( max 3 ) */
330*4882a593Smuzhiyun 		/* 1 bit gap */
331*4882a593Smuzhiyun 		feature_buffer[4] |= (stop_bits << 3);   /* assign stop bits in 1 bit space */
332*4882a593Smuzhiyun 		feature_buffer[4] |= (parity_enable << 4);   /* assign parity flag in 1 bit space */
333*4882a593Smuzhiyun 		feature_buffer[4] |= (parity_type << 5);   /* assign parity type in 1 bit space */
334*4882a593Smuzhiyun 		/* 1 bit gap */
335*4882a593Smuzhiyun 		feature_buffer[4] |= (reset << 7);   /* assign reset at end of byte, 1 bit space */
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun 		dev_dbg(dev, "%s - device is being sent this feature report:\n", __func__);
338*4882a593Smuzhiyun 		dev_dbg(dev, "%s - %02X - %02X - %02X - %02X - %02X\n", __func__,
339*4882a593Smuzhiyun 			feature_buffer[0], feature_buffer[1],
340*4882a593Smuzhiyun 			feature_buffer[2], feature_buffer[3],
341*4882a593Smuzhiyun 			feature_buffer[4]);
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun 		do {
344*4882a593Smuzhiyun 			retval = usb_control_msg(port->serial->dev,
345*4882a593Smuzhiyun 					usb_sndctrlpipe(port->serial->dev, 0),
346*4882a593Smuzhiyun 					HID_REQ_SET_REPORT,
347*4882a593Smuzhiyun 					USB_DIR_OUT | USB_RECIP_INTERFACE | USB_TYPE_CLASS,
348*4882a593Smuzhiyun 					0x0300, 0, feature_buffer,
349*4882a593Smuzhiyun 					feature_len, 500);
350*4882a593Smuzhiyun 
351*4882a593Smuzhiyun 			if (tries++ >= 3)
352*4882a593Smuzhiyun 				break;
353*4882a593Smuzhiyun 
354*4882a593Smuzhiyun 		} while (retval != feature_len &&
355*4882a593Smuzhiyun 			 retval != -ENODEV);
356*4882a593Smuzhiyun 
357*4882a593Smuzhiyun 		if (retval != feature_len) {
358*4882a593Smuzhiyun 			dev_err(dev, "%s - failed sending serial line settings - %d\n",
359*4882a593Smuzhiyun 				__func__, retval);
360*4882a593Smuzhiyun 			cypress_set_dead(port);
361*4882a593Smuzhiyun 		} else {
362*4882a593Smuzhiyun 			spin_lock_irqsave(&priv->lock, flags);
363*4882a593Smuzhiyun 			priv->baud_rate = new_baudrate;
364*4882a593Smuzhiyun 			priv->current_config = feature_buffer[4];
365*4882a593Smuzhiyun 			spin_unlock_irqrestore(&priv->lock, flags);
366*4882a593Smuzhiyun 			/* If we asked for a speed change encode it */
367*4882a593Smuzhiyun 			if (baud_rate)
368*4882a593Smuzhiyun 				tty_encode_baud_rate(tty,
369*4882a593Smuzhiyun 					new_baudrate, new_baudrate);
370*4882a593Smuzhiyun 		}
371*4882a593Smuzhiyun 	break;
372*4882a593Smuzhiyun 	case CYPRESS_GET_CONFIG:
373*4882a593Smuzhiyun 		if (priv->get_cfg_unsafe) {
374*4882a593Smuzhiyun 			/* Not implemented for this device,
375*4882a593Smuzhiyun 			   and if we try to do it we're likely
376*4882a593Smuzhiyun 			   to crash the hardware. */
377*4882a593Smuzhiyun 			retval = -ENOTTY;
378*4882a593Smuzhiyun 			goto out;
379*4882a593Smuzhiyun 		}
380*4882a593Smuzhiyun 		dev_dbg(dev, "%s - retrieving serial line settings\n", __func__);
381*4882a593Smuzhiyun 		do {
382*4882a593Smuzhiyun 			retval = usb_control_msg(port->serial->dev,
383*4882a593Smuzhiyun 					usb_rcvctrlpipe(port->serial->dev, 0),
384*4882a593Smuzhiyun 					HID_REQ_GET_REPORT,
385*4882a593Smuzhiyun 					USB_DIR_IN | USB_RECIP_INTERFACE | USB_TYPE_CLASS,
386*4882a593Smuzhiyun 					0x0300, 0, feature_buffer,
387*4882a593Smuzhiyun 					feature_len, 500);
388*4882a593Smuzhiyun 
389*4882a593Smuzhiyun 			if (tries++ >= 3)
390*4882a593Smuzhiyun 				break;
391*4882a593Smuzhiyun 		} while (retval != feature_len
392*4882a593Smuzhiyun 						&& retval != -ENODEV);
393*4882a593Smuzhiyun 
394*4882a593Smuzhiyun 		if (retval != feature_len) {
395*4882a593Smuzhiyun 			dev_err(dev, "%s - failed to retrieve serial line settings - %d\n",
396*4882a593Smuzhiyun 				__func__, retval);
397*4882a593Smuzhiyun 			cypress_set_dead(port);
398*4882a593Smuzhiyun 			goto out;
399*4882a593Smuzhiyun 		} else {
400*4882a593Smuzhiyun 			spin_lock_irqsave(&priv->lock, flags);
401*4882a593Smuzhiyun 			/* store the config in one byte, and later
402*4882a593Smuzhiyun 			   use bit masks to check values */
403*4882a593Smuzhiyun 			priv->current_config = feature_buffer[4];
404*4882a593Smuzhiyun 			priv->baud_rate = get_unaligned_le32(feature_buffer);
405*4882a593Smuzhiyun 			spin_unlock_irqrestore(&priv->lock, flags);
406*4882a593Smuzhiyun 		}
407*4882a593Smuzhiyun 	}
408*4882a593Smuzhiyun 	spin_lock_irqsave(&priv->lock, flags);
409*4882a593Smuzhiyun 	++priv->cmd_count;
410*4882a593Smuzhiyun 	spin_unlock_irqrestore(&priv->lock, flags);
411*4882a593Smuzhiyun out:
412*4882a593Smuzhiyun 	kfree(feature_buffer);
413*4882a593Smuzhiyun 	return retval;
414*4882a593Smuzhiyun } /* cypress_serial_control */
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun 
cypress_set_dead(struct usb_serial_port * port)417*4882a593Smuzhiyun static void cypress_set_dead(struct usb_serial_port *port)
418*4882a593Smuzhiyun {
419*4882a593Smuzhiyun 	struct cypress_private *priv = usb_get_serial_port_data(port);
420*4882a593Smuzhiyun 	unsigned long flags;
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun 	spin_lock_irqsave(&priv->lock, flags);
423*4882a593Smuzhiyun 	if (!priv->comm_is_ok) {
424*4882a593Smuzhiyun 		spin_unlock_irqrestore(&priv->lock, flags);
425*4882a593Smuzhiyun 		return;
426*4882a593Smuzhiyun 	}
427*4882a593Smuzhiyun 	priv->comm_is_ok = 0;
428*4882a593Smuzhiyun 	spin_unlock_irqrestore(&priv->lock, flags);
429*4882a593Smuzhiyun 
430*4882a593Smuzhiyun 	dev_err(&port->dev, "cypress_m8 suspending failing port %d - "
431*4882a593Smuzhiyun 		"interval might be too short\n", port->port_number);
432*4882a593Smuzhiyun }
433*4882a593Smuzhiyun 
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun /*****************************************************************************
436*4882a593Smuzhiyun  * Cypress serial driver functions
437*4882a593Smuzhiyun  *****************************************************************************/
438*4882a593Smuzhiyun 
439*4882a593Smuzhiyun 
cypress_generic_port_probe(struct usb_serial_port * port)440*4882a593Smuzhiyun static int cypress_generic_port_probe(struct usb_serial_port *port)
441*4882a593Smuzhiyun {
442*4882a593Smuzhiyun 	struct usb_serial *serial = port->serial;
443*4882a593Smuzhiyun 	struct cypress_private *priv;
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun 	if (!port->interrupt_out_urb || !port->interrupt_in_urb) {
446*4882a593Smuzhiyun 		dev_err(&port->dev, "required endpoint is missing\n");
447*4882a593Smuzhiyun 		return -ENODEV;
448*4882a593Smuzhiyun 	}
449*4882a593Smuzhiyun 
450*4882a593Smuzhiyun 	priv = kzalloc(sizeof(struct cypress_private), GFP_KERNEL);
451*4882a593Smuzhiyun 	if (!priv)
452*4882a593Smuzhiyun 		return -ENOMEM;
453*4882a593Smuzhiyun 
454*4882a593Smuzhiyun 	priv->comm_is_ok = !0;
455*4882a593Smuzhiyun 	spin_lock_init(&priv->lock);
456*4882a593Smuzhiyun 	if (kfifo_alloc(&priv->write_fifo, CYPRESS_BUF_SIZE, GFP_KERNEL)) {
457*4882a593Smuzhiyun 		kfree(priv);
458*4882a593Smuzhiyun 		return -ENOMEM;
459*4882a593Smuzhiyun 	}
460*4882a593Smuzhiyun 
461*4882a593Smuzhiyun 	/* Skip reset for FRWD device. It is a workaound:
462*4882a593Smuzhiyun 	   device hangs if it receives SET_CONFIGURE in Configured
463*4882a593Smuzhiyun 	   state. */
464*4882a593Smuzhiyun 	if (!is_frwd(serial->dev))
465*4882a593Smuzhiyun 		usb_reset_configuration(serial->dev);
466*4882a593Smuzhiyun 
467*4882a593Smuzhiyun 	priv->cmd_ctrl = 0;
468*4882a593Smuzhiyun 	priv->line_control = 0;
469*4882a593Smuzhiyun 	priv->rx_flags = 0;
470*4882a593Smuzhiyun 	/* Default packet format setting is determined by packet size.
471*4882a593Smuzhiyun 	   Anything with a size larger then 9 must have a separate
472*4882a593Smuzhiyun 	   count field since the 3 bit count field is otherwise too
473*4882a593Smuzhiyun 	   small.  Otherwise we can use the slightly more compact
474*4882a593Smuzhiyun 	   format.  This is in accordance with the cypress_m8 serial
475*4882a593Smuzhiyun 	   converter app note. */
476*4882a593Smuzhiyun 	if (port->interrupt_out_size > 9)
477*4882a593Smuzhiyun 		priv->pkt_fmt = packet_format_1;
478*4882a593Smuzhiyun 	else
479*4882a593Smuzhiyun 		priv->pkt_fmt = packet_format_2;
480*4882a593Smuzhiyun 
481*4882a593Smuzhiyun 	if (interval > 0) {
482*4882a593Smuzhiyun 		priv->write_urb_interval = interval;
483*4882a593Smuzhiyun 		priv->read_urb_interval = interval;
484*4882a593Smuzhiyun 		dev_dbg(&port->dev, "%s - read & write intervals forced to %d\n",
485*4882a593Smuzhiyun 			__func__, interval);
486*4882a593Smuzhiyun 	} else {
487*4882a593Smuzhiyun 		priv->write_urb_interval = port->interrupt_out_urb->interval;
488*4882a593Smuzhiyun 		priv->read_urb_interval = port->interrupt_in_urb->interval;
489*4882a593Smuzhiyun 		dev_dbg(&port->dev, "%s - intervals: read=%d write=%d\n",
490*4882a593Smuzhiyun 			__func__, priv->read_urb_interval,
491*4882a593Smuzhiyun 			priv->write_urb_interval);
492*4882a593Smuzhiyun 	}
493*4882a593Smuzhiyun 	usb_set_serial_port_data(port, priv);
494*4882a593Smuzhiyun 
495*4882a593Smuzhiyun 	port->port.drain_delay = 256;
496*4882a593Smuzhiyun 
497*4882a593Smuzhiyun 	return 0;
498*4882a593Smuzhiyun }
499*4882a593Smuzhiyun 
500*4882a593Smuzhiyun 
cypress_earthmate_port_probe(struct usb_serial_port * port)501*4882a593Smuzhiyun static int cypress_earthmate_port_probe(struct usb_serial_port *port)
502*4882a593Smuzhiyun {
503*4882a593Smuzhiyun 	struct usb_serial *serial = port->serial;
504*4882a593Smuzhiyun 	struct cypress_private *priv;
505*4882a593Smuzhiyun 	int ret;
506*4882a593Smuzhiyun 
507*4882a593Smuzhiyun 	ret = cypress_generic_port_probe(port);
508*4882a593Smuzhiyun 	if (ret) {
509*4882a593Smuzhiyun 		dev_dbg(&port->dev, "%s - Failed setting up port\n", __func__);
510*4882a593Smuzhiyun 		return ret;
511*4882a593Smuzhiyun 	}
512*4882a593Smuzhiyun 
513*4882a593Smuzhiyun 	priv = usb_get_serial_port_data(port);
514*4882a593Smuzhiyun 	priv->chiptype = CT_EARTHMATE;
515*4882a593Smuzhiyun 	/* All Earthmate devices use the separated-count packet
516*4882a593Smuzhiyun 	   format!  Idiotic. */
517*4882a593Smuzhiyun 	priv->pkt_fmt = packet_format_1;
518*4882a593Smuzhiyun 	if (serial->dev->descriptor.idProduct !=
519*4882a593Smuzhiyun 				cpu_to_le16(PRODUCT_ID_EARTHMATEUSB)) {
520*4882a593Smuzhiyun 		/* The old original USB Earthmate seemed able to
521*4882a593Smuzhiyun 		   handle GET_CONFIG requests; everything they've
522*4882a593Smuzhiyun 		   produced since that time crashes if this command is
523*4882a593Smuzhiyun 		   attempted :-( */
524*4882a593Smuzhiyun 		dev_dbg(&port->dev,
525*4882a593Smuzhiyun 			"%s - Marking this device as unsafe for GET_CONFIG commands\n",
526*4882a593Smuzhiyun 			__func__);
527*4882a593Smuzhiyun 		priv->get_cfg_unsafe = !0;
528*4882a593Smuzhiyun 	}
529*4882a593Smuzhiyun 
530*4882a593Smuzhiyun 	return 0;
531*4882a593Smuzhiyun }
532*4882a593Smuzhiyun 
cypress_hidcom_port_probe(struct usb_serial_port * port)533*4882a593Smuzhiyun static int cypress_hidcom_port_probe(struct usb_serial_port *port)
534*4882a593Smuzhiyun {
535*4882a593Smuzhiyun 	struct cypress_private *priv;
536*4882a593Smuzhiyun 	int ret;
537*4882a593Smuzhiyun 
538*4882a593Smuzhiyun 	ret = cypress_generic_port_probe(port);
539*4882a593Smuzhiyun 	if (ret) {
540*4882a593Smuzhiyun 		dev_dbg(&port->dev, "%s - Failed setting up port\n", __func__);
541*4882a593Smuzhiyun 		return ret;
542*4882a593Smuzhiyun 	}
543*4882a593Smuzhiyun 
544*4882a593Smuzhiyun 	priv = usb_get_serial_port_data(port);
545*4882a593Smuzhiyun 	priv->chiptype = CT_CYPHIDCOM;
546*4882a593Smuzhiyun 
547*4882a593Smuzhiyun 	return 0;
548*4882a593Smuzhiyun }
549*4882a593Smuzhiyun 
cypress_ca42v2_port_probe(struct usb_serial_port * port)550*4882a593Smuzhiyun static int cypress_ca42v2_port_probe(struct usb_serial_port *port)
551*4882a593Smuzhiyun {
552*4882a593Smuzhiyun 	struct cypress_private *priv;
553*4882a593Smuzhiyun 	int ret;
554*4882a593Smuzhiyun 
555*4882a593Smuzhiyun 	ret = cypress_generic_port_probe(port);
556*4882a593Smuzhiyun 	if (ret) {
557*4882a593Smuzhiyun 		dev_dbg(&port->dev, "%s - Failed setting up port\n", __func__);
558*4882a593Smuzhiyun 		return ret;
559*4882a593Smuzhiyun 	}
560*4882a593Smuzhiyun 
561*4882a593Smuzhiyun 	priv = usb_get_serial_port_data(port);
562*4882a593Smuzhiyun 	priv->chiptype = CT_CA42V2;
563*4882a593Smuzhiyun 
564*4882a593Smuzhiyun 	return 0;
565*4882a593Smuzhiyun }
566*4882a593Smuzhiyun 
cypress_port_remove(struct usb_serial_port * port)567*4882a593Smuzhiyun static int cypress_port_remove(struct usb_serial_port *port)
568*4882a593Smuzhiyun {
569*4882a593Smuzhiyun 	struct cypress_private *priv;
570*4882a593Smuzhiyun 
571*4882a593Smuzhiyun 	priv = usb_get_serial_port_data(port);
572*4882a593Smuzhiyun 
573*4882a593Smuzhiyun 	kfifo_free(&priv->write_fifo);
574*4882a593Smuzhiyun 	kfree(priv);
575*4882a593Smuzhiyun 
576*4882a593Smuzhiyun 	return 0;
577*4882a593Smuzhiyun }
578*4882a593Smuzhiyun 
cypress_open(struct tty_struct * tty,struct usb_serial_port * port)579*4882a593Smuzhiyun static int cypress_open(struct tty_struct *tty, struct usb_serial_port *port)
580*4882a593Smuzhiyun {
581*4882a593Smuzhiyun 	struct cypress_private *priv = usb_get_serial_port_data(port);
582*4882a593Smuzhiyun 	struct usb_serial *serial = port->serial;
583*4882a593Smuzhiyun 	unsigned long flags;
584*4882a593Smuzhiyun 	int result = 0;
585*4882a593Smuzhiyun 
586*4882a593Smuzhiyun 	if (!priv->comm_is_ok)
587*4882a593Smuzhiyun 		return -EIO;
588*4882a593Smuzhiyun 
589*4882a593Smuzhiyun 	/* clear halts before open */
590*4882a593Smuzhiyun 	usb_clear_halt(serial->dev, 0x81);
591*4882a593Smuzhiyun 	usb_clear_halt(serial->dev, 0x02);
592*4882a593Smuzhiyun 
593*4882a593Smuzhiyun 	spin_lock_irqsave(&priv->lock, flags);
594*4882a593Smuzhiyun 	/* reset read/write statistics */
595*4882a593Smuzhiyun 	priv->bytes_in = 0;
596*4882a593Smuzhiyun 	priv->bytes_out = 0;
597*4882a593Smuzhiyun 	priv->cmd_count = 0;
598*4882a593Smuzhiyun 	priv->rx_flags = 0;
599*4882a593Smuzhiyun 	spin_unlock_irqrestore(&priv->lock, flags);
600*4882a593Smuzhiyun 
601*4882a593Smuzhiyun 	/* Set termios */
602*4882a593Smuzhiyun 	cypress_send(port);
603*4882a593Smuzhiyun 
604*4882a593Smuzhiyun 	if (tty)
605*4882a593Smuzhiyun 		cypress_set_termios(tty, port, NULL);
606*4882a593Smuzhiyun 
607*4882a593Smuzhiyun 	/* setup the port and start reading from the device */
608*4882a593Smuzhiyun 	usb_fill_int_urb(port->interrupt_in_urb, serial->dev,
609*4882a593Smuzhiyun 		usb_rcvintpipe(serial->dev, port->interrupt_in_endpointAddress),
610*4882a593Smuzhiyun 		port->interrupt_in_urb->transfer_buffer,
611*4882a593Smuzhiyun 		port->interrupt_in_urb->transfer_buffer_length,
612*4882a593Smuzhiyun 		cypress_read_int_callback, port, priv->read_urb_interval);
613*4882a593Smuzhiyun 	result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
614*4882a593Smuzhiyun 
615*4882a593Smuzhiyun 	if (result) {
616*4882a593Smuzhiyun 		dev_err(&port->dev,
617*4882a593Smuzhiyun 			"%s - failed submitting read urb, error %d\n",
618*4882a593Smuzhiyun 							__func__, result);
619*4882a593Smuzhiyun 		cypress_set_dead(port);
620*4882a593Smuzhiyun 	}
621*4882a593Smuzhiyun 
622*4882a593Smuzhiyun 	return result;
623*4882a593Smuzhiyun } /* cypress_open */
624*4882a593Smuzhiyun 
cypress_dtr_rts(struct usb_serial_port * port,int on)625*4882a593Smuzhiyun static void cypress_dtr_rts(struct usb_serial_port *port, int on)
626*4882a593Smuzhiyun {
627*4882a593Smuzhiyun 	struct cypress_private *priv = usb_get_serial_port_data(port);
628*4882a593Smuzhiyun 	/* drop dtr and rts */
629*4882a593Smuzhiyun 	spin_lock_irq(&priv->lock);
630*4882a593Smuzhiyun 	if (on == 0)
631*4882a593Smuzhiyun 		priv->line_control = 0;
632*4882a593Smuzhiyun 	else
633*4882a593Smuzhiyun 		priv->line_control = CONTROL_DTR | CONTROL_RTS;
634*4882a593Smuzhiyun 	priv->cmd_ctrl = 1;
635*4882a593Smuzhiyun 	spin_unlock_irq(&priv->lock);
636*4882a593Smuzhiyun 	cypress_write(NULL, port, NULL, 0);
637*4882a593Smuzhiyun }
638*4882a593Smuzhiyun 
cypress_close(struct usb_serial_port * port)639*4882a593Smuzhiyun static void cypress_close(struct usb_serial_port *port)
640*4882a593Smuzhiyun {
641*4882a593Smuzhiyun 	struct cypress_private *priv = usb_get_serial_port_data(port);
642*4882a593Smuzhiyun 	unsigned long flags;
643*4882a593Smuzhiyun 
644*4882a593Smuzhiyun 	spin_lock_irqsave(&priv->lock, flags);
645*4882a593Smuzhiyun 	kfifo_reset_out(&priv->write_fifo);
646*4882a593Smuzhiyun 	spin_unlock_irqrestore(&priv->lock, flags);
647*4882a593Smuzhiyun 
648*4882a593Smuzhiyun 	dev_dbg(&port->dev, "%s - stopping urbs\n", __func__);
649*4882a593Smuzhiyun 	usb_kill_urb(port->interrupt_in_urb);
650*4882a593Smuzhiyun 	usb_kill_urb(port->interrupt_out_urb);
651*4882a593Smuzhiyun 
652*4882a593Smuzhiyun 	if (stats)
653*4882a593Smuzhiyun 		dev_info(&port->dev, "Statistics: %d Bytes In | %d Bytes Out | %d Commands Issued\n",
654*4882a593Smuzhiyun 			priv->bytes_in, priv->bytes_out, priv->cmd_count);
655*4882a593Smuzhiyun } /* cypress_close */
656*4882a593Smuzhiyun 
657*4882a593Smuzhiyun 
cypress_write(struct tty_struct * tty,struct usb_serial_port * port,const unsigned char * buf,int count)658*4882a593Smuzhiyun static int cypress_write(struct tty_struct *tty, struct usb_serial_port *port,
659*4882a593Smuzhiyun 					const unsigned char *buf, int count)
660*4882a593Smuzhiyun {
661*4882a593Smuzhiyun 	struct cypress_private *priv = usb_get_serial_port_data(port);
662*4882a593Smuzhiyun 
663*4882a593Smuzhiyun 	dev_dbg(&port->dev, "%s - %d bytes\n", __func__, count);
664*4882a593Smuzhiyun 
665*4882a593Smuzhiyun 	/* line control commands, which need to be executed immediately,
666*4882a593Smuzhiyun 	   are not put into the buffer for obvious reasons.
667*4882a593Smuzhiyun 	 */
668*4882a593Smuzhiyun 	if (priv->cmd_ctrl) {
669*4882a593Smuzhiyun 		count = 0;
670*4882a593Smuzhiyun 		goto finish;
671*4882a593Smuzhiyun 	}
672*4882a593Smuzhiyun 
673*4882a593Smuzhiyun 	if (!count)
674*4882a593Smuzhiyun 		return count;
675*4882a593Smuzhiyun 
676*4882a593Smuzhiyun 	count = kfifo_in_locked(&priv->write_fifo, buf, count, &priv->lock);
677*4882a593Smuzhiyun 
678*4882a593Smuzhiyun finish:
679*4882a593Smuzhiyun 	cypress_send(port);
680*4882a593Smuzhiyun 
681*4882a593Smuzhiyun 	return count;
682*4882a593Smuzhiyun } /* cypress_write */
683*4882a593Smuzhiyun 
684*4882a593Smuzhiyun 
cypress_send(struct usb_serial_port * port)685*4882a593Smuzhiyun static void cypress_send(struct usb_serial_port *port)
686*4882a593Smuzhiyun {
687*4882a593Smuzhiyun 	int count = 0, result, offset, actual_size;
688*4882a593Smuzhiyun 	struct cypress_private *priv = usb_get_serial_port_data(port);
689*4882a593Smuzhiyun 	struct device *dev = &port->dev;
690*4882a593Smuzhiyun 	unsigned long flags;
691*4882a593Smuzhiyun 
692*4882a593Smuzhiyun 	if (!priv->comm_is_ok)
693*4882a593Smuzhiyun 		return;
694*4882a593Smuzhiyun 
695*4882a593Smuzhiyun 	dev_dbg(dev, "%s - interrupt out size is %d\n", __func__,
696*4882a593Smuzhiyun 		port->interrupt_out_size);
697*4882a593Smuzhiyun 
698*4882a593Smuzhiyun 	spin_lock_irqsave(&priv->lock, flags);
699*4882a593Smuzhiyun 	if (priv->write_urb_in_use) {
700*4882a593Smuzhiyun 		dev_dbg(dev, "%s - can't write, urb in use\n", __func__);
701*4882a593Smuzhiyun 		spin_unlock_irqrestore(&priv->lock, flags);
702*4882a593Smuzhiyun 		return;
703*4882a593Smuzhiyun 	}
704*4882a593Smuzhiyun 	spin_unlock_irqrestore(&priv->lock, flags);
705*4882a593Smuzhiyun 
706*4882a593Smuzhiyun 	/* clear buffer */
707*4882a593Smuzhiyun 	memset(port->interrupt_out_urb->transfer_buffer, 0,
708*4882a593Smuzhiyun 						port->interrupt_out_size);
709*4882a593Smuzhiyun 
710*4882a593Smuzhiyun 	spin_lock_irqsave(&priv->lock, flags);
711*4882a593Smuzhiyun 	switch (priv->pkt_fmt) {
712*4882a593Smuzhiyun 	default:
713*4882a593Smuzhiyun 	case packet_format_1:
714*4882a593Smuzhiyun 		/* this is for the CY7C64013... */
715*4882a593Smuzhiyun 		offset = 2;
716*4882a593Smuzhiyun 		port->interrupt_out_buffer[0] = priv->line_control;
717*4882a593Smuzhiyun 		break;
718*4882a593Smuzhiyun 	case packet_format_2:
719*4882a593Smuzhiyun 		/* this is for the CY7C63743... */
720*4882a593Smuzhiyun 		offset = 1;
721*4882a593Smuzhiyun 		port->interrupt_out_buffer[0] = priv->line_control;
722*4882a593Smuzhiyun 		break;
723*4882a593Smuzhiyun 	}
724*4882a593Smuzhiyun 
725*4882a593Smuzhiyun 	if (priv->line_control & CONTROL_RESET)
726*4882a593Smuzhiyun 		priv->line_control &= ~CONTROL_RESET;
727*4882a593Smuzhiyun 
728*4882a593Smuzhiyun 	if (priv->cmd_ctrl) {
729*4882a593Smuzhiyun 		priv->cmd_count++;
730*4882a593Smuzhiyun 		dev_dbg(dev, "%s - line control command being issued\n", __func__);
731*4882a593Smuzhiyun 		spin_unlock_irqrestore(&priv->lock, flags);
732*4882a593Smuzhiyun 		goto send;
733*4882a593Smuzhiyun 	} else
734*4882a593Smuzhiyun 		spin_unlock_irqrestore(&priv->lock, flags);
735*4882a593Smuzhiyun 
736*4882a593Smuzhiyun 	count = kfifo_out_locked(&priv->write_fifo,
737*4882a593Smuzhiyun 					&port->interrupt_out_buffer[offset],
738*4882a593Smuzhiyun 					port->interrupt_out_size - offset,
739*4882a593Smuzhiyun 					&priv->lock);
740*4882a593Smuzhiyun 	if (count == 0)
741*4882a593Smuzhiyun 		return;
742*4882a593Smuzhiyun 
743*4882a593Smuzhiyun 	switch (priv->pkt_fmt) {
744*4882a593Smuzhiyun 	default:
745*4882a593Smuzhiyun 	case packet_format_1:
746*4882a593Smuzhiyun 		port->interrupt_out_buffer[1] = count;
747*4882a593Smuzhiyun 		break;
748*4882a593Smuzhiyun 	case packet_format_2:
749*4882a593Smuzhiyun 		port->interrupt_out_buffer[0] |= count;
750*4882a593Smuzhiyun 	}
751*4882a593Smuzhiyun 
752*4882a593Smuzhiyun 	dev_dbg(dev, "%s - count is %d\n", __func__, count);
753*4882a593Smuzhiyun 
754*4882a593Smuzhiyun send:
755*4882a593Smuzhiyun 	spin_lock_irqsave(&priv->lock, flags);
756*4882a593Smuzhiyun 	priv->write_urb_in_use = 1;
757*4882a593Smuzhiyun 	spin_unlock_irqrestore(&priv->lock, flags);
758*4882a593Smuzhiyun 
759*4882a593Smuzhiyun 	if (priv->cmd_ctrl)
760*4882a593Smuzhiyun 		actual_size = 1;
761*4882a593Smuzhiyun 	else
762*4882a593Smuzhiyun 		actual_size = count +
763*4882a593Smuzhiyun 			      (priv->pkt_fmt == packet_format_1 ? 2 : 1);
764*4882a593Smuzhiyun 
765*4882a593Smuzhiyun 	usb_serial_debug_data(dev, __func__, port->interrupt_out_size,
766*4882a593Smuzhiyun 			      port->interrupt_out_urb->transfer_buffer);
767*4882a593Smuzhiyun 
768*4882a593Smuzhiyun 	usb_fill_int_urb(port->interrupt_out_urb, port->serial->dev,
769*4882a593Smuzhiyun 		usb_sndintpipe(port->serial->dev, port->interrupt_out_endpointAddress),
770*4882a593Smuzhiyun 		port->interrupt_out_buffer, actual_size,
771*4882a593Smuzhiyun 		cypress_write_int_callback, port, priv->write_urb_interval);
772*4882a593Smuzhiyun 	result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC);
773*4882a593Smuzhiyun 	if (result) {
774*4882a593Smuzhiyun 		dev_err_console(port,
775*4882a593Smuzhiyun 				"%s - failed submitting write urb, error %d\n",
776*4882a593Smuzhiyun 							__func__, result);
777*4882a593Smuzhiyun 		priv->write_urb_in_use = 0;
778*4882a593Smuzhiyun 		cypress_set_dead(port);
779*4882a593Smuzhiyun 	}
780*4882a593Smuzhiyun 
781*4882a593Smuzhiyun 	spin_lock_irqsave(&priv->lock, flags);
782*4882a593Smuzhiyun 	if (priv->cmd_ctrl)
783*4882a593Smuzhiyun 		priv->cmd_ctrl = 0;
784*4882a593Smuzhiyun 
785*4882a593Smuzhiyun 	/* do not count the line control and size bytes */
786*4882a593Smuzhiyun 	priv->bytes_out += count;
787*4882a593Smuzhiyun 	spin_unlock_irqrestore(&priv->lock, flags);
788*4882a593Smuzhiyun 
789*4882a593Smuzhiyun 	usb_serial_port_softint(port);
790*4882a593Smuzhiyun } /* cypress_send */
791*4882a593Smuzhiyun 
792*4882a593Smuzhiyun 
793*4882a593Smuzhiyun /* returns how much space is available in the soft buffer */
cypress_write_room(struct tty_struct * tty)794*4882a593Smuzhiyun static int cypress_write_room(struct tty_struct *tty)
795*4882a593Smuzhiyun {
796*4882a593Smuzhiyun 	struct usb_serial_port *port = tty->driver_data;
797*4882a593Smuzhiyun 	struct cypress_private *priv = usb_get_serial_port_data(port);
798*4882a593Smuzhiyun 	int room = 0;
799*4882a593Smuzhiyun 	unsigned long flags;
800*4882a593Smuzhiyun 
801*4882a593Smuzhiyun 	spin_lock_irqsave(&priv->lock, flags);
802*4882a593Smuzhiyun 	room = kfifo_avail(&priv->write_fifo);
803*4882a593Smuzhiyun 	spin_unlock_irqrestore(&priv->lock, flags);
804*4882a593Smuzhiyun 
805*4882a593Smuzhiyun 	dev_dbg(&port->dev, "%s - returns %d\n", __func__, room);
806*4882a593Smuzhiyun 	return room;
807*4882a593Smuzhiyun }
808*4882a593Smuzhiyun 
809*4882a593Smuzhiyun 
cypress_tiocmget(struct tty_struct * tty)810*4882a593Smuzhiyun static int cypress_tiocmget(struct tty_struct *tty)
811*4882a593Smuzhiyun {
812*4882a593Smuzhiyun 	struct usb_serial_port *port = tty->driver_data;
813*4882a593Smuzhiyun 	struct cypress_private *priv = usb_get_serial_port_data(port);
814*4882a593Smuzhiyun 	__u8 status, control;
815*4882a593Smuzhiyun 	unsigned int result = 0;
816*4882a593Smuzhiyun 	unsigned long flags;
817*4882a593Smuzhiyun 
818*4882a593Smuzhiyun 	spin_lock_irqsave(&priv->lock, flags);
819*4882a593Smuzhiyun 	control = priv->line_control;
820*4882a593Smuzhiyun 	status = priv->current_status;
821*4882a593Smuzhiyun 	spin_unlock_irqrestore(&priv->lock, flags);
822*4882a593Smuzhiyun 
823*4882a593Smuzhiyun 	result = ((control & CONTROL_DTR)        ? TIOCM_DTR : 0)
824*4882a593Smuzhiyun 		| ((control & CONTROL_RTS)       ? TIOCM_RTS : 0)
825*4882a593Smuzhiyun 		| ((status & UART_CTS)        ? TIOCM_CTS : 0)
826*4882a593Smuzhiyun 		| ((status & UART_DSR)        ? TIOCM_DSR : 0)
827*4882a593Smuzhiyun 		| ((status & UART_RI)         ? TIOCM_RI  : 0)
828*4882a593Smuzhiyun 		| ((status & UART_CD)         ? TIOCM_CD  : 0);
829*4882a593Smuzhiyun 
830*4882a593Smuzhiyun 	dev_dbg(&port->dev, "%s - result = %x\n", __func__, result);
831*4882a593Smuzhiyun 
832*4882a593Smuzhiyun 	return result;
833*4882a593Smuzhiyun }
834*4882a593Smuzhiyun 
835*4882a593Smuzhiyun 
cypress_tiocmset(struct tty_struct * tty,unsigned int set,unsigned int clear)836*4882a593Smuzhiyun static int cypress_tiocmset(struct tty_struct *tty,
837*4882a593Smuzhiyun 			       unsigned int set, unsigned int clear)
838*4882a593Smuzhiyun {
839*4882a593Smuzhiyun 	struct usb_serial_port *port = tty->driver_data;
840*4882a593Smuzhiyun 	struct cypress_private *priv = usb_get_serial_port_data(port);
841*4882a593Smuzhiyun 	unsigned long flags;
842*4882a593Smuzhiyun 
843*4882a593Smuzhiyun 	spin_lock_irqsave(&priv->lock, flags);
844*4882a593Smuzhiyun 	if (set & TIOCM_RTS)
845*4882a593Smuzhiyun 		priv->line_control |= CONTROL_RTS;
846*4882a593Smuzhiyun 	if (set & TIOCM_DTR)
847*4882a593Smuzhiyun 		priv->line_control |= CONTROL_DTR;
848*4882a593Smuzhiyun 	if (clear & TIOCM_RTS)
849*4882a593Smuzhiyun 		priv->line_control &= ~CONTROL_RTS;
850*4882a593Smuzhiyun 	if (clear & TIOCM_DTR)
851*4882a593Smuzhiyun 		priv->line_control &= ~CONTROL_DTR;
852*4882a593Smuzhiyun 	priv->cmd_ctrl = 1;
853*4882a593Smuzhiyun 	spin_unlock_irqrestore(&priv->lock, flags);
854*4882a593Smuzhiyun 
855*4882a593Smuzhiyun 	return cypress_write(tty, port, NULL, 0);
856*4882a593Smuzhiyun }
857*4882a593Smuzhiyun 
cypress_earthmate_init_termios(struct tty_struct * tty)858*4882a593Smuzhiyun static void cypress_earthmate_init_termios(struct tty_struct *tty)
859*4882a593Smuzhiyun {
860*4882a593Smuzhiyun 	tty_encode_baud_rate(tty, 4800, 4800);
861*4882a593Smuzhiyun }
862*4882a593Smuzhiyun 
cypress_set_termios(struct tty_struct * tty,struct usb_serial_port * port,struct ktermios * old_termios)863*4882a593Smuzhiyun static void cypress_set_termios(struct tty_struct *tty,
864*4882a593Smuzhiyun 	struct usb_serial_port *port, struct ktermios *old_termios)
865*4882a593Smuzhiyun {
866*4882a593Smuzhiyun 	struct cypress_private *priv = usb_get_serial_port_data(port);
867*4882a593Smuzhiyun 	struct device *dev = &port->dev;
868*4882a593Smuzhiyun 	int data_bits, stop_bits, parity_type, parity_enable;
869*4882a593Smuzhiyun 	unsigned int cflag;
870*4882a593Smuzhiyun 	unsigned long flags;
871*4882a593Smuzhiyun 	__u8 oldlines;
872*4882a593Smuzhiyun 	int linechange = 0;
873*4882a593Smuzhiyun 
874*4882a593Smuzhiyun 	/* Unsupported features need clearing */
875*4882a593Smuzhiyun 	tty->termios.c_cflag &= ~(CMSPAR|CRTSCTS);
876*4882a593Smuzhiyun 
877*4882a593Smuzhiyun 	cflag = tty->termios.c_cflag;
878*4882a593Smuzhiyun 
879*4882a593Smuzhiyun 	/* set number of data bits, parity, stop bits */
880*4882a593Smuzhiyun 	/* when parity is disabled the parity type bit is ignored */
881*4882a593Smuzhiyun 
882*4882a593Smuzhiyun 	/* 1 means 2 stop bits, 0 means 1 stop bit */
883*4882a593Smuzhiyun 	stop_bits = cflag & CSTOPB ? 1 : 0;
884*4882a593Smuzhiyun 
885*4882a593Smuzhiyun 	if (cflag & PARENB) {
886*4882a593Smuzhiyun 		parity_enable = 1;
887*4882a593Smuzhiyun 		/* 1 means odd parity, 0 means even parity */
888*4882a593Smuzhiyun 		parity_type = cflag & PARODD ? 1 : 0;
889*4882a593Smuzhiyun 	} else
890*4882a593Smuzhiyun 		parity_enable = parity_type = 0;
891*4882a593Smuzhiyun 
892*4882a593Smuzhiyun 	switch (cflag & CSIZE) {
893*4882a593Smuzhiyun 	case CS5:
894*4882a593Smuzhiyun 		data_bits = 0;
895*4882a593Smuzhiyun 		break;
896*4882a593Smuzhiyun 	case CS6:
897*4882a593Smuzhiyun 		data_bits = 1;
898*4882a593Smuzhiyun 		break;
899*4882a593Smuzhiyun 	case CS7:
900*4882a593Smuzhiyun 		data_bits = 2;
901*4882a593Smuzhiyun 		break;
902*4882a593Smuzhiyun 	case CS8:
903*4882a593Smuzhiyun 		data_bits = 3;
904*4882a593Smuzhiyun 		break;
905*4882a593Smuzhiyun 	default:
906*4882a593Smuzhiyun 		dev_err(dev, "%s - CSIZE was set, but not CS5-CS8\n", __func__);
907*4882a593Smuzhiyun 		data_bits = 3;
908*4882a593Smuzhiyun 	}
909*4882a593Smuzhiyun 	spin_lock_irqsave(&priv->lock, flags);
910*4882a593Smuzhiyun 	oldlines = priv->line_control;
911*4882a593Smuzhiyun 	if ((cflag & CBAUD) == B0) {
912*4882a593Smuzhiyun 		/* drop dtr and rts */
913*4882a593Smuzhiyun 		dev_dbg(dev, "%s - dropping the lines, baud rate 0bps\n", __func__);
914*4882a593Smuzhiyun 		priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
915*4882a593Smuzhiyun 	} else
916*4882a593Smuzhiyun 		priv->line_control = (CONTROL_DTR | CONTROL_RTS);
917*4882a593Smuzhiyun 	spin_unlock_irqrestore(&priv->lock, flags);
918*4882a593Smuzhiyun 
919*4882a593Smuzhiyun 	dev_dbg(dev, "%s - sending %d stop_bits, %d parity_enable, %d parity_type, %d data_bits (+5)\n",
920*4882a593Smuzhiyun 		__func__, stop_bits, parity_enable, parity_type, data_bits);
921*4882a593Smuzhiyun 
922*4882a593Smuzhiyun 	cypress_serial_control(tty, port, tty_get_baud_rate(tty),
923*4882a593Smuzhiyun 			data_bits, stop_bits,
924*4882a593Smuzhiyun 			parity_enable, parity_type,
925*4882a593Smuzhiyun 			0, CYPRESS_SET_CONFIG);
926*4882a593Smuzhiyun 
927*4882a593Smuzhiyun 	/* we perform a CYPRESS_GET_CONFIG so that the current settings are
928*4882a593Smuzhiyun 	 * filled into the private structure this should confirm that all is
929*4882a593Smuzhiyun 	 * working if it returns what we just set */
930*4882a593Smuzhiyun 	cypress_serial_control(tty, port, 0, 0, 0, 0, 0, 0, CYPRESS_GET_CONFIG);
931*4882a593Smuzhiyun 
932*4882a593Smuzhiyun 	/* Here we can define custom tty settings for devices; the main tty
933*4882a593Smuzhiyun 	 * termios flag base comes from empeg.c */
934*4882a593Smuzhiyun 
935*4882a593Smuzhiyun 	spin_lock_irqsave(&priv->lock, flags);
936*4882a593Smuzhiyun 	if (priv->chiptype == CT_EARTHMATE && priv->baud_rate == 4800) {
937*4882a593Smuzhiyun 		dev_dbg(dev, "Using custom termios settings for a baud rate of 4800bps.\n");
938*4882a593Smuzhiyun 		/* define custom termios settings for NMEA protocol */
939*4882a593Smuzhiyun 
940*4882a593Smuzhiyun 		tty->termios.c_iflag /* input modes - */
941*4882a593Smuzhiyun 			&= ~(IGNBRK  /* disable ignore break */
942*4882a593Smuzhiyun 			| BRKINT     /* disable break causes interrupt */
943*4882a593Smuzhiyun 			| PARMRK     /* disable mark parity errors */
944*4882a593Smuzhiyun 			| ISTRIP     /* disable clear high bit of input char */
945*4882a593Smuzhiyun 			| INLCR      /* disable translate NL to CR */
946*4882a593Smuzhiyun 			| IGNCR      /* disable ignore CR */
947*4882a593Smuzhiyun 			| ICRNL      /* disable translate CR to NL */
948*4882a593Smuzhiyun 			| IXON);     /* disable enable XON/XOFF flow control */
949*4882a593Smuzhiyun 
950*4882a593Smuzhiyun 		tty->termios.c_oflag /* output modes */
951*4882a593Smuzhiyun 			&= ~OPOST;    /* disable postprocess output char */
952*4882a593Smuzhiyun 
953*4882a593Smuzhiyun 		tty->termios.c_lflag /* line discipline modes */
954*4882a593Smuzhiyun 			&= ~(ECHO     /* disable echo input characters */
955*4882a593Smuzhiyun 			| ECHONL      /* disable echo new line */
956*4882a593Smuzhiyun 			| ICANON      /* disable erase, kill, werase, and rprnt
957*4882a593Smuzhiyun 					 special characters */
958*4882a593Smuzhiyun 			| ISIG        /* disable interrupt, quit, and suspend
959*4882a593Smuzhiyun 					 special characters */
960*4882a593Smuzhiyun 			| IEXTEN);    /* disable non-POSIX special characters */
961*4882a593Smuzhiyun 	} /* CT_CYPHIDCOM: Application should handle this for device */
962*4882a593Smuzhiyun 
963*4882a593Smuzhiyun 	linechange = (priv->line_control != oldlines);
964*4882a593Smuzhiyun 	spin_unlock_irqrestore(&priv->lock, flags);
965*4882a593Smuzhiyun 
966*4882a593Smuzhiyun 	/* if necessary, set lines */
967*4882a593Smuzhiyun 	if (linechange) {
968*4882a593Smuzhiyun 		priv->cmd_ctrl = 1;
969*4882a593Smuzhiyun 		cypress_write(tty, port, NULL, 0);
970*4882a593Smuzhiyun 	}
971*4882a593Smuzhiyun } /* cypress_set_termios */
972*4882a593Smuzhiyun 
973*4882a593Smuzhiyun 
974*4882a593Smuzhiyun /* returns amount of data still left in soft buffer */
cypress_chars_in_buffer(struct tty_struct * tty)975*4882a593Smuzhiyun static int cypress_chars_in_buffer(struct tty_struct *tty)
976*4882a593Smuzhiyun {
977*4882a593Smuzhiyun 	struct usb_serial_port *port = tty->driver_data;
978*4882a593Smuzhiyun 	struct cypress_private *priv = usb_get_serial_port_data(port);
979*4882a593Smuzhiyun 	int chars = 0;
980*4882a593Smuzhiyun 	unsigned long flags;
981*4882a593Smuzhiyun 
982*4882a593Smuzhiyun 	spin_lock_irqsave(&priv->lock, flags);
983*4882a593Smuzhiyun 	chars = kfifo_len(&priv->write_fifo);
984*4882a593Smuzhiyun 	spin_unlock_irqrestore(&priv->lock, flags);
985*4882a593Smuzhiyun 
986*4882a593Smuzhiyun 	dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars);
987*4882a593Smuzhiyun 	return chars;
988*4882a593Smuzhiyun }
989*4882a593Smuzhiyun 
990*4882a593Smuzhiyun 
cypress_throttle(struct tty_struct * tty)991*4882a593Smuzhiyun static void cypress_throttle(struct tty_struct *tty)
992*4882a593Smuzhiyun {
993*4882a593Smuzhiyun 	struct usb_serial_port *port = tty->driver_data;
994*4882a593Smuzhiyun 	struct cypress_private *priv = usb_get_serial_port_data(port);
995*4882a593Smuzhiyun 
996*4882a593Smuzhiyun 	spin_lock_irq(&priv->lock);
997*4882a593Smuzhiyun 	priv->rx_flags = THROTTLED;
998*4882a593Smuzhiyun 	spin_unlock_irq(&priv->lock);
999*4882a593Smuzhiyun }
1000*4882a593Smuzhiyun 
1001*4882a593Smuzhiyun 
cypress_unthrottle(struct tty_struct * tty)1002*4882a593Smuzhiyun static void cypress_unthrottle(struct tty_struct *tty)
1003*4882a593Smuzhiyun {
1004*4882a593Smuzhiyun 	struct usb_serial_port *port = tty->driver_data;
1005*4882a593Smuzhiyun 	struct cypress_private *priv = usb_get_serial_port_data(port);
1006*4882a593Smuzhiyun 	int actually_throttled, result;
1007*4882a593Smuzhiyun 
1008*4882a593Smuzhiyun 	spin_lock_irq(&priv->lock);
1009*4882a593Smuzhiyun 	actually_throttled = priv->rx_flags & ACTUALLY_THROTTLED;
1010*4882a593Smuzhiyun 	priv->rx_flags = 0;
1011*4882a593Smuzhiyun 	spin_unlock_irq(&priv->lock);
1012*4882a593Smuzhiyun 
1013*4882a593Smuzhiyun 	if (!priv->comm_is_ok)
1014*4882a593Smuzhiyun 		return;
1015*4882a593Smuzhiyun 
1016*4882a593Smuzhiyun 	if (actually_throttled) {
1017*4882a593Smuzhiyun 		result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
1018*4882a593Smuzhiyun 		if (result) {
1019*4882a593Smuzhiyun 			dev_err(&port->dev, "%s - failed submitting read urb, "
1020*4882a593Smuzhiyun 					"error %d\n", __func__, result);
1021*4882a593Smuzhiyun 			cypress_set_dead(port);
1022*4882a593Smuzhiyun 		}
1023*4882a593Smuzhiyun 	}
1024*4882a593Smuzhiyun }
1025*4882a593Smuzhiyun 
1026*4882a593Smuzhiyun 
cypress_read_int_callback(struct urb * urb)1027*4882a593Smuzhiyun static void cypress_read_int_callback(struct urb *urb)
1028*4882a593Smuzhiyun {
1029*4882a593Smuzhiyun 	struct usb_serial_port *port = urb->context;
1030*4882a593Smuzhiyun 	struct cypress_private *priv = usb_get_serial_port_data(port);
1031*4882a593Smuzhiyun 	struct device *dev = &urb->dev->dev;
1032*4882a593Smuzhiyun 	struct tty_struct *tty;
1033*4882a593Smuzhiyun 	unsigned char *data = urb->transfer_buffer;
1034*4882a593Smuzhiyun 	unsigned long flags;
1035*4882a593Smuzhiyun 	char tty_flag = TTY_NORMAL;
1036*4882a593Smuzhiyun 	int bytes = 0;
1037*4882a593Smuzhiyun 	int result;
1038*4882a593Smuzhiyun 	int i = 0;
1039*4882a593Smuzhiyun 	int status = urb->status;
1040*4882a593Smuzhiyun 
1041*4882a593Smuzhiyun 	switch (status) {
1042*4882a593Smuzhiyun 	case 0: /* success */
1043*4882a593Smuzhiyun 		break;
1044*4882a593Smuzhiyun 	case -ECONNRESET:
1045*4882a593Smuzhiyun 	case -ENOENT:
1046*4882a593Smuzhiyun 	case -ESHUTDOWN:
1047*4882a593Smuzhiyun 		/* precursor to disconnect so just go away */
1048*4882a593Smuzhiyun 		return;
1049*4882a593Smuzhiyun 	case -EPIPE:
1050*4882a593Smuzhiyun 		/* Can't call usb_clear_halt while in_interrupt */
1051*4882a593Smuzhiyun 		fallthrough;
1052*4882a593Smuzhiyun 	default:
1053*4882a593Smuzhiyun 		/* something ugly is going on... */
1054*4882a593Smuzhiyun 		dev_err(dev, "%s - unexpected nonzero read status received: %d\n",
1055*4882a593Smuzhiyun 			__func__, status);
1056*4882a593Smuzhiyun 		cypress_set_dead(port);
1057*4882a593Smuzhiyun 		return;
1058*4882a593Smuzhiyun 	}
1059*4882a593Smuzhiyun 
1060*4882a593Smuzhiyun 	spin_lock_irqsave(&priv->lock, flags);
1061*4882a593Smuzhiyun 	if (priv->rx_flags & THROTTLED) {
1062*4882a593Smuzhiyun 		dev_dbg(dev, "%s - now throttling\n", __func__);
1063*4882a593Smuzhiyun 		priv->rx_flags |= ACTUALLY_THROTTLED;
1064*4882a593Smuzhiyun 		spin_unlock_irqrestore(&priv->lock, flags);
1065*4882a593Smuzhiyun 		return;
1066*4882a593Smuzhiyun 	}
1067*4882a593Smuzhiyun 	spin_unlock_irqrestore(&priv->lock, flags);
1068*4882a593Smuzhiyun 
1069*4882a593Smuzhiyun 	tty = tty_port_tty_get(&port->port);
1070*4882a593Smuzhiyun 	if (!tty) {
1071*4882a593Smuzhiyun 		dev_dbg(dev, "%s - bad tty pointer - exiting\n", __func__);
1072*4882a593Smuzhiyun 		return;
1073*4882a593Smuzhiyun 	}
1074*4882a593Smuzhiyun 
1075*4882a593Smuzhiyun 	spin_lock_irqsave(&priv->lock, flags);
1076*4882a593Smuzhiyun 	result = urb->actual_length;
1077*4882a593Smuzhiyun 	switch (priv->pkt_fmt) {
1078*4882a593Smuzhiyun 	default:
1079*4882a593Smuzhiyun 	case packet_format_1:
1080*4882a593Smuzhiyun 		/* This is for the CY7C64013... */
1081*4882a593Smuzhiyun 		priv->current_status = data[0] & 0xF8;
1082*4882a593Smuzhiyun 		bytes = data[1] + 2;
1083*4882a593Smuzhiyun 		i = 2;
1084*4882a593Smuzhiyun 		break;
1085*4882a593Smuzhiyun 	case packet_format_2:
1086*4882a593Smuzhiyun 		/* This is for the CY7C63743... */
1087*4882a593Smuzhiyun 		priv->current_status = data[0] & 0xF8;
1088*4882a593Smuzhiyun 		bytes = (data[0] & 0x07) + 1;
1089*4882a593Smuzhiyun 		i = 1;
1090*4882a593Smuzhiyun 		break;
1091*4882a593Smuzhiyun 	}
1092*4882a593Smuzhiyun 	spin_unlock_irqrestore(&priv->lock, flags);
1093*4882a593Smuzhiyun 	if (result < bytes) {
1094*4882a593Smuzhiyun 		dev_dbg(dev,
1095*4882a593Smuzhiyun 			"%s - wrong packet size - received %d bytes but packet said %d bytes\n",
1096*4882a593Smuzhiyun 			__func__, result, bytes);
1097*4882a593Smuzhiyun 		goto continue_read;
1098*4882a593Smuzhiyun 	}
1099*4882a593Smuzhiyun 
1100*4882a593Smuzhiyun 	usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
1101*4882a593Smuzhiyun 
1102*4882a593Smuzhiyun 	spin_lock_irqsave(&priv->lock, flags);
1103*4882a593Smuzhiyun 	/* check to see if status has changed */
1104*4882a593Smuzhiyun 	if (priv->current_status != priv->prev_status) {
1105*4882a593Smuzhiyun 		u8 delta = priv->current_status ^ priv->prev_status;
1106*4882a593Smuzhiyun 
1107*4882a593Smuzhiyun 		if (delta & UART_MSR_MASK) {
1108*4882a593Smuzhiyun 			if (delta & UART_CTS)
1109*4882a593Smuzhiyun 				port->icount.cts++;
1110*4882a593Smuzhiyun 			if (delta & UART_DSR)
1111*4882a593Smuzhiyun 				port->icount.dsr++;
1112*4882a593Smuzhiyun 			if (delta & UART_RI)
1113*4882a593Smuzhiyun 				port->icount.rng++;
1114*4882a593Smuzhiyun 			if (delta & UART_CD)
1115*4882a593Smuzhiyun 				port->icount.dcd++;
1116*4882a593Smuzhiyun 
1117*4882a593Smuzhiyun 			wake_up_interruptible(&port->port.delta_msr_wait);
1118*4882a593Smuzhiyun 		}
1119*4882a593Smuzhiyun 
1120*4882a593Smuzhiyun 		priv->prev_status = priv->current_status;
1121*4882a593Smuzhiyun 	}
1122*4882a593Smuzhiyun 	spin_unlock_irqrestore(&priv->lock, flags);
1123*4882a593Smuzhiyun 
1124*4882a593Smuzhiyun 	/* hangup, as defined in acm.c... this might be a bad place for it
1125*4882a593Smuzhiyun 	 * though */
1126*4882a593Smuzhiyun 	if (tty && !C_CLOCAL(tty) && !(priv->current_status & UART_CD)) {
1127*4882a593Smuzhiyun 		dev_dbg(dev, "%s - calling hangup\n", __func__);
1128*4882a593Smuzhiyun 		tty_hangup(tty);
1129*4882a593Smuzhiyun 		goto continue_read;
1130*4882a593Smuzhiyun 	}
1131*4882a593Smuzhiyun 
1132*4882a593Smuzhiyun 	/* There is one error bit... I'm assuming it is a parity error
1133*4882a593Smuzhiyun 	 * indicator as the generic firmware will set this bit to 1 if a
1134*4882a593Smuzhiyun 	 * parity error occurs.
1135*4882a593Smuzhiyun 	 * I can not find reference to any other error events. */
1136*4882a593Smuzhiyun 	spin_lock_irqsave(&priv->lock, flags);
1137*4882a593Smuzhiyun 	if (priv->current_status & CYP_ERROR) {
1138*4882a593Smuzhiyun 		spin_unlock_irqrestore(&priv->lock, flags);
1139*4882a593Smuzhiyun 		tty_flag = TTY_PARITY;
1140*4882a593Smuzhiyun 		dev_dbg(dev, "%s - Parity Error detected\n", __func__);
1141*4882a593Smuzhiyun 	} else
1142*4882a593Smuzhiyun 		spin_unlock_irqrestore(&priv->lock, flags);
1143*4882a593Smuzhiyun 
1144*4882a593Smuzhiyun 	/* process read if there is data other than line status */
1145*4882a593Smuzhiyun 	if (bytes > i) {
1146*4882a593Smuzhiyun 		tty_insert_flip_string_fixed_flag(&port->port, data + i,
1147*4882a593Smuzhiyun 				tty_flag, bytes - i);
1148*4882a593Smuzhiyun 		tty_flip_buffer_push(&port->port);
1149*4882a593Smuzhiyun 	}
1150*4882a593Smuzhiyun 
1151*4882a593Smuzhiyun 	spin_lock_irqsave(&priv->lock, flags);
1152*4882a593Smuzhiyun 	/* control and status byte(s) are also counted */
1153*4882a593Smuzhiyun 	priv->bytes_in += bytes;
1154*4882a593Smuzhiyun 	spin_unlock_irqrestore(&priv->lock, flags);
1155*4882a593Smuzhiyun 
1156*4882a593Smuzhiyun continue_read:
1157*4882a593Smuzhiyun 	tty_kref_put(tty);
1158*4882a593Smuzhiyun 
1159*4882a593Smuzhiyun 	/* Continue trying to always read */
1160*4882a593Smuzhiyun 
1161*4882a593Smuzhiyun 	if (priv->comm_is_ok) {
1162*4882a593Smuzhiyun 		usb_fill_int_urb(port->interrupt_in_urb, port->serial->dev,
1163*4882a593Smuzhiyun 				usb_rcvintpipe(port->serial->dev,
1164*4882a593Smuzhiyun 					port->interrupt_in_endpointAddress),
1165*4882a593Smuzhiyun 				port->interrupt_in_urb->transfer_buffer,
1166*4882a593Smuzhiyun 				port->interrupt_in_urb->transfer_buffer_length,
1167*4882a593Smuzhiyun 				cypress_read_int_callback, port,
1168*4882a593Smuzhiyun 				priv->read_urb_interval);
1169*4882a593Smuzhiyun 		result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
1170*4882a593Smuzhiyun 		if (result && result != -EPERM) {
1171*4882a593Smuzhiyun 			dev_err(dev, "%s - failed resubmitting read urb, error %d\n",
1172*4882a593Smuzhiyun 				__func__, result);
1173*4882a593Smuzhiyun 			cypress_set_dead(port);
1174*4882a593Smuzhiyun 		}
1175*4882a593Smuzhiyun 	}
1176*4882a593Smuzhiyun } /* cypress_read_int_callback */
1177*4882a593Smuzhiyun 
1178*4882a593Smuzhiyun 
cypress_write_int_callback(struct urb * urb)1179*4882a593Smuzhiyun static void cypress_write_int_callback(struct urb *urb)
1180*4882a593Smuzhiyun {
1181*4882a593Smuzhiyun 	struct usb_serial_port *port = urb->context;
1182*4882a593Smuzhiyun 	struct cypress_private *priv = usb_get_serial_port_data(port);
1183*4882a593Smuzhiyun 	struct device *dev = &urb->dev->dev;
1184*4882a593Smuzhiyun 	int status = urb->status;
1185*4882a593Smuzhiyun 
1186*4882a593Smuzhiyun 	switch (status) {
1187*4882a593Smuzhiyun 	case 0:
1188*4882a593Smuzhiyun 		/* success */
1189*4882a593Smuzhiyun 		break;
1190*4882a593Smuzhiyun 	case -ECONNRESET:
1191*4882a593Smuzhiyun 	case -ENOENT:
1192*4882a593Smuzhiyun 	case -ESHUTDOWN:
1193*4882a593Smuzhiyun 		/* this urb is terminated, clean up */
1194*4882a593Smuzhiyun 		dev_dbg(dev, "%s - urb shutting down with status: %d\n",
1195*4882a593Smuzhiyun 			__func__, status);
1196*4882a593Smuzhiyun 		priv->write_urb_in_use = 0;
1197*4882a593Smuzhiyun 		return;
1198*4882a593Smuzhiyun 	case -EPIPE:
1199*4882a593Smuzhiyun 		/* Cannot call usb_clear_halt while in_interrupt */
1200*4882a593Smuzhiyun 		fallthrough;
1201*4882a593Smuzhiyun 	default:
1202*4882a593Smuzhiyun 		dev_err(dev, "%s - unexpected nonzero write status received: %d\n",
1203*4882a593Smuzhiyun 			__func__, status);
1204*4882a593Smuzhiyun 		cypress_set_dead(port);
1205*4882a593Smuzhiyun 		break;
1206*4882a593Smuzhiyun 	}
1207*4882a593Smuzhiyun 	priv->write_urb_in_use = 0;
1208*4882a593Smuzhiyun 
1209*4882a593Smuzhiyun 	/* send any buffered data */
1210*4882a593Smuzhiyun 	cypress_send(port);
1211*4882a593Smuzhiyun }
1212*4882a593Smuzhiyun 
1213*4882a593Smuzhiyun module_usb_serial_driver(serial_drivers, id_table_combined);
1214*4882a593Smuzhiyun 
1215*4882a593Smuzhiyun MODULE_AUTHOR(DRIVER_AUTHOR);
1216*4882a593Smuzhiyun MODULE_DESCRIPTION(DRIVER_DESC);
1217*4882a593Smuzhiyun MODULE_LICENSE("GPL");
1218*4882a593Smuzhiyun 
1219*4882a593Smuzhiyun module_param(stats, bool, S_IRUGO | S_IWUSR);
1220*4882a593Smuzhiyun MODULE_PARM_DESC(stats, "Enable statistics or not");
1221*4882a593Smuzhiyun module_param(interval, int, S_IRUGO | S_IWUSR);
1222*4882a593Smuzhiyun MODULE_PARM_DESC(interval, "Overrides interrupt interval");
1223*4882a593Smuzhiyun module_param(unstable_bauds, bool, S_IRUGO | S_IWUSR);
1224*4882a593Smuzhiyun MODULE_PARM_DESC(unstable_bauds, "Allow unstable baud rates");
1225