xref: /linux/drivers/usb/serial/cypress_m8.c (revision c771600c6af14749609b49565ffb4cac2959710d)
15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * USB Cypress M8 driver
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * 	Copyright (C) 2004
61da177e4SLinus Torvalds  * 	    Lonnie Mendez (dignome@gmail.com)
71da177e4SLinus Torvalds  *	Copyright (C) 2003,2004
81da177e4SLinus Torvalds  *	    Neil Whelchel (koyama@firstlight.net)
91da177e4SLinus Torvalds  *
10ecefae6dSMauro Carvalho Chehab  * See Documentation/usb/usb-serial.rst for more information on using this
11813a224fSAlan Cox  * driver
121da177e4SLinus Torvalds  *
131da177e4SLinus Torvalds  * See http://geocities.com/i0xox0i for information on this driver and the
141da177e4SLinus Torvalds  * earthmate usb device.
151da177e4SLinus Torvalds  */
161da177e4SLinus Torvalds 
17813a224fSAlan Cox /* Thanks to Neil Whelchel for writing the first cypress m8 implementation
18813a224fSAlan Cox    for linux. */
191da177e4SLinus Torvalds /* Thanks to cypress for providing references for the hid reports. */
201da177e4SLinus Torvalds /* Thanks to Jiang Zhang for providing links and for general help. */
211da177e4SLinus Torvalds /* Code originates and was built up from ftdi_sio, belkin, pl2303 and others.*/
221da177e4SLinus Torvalds 
231da177e4SLinus Torvalds 
241da177e4SLinus Torvalds #include <linux/kernel.h>
251da177e4SLinus Torvalds #include <linux/errno.h>
261da177e4SLinus Torvalds #include <linux/slab.h>
271da177e4SLinus Torvalds #include <linux/tty.h>
281da177e4SLinus Torvalds #include <linux/tty_driver.h>
291da177e4SLinus Torvalds #include <linux/tty_flip.h>
301da177e4SLinus Torvalds #include <linux/module.h>
311da177e4SLinus Torvalds #include <linux/moduleparam.h>
321da177e4SLinus Torvalds #include <linux/spinlock.h>
331da177e4SLinus Torvalds #include <linux/usb.h>
34a969888cSGreg Kroah-Hartman #include <linux/usb/serial.h>
351da177e4SLinus Torvalds #include <linux/serial.h>
36117fb8d0SJohan Hovold #include <linux/kfifo.h>
371da177e4SLinus Torvalds #include <linux/delay.h>
38813a224fSAlan Cox #include <linux/uaccess.h>
395f60d5f6SAl Viro #include <linux/unaligned.h>
401da177e4SLinus Torvalds 
411da177e4SLinus Torvalds #include "cypress_m8.h"
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds 
4490ab5ee9SRusty Russell static bool stats;
453cb4a4f7SLonnie Mendez static int interval;
4690ab5ee9SRusty Russell static bool unstable_bauds;
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds #define DRIVER_AUTHOR "Lonnie Mendez <dignome@gmail.com>, Neil Whelchel <koyama@firstlight.net>"
491da177e4SLinus Torvalds #define DRIVER_DESC "Cypress USB to Serial Driver"
501da177e4SLinus Torvalds 
511da177e4SLinus Torvalds /* write buffer size defines */
521da177e4SLinus Torvalds #define CYPRESS_BUF_SIZE	1024
531da177e4SLinus Torvalds 
547d40d7e8SNémeth Márton static const struct usb_device_id id_table_earthmate[] = {
551da177e4SLinus Torvalds 	{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB) },
5625b6f08eSLonnie Mendez 	{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB_LT20) },
571da177e4SLinus Torvalds 	{ }						/* Terminating entry */
581da177e4SLinus Torvalds };
591da177e4SLinus Torvalds 
607d40d7e8SNémeth Márton static const struct usb_device_id id_table_cyphidcomrs232[] = {
611da177e4SLinus Torvalds 	{ USB_DEVICE(VENDOR_ID_CYPRESS, PRODUCT_ID_CYPHIDCOM) },
625c45d04cSJames Hilliard 	{ USB_DEVICE(VENDOR_ID_SAI, PRODUCT_ID_CYPHIDCOM) },
636f6f06eeSDmitry Shapin 	{ USB_DEVICE(VENDOR_ID_POWERCOM, PRODUCT_ID_UPS) },
646529591eSRobert Butora 	{ USB_DEVICE(VENDOR_ID_FRWD, PRODUCT_ID_CYPHIDCOM_FRWD) },
651da177e4SLinus Torvalds 	{ }						/* Terminating entry */
661da177e4SLinus Torvalds };
671da177e4SLinus Torvalds 
687d40d7e8SNémeth Márton static const struct usb_device_id id_table_nokiaca42v2[] = {
69a5c44e29SLonnie Mendez 	{ USB_DEVICE(VENDOR_ID_DAZZLE, PRODUCT_ID_CA42) },
70a5c44e29SLonnie Mendez 	{ }						/* Terminating entry */
71a5c44e29SLonnie Mendez };
72a5c44e29SLonnie Mendez 
737d40d7e8SNémeth Márton static const struct usb_device_id id_table_combined[] = {
741da177e4SLinus Torvalds 	{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB) },
7525b6f08eSLonnie Mendez 	{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB_LT20) },
761da177e4SLinus Torvalds 	{ USB_DEVICE(VENDOR_ID_CYPRESS, PRODUCT_ID_CYPHIDCOM) },
775c45d04cSJames Hilliard 	{ USB_DEVICE(VENDOR_ID_SAI, PRODUCT_ID_CYPHIDCOM) },
786f6f06eeSDmitry Shapin 	{ USB_DEVICE(VENDOR_ID_POWERCOM, PRODUCT_ID_UPS) },
796529591eSRobert Butora 	{ USB_DEVICE(VENDOR_ID_FRWD, PRODUCT_ID_CYPHIDCOM_FRWD) },
80a5c44e29SLonnie Mendez 	{ USB_DEVICE(VENDOR_ID_DAZZLE, PRODUCT_ID_CA42) },
811da177e4SLinus Torvalds 	{ }						/* Terminating entry */
821da177e4SLinus Torvalds };
831da177e4SLinus Torvalds 
841da177e4SLinus Torvalds MODULE_DEVICE_TABLE(usb, id_table_combined);
851da177e4SLinus Torvalds 
863416eaa1SMike Isely enum packet_format {
873416eaa1SMike Isely 	packet_format_1,  /* b0:status, b1:payload count */
883416eaa1SMike Isely 	packet_format_2   /* b0[7:3]:status, b0[2:0]:payload count */
893416eaa1SMike Isely };
903416eaa1SMike Isely 
911da177e4SLinus Torvalds struct cypress_private {
921da177e4SLinus Torvalds 	spinlock_t lock;		   /* private lock */
931da177e4SLinus Torvalds 	int chiptype;			   /* identifier of device, for quirks/etc */
941da177e4SLinus Torvalds 	int bytes_in;			   /* used for statistics */
951da177e4SLinus Torvalds 	int bytes_out;			   /* used for statistics */
961da177e4SLinus Torvalds 	int cmd_count;			   /* used for statistics */
971da177e4SLinus Torvalds 	int cmd_ctrl;			   /* always set this to 1 before issuing a command */
98117fb8d0SJohan Hovold 	struct kfifo write_fifo;	   /* write fifo */
991da177e4SLinus Torvalds 	int write_urb_in_use;		   /* write urb in use indicator */
1000257fa9fSMike Isely 	int write_urb_interval;            /* interval to use for write urb */
1010257fa9fSMike Isely 	int read_urb_interval;             /* interval to use for read urb */
10278aef519SMike Isely 	int comm_is_ok;                    /* true if communication is (still) ok */
1031da177e4SLinus Torvalds 	__u8 line_control;	   	   /* holds dtr / rts value */
1041da177e4SLinus Torvalds 	__u8 current_status;	   	   /* received from last read - info on dsr,cts,cd,ri,etc */
1051da177e4SLinus Torvalds 	__u8 current_config;	   	   /* stores the current configuration byte */
1061da177e4SLinus Torvalds 	__u8 rx_flags;			   /* throttling - used from whiteheat/ftdi_sio */
1073416eaa1SMike Isely 	enum packet_format pkt_fmt;	   /* format to use for packet send / receive */
1083d6aa320SMike Isely 	int get_cfg_unsafe;		   /* If true, the CYPRESS_GET_CONFIG is unsafe */
109813a224fSAlan Cox 	int baud_rate;			   /* stores current baud rate in
110813a224fSAlan Cox 					      integer form */
111ab62a585SJohan Hovold 	char prev_status;		   /* used for TIOCMIWAIT */
1121da177e4SLinus Torvalds };
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds /* function prototypes for the Cypress USB to serial device */
1155c1a0f41SJohan Hovold static int  cypress_earthmate_port_probe(struct usb_serial_port *port);
1165c1a0f41SJohan Hovold static int  cypress_hidcom_port_probe(struct usb_serial_port *port);
1175c1a0f41SJohan Hovold static int  cypress_ca42v2_port_probe(struct usb_serial_port *port);
118c5d1448fSUwe Kleine-König static void cypress_port_remove(struct usb_serial_port *port);
119a509a7e4SAlan Cox static int  cypress_open(struct tty_struct *tty, struct usb_serial_port *port);
120335f8514SAlan Cox static void cypress_close(struct usb_serial_port *port);
121335f8514SAlan Cox static void cypress_dtr_rts(struct usb_serial_port *port, int on);
122813a224fSAlan Cox static int  cypress_write(struct tty_struct *tty, struct usb_serial_port *port,
123813a224fSAlan Cox 			const unsigned char *buf, int count);
1241da177e4SLinus Torvalds static void cypress_send(struct usb_serial_port *port);
12594cc7aeaSJiri Slaby static unsigned int cypress_write_room(struct tty_struct *tty);
1262e75232bSJohan Hovold static void cypress_earthmate_init_termios(struct tty_struct *tty);
127813a224fSAlan Cox static void cypress_set_termios(struct tty_struct *tty,
128f6d47fe5SIlpo Järvinen 				struct usb_serial_port *port,
129f6d47fe5SIlpo Järvinen 				const struct ktermios *old_termios);
13060b33c13SAlan Cox static int  cypress_tiocmget(struct tty_struct *tty);
13120b9d177SAlan Cox static int  cypress_tiocmset(struct tty_struct *tty,
132813a224fSAlan Cox 			unsigned int set, unsigned int clear);
133155591d3SJiri Slaby static unsigned int cypress_chars_in_buffer(struct tty_struct *tty);
13495da310eSAlan Cox static void cypress_throttle(struct tty_struct *tty);
13595da310eSAlan Cox static void cypress_unthrottle(struct tty_struct *tty);
13678aef519SMike Isely static void cypress_set_dead(struct usb_serial_port *port);
1377d12e780SDavid Howells static void cypress_read_int_callback(struct urb *urb);
1387d12e780SDavid Howells static void cypress_write_int_callback(struct urb *urb);
1391da177e4SLinus Torvalds 
140ea65370dSGreg Kroah-Hartman static struct usb_serial_driver cypress_earthmate_device = {
14118fcac35SGreg Kroah-Hartman 	.driver = {
142269bda1cSGreg Kroah-Hartman 		.name =			"earthmate",
14318fcac35SGreg Kroah-Hartman 	},
144269bda1cSGreg Kroah-Hartman 	.description =			"DeLorme Earthmate USB",
1451da177e4SLinus Torvalds 	.id_table =			id_table_earthmate,
1461da177e4SLinus Torvalds 	.num_ports =			1,
1475c1a0f41SJohan Hovold 	.port_probe =			cypress_earthmate_port_probe,
1485c1a0f41SJohan Hovold 	.port_remove =			cypress_port_remove,
1491da177e4SLinus Torvalds 	.open =				cypress_open,
1501da177e4SLinus Torvalds 	.close =			cypress_close,
151335f8514SAlan Cox 	.dtr_rts =			cypress_dtr_rts,
1521da177e4SLinus Torvalds 	.write =			cypress_write,
1531da177e4SLinus Torvalds 	.write_room =			cypress_write_room,
1542e75232bSJohan Hovold 	.init_termios =			cypress_earthmate_init_termios,
1551da177e4SLinus Torvalds 	.set_termios =			cypress_set_termios,
1561da177e4SLinus Torvalds 	.tiocmget =			cypress_tiocmget,
1571da177e4SLinus Torvalds 	.tiocmset =			cypress_tiocmset,
158ab62a585SJohan Hovold 	.tiocmiwait =			usb_serial_generic_tiocmiwait,
1591da177e4SLinus Torvalds 	.chars_in_buffer =		cypress_chars_in_buffer,
1601da177e4SLinus Torvalds 	.throttle =		 	cypress_throttle,
1611da177e4SLinus Torvalds 	.unthrottle =			cypress_unthrottle,
1621da177e4SLinus Torvalds 	.read_int_callback =		cypress_read_int_callback,
1631da177e4SLinus Torvalds 	.write_int_callback =		cypress_write_int_callback,
1641da177e4SLinus Torvalds };
1651da177e4SLinus Torvalds 
166ea65370dSGreg Kroah-Hartman static struct usb_serial_driver cypress_hidcom_device = {
16718fcac35SGreg Kroah-Hartman 	.driver = {
168269bda1cSGreg Kroah-Hartman 		.name =			"cyphidcom",
16918fcac35SGreg Kroah-Hartman 	},
170269bda1cSGreg Kroah-Hartman 	.description =			"HID->COM RS232 Adapter",
1711da177e4SLinus Torvalds 	.id_table =			id_table_cyphidcomrs232,
1721da177e4SLinus Torvalds 	.num_ports =			1,
1735c1a0f41SJohan Hovold 	.port_probe =			cypress_hidcom_port_probe,
1745c1a0f41SJohan Hovold 	.port_remove =			cypress_port_remove,
1751da177e4SLinus Torvalds 	.open =				cypress_open,
1761da177e4SLinus Torvalds 	.close =			cypress_close,
177335f8514SAlan Cox 	.dtr_rts =			cypress_dtr_rts,
1781da177e4SLinus Torvalds 	.write =			cypress_write,
1791da177e4SLinus Torvalds 	.write_room =			cypress_write_room,
1801da177e4SLinus Torvalds 	.set_termios =			cypress_set_termios,
1811da177e4SLinus Torvalds 	.tiocmget =			cypress_tiocmget,
1821da177e4SLinus Torvalds 	.tiocmset =			cypress_tiocmset,
183ab62a585SJohan Hovold 	.tiocmiwait =			usb_serial_generic_tiocmiwait,
1841da177e4SLinus Torvalds 	.chars_in_buffer =		cypress_chars_in_buffer,
1851da177e4SLinus Torvalds 	.throttle =			cypress_throttle,
1861da177e4SLinus Torvalds 	.unthrottle =			cypress_unthrottle,
1871da177e4SLinus Torvalds 	.read_int_callback =		cypress_read_int_callback,
1881da177e4SLinus Torvalds 	.write_int_callback =		cypress_write_int_callback,
1891da177e4SLinus Torvalds };
1901da177e4SLinus Torvalds 
191a5c44e29SLonnie Mendez static struct usb_serial_driver cypress_ca42v2_device = {
192a5c44e29SLonnie Mendez 	.driver = {
193a5c44e29SLonnie Mendez 		.name =			"nokiaca42v2",
194a5c44e29SLonnie Mendez 	},
195a5c44e29SLonnie Mendez 	.description =			"Nokia CA-42 V2 Adapter",
196a5c44e29SLonnie Mendez 	.id_table =			id_table_nokiaca42v2,
197a5c44e29SLonnie Mendez 	.num_ports =			1,
1985c1a0f41SJohan Hovold 	.port_probe =			cypress_ca42v2_port_probe,
1995c1a0f41SJohan Hovold 	.port_remove =			cypress_port_remove,
200a5c44e29SLonnie Mendez 	.open =				cypress_open,
201a5c44e29SLonnie Mendez 	.close =			cypress_close,
202335f8514SAlan Cox 	.dtr_rts =			cypress_dtr_rts,
203a5c44e29SLonnie Mendez 	.write =			cypress_write,
204a5c44e29SLonnie Mendez 	.write_room =			cypress_write_room,
205a5c44e29SLonnie Mendez 	.set_termios =			cypress_set_termios,
206a5c44e29SLonnie Mendez 	.tiocmget =			cypress_tiocmget,
207a5c44e29SLonnie Mendez 	.tiocmset =			cypress_tiocmset,
208ab62a585SJohan Hovold 	.tiocmiwait =			usb_serial_generic_tiocmiwait,
209a5c44e29SLonnie Mendez 	.chars_in_buffer =		cypress_chars_in_buffer,
210a5c44e29SLonnie Mendez 	.throttle =			cypress_throttle,
211a5c44e29SLonnie Mendez 	.unthrottle =			cypress_unthrottle,
212a5c44e29SLonnie Mendez 	.read_int_callback =		cypress_read_int_callback,
213a5c44e29SLonnie Mendez 	.write_int_callback =		cypress_write_int_callback,
214a5c44e29SLonnie Mendez };
2151da177e4SLinus Torvalds 
21608a4f6bcSAlan Stern static struct usb_serial_driver * const serial_drivers[] = {
21708a4f6bcSAlan Stern 	&cypress_earthmate_device, &cypress_hidcom_device,
21808a4f6bcSAlan Stern 	&cypress_ca42v2_device, NULL
21908a4f6bcSAlan Stern };
22008a4f6bcSAlan Stern 
2211da177e4SLinus Torvalds /*****************************************************************************
2221da177e4SLinus Torvalds  * Cypress serial helper functions
2231da177e4SLinus Torvalds  *****************************************************************************/
2241da177e4SLinus Torvalds 
2256529591eSRobert Butora /* FRWD Dongle hidcom needs to skip reset and speed checks */
is_frwd(struct usb_device * dev)2266529591eSRobert Butora static inline bool is_frwd(struct usb_device *dev)
2276529591eSRobert Butora {
2286529591eSRobert Butora 	return ((le16_to_cpu(dev->descriptor.idVendor) == VENDOR_ID_FRWD) &&
2296529591eSRobert Butora 		(le16_to_cpu(dev->descriptor.idProduct) == PRODUCT_ID_CYPHIDCOM_FRWD));
2306529591eSRobert Butora }
2311da177e4SLinus Torvalds 
analyze_baud_rate(struct usb_serial_port * port,speed_t new_rate)2328873aaa6SAlan Cox static int analyze_baud_rate(struct usb_serial_port *port, speed_t new_rate)
23392983c21SMike Isely {
23492983c21SMike Isely 	struct cypress_private *priv;
23592983c21SMike Isely 	priv = usb_get_serial_port_data(port);
23692983c21SMike Isely 
237c312659cSMike Frysinger 	if (unstable_bauds)
238c312659cSMike Frysinger 		return new_rate;
239c312659cSMike Frysinger 
2406529591eSRobert Butora 	/* FRWD Dongle uses 115200 bps */
2416529591eSRobert Butora 	if (is_frwd(port->serial->dev))
2426529591eSRobert Butora 		return new_rate;
2436529591eSRobert Butora 
24492983c21SMike Isely 	/*
24592983c21SMike Isely 	 * The general purpose firmware for the Cypress M8 allows for
24692983c21SMike Isely 	 * a maximum speed of 57600bps (I have no idea whether DeLorme
24792983c21SMike Isely 	 * chose to use the general purpose firmware or not), if you
24892983c21SMike Isely 	 * need to modify this speed setting for your own project
24992983c21SMike Isely 	 * please add your own chiptype and modify the code likewise.
25092983c21SMike Isely 	 * The Cypress HID->COM device will work successfully up to
25192983c21SMike Isely 	 * 115200bps (but the actual throughput is around 3kBps).
25292983c21SMike Isely 	 */
25392983c21SMike Isely 	if (port->serial->dev->speed == USB_SPEED_LOW) {
25492983c21SMike Isely 		/*
25592983c21SMike Isely 		 * Mike Isely <isely@pobox.com> 2-Feb-2008: The
25692983c21SMike Isely 		 * Cypress app note that describes this mechanism
2579ec7e8d5SJiang Jian 		 * states that the low-speed part can't handle more
25892983c21SMike Isely 		 * than 800 bytes/sec, in which case 4800 baud is the
25992983c21SMike Isely 		 * safest speed for a part like that.
26092983c21SMike Isely 		 */
26192983c21SMike Isely 		if (new_rate > 4800) {
262dfa1c315SGreg Kroah-Hartman 			dev_dbg(&port->dev,
263dfa1c315SGreg Kroah-Hartman 				"%s - failed setting baud rate, device incapable speed %d\n",
264dfa1c315SGreg Kroah-Hartman 				__func__, new_rate);
26592983c21SMike Isely 			return -1;
26692983c21SMike Isely 		}
26792983c21SMike Isely 	}
26892983c21SMike Isely 	switch (priv->chiptype) {
26992983c21SMike Isely 	case CT_EARTHMATE:
27092983c21SMike Isely 		if (new_rate <= 600) {
27192983c21SMike Isely 			/* 300 and 600 baud rates are supported under
27292983c21SMike Isely 			 * the generic firmware, but are not used with
27392983c21SMike Isely 			 * NMEA and SiRF protocols */
274dfa1c315SGreg Kroah-Hartman 			dev_dbg(&port->dev,
275d9a38a87SJohan Hovold 				"%s - failed setting baud rate, unsupported speed of %d on Earthmate GPS\n",
276dfa1c315SGreg Kroah-Hartman 				__func__, new_rate);
27792983c21SMike Isely 			return -1;
27892983c21SMike Isely 		}
27992983c21SMike Isely 		break;
28092983c21SMike Isely 	default:
28192983c21SMike Isely 		break;
28292983c21SMike Isely 	}
28392983c21SMike Isely 	return new_rate;
28492983c21SMike Isely }
28592983c21SMike Isely 
28692983c21SMike Isely 
287093cf723SSteven Cole /* 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)28895da310eSAlan Cox static int cypress_serial_control(struct tty_struct *tty,
28995da310eSAlan Cox 	struct usb_serial_port *port, speed_t baud_rate, int data_bits,
29095da310eSAlan Cox 	int stop_bits, int parity_enable, int parity_type, int reset,
29195da310eSAlan Cox 	int cypress_request_type)
2921da177e4SLinus Torvalds {
2933cb4a4f7SLonnie Mendez 	int new_baudrate = 0, retval = 0, tries = 0;
2941da177e4SLinus Torvalds 	struct cypress_private *priv;
295dfa1c315SGreg Kroah-Hartman 	struct device *dev = &port->dev;
2960954644bSJohan Hovold 	u8 *feature_buffer;
2970954644bSJohan Hovold 	const unsigned int feature_len = 5;
2981da177e4SLinus Torvalds 	unsigned long flags;
2991da177e4SLinus Torvalds 
3001da177e4SLinus Torvalds 	priv = usb_get_serial_port_data(port);
3011da177e4SLinus Torvalds 
30278aef519SMike Isely 	if (!priv->comm_is_ok)
30378aef519SMike Isely 		return -ENODEV;
30478aef519SMike Isely 
3050954644bSJohan Hovold 	feature_buffer = kcalloc(feature_len, sizeof(u8), GFP_KERNEL);
3060954644bSJohan Hovold 	if (!feature_buffer)
3070954644bSJohan Hovold 		return -ENOMEM;
3080954644bSJohan Hovold 
3091da177e4SLinus Torvalds 	switch (cypress_request_type) {
3101da177e4SLinus Torvalds 	case CYPRESS_SET_CONFIG:
3118873aaa6SAlan Cox 		/* 0 means 'Hang up' so doesn't change the true bit rate */
3128873aaa6SAlan Cox 		new_baudrate = priv->baud_rate;
3132805eb13SMike Frysinger 		if (baud_rate && baud_rate != priv->baud_rate) {
314dfa1c315SGreg Kroah-Hartman 			dev_dbg(dev, "%s - baud rate is changing\n", __func__);
3158873aaa6SAlan Cox 			retval = analyze_baud_rate(port, baud_rate);
31692983c21SMike Isely 			if (retval >= 0) {
31792983c21SMike Isely 				new_baudrate = retval;
318dfa1c315SGreg Kroah-Hartman 				dev_dbg(dev, "%s - New baud rate set to %d\n",
31992983c21SMike Isely 					__func__, new_baudrate);
3201da177e4SLinus Torvalds 			}
3211da177e4SLinus Torvalds 		}
322dfa1c315SGreg Kroah-Hartman 		dev_dbg(dev, "%s - baud rate is being sent as %d\n", __func__,
323dfa1c315SGreg Kroah-Hartman 			new_baudrate);
3241da177e4SLinus Torvalds 
3253cb4a4f7SLonnie Mendez 		/* fill the feature_buffer with new configuration */
3260f2c2d7bSJohan Hovold 		put_unaligned_le32(new_baudrate, feature_buffer);
327d8f0209bSJiri Slaby 		feature_buffer[4] |= data_bits - 5;   /* assign data bits in 2 bit space ( max 3 ) */
3281da177e4SLinus Torvalds 		/* 1 bit gap */
3293cb4a4f7SLonnie Mendez 		feature_buffer[4] |= (stop_bits << 3);   /* assign stop bits in 1 bit space */
3303cb4a4f7SLonnie Mendez 		feature_buffer[4] |= (parity_enable << 4);   /* assign parity flag in 1 bit space */
3313cb4a4f7SLonnie Mendez 		feature_buffer[4] |= (parity_type << 5);   /* assign parity type in 1 bit space */
3321da177e4SLinus Torvalds 		/* 1 bit gap */
3333cb4a4f7SLonnie Mendez 		feature_buffer[4] |= (reset << 7);   /* assign reset at end of byte, 1 bit space */
3341da177e4SLinus Torvalds 
335dfa1c315SGreg Kroah-Hartman 		dev_dbg(dev, "%s - device is being sent this feature report:\n", __func__);
336dfa1c315SGreg Kroah-Hartman 		dev_dbg(dev, "%s - %02X - %02X - %02X - %02X - %02X\n", __func__,
337813a224fSAlan Cox 			feature_buffer[0], feature_buffer[1],
338813a224fSAlan Cox 			feature_buffer[2], feature_buffer[3],
339813a224fSAlan Cox 			feature_buffer[4]);
3401da177e4SLinus Torvalds 
3413cb4a4f7SLonnie Mendez 		do {
34293075544SMike Isely 			retval = usb_control_msg(port->serial->dev,
34393075544SMike Isely 					usb_sndctrlpipe(port->serial->dev, 0),
34493075544SMike Isely 					HID_REQ_SET_REPORT,
34593075544SMike Isely 					USB_DIR_OUT | USB_RECIP_INTERFACE | USB_TYPE_CLASS,
34693075544SMike Isely 					0x0300, 0, feature_buffer,
3470954644bSJohan Hovold 					feature_len, 500);
3481da177e4SLinus Torvalds 
3493cb4a4f7SLonnie Mendez 			if (tries++ >= 3)
3503cb4a4f7SLonnie Mendez 				break;
3513cb4a4f7SLonnie Mendez 
3520954644bSJohan Hovold 		} while (retval != feature_len &&
35393075544SMike Isely 			 retval != -ENODEV);
3543cb4a4f7SLonnie Mendez 
3550954644bSJohan Hovold 		if (retval != feature_len) {
356dfa1c315SGreg Kroah-Hartman 			dev_err(dev, "%s - failed sending serial line settings - %d\n",
357dfa1c315SGreg Kroah-Hartman 				__func__, retval);
35878aef519SMike Isely 			cypress_set_dead(port);
35978aef519SMike Isely 		} else {
3601da177e4SLinus Torvalds 			spin_lock_irqsave(&priv->lock, flags);
3613cb4a4f7SLonnie Mendez 			priv->baud_rate = new_baudrate;
3623cb4a4f7SLonnie Mendez 			priv->current_config = feature_buffer[4];
3631da177e4SLinus Torvalds 			spin_unlock_irqrestore(&priv->lock, flags);
3648873aaa6SAlan Cox 			/* If we asked for a speed change encode it */
3658873aaa6SAlan Cox 			if (baud_rate)
36695da310eSAlan Cox 				tty_encode_baud_rate(tty,
3678873aaa6SAlan Cox 					new_baudrate, new_baudrate);
3681da177e4SLinus Torvalds 		}
3691da177e4SLinus Torvalds 	break;
3701da177e4SLinus Torvalds 	case CYPRESS_GET_CONFIG:
3713d6aa320SMike Isely 		if (priv->get_cfg_unsafe) {
3723d6aa320SMike Isely 			/* Not implemented for this device,
3733d6aa320SMike Isely 			   and if we try to do it we're likely
3743d6aa320SMike Isely 			   to crash the hardware. */
3750954644bSJohan Hovold 			retval = -ENOTTY;
3760954644bSJohan Hovold 			goto out;
3773d6aa320SMike Isely 		}
378bbc1f57aSColin Ian King 		dev_dbg(dev, "%s - retrieving serial line settings\n", __func__);
3793cb4a4f7SLonnie Mendez 		do {
38093075544SMike Isely 			retval = usb_control_msg(port->serial->dev,
38193075544SMike Isely 					usb_rcvctrlpipe(port->serial->dev, 0),
38293075544SMike Isely 					HID_REQ_GET_REPORT,
38393075544SMike Isely 					USB_DIR_IN | USB_RECIP_INTERFACE | USB_TYPE_CLASS,
38493075544SMike Isely 					0x0300, 0, feature_buffer,
3850954644bSJohan Hovold 					feature_len, 500);
3863cb4a4f7SLonnie Mendez 
3873cb4a4f7SLonnie Mendez 			if (tries++ >= 3)
3883cb4a4f7SLonnie Mendez 				break;
3890954644bSJohan Hovold 		} while (retval != feature_len
390813a224fSAlan Cox 						&& retval != -ENODEV);
3913cb4a4f7SLonnie Mendez 
3920954644bSJohan Hovold 		if (retval != feature_len) {
393dfa1c315SGreg Kroah-Hartman 			dev_err(dev, "%s - failed to retrieve serial line settings - %d\n",
394dfa1c315SGreg Kroah-Hartman 				__func__, retval);
39578aef519SMike Isely 			cypress_set_dead(port);
3960954644bSJohan Hovold 			goto out;
3971da177e4SLinus Torvalds 		} else {
3981da177e4SLinus Torvalds 			spin_lock_irqsave(&priv->lock, flags);
399813a224fSAlan Cox 			/* store the config in one byte, and later
400813a224fSAlan Cox 			   use bit masks to check values */
4011da177e4SLinus Torvalds 			priv->current_config = feature_buffer[4];
4020f2c2d7bSJohan Hovold 			priv->baud_rate = get_unaligned_le32(feature_buffer);
4031da177e4SLinus Torvalds 			spin_unlock_irqrestore(&priv->lock, flags);
4041da177e4SLinus Torvalds 		}
4051da177e4SLinus Torvalds 	}
4063cb4a4f7SLonnie Mendez 	spin_lock_irqsave(&priv->lock, flags);
4073cb4a4f7SLonnie Mendez 	++priv->cmd_count;
4083cb4a4f7SLonnie Mendez 	spin_unlock_irqrestore(&priv->lock, flags);
4090954644bSJohan Hovold out:
4100954644bSJohan Hovold 	kfree(feature_buffer);
4111da177e4SLinus Torvalds 	return retval;
4121da177e4SLinus Torvalds } /* cypress_serial_control */
4131da177e4SLinus Torvalds 
4141da177e4SLinus Torvalds 
cypress_set_dead(struct usb_serial_port * port)41578aef519SMike Isely static void cypress_set_dead(struct usb_serial_port *port)
41678aef519SMike Isely {
41778aef519SMike Isely 	struct cypress_private *priv = usb_get_serial_port_data(port);
41878aef519SMike Isely 	unsigned long flags;
41978aef519SMike Isely 
42078aef519SMike Isely 	spin_lock_irqsave(&priv->lock, flags);
42178aef519SMike Isely 	if (!priv->comm_is_ok) {
42278aef519SMike Isely 		spin_unlock_irqrestore(&priv->lock, flags);
42378aef519SMike Isely 		return;
42478aef519SMike Isely 	}
42578aef519SMike Isely 	priv->comm_is_ok = 0;
42678aef519SMike Isely 	spin_unlock_irqrestore(&priv->lock, flags);
42778aef519SMike Isely 
428194343d9SGreg Kroah-Hartman 	dev_err(&port->dev, "cypress_m8 suspending failing port %d - "
4291143832eSGreg Kroah-Hartman 		"interval might be too short\n", port->port_number);
43078aef519SMike Isely }
43178aef519SMike Isely 
43278aef519SMike Isely 
4331da177e4SLinus Torvalds /*****************************************************************************
4341da177e4SLinus Torvalds  * Cypress serial driver functions
4351da177e4SLinus Torvalds  *****************************************************************************/
4361da177e4SLinus Torvalds 
4371da177e4SLinus Torvalds 
cypress_generic_port_probe(struct usb_serial_port * port)4385c1a0f41SJohan Hovold static int cypress_generic_port_probe(struct usb_serial_port *port)
4391da177e4SLinus Torvalds {
4405c1a0f41SJohan Hovold 	struct usb_serial *serial = port->serial;
4411da177e4SLinus Torvalds 	struct cypress_private *priv;
4421da177e4SLinus Torvalds 
443c55aee1bSOliver Neukum 	if (!port->interrupt_out_urb || !port->interrupt_in_urb) {
444c55aee1bSOliver Neukum 		dev_err(&port->dev, "required endpoint is missing\n");
445c55aee1bSOliver Neukum 		return -ENODEV;
446c55aee1bSOliver Neukum 	}
447c55aee1bSOliver Neukum 
44880b6ca48SEric Sesterhenn 	priv = kzalloc(sizeof(struct cypress_private), GFP_KERNEL);
4491da177e4SLinus Torvalds 	if (!priv)
4501da177e4SLinus Torvalds 		return -ENOMEM;
4511da177e4SLinus Torvalds 
45278aef519SMike Isely 	priv->comm_is_ok = !0;
4531da177e4SLinus Torvalds 	spin_lock_init(&priv->lock);
454117fb8d0SJohan Hovold 	if (kfifo_alloc(&priv->write_fifo, CYPRESS_BUF_SIZE, GFP_KERNEL)) {
4551da177e4SLinus Torvalds 		kfree(priv);
4561da177e4SLinus Torvalds 		return -ENOMEM;
4571da177e4SLinus Torvalds 	}
4581da177e4SLinus Torvalds 
4596529591eSRobert Butora 	/* Skip reset for FRWD device. It is a workaound:
4606529591eSRobert Butora 	   device hangs if it receives SET_CONFIGURE in Configured
4616529591eSRobert Butora 	   state. */
4626529591eSRobert Butora 	if (!is_frwd(serial->dev))
4631da177e4SLinus Torvalds 		usb_reset_configuration(serial->dev);
4641da177e4SLinus Torvalds 
4651da177e4SLinus Torvalds 	priv->cmd_ctrl = 0;
4661da177e4SLinus Torvalds 	priv->line_control = 0;
4671da177e4SLinus Torvalds 	priv->rx_flags = 0;
4683416eaa1SMike Isely 	/* Default packet format setting is determined by packet size.
4693416eaa1SMike Isely 	   Anything with a size larger then 9 must have a separate
4703416eaa1SMike Isely 	   count field since the 3 bit count field is otherwise too
4713416eaa1SMike Isely 	   small.  Otherwise we can use the slightly more compact
4723416eaa1SMike Isely 	   format.  This is in accordance with the cypress_m8 serial
4733416eaa1SMike Isely 	   converter app note. */
474813a224fSAlan Cox 	if (port->interrupt_out_size > 9)
4753416eaa1SMike Isely 		priv->pkt_fmt = packet_format_1;
476813a224fSAlan Cox 	else
4773416eaa1SMike Isely 		priv->pkt_fmt = packet_format_2;
478813a224fSAlan Cox 
4790257fa9fSMike Isely 	if (interval > 0) {
4800257fa9fSMike Isely 		priv->write_urb_interval = interval;
4810257fa9fSMike Isely 		priv->read_urb_interval = interval;
482dfa1c315SGreg Kroah-Hartman 		dev_dbg(&port->dev, "%s - read & write intervals forced to %d\n",
483dfa1c315SGreg Kroah-Hartman 			__func__, interval);
4840257fa9fSMike Isely 	} else {
4850257fa9fSMike Isely 		priv->write_urb_interval = port->interrupt_out_urb->interval;
4860257fa9fSMike Isely 		priv->read_urb_interval = port->interrupt_in_urb->interval;
487dfa1c315SGreg Kroah-Hartman 		dev_dbg(&port->dev, "%s - intervals: read=%d write=%d\n",
488dfa1c315SGreg Kroah-Hartman 			__func__, priv->read_urb_interval,
489dfa1c315SGreg Kroah-Hartman 			priv->write_urb_interval);
4900257fa9fSMike Isely 	}
4910257fa9fSMike Isely 	usb_set_serial_port_data(port, priv);
4921da177e4SLinus Torvalds 
493d7be6221SJohan Hovold 	port->port.drain_delay = 256;
494d7be6221SJohan Hovold 
495b9db07fbSLonnie Mendez 	return 0;
4961da177e4SLinus Torvalds }
4971da177e4SLinus Torvalds 
4981da177e4SLinus Torvalds 
cypress_earthmate_port_probe(struct usb_serial_port * port)4995c1a0f41SJohan Hovold static int cypress_earthmate_port_probe(struct usb_serial_port *port)
5001da177e4SLinus Torvalds {
5015c1a0f41SJohan Hovold 	struct usb_serial *serial = port->serial;
5021da177e4SLinus Torvalds 	struct cypress_private *priv;
5035c1a0f41SJohan Hovold 	int ret;
5041da177e4SLinus Torvalds 
5055c1a0f41SJohan Hovold 	ret = cypress_generic_port_probe(port);
5065c1a0f41SJohan Hovold 	if (ret) {
507dfa1c315SGreg Kroah-Hartman 		dev_dbg(&port->dev, "%s - Failed setting up port\n", __func__);
5085c1a0f41SJohan Hovold 		return ret;
5091da177e4SLinus Torvalds 	}
5101da177e4SLinus Torvalds 
5113d6aa320SMike Isely 	priv = usb_get_serial_port_data(port);
5121da177e4SLinus Torvalds 	priv->chiptype = CT_EARTHMATE;
5133416eaa1SMike Isely 	/* All Earthmate devices use the separated-count packet
5143416eaa1SMike Isely 	   format!  Idiotic. */
5153416eaa1SMike Isely 	priv->pkt_fmt = packet_format_1;
516813a224fSAlan Cox 	if (serial->dev->descriptor.idProduct !=
517813a224fSAlan Cox 				cpu_to_le16(PRODUCT_ID_EARTHMATEUSB)) {
5183d6aa320SMike Isely 		/* The old original USB Earthmate seemed able to
5193d6aa320SMike Isely 		   handle GET_CONFIG requests; everything they've
5203d6aa320SMike Isely 		   produced since that time crashes if this command is
5213d6aa320SMike Isely 		   attempted :-( */
522dfa1c315SGreg Kroah-Hartman 		dev_dbg(&port->dev,
523dfa1c315SGreg Kroah-Hartman 			"%s - Marking this device as unsafe for GET_CONFIG commands\n",
524dfa1c315SGreg Kroah-Hartman 			__func__);
5253d6aa320SMike Isely 		priv->get_cfg_unsafe = !0;
5263d6aa320SMike Isely 	}
5271da177e4SLinus Torvalds 
528b9db07fbSLonnie Mendez 	return 0;
5295c1a0f41SJohan Hovold }
5301da177e4SLinus Torvalds 
cypress_hidcom_port_probe(struct usb_serial_port * port)5315c1a0f41SJohan Hovold static int cypress_hidcom_port_probe(struct usb_serial_port *port)
5321da177e4SLinus Torvalds {
5331da177e4SLinus Torvalds 	struct cypress_private *priv;
5345c1a0f41SJohan Hovold 	int ret;
5351da177e4SLinus Torvalds 
5365c1a0f41SJohan Hovold 	ret = cypress_generic_port_probe(port);
5375c1a0f41SJohan Hovold 	if (ret) {
538dfa1c315SGreg Kroah-Hartman 		dev_dbg(&port->dev, "%s - Failed setting up port\n", __func__);
5395c1a0f41SJohan Hovold 		return ret;
5401da177e4SLinus Torvalds 	}
5411da177e4SLinus Torvalds 
542dfa1c315SGreg Kroah-Hartman 	priv = usb_get_serial_port_data(port);
5431da177e4SLinus Torvalds 	priv->chiptype = CT_CYPHIDCOM;
5441da177e4SLinus Torvalds 
545b9db07fbSLonnie Mendez 	return 0;
5465c1a0f41SJohan Hovold }
5471da177e4SLinus Torvalds 
cypress_ca42v2_port_probe(struct usb_serial_port * port)5485c1a0f41SJohan Hovold static int cypress_ca42v2_port_probe(struct usb_serial_port *port)
549a5c44e29SLonnie Mendez {
550a5c44e29SLonnie Mendez 	struct cypress_private *priv;
5515c1a0f41SJohan Hovold 	int ret;
552a5c44e29SLonnie Mendez 
5535c1a0f41SJohan Hovold 	ret = cypress_generic_port_probe(port);
5545c1a0f41SJohan Hovold 	if (ret) {
555dfa1c315SGreg Kroah-Hartman 		dev_dbg(&port->dev, "%s - Failed setting up port\n", __func__);
5565c1a0f41SJohan Hovold 		return ret;
557a5c44e29SLonnie Mendez 	}
558a5c44e29SLonnie Mendez 
559dfa1c315SGreg Kroah-Hartman 	priv = usb_get_serial_port_data(port);
560a5c44e29SLonnie Mendez 	priv->chiptype = CT_CA42V2;
561a5c44e29SLonnie Mendez 
562a5c44e29SLonnie Mendez 	return 0;
5635c1a0f41SJohan Hovold }
564a5c44e29SLonnie Mendez 
cypress_port_remove(struct usb_serial_port * port)565c5d1448fSUwe Kleine-König static void cypress_port_remove(struct usb_serial_port *port)
5661da177e4SLinus Torvalds {
5671da177e4SLinus Torvalds 	struct cypress_private *priv;
5681da177e4SLinus Torvalds 
5695c1a0f41SJohan Hovold 	priv = usb_get_serial_port_data(port);
5701da177e4SLinus Torvalds 
571117fb8d0SJohan Hovold 	kfifo_free(&priv->write_fifo);
5721da177e4SLinus Torvalds 	kfree(priv);
5735c1a0f41SJohan Hovold }
5741da177e4SLinus Torvalds 
cypress_open(struct tty_struct * tty,struct usb_serial_port * port)575a509a7e4SAlan Cox static int cypress_open(struct tty_struct *tty, struct usb_serial_port *port)
5761da177e4SLinus Torvalds {
5771da177e4SLinus Torvalds 	struct cypress_private *priv = usb_get_serial_port_data(port);
5781da177e4SLinus Torvalds 	struct usb_serial *serial = port->serial;
5791da177e4SLinus Torvalds 	unsigned long flags;
5801da177e4SLinus Torvalds 	int result = 0;
5811da177e4SLinus Torvalds 
58278aef519SMike Isely 	if (!priv->comm_is_ok)
58378aef519SMike Isely 		return -EIO;
58478aef519SMike Isely 
5851da177e4SLinus Torvalds 	/* clear halts before open */
5861da177e4SLinus Torvalds 	usb_clear_halt(serial->dev, 0x81);
5871da177e4SLinus Torvalds 	usb_clear_halt(serial->dev, 0x02);
5881da177e4SLinus Torvalds 
5891da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
5901da177e4SLinus Torvalds 	/* reset read/write statistics */
5911da177e4SLinus Torvalds 	priv->bytes_in = 0;
5921da177e4SLinus Torvalds 	priv->bytes_out = 0;
5931da177e4SLinus Torvalds 	priv->cmd_count = 0;
5941da177e4SLinus Torvalds 	priv->rx_flags = 0;
5951da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->lock, flags);
5961da177e4SLinus Torvalds 
597335f8514SAlan Cox 	/* Set termios */
598fe1ae7fdSAlan Cox 	cypress_send(port);
5991da177e4SLinus Torvalds 
60095da310eSAlan Cox 	if (tty)
601817c0cfcSJohan Hovold 		cypress_set_termios(tty, port, NULL);
6021da177e4SLinus Torvalds 
6031da177e4SLinus Torvalds 	/* setup the port and start reading from the device */
6041da177e4SLinus Torvalds 	usb_fill_int_urb(port->interrupt_in_urb, serial->dev,
6051da177e4SLinus Torvalds 		usb_rcvintpipe(serial->dev, port->interrupt_in_endpointAddress),
606813a224fSAlan Cox 		port->interrupt_in_urb->transfer_buffer,
607813a224fSAlan Cox 		port->interrupt_in_urb->transfer_buffer_length,
6080257fa9fSMike Isely 		cypress_read_int_callback, port, priv->read_urb_interval);
6091da177e4SLinus Torvalds 	result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
6101da177e4SLinus Torvalds 
6111da177e4SLinus Torvalds 	if (result) {
612813a224fSAlan Cox 		dev_err(&port->dev,
613813a224fSAlan Cox 			"%s - failed submitting read urb, error %d\n",
614813a224fSAlan Cox 							__func__, result);
61578aef519SMike Isely 		cypress_set_dead(port);
6161da177e4SLinus Torvalds 	}
617d7be6221SJohan Hovold 
6181da177e4SLinus Torvalds 	return result;
6191da177e4SLinus Torvalds } /* cypress_open */
6201da177e4SLinus Torvalds 
cypress_dtr_rts(struct usb_serial_port * port,int on)621335f8514SAlan Cox static void cypress_dtr_rts(struct usb_serial_port *port, int on)
6221da177e4SLinus Torvalds {
6231da177e4SLinus Torvalds 	struct cypress_private *priv = usb_get_serial_port_data(port);
624335f8514SAlan Cox 	/* drop dtr and rts */
625335f8514SAlan Cox 	spin_lock_irq(&priv->lock);
626335f8514SAlan Cox 	if (on == 0)
627335f8514SAlan Cox 		priv->line_control = 0;
628335f8514SAlan Cox 	else
629335f8514SAlan Cox 		priv->line_control = CONTROL_DTR | CONTROL_RTS;
630335f8514SAlan Cox 	priv->cmd_ctrl = 1;
631335f8514SAlan Cox 	spin_unlock_irq(&priv->lock);
632335f8514SAlan Cox 	cypress_write(NULL, port, NULL, 0);
633335f8514SAlan Cox }
634335f8514SAlan Cox 
cypress_close(struct usb_serial_port * port)635335f8514SAlan Cox static void cypress_close(struct usb_serial_port *port)
636335f8514SAlan Cox {
637335f8514SAlan Cox 	struct cypress_private *priv = usb_get_serial_port_data(port);
638117fb8d0SJohan Hovold 	unsigned long flags;
6391da177e4SLinus Torvalds 
640117fb8d0SJohan Hovold 	spin_lock_irqsave(&priv->lock, flags);
641117fb8d0SJohan Hovold 	kfifo_reset_out(&priv->write_fifo);
642117fb8d0SJohan Hovold 	spin_unlock_irqrestore(&priv->lock, flags);
643117fb8d0SJohan Hovold 
644dfa1c315SGreg Kroah-Hartman 	dev_dbg(&port->dev, "%s - stopping urbs\n", __func__);
6451da177e4SLinus Torvalds 	usb_kill_urb(port->interrupt_in_urb);
6461da177e4SLinus Torvalds 	usb_kill_urb(port->interrupt_out_urb);
6471da177e4SLinus Torvalds 
6481da177e4SLinus Torvalds 	if (stats)
6491da177e4SLinus Torvalds 		dev_info(&port->dev, "Statistics: %d Bytes In | %d Bytes Out | %d Commands Issued\n",
6501da177e4SLinus Torvalds 			priv->bytes_in, priv->bytes_out, priv->cmd_count);
6511da177e4SLinus Torvalds } /* cypress_close */
6521da177e4SLinus Torvalds 
6531da177e4SLinus Torvalds 
cypress_write(struct tty_struct * tty,struct usb_serial_port * port,const unsigned char * buf,int count)65495da310eSAlan Cox static int cypress_write(struct tty_struct *tty, struct usb_serial_port *port,
65595da310eSAlan Cox 					const unsigned char *buf, int count)
6561da177e4SLinus Torvalds {
6571da177e4SLinus Torvalds 	struct cypress_private *priv = usb_get_serial_port_data(port);
6581da177e4SLinus Torvalds 
6591143832eSGreg Kroah-Hartman 	dev_dbg(&port->dev, "%s - %d bytes\n", __func__, count);
6601da177e4SLinus Torvalds 
6611da177e4SLinus Torvalds 	/* line control commands, which need to be executed immediately,
6621da177e4SLinus Torvalds 	   are not put into the buffer for obvious reasons.
6631da177e4SLinus Torvalds 	 */
6641da177e4SLinus Torvalds 	if (priv->cmd_ctrl) {
6651da177e4SLinus Torvalds 		count = 0;
6661da177e4SLinus Torvalds 		goto finish;
6671da177e4SLinus Torvalds 	}
6681da177e4SLinus Torvalds 
6691da177e4SLinus Torvalds 	if (!count)
6701da177e4SLinus Torvalds 		return count;
6711da177e4SLinus Torvalds 
672117fb8d0SJohan Hovold 	count = kfifo_in_locked(&priv->write_fifo, buf, count, &priv->lock);
6731da177e4SLinus Torvalds 
6741da177e4SLinus Torvalds finish:
6751da177e4SLinus Torvalds 	cypress_send(port);
6761da177e4SLinus Torvalds 
6771da177e4SLinus Torvalds 	return count;
6781da177e4SLinus Torvalds } /* cypress_write */
6791da177e4SLinus Torvalds 
6801da177e4SLinus Torvalds 
cypress_send(struct usb_serial_port * port)6811da177e4SLinus Torvalds static void cypress_send(struct usb_serial_port *port)
6821da177e4SLinus Torvalds {
6831da177e4SLinus Torvalds 	int count = 0, result, offset, actual_size;
6841da177e4SLinus Torvalds 	struct cypress_private *priv = usb_get_serial_port_data(port);
685dfa1c315SGreg Kroah-Hartman 	struct device *dev = &port->dev;
6861da177e4SLinus Torvalds 	unsigned long flags;
6871da177e4SLinus Torvalds 
68878aef519SMike Isely 	if (!priv->comm_is_ok)
68978aef519SMike Isely 		return;
69078aef519SMike Isely 
691dfa1c315SGreg Kroah-Hartman 	dev_dbg(dev, "%s - interrupt out size is %d\n", __func__,
692813a224fSAlan Cox 		port->interrupt_out_size);
6931da177e4SLinus Torvalds 
6941da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
6951da177e4SLinus Torvalds 	if (priv->write_urb_in_use) {
696dfa1c315SGreg Kroah-Hartman 		dev_dbg(dev, "%s - can't write, urb in use\n", __func__);
6971da177e4SLinus Torvalds 		spin_unlock_irqrestore(&priv->lock, flags);
6981da177e4SLinus Torvalds 		return;
6991da177e4SLinus Torvalds 	}
7001da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->lock, flags);
7011da177e4SLinus Torvalds 
7021da177e4SLinus Torvalds 	/* clear buffer */
703813a224fSAlan Cox 	memset(port->interrupt_out_urb->transfer_buffer, 0,
704813a224fSAlan Cox 						port->interrupt_out_size);
7051da177e4SLinus Torvalds 
7061da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
7073416eaa1SMike Isely 	switch (priv->pkt_fmt) {
7083416eaa1SMike Isely 	default:
7093416eaa1SMike Isely 	case packet_format_1:
7103cb4a4f7SLonnie Mendez 		/* this is for the CY7C64013... */
7111da177e4SLinus Torvalds 		offset = 2;
7121da177e4SLinus Torvalds 		port->interrupt_out_buffer[0] = priv->line_control;
7131da177e4SLinus Torvalds 		break;
7143416eaa1SMike Isely 	case packet_format_2:
7153cb4a4f7SLonnie Mendez 		/* this is for the CY7C63743... */
7161da177e4SLinus Torvalds 		offset = 1;
7171da177e4SLinus Torvalds 		port->interrupt_out_buffer[0] = priv->line_control;
7181da177e4SLinus Torvalds 		break;
7191da177e4SLinus Torvalds 	}
7201da177e4SLinus Torvalds 
7211da177e4SLinus Torvalds 	if (priv->line_control & CONTROL_RESET)
7221da177e4SLinus Torvalds 		priv->line_control &= ~CONTROL_RESET;
7231da177e4SLinus Torvalds 
7241da177e4SLinus Torvalds 	if (priv->cmd_ctrl) {
7251da177e4SLinus Torvalds 		priv->cmd_count++;
726dfa1c315SGreg Kroah-Hartman 		dev_dbg(dev, "%s - line control command being issued\n", __func__);
7271da177e4SLinus Torvalds 		spin_unlock_irqrestore(&priv->lock, flags);
7281da177e4SLinus Torvalds 		goto send;
7291da177e4SLinus Torvalds 	} else
7301da177e4SLinus Torvalds 		spin_unlock_irqrestore(&priv->lock, flags);
7311da177e4SLinus Torvalds 
732117fb8d0SJohan Hovold 	count = kfifo_out_locked(&priv->write_fifo,
733117fb8d0SJohan Hovold 					&port->interrupt_out_buffer[offset],
734117fb8d0SJohan Hovold 					port->interrupt_out_size - offset,
735117fb8d0SJohan Hovold 					&priv->lock);
736813a224fSAlan Cox 	if (count == 0)
7371da177e4SLinus Torvalds 		return;
7381da177e4SLinus Torvalds 
7393416eaa1SMike Isely 	switch (priv->pkt_fmt) {
7403416eaa1SMike Isely 	default:
7413416eaa1SMike Isely 	case packet_format_1:
7421da177e4SLinus Torvalds 		port->interrupt_out_buffer[1] = count;
7431da177e4SLinus Torvalds 		break;
7443416eaa1SMike Isely 	case packet_format_2:
7451da177e4SLinus Torvalds 		port->interrupt_out_buffer[0] |= count;
7461da177e4SLinus Torvalds 	}
7471da177e4SLinus Torvalds 
748dfa1c315SGreg Kroah-Hartman 	dev_dbg(dev, "%s - count is %d\n", __func__, count);
7491da177e4SLinus Torvalds 
7501da177e4SLinus Torvalds send:
7511da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
7521da177e4SLinus Torvalds 	priv->write_urb_in_use = 1;
7531da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->lock, flags);
7541da177e4SLinus Torvalds 
7551da177e4SLinus Torvalds 	if (priv->cmd_ctrl)
7561da177e4SLinus Torvalds 		actual_size = 1;
7571da177e4SLinus Torvalds 	else
7583416eaa1SMike Isely 		actual_size = count +
7593416eaa1SMike Isely 			      (priv->pkt_fmt == packet_format_1 ? 2 : 1);
7601da177e4SLinus Torvalds 
76159d33f2fSGreg Kroah-Hartman 	usb_serial_debug_data(dev, __func__, port->interrupt_out_size,
7621da177e4SLinus Torvalds 			      port->interrupt_out_urb->transfer_buffer);
7631da177e4SLinus Torvalds 
7649aa8dae7SMike Isely 	usb_fill_int_urb(port->interrupt_out_urb, port->serial->dev,
7659aa8dae7SMike Isely 		usb_sndintpipe(port->serial->dev, port->interrupt_out_endpointAddress),
76656445eefSJohan Hovold 		port->interrupt_out_buffer, actual_size,
7679aa8dae7SMike Isely 		cypress_write_int_callback, port, priv->write_urb_interval);
7681da177e4SLinus Torvalds 	result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC);
7691da177e4SLinus Torvalds 	if (result) {
77022a416c4SJohan Hovold 		dev_err_console(port,
771813a224fSAlan Cox 				"%s - failed submitting write urb, error %d\n",
772813a224fSAlan Cox 							__func__, result);
7731da177e4SLinus Torvalds 		priv->write_urb_in_use = 0;
77478aef519SMike Isely 		cypress_set_dead(port);
7751da177e4SLinus Torvalds 	}
7761da177e4SLinus Torvalds 
7771da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
778813a224fSAlan Cox 	if (priv->cmd_ctrl)
7791da177e4SLinus Torvalds 		priv->cmd_ctrl = 0;
780813a224fSAlan Cox 
781813a224fSAlan Cox 	/* do not count the line control and size bytes */
782813a224fSAlan Cox 	priv->bytes_out += count;
7831da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->lock, flags);
7841da177e4SLinus Torvalds 
785cf2c7481SPete Zaitcev 	usb_serial_port_softint(port);
7861da177e4SLinus Torvalds } /* cypress_send */
7871da177e4SLinus Torvalds 
7881da177e4SLinus Torvalds 
7891da177e4SLinus Torvalds /* returns how much space is available in the soft buffer */
cypress_write_room(struct tty_struct * tty)79094cc7aeaSJiri Slaby static unsigned int cypress_write_room(struct tty_struct *tty)
7911da177e4SLinus Torvalds {
79295da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
7931da177e4SLinus Torvalds 	struct cypress_private *priv = usb_get_serial_port_data(port);
79494cc7aeaSJiri Slaby 	unsigned int room;
7951da177e4SLinus Torvalds 	unsigned long flags;
7961da177e4SLinus Torvalds 
7971da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
798117fb8d0SJohan Hovold 	room = kfifo_avail(&priv->write_fifo);
7991da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->lock, flags);
8001da177e4SLinus Torvalds 
80194cc7aeaSJiri Slaby 	dev_dbg(&port->dev, "%s - returns %u\n", __func__, room);
8021da177e4SLinus Torvalds 	return room;
8031da177e4SLinus Torvalds }
8041da177e4SLinus Torvalds 
8051da177e4SLinus Torvalds 
cypress_tiocmget(struct tty_struct * tty)80660b33c13SAlan Cox static int cypress_tiocmget(struct tty_struct *tty)
8071da177e4SLinus Torvalds {
80895da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
8091da177e4SLinus Torvalds 	struct cypress_private *priv = usb_get_serial_port_data(port);
8101da177e4SLinus Torvalds 	__u8 status, control;
8111da177e4SLinus Torvalds 	unsigned int result = 0;
8121da177e4SLinus Torvalds 	unsigned long flags;
8131da177e4SLinus Torvalds 
8141da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
8151da177e4SLinus Torvalds 	control = priv->line_control;
8161da177e4SLinus Torvalds 	status = priv->current_status;
8171da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->lock, flags);
8181da177e4SLinus Torvalds 
8191da177e4SLinus Torvalds 	result = ((control & CONTROL_DTR)        ? TIOCM_DTR : 0)
8201da177e4SLinus Torvalds 		| ((control & CONTROL_RTS)       ? TIOCM_RTS : 0)
8211da177e4SLinus Torvalds 		| ((status & UART_CTS)        ? TIOCM_CTS : 0)
8221da177e4SLinus Torvalds 		| ((status & UART_DSR)        ? TIOCM_DSR : 0)
8231da177e4SLinus Torvalds 		| ((status & UART_RI)         ? TIOCM_RI  : 0)
8241da177e4SLinus Torvalds 		| ((status & UART_CD)         ? TIOCM_CD  : 0);
8251da177e4SLinus Torvalds 
826dfa1c315SGreg Kroah-Hartman 	dev_dbg(&port->dev, "%s - result = %x\n", __func__, result);
8271da177e4SLinus Torvalds 
8281da177e4SLinus Torvalds 	return result;
8291da177e4SLinus Torvalds }
8301da177e4SLinus Torvalds 
8311da177e4SLinus Torvalds 
cypress_tiocmset(struct tty_struct * tty,unsigned int set,unsigned int clear)83220b9d177SAlan Cox static int cypress_tiocmset(struct tty_struct *tty,
8331da177e4SLinus Torvalds 			       unsigned int set, unsigned int clear)
8341da177e4SLinus Torvalds {
83595da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
8361da177e4SLinus Torvalds 	struct cypress_private *priv = usb_get_serial_port_data(port);
8371da177e4SLinus Torvalds 	unsigned long flags;
8381da177e4SLinus Torvalds 
8391da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
8401da177e4SLinus Torvalds 	if (set & TIOCM_RTS)
8411da177e4SLinus Torvalds 		priv->line_control |= CONTROL_RTS;
8421da177e4SLinus Torvalds 	if (set & TIOCM_DTR)
8431da177e4SLinus Torvalds 		priv->line_control |= CONTROL_DTR;
8441da177e4SLinus Torvalds 	if (clear & TIOCM_RTS)
8451da177e4SLinus Torvalds 		priv->line_control &= ~CONTROL_RTS;
8461da177e4SLinus Torvalds 	if (clear & TIOCM_DTR)
8471da177e4SLinus Torvalds 		priv->line_control &= ~CONTROL_DTR;
8488873aaa6SAlan Cox 	priv->cmd_ctrl = 1;
8491da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->lock, flags);
8501da177e4SLinus Torvalds 
85195da310eSAlan Cox 	return cypress_write(tty, port, NULL, 0);
8521da177e4SLinus Torvalds }
8531da177e4SLinus Torvalds 
cypress_earthmate_init_termios(struct tty_struct * tty)8542e75232bSJohan Hovold static void cypress_earthmate_init_termios(struct tty_struct *tty)
8552e75232bSJohan Hovold {
8562e75232bSJohan Hovold 	tty_encode_baud_rate(tty, 4800, 4800);
8572e75232bSJohan Hovold }
8582e75232bSJohan Hovold 
cypress_set_termios(struct tty_struct * tty,struct usb_serial_port * port,const struct ktermios * old_termios)85995da310eSAlan Cox static void cypress_set_termios(struct tty_struct *tty,
860f6d47fe5SIlpo Järvinen 				struct usb_serial_port *port,
861f6d47fe5SIlpo Järvinen 				const struct ktermios *old_termios)
8621da177e4SLinus Torvalds {
8631da177e4SLinus Torvalds 	struct cypress_private *priv = usb_get_serial_port_data(port);
864dfa1c315SGreg Kroah-Hartman 	struct device *dev = &port->dev;
8651da177e4SLinus Torvalds 	int data_bits, stop_bits, parity_type, parity_enable;
86617c42e34SYueHaibing 	unsigned int cflag;
8671da177e4SLinus Torvalds 	unsigned long flags;
8681da177e4SLinus Torvalds 	__u8 oldlines;
8693cb4a4f7SLonnie Mendez 	int linechange = 0;
8701da177e4SLinus Torvalds 
8718873aaa6SAlan Cox 	/* Unsupported features need clearing */
872adc8d746SAlan Cox 	tty->termios.c_cflag &= ~(CMSPAR|CRTSCTS);
8738873aaa6SAlan Cox 
874adc8d746SAlan Cox 	cflag = tty->termios.c_cflag;
8751da177e4SLinus Torvalds 
8761da177e4SLinus Torvalds 	/* set number of data bits, parity, stop bits */
8771da177e4SLinus Torvalds 	/* when parity is disabled the parity type bit is ignored */
8781da177e4SLinus Torvalds 
879b9db07fbSLonnie Mendez 	/* 1 means 2 stop bits, 0 means 1 stop bit */
880b9db07fbSLonnie Mendez 	stop_bits = cflag & CSTOPB ? 1 : 0;
8811da177e4SLinus Torvalds 
8821da177e4SLinus Torvalds 	if (cflag & PARENB) {
8831da177e4SLinus Torvalds 		parity_enable = 1;
884b9db07fbSLonnie Mendez 		/* 1 means odd parity, 0 means even parity */
885b9db07fbSLonnie Mendez 		parity_type = cflag & PARODD ? 1 : 0;
8861da177e4SLinus Torvalds 	} else
8871da177e4SLinus Torvalds 		parity_enable = parity_type = 0;
8881da177e4SLinus Torvalds 
8893ec2ff37SJiri Slaby 	data_bits = tty_get_char_size(cflag);
8903ec2ff37SJiri Slaby 
8911da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
8921da177e4SLinus Torvalds 	oldlines = priv->line_control;
8931da177e4SLinus Torvalds 	if ((cflag & CBAUD) == B0) {
8941da177e4SLinus Torvalds 		/* drop dtr and rts */
895dfa1c315SGreg Kroah-Hartman 		dev_dbg(dev, "%s - dropping the lines, baud rate 0bps\n", __func__);
8961da177e4SLinus Torvalds 		priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
8978873aaa6SAlan Cox 	} else
8983cb4a4f7SLonnie Mendez 		priv->line_control = (CONTROL_DTR | CONTROL_RTS);
8991da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->lock, flags);
9001da177e4SLinus Torvalds 
901dfa1c315SGreg Kroah-Hartman 	dev_dbg(dev, "%s - sending %d stop_bits, %d parity_enable, %d parity_type, %d data_bits (+5)\n",
902dfa1c315SGreg Kroah-Hartman 		__func__, stop_bits, parity_enable, parity_type, data_bits);
9031da177e4SLinus Torvalds 
904813a224fSAlan Cox 	cypress_serial_control(tty, port, tty_get_baud_rate(tty),
905813a224fSAlan Cox 			data_bits, stop_bits,
906813a224fSAlan Cox 			parity_enable, parity_type,
907813a224fSAlan Cox 			0, CYPRESS_SET_CONFIG);
9081da177e4SLinus Torvalds 
909b9db07fbSLonnie Mendez 	/* we perform a CYPRESS_GET_CONFIG so that the current settings are
910b9db07fbSLonnie Mendez 	 * filled into the private structure this should confirm that all is
911b9db07fbSLonnie Mendez 	 * working if it returns what we just set */
91295da310eSAlan Cox 	cypress_serial_control(tty, port, 0, 0, 0, 0, 0, 0, CYPRESS_GET_CONFIG);
9131da177e4SLinus Torvalds 
914b9db07fbSLonnie Mendez 	/* Here we can define custom tty settings for devices; the main tty
915b9db07fbSLonnie Mendez 	 * termios flag base comes from empeg.c */
9161da177e4SLinus Torvalds 
9171da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
918813a224fSAlan Cox 	if (priv->chiptype == CT_EARTHMATE && priv->baud_rate == 4800) {
919dfa1c315SGreg Kroah-Hartman 		dev_dbg(dev, "Using custom termios settings for a baud rate of 4800bps.\n");
9201da177e4SLinus Torvalds 		/* define custom termios settings for NMEA protocol */
9211da177e4SLinus Torvalds 
922adc8d746SAlan Cox 		tty->termios.c_iflag /* input modes - */
9231da177e4SLinus Torvalds 			&= ~(IGNBRK  /* disable ignore break */
9241da177e4SLinus Torvalds 			| BRKINT     /* disable break causes interrupt */
9251da177e4SLinus Torvalds 			| PARMRK     /* disable mark parity errors */
926b9db07fbSLonnie Mendez 			| ISTRIP     /* disable clear high bit of input char */
9271da177e4SLinus Torvalds 			| INLCR      /* disable translate NL to CR */
9281da177e4SLinus Torvalds 			| IGNCR      /* disable ignore CR */
9291da177e4SLinus Torvalds 			| ICRNL      /* disable translate CR to NL */
9301da177e4SLinus Torvalds 			| IXON);     /* disable enable XON/XOFF flow control */
9311da177e4SLinus Torvalds 
932adc8d746SAlan Cox 		tty->termios.c_oflag /* output modes */
933b9db07fbSLonnie Mendez 			&= ~OPOST;    /* disable postprocess output char */
9341da177e4SLinus Torvalds 
935adc8d746SAlan Cox 		tty->termios.c_lflag /* line discipline modes */
9361da177e4SLinus Torvalds 			&= ~(ECHO     /* disable echo input characters */
9371da177e4SLinus Torvalds 			| ECHONL      /* disable echo new line */
938b9db07fbSLonnie Mendez 			| ICANON      /* disable erase, kill, werase, and rprnt
939b9db07fbSLonnie Mendez 					 special characters */
940b9db07fbSLonnie Mendez 			| ISIG        /* disable interrupt, quit, and suspend
941b9db07fbSLonnie Mendez 					 special characters */
9421da177e4SLinus Torvalds 			| IEXTEN);    /* disable non-POSIX special characters */
9433cb4a4f7SLonnie Mendez 	} /* CT_CYPHIDCOM: Application should handle this for device */
9441da177e4SLinus Torvalds 
9451da177e4SLinus Torvalds 	linechange = (priv->line_control != oldlines);
9461da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->lock, flags);
9471da177e4SLinus Torvalds 
9481da177e4SLinus Torvalds 	/* if necessary, set lines */
9493cb4a4f7SLonnie Mendez 	if (linechange) {
9501da177e4SLinus Torvalds 		priv->cmd_ctrl = 1;
95195da310eSAlan Cox 		cypress_write(tty, port, NULL, 0);
9521da177e4SLinus Torvalds 	}
9531da177e4SLinus Torvalds } /* cypress_set_termios */
9541da177e4SLinus Torvalds 
9551da177e4SLinus Torvalds 
9561da177e4SLinus Torvalds /* returns amount of data still left in soft buffer */
cypress_chars_in_buffer(struct tty_struct * tty)957155591d3SJiri Slaby static unsigned int cypress_chars_in_buffer(struct tty_struct *tty)
9581da177e4SLinus Torvalds {
95995da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
9601da177e4SLinus Torvalds 	struct cypress_private *priv = usb_get_serial_port_data(port);
961155591d3SJiri Slaby 	unsigned int chars;
9621da177e4SLinus Torvalds 	unsigned long flags;
9631da177e4SLinus Torvalds 
9641da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
965117fb8d0SJohan Hovold 	chars = kfifo_len(&priv->write_fifo);
9661da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->lock, flags);
9671da177e4SLinus Torvalds 
968155591d3SJiri Slaby 	dev_dbg(&port->dev, "%s - returns %u\n", __func__, chars);
9691da177e4SLinus Torvalds 	return chars;
9701da177e4SLinus Torvalds }
9711da177e4SLinus Torvalds 
9721da177e4SLinus Torvalds 
cypress_throttle(struct tty_struct * tty)97395da310eSAlan Cox static void cypress_throttle(struct tty_struct *tty)
9741da177e4SLinus Torvalds {
97595da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
9761da177e4SLinus Torvalds 	struct cypress_private *priv = usb_get_serial_port_data(port);
9771da177e4SLinus Torvalds 
97863832515SOliver Neukum 	spin_lock_irq(&priv->lock);
9791da177e4SLinus Torvalds 	priv->rx_flags = THROTTLED;
98063832515SOliver Neukum 	spin_unlock_irq(&priv->lock);
9811da177e4SLinus Torvalds }
9821da177e4SLinus Torvalds 
9831da177e4SLinus Torvalds 
cypress_unthrottle(struct tty_struct * tty)98495da310eSAlan Cox static void cypress_unthrottle(struct tty_struct *tty)
9851da177e4SLinus Torvalds {
98695da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
9871da177e4SLinus Torvalds 	struct cypress_private *priv = usb_get_serial_port_data(port);
9881da177e4SLinus Torvalds 	int actually_throttled, result;
9891da177e4SLinus Torvalds 
99063832515SOliver Neukum 	spin_lock_irq(&priv->lock);
9911da177e4SLinus Torvalds 	actually_throttled = priv->rx_flags & ACTUALLY_THROTTLED;
9921da177e4SLinus Torvalds 	priv->rx_flags = 0;
99363832515SOliver Neukum 	spin_unlock_irq(&priv->lock);
9941da177e4SLinus Torvalds 
99578aef519SMike Isely 	if (!priv->comm_is_ok)
99678aef519SMike Isely 		return;
99778aef519SMike Isely 
9981da177e4SLinus Torvalds 	if (actually_throttled) {
99963832515SOliver Neukum 		result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
100078aef519SMike Isely 		if (result) {
1001b9db07fbSLonnie Mendez 			dev_err(&port->dev, "%s - failed submitting read urb, "
1002441b62c1SHarvey Harrison 					"error %d\n", __func__, result);
100378aef519SMike Isely 			cypress_set_dead(port);
100478aef519SMike Isely 		}
10051da177e4SLinus Torvalds 	}
10061da177e4SLinus Torvalds }
10071da177e4SLinus Torvalds 
10081da177e4SLinus Torvalds 
cypress_read_int_callback(struct urb * urb)10097d12e780SDavid Howells static void cypress_read_int_callback(struct urb *urb)
10101da177e4SLinus Torvalds {
1011cdc97792SMing Lei 	struct usb_serial_port *port = urb->context;
10121da177e4SLinus Torvalds 	struct cypress_private *priv = usb_get_serial_port_data(port);
1013dfa1c315SGreg Kroah-Hartman 	struct device *dev = &urb->dev->dev;
10141da177e4SLinus Torvalds 	struct tty_struct *tty;
10151da177e4SLinus Torvalds 	unsigned char *data = urb->transfer_buffer;
10161da177e4SLinus Torvalds 	unsigned long flags;
10171da177e4SLinus Torvalds 	char tty_flag = TTY_NORMAL;
10181da177e4SLinus Torvalds 	int bytes = 0;
10191da177e4SLinus Torvalds 	int result;
10201da177e4SLinus Torvalds 	int i = 0;
10218d7bc55eSGreg Kroah-Hartman 	int status = urb->status;
10221da177e4SLinus Torvalds 
10238d7bc55eSGreg Kroah-Hartman 	switch (status) {
102478aef519SMike Isely 	case 0: /* success */
102578aef519SMike Isely 		break;
102678aef519SMike Isely 	case -ECONNRESET:
102778aef519SMike Isely 	case -ENOENT:
102878aef519SMike Isely 	case -ESHUTDOWN:
102978aef519SMike Isely 		/* precursor to disconnect so just go away */
103078aef519SMike Isely 		return;
103178aef519SMike Isely 	case -EPIPE:
10324d2fae8bSAlan Stern 		/* Can't call usb_clear_halt while in_interrupt */
103374b76256SGustavo A. R. Silva 		fallthrough;
103478aef519SMike Isely 	default:
103578aef519SMike Isely 		/* something ugly is going on... */
1036dfa1c315SGreg Kroah-Hartman 		dev_err(dev, "%s - unexpected nonzero read status received: %d\n",
1037441b62c1SHarvey Harrison 			__func__, status);
103878aef519SMike Isely 		cypress_set_dead(port);
10391da177e4SLinus Torvalds 		return;
10401da177e4SLinus Torvalds 	}
10411da177e4SLinus Torvalds 
10421da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
10431da177e4SLinus Torvalds 	if (priv->rx_flags & THROTTLED) {
1044dfa1c315SGreg Kroah-Hartman 		dev_dbg(dev, "%s - now throttling\n", __func__);
10451da177e4SLinus Torvalds 		priv->rx_flags |= ACTUALLY_THROTTLED;
10461da177e4SLinus Torvalds 		spin_unlock_irqrestore(&priv->lock, flags);
10471da177e4SLinus Torvalds 		return;
10481da177e4SLinus Torvalds 	}
10491da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->lock, flags);
10501da177e4SLinus Torvalds 
10514a90f09bSAlan Cox 	tty = tty_port_tty_get(&port->port);
10521da177e4SLinus Torvalds 	if (!tty) {
1053dfa1c315SGreg Kroah-Hartman 		dev_dbg(dev, "%s - bad tty pointer - exiting\n", __func__);
10541da177e4SLinus Torvalds 		return;
10551da177e4SLinus Torvalds 	}
10561da177e4SLinus Torvalds 
10571da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
10583416eaa1SMike Isely 	result = urb->actual_length;
10593416eaa1SMike Isely 	switch (priv->pkt_fmt) {
10603416eaa1SMike Isely 	default:
10613416eaa1SMike Isely 	case packet_format_1:
10623cb4a4f7SLonnie Mendez 		/* This is for the CY7C64013... */
10631da177e4SLinus Torvalds 		priv->current_status = data[0] & 0xF8;
10641da177e4SLinus Torvalds 		bytes = data[1] + 2;
10651da177e4SLinus Torvalds 		i = 2;
10661da177e4SLinus Torvalds 		break;
10673416eaa1SMike Isely 	case packet_format_2:
10683cb4a4f7SLonnie Mendez 		/* This is for the CY7C63743... */
10691da177e4SLinus Torvalds 		priv->current_status = data[0] & 0xF8;
10701da177e4SLinus Torvalds 		bytes = (data[0] & 0x07) + 1;
10711da177e4SLinus Torvalds 		i = 1;
10721da177e4SLinus Torvalds 		break;
10731da177e4SLinus Torvalds 	}
10741da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->lock, flags);
10753416eaa1SMike Isely 	if (result < bytes) {
1076dfa1c315SGreg Kroah-Hartman 		dev_dbg(dev,
1077dfa1c315SGreg Kroah-Hartman 			"%s - wrong packet size - received %d bytes but packet said %d bytes\n",
1078dfa1c315SGreg Kroah-Hartman 			__func__, result, bytes);
10793416eaa1SMike Isely 		goto continue_read;
10803416eaa1SMike Isely 	}
10811da177e4SLinus Torvalds 
108259d33f2fSGreg Kroah-Hartman 	usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
10831da177e4SLinus Torvalds 
10841da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
10851da177e4SLinus Torvalds 	/* check to see if status has changed */
10861da177e4SLinus Torvalds 	if (priv->current_status != priv->prev_status) {
1087ab62a585SJohan Hovold 		u8 delta = priv->current_status ^ priv->prev_status;
10887603381eSJohan Hovold 
1089ab62a585SJohan Hovold 		if (delta & UART_MSR_MASK) {
1090ab62a585SJohan Hovold 			if (delta & UART_CTS)
1091ab62a585SJohan Hovold 				port->icount.cts++;
1092ab62a585SJohan Hovold 			if (delta & UART_DSR)
1093ab62a585SJohan Hovold 				port->icount.dsr++;
1094ab62a585SJohan Hovold 			if (delta & UART_RI)
1095ab62a585SJohan Hovold 				port->icount.rng++;
1096ab62a585SJohan Hovold 			if (delta & UART_CD)
1097ab62a585SJohan Hovold 				port->icount.dcd++;
1098ab62a585SJohan Hovold 
1099c648e94eSJohan Hovold 			wake_up_interruptible(&port->port.delta_msr_wait);
1100ab62a585SJohan Hovold 		}
11017603381eSJohan Hovold 
11021da177e4SLinus Torvalds 		priv->prev_status = priv->current_status;
11031da177e4SLinus Torvalds 	}
11041da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->lock, flags);
11051da177e4SLinus Torvalds 
1106b9db07fbSLonnie Mendez 	/* hangup, as defined in acm.c... this might be a bad place for it
1107b9db07fbSLonnie Mendez 	 * though */
11089db276f8SPeter Hurley 	if (tty && !C_CLOCAL(tty) && !(priv->current_status & UART_CD)) {
1109dfa1c315SGreg Kroah-Hartman 		dev_dbg(dev, "%s - calling hangup\n", __func__);
11101da177e4SLinus Torvalds 		tty_hangup(tty);
11111da177e4SLinus Torvalds 		goto continue_read;
11121da177e4SLinus Torvalds 	}
11131da177e4SLinus Torvalds 
1114b9db07fbSLonnie Mendez 	/* There is one error bit... I'm assuming it is a parity error
1115b9db07fbSLonnie Mendez 	 * indicator as the generic firmware will set this bit to 1 if a
1116b9db07fbSLonnie Mendez 	 * parity error occurs.
1117b9db07fbSLonnie Mendez 	 * I can not find reference to any other error events. */
11181da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
11191da177e4SLinus Torvalds 	if (priv->current_status & CYP_ERROR) {
11201da177e4SLinus Torvalds 		spin_unlock_irqrestore(&priv->lock, flags);
11211da177e4SLinus Torvalds 		tty_flag = TTY_PARITY;
1122dfa1c315SGreg Kroah-Hartman 		dev_dbg(dev, "%s - Parity Error detected\n", __func__);
11231da177e4SLinus Torvalds 	} else
11241da177e4SLinus Torvalds 		spin_unlock_irqrestore(&priv->lock, flags);
11251da177e4SLinus Torvalds 
11261da177e4SLinus Torvalds 	/* process read if there is data other than line status */
11272e124b4aSJiri Slaby 	if (bytes > i) {
11282f693357SJiri Slaby 		tty_insert_flip_string_fixed_flag(&port->port, data + i,
112970ced221SJohan Hovold 				tty_flag, bytes - i);
11302e124b4aSJiri Slaby 		tty_flip_buffer_push(&port->port);
11311da177e4SLinus Torvalds 	}
11321da177e4SLinus Torvalds 
11331da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
1134b9db07fbSLonnie Mendez 	/* control and status byte(s) are also counted */
1135b9db07fbSLonnie Mendez 	priv->bytes_in += bytes;
11361da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->lock, flags);
11371da177e4SLinus Torvalds 
11381da177e4SLinus Torvalds continue_read:
11394a90f09bSAlan Cox 	tty_kref_put(tty);
11401da177e4SLinus Torvalds 
11411f87158eSAlan Stern 	/* Continue trying to always read */
11421da177e4SLinus Torvalds 
11431f87158eSAlan Stern 	if (priv->comm_is_ok) {
11441da177e4SLinus Torvalds 		usb_fill_int_urb(port->interrupt_in_urb, port->serial->dev,
1145b9db07fbSLonnie Mendez 				usb_rcvintpipe(port->serial->dev,
1146b9db07fbSLonnie Mendez 					port->interrupt_in_endpointAddress),
11471da177e4SLinus Torvalds 				port->interrupt_in_urb->transfer_buffer,
11481da177e4SLinus Torvalds 				port->interrupt_in_urb->transfer_buffer_length,
1149813a224fSAlan Cox 				cypress_read_int_callback, port,
1150813a224fSAlan Cox 				priv->read_urb_interval);
11511da177e4SLinus Torvalds 		result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
11521f87158eSAlan Stern 		if (result && result != -EPERM) {
1153dfa1c315SGreg Kroah-Hartman 			dev_err(dev, "%s - failed resubmitting read urb, error %d\n",
1154dfa1c315SGreg Kroah-Hartman 				__func__, result);
115578aef519SMike Isely 			cypress_set_dead(port);
115678aef519SMike Isely 		}
11571da177e4SLinus Torvalds 	}
11581da177e4SLinus Torvalds } /* cypress_read_int_callback */
11591da177e4SLinus Torvalds 
11601da177e4SLinus Torvalds 
cypress_write_int_callback(struct urb * urb)11617d12e780SDavid Howells static void cypress_write_int_callback(struct urb *urb)
11621da177e4SLinus Torvalds {
1163cdc97792SMing Lei 	struct usb_serial_port *port = urb->context;
11641da177e4SLinus Torvalds 	struct cypress_private *priv = usb_get_serial_port_data(port);
1165dfa1c315SGreg Kroah-Hartman 	struct device *dev = &urb->dev->dev;
11668d7bc55eSGreg Kroah-Hartman 	int status = urb->status;
11671da177e4SLinus Torvalds 
11688d7bc55eSGreg Kroah-Hartman 	switch (status) {
11691da177e4SLinus Torvalds 	case 0:
11701da177e4SLinus Torvalds 		/* success */
11711da177e4SLinus Torvalds 		break;
11721da177e4SLinus Torvalds 	case -ECONNRESET:
11731da177e4SLinus Torvalds 	case -ENOENT:
11741da177e4SLinus Torvalds 	case -ESHUTDOWN:
11751da177e4SLinus Torvalds 		/* this urb is terminated, clean up */
1176dfa1c315SGreg Kroah-Hartman 		dev_dbg(dev, "%s - urb shutting down with status: %d\n",
1177441b62c1SHarvey Harrison 			__func__, status);
11781da177e4SLinus Torvalds 		priv->write_urb_in_use = 0;
11791da177e4SLinus Torvalds 		return;
1180d7c933aeSJohan Hovold 	case -EPIPE:
1181d7c933aeSJohan Hovold 		/* Cannot call usb_clear_halt while in_interrupt */
118274b76256SGustavo A. R. Silva 		fallthrough;
118378aef519SMike Isely 	default:
1184dfa1c315SGreg Kroah-Hartman 		dev_err(dev, "%s - unexpected nonzero write status received: %d\n",
1185441b62c1SHarvey Harrison 			__func__, status);
118678aef519SMike Isely 		cypress_set_dead(port);
118778aef519SMike Isely 		break;
11881da177e4SLinus Torvalds 	}
11891da177e4SLinus Torvalds 	priv->write_urb_in_use = 0;
11901da177e4SLinus Torvalds 
11911da177e4SLinus Torvalds 	/* send any buffered data */
11921da177e4SLinus Torvalds 	cypress_send(port);
11931da177e4SLinus Torvalds }
11941da177e4SLinus Torvalds 
119568e24113SGreg Kroah-Hartman module_usb_serial_driver(serial_drivers, id_table_combined);
11961da177e4SLinus Torvalds 
11971da177e4SLinus Torvalds MODULE_AUTHOR(DRIVER_AUTHOR);
11981da177e4SLinus Torvalds MODULE_DESCRIPTION(DRIVER_DESC);
11991da177e4SLinus Torvalds MODULE_LICENSE("GPL");
12001da177e4SLinus Torvalds 
1201a65ab973SUtkarsh Verma module_param(stats, bool, 0644);
12021da177e4SLinus Torvalds MODULE_PARM_DESC(stats, "Enable statistics or not");
1203a65ab973SUtkarsh Verma module_param(interval, int, 0644);
12043cb4a4f7SLonnie Mendez MODULE_PARM_DESC(interval, "Overrides interrupt interval");
1205a65ab973SUtkarsh Verma module_param(unstable_bauds, bool, 0644);
1206c312659cSMike Frysinger MODULE_PARM_DESC(unstable_bauds, "Allow unstable baud rates");
1207