18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * USB Cypress M8 driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * 	Copyright (C) 2004
68c2ecf20Sopenharmony_ci * 	    Lonnie Mendez (dignome@gmail.com)
78c2ecf20Sopenharmony_ci *	Copyright (C) 2003,2004
88c2ecf20Sopenharmony_ci *	    Neil Whelchel (koyama@firstlight.net)
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * See Documentation/usb/usb-serial.rst for more information on using this
118c2ecf20Sopenharmony_ci * driver
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * See http://geocities.com/i0xox0i for information on this driver and the
148c2ecf20Sopenharmony_ci * earthmate usb device.
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/* Thanks to Neil Whelchel for writing the first cypress m8 implementation
188c2ecf20Sopenharmony_ci   for linux. */
198c2ecf20Sopenharmony_ci/* Thanks to cypress for providing references for the hid reports. */
208c2ecf20Sopenharmony_ci/* Thanks to Jiang Zhang for providing links and for general help. */
218c2ecf20Sopenharmony_ci/* Code originates and was built up from ftdi_sio, belkin, pl2303 and others.*/
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include <linux/kernel.h>
258c2ecf20Sopenharmony_ci#include <linux/errno.h>
268c2ecf20Sopenharmony_ci#include <linux/slab.h>
278c2ecf20Sopenharmony_ci#include <linux/tty.h>
288c2ecf20Sopenharmony_ci#include <linux/tty_driver.h>
298c2ecf20Sopenharmony_ci#include <linux/tty_flip.h>
308c2ecf20Sopenharmony_ci#include <linux/module.h>
318c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
328c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
338c2ecf20Sopenharmony_ci#include <linux/usb.h>
348c2ecf20Sopenharmony_ci#include <linux/usb/serial.h>
358c2ecf20Sopenharmony_ci#include <linux/serial.h>
368c2ecf20Sopenharmony_ci#include <linux/kfifo.h>
378c2ecf20Sopenharmony_ci#include <linux/delay.h>
388c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
398c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#include "cypress_m8.h"
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic bool stats;
458c2ecf20Sopenharmony_cistatic int interval;
468c2ecf20Sopenharmony_cistatic bool unstable_bauds;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Lonnie Mendez <dignome@gmail.com>, Neil Whelchel <koyama@firstlight.net>"
498c2ecf20Sopenharmony_ci#define DRIVER_DESC "Cypress USB to Serial Driver"
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/* write buffer size defines */
528c2ecf20Sopenharmony_ci#define CYPRESS_BUF_SIZE	1024
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table_earthmate[] = {
558c2ecf20Sopenharmony_ci	{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB) },
568c2ecf20Sopenharmony_ci	{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB_LT20) },
578c2ecf20Sopenharmony_ci	{ }						/* Terminating entry */
588c2ecf20Sopenharmony_ci};
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table_cyphidcomrs232[] = {
618c2ecf20Sopenharmony_ci	{ USB_DEVICE(VENDOR_ID_CYPRESS, PRODUCT_ID_CYPHIDCOM) },
628c2ecf20Sopenharmony_ci	{ USB_DEVICE(VENDOR_ID_SAI, PRODUCT_ID_CYPHIDCOM) },
638c2ecf20Sopenharmony_ci	{ USB_DEVICE(VENDOR_ID_POWERCOM, PRODUCT_ID_UPS) },
648c2ecf20Sopenharmony_ci	{ USB_DEVICE(VENDOR_ID_FRWD, PRODUCT_ID_CYPHIDCOM_FRWD) },
658c2ecf20Sopenharmony_ci	{ }						/* Terminating entry */
668c2ecf20Sopenharmony_ci};
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table_nokiaca42v2[] = {
698c2ecf20Sopenharmony_ci	{ USB_DEVICE(VENDOR_ID_DAZZLE, PRODUCT_ID_CA42) },
708c2ecf20Sopenharmony_ci	{ }						/* Terminating entry */
718c2ecf20Sopenharmony_ci};
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table_combined[] = {
748c2ecf20Sopenharmony_ci	{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB) },
758c2ecf20Sopenharmony_ci	{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB_LT20) },
768c2ecf20Sopenharmony_ci	{ USB_DEVICE(VENDOR_ID_CYPRESS, PRODUCT_ID_CYPHIDCOM) },
778c2ecf20Sopenharmony_ci	{ USB_DEVICE(VENDOR_ID_SAI, PRODUCT_ID_CYPHIDCOM) },
788c2ecf20Sopenharmony_ci	{ USB_DEVICE(VENDOR_ID_POWERCOM, PRODUCT_ID_UPS) },
798c2ecf20Sopenharmony_ci	{ USB_DEVICE(VENDOR_ID_FRWD, PRODUCT_ID_CYPHIDCOM_FRWD) },
808c2ecf20Sopenharmony_ci	{ USB_DEVICE(VENDOR_ID_DAZZLE, PRODUCT_ID_CA42) },
818c2ecf20Sopenharmony_ci	{ }						/* Terminating entry */
828c2ecf20Sopenharmony_ci};
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table_combined);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cienum packet_format {
878c2ecf20Sopenharmony_ci	packet_format_1,  /* b0:status, b1:payload count */
888c2ecf20Sopenharmony_ci	packet_format_2   /* b0[7:3]:status, b0[2:0]:payload count */
898c2ecf20Sopenharmony_ci};
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistruct cypress_private {
928c2ecf20Sopenharmony_ci	spinlock_t lock;		   /* private lock */
938c2ecf20Sopenharmony_ci	int chiptype;			   /* identifier of device, for quirks/etc */
948c2ecf20Sopenharmony_ci	int bytes_in;			   /* used for statistics */
958c2ecf20Sopenharmony_ci	int bytes_out;			   /* used for statistics */
968c2ecf20Sopenharmony_ci	int cmd_count;			   /* used for statistics */
978c2ecf20Sopenharmony_ci	int cmd_ctrl;			   /* always set this to 1 before issuing a command */
988c2ecf20Sopenharmony_ci	struct kfifo write_fifo;	   /* write fifo */
998c2ecf20Sopenharmony_ci	int write_urb_in_use;		   /* write urb in use indicator */
1008c2ecf20Sopenharmony_ci	int write_urb_interval;            /* interval to use for write urb */
1018c2ecf20Sopenharmony_ci	int read_urb_interval;             /* interval to use for read urb */
1028c2ecf20Sopenharmony_ci	int comm_is_ok;                    /* true if communication is (still) ok */
1038c2ecf20Sopenharmony_ci	__u8 line_control;	   	   /* holds dtr / rts value */
1048c2ecf20Sopenharmony_ci	__u8 current_status;	   	   /* received from last read - info on dsr,cts,cd,ri,etc */
1058c2ecf20Sopenharmony_ci	__u8 current_config;	   	   /* stores the current configuration byte */
1068c2ecf20Sopenharmony_ci	__u8 rx_flags;			   /* throttling - used from whiteheat/ftdi_sio */
1078c2ecf20Sopenharmony_ci	enum packet_format pkt_fmt;	   /* format to use for packet send / receive */
1088c2ecf20Sopenharmony_ci	int get_cfg_unsafe;		   /* If true, the CYPRESS_GET_CONFIG is unsafe */
1098c2ecf20Sopenharmony_ci	int baud_rate;			   /* stores current baud rate in
1108c2ecf20Sopenharmony_ci					      integer form */
1118c2ecf20Sopenharmony_ci	char prev_status;		   /* used for TIOCMIWAIT */
1128c2ecf20Sopenharmony_ci};
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci/* function prototypes for the Cypress USB to serial device */
1158c2ecf20Sopenharmony_cistatic int  cypress_earthmate_port_probe(struct usb_serial_port *port);
1168c2ecf20Sopenharmony_cistatic int  cypress_hidcom_port_probe(struct usb_serial_port *port);
1178c2ecf20Sopenharmony_cistatic int  cypress_ca42v2_port_probe(struct usb_serial_port *port);
1188c2ecf20Sopenharmony_cistatic int  cypress_port_remove(struct usb_serial_port *port);
1198c2ecf20Sopenharmony_cistatic int  cypress_open(struct tty_struct *tty, struct usb_serial_port *port);
1208c2ecf20Sopenharmony_cistatic void cypress_close(struct usb_serial_port *port);
1218c2ecf20Sopenharmony_cistatic void cypress_dtr_rts(struct usb_serial_port *port, int on);
1228c2ecf20Sopenharmony_cistatic int  cypress_write(struct tty_struct *tty, struct usb_serial_port *port,
1238c2ecf20Sopenharmony_ci			const unsigned char *buf, int count);
1248c2ecf20Sopenharmony_cistatic void cypress_send(struct usb_serial_port *port);
1258c2ecf20Sopenharmony_cistatic int  cypress_write_room(struct tty_struct *tty);
1268c2ecf20Sopenharmony_cistatic void cypress_earthmate_init_termios(struct tty_struct *tty);
1278c2ecf20Sopenharmony_cistatic void cypress_set_termios(struct tty_struct *tty,
1288c2ecf20Sopenharmony_ci			struct usb_serial_port *port, struct ktermios *old);
1298c2ecf20Sopenharmony_cistatic int  cypress_tiocmget(struct tty_struct *tty);
1308c2ecf20Sopenharmony_cistatic int  cypress_tiocmset(struct tty_struct *tty,
1318c2ecf20Sopenharmony_ci			unsigned int set, unsigned int clear);
1328c2ecf20Sopenharmony_cistatic int  cypress_chars_in_buffer(struct tty_struct *tty);
1338c2ecf20Sopenharmony_cistatic void cypress_throttle(struct tty_struct *tty);
1348c2ecf20Sopenharmony_cistatic void cypress_unthrottle(struct tty_struct *tty);
1358c2ecf20Sopenharmony_cistatic void cypress_set_dead(struct usb_serial_port *port);
1368c2ecf20Sopenharmony_cistatic void cypress_read_int_callback(struct urb *urb);
1378c2ecf20Sopenharmony_cistatic void cypress_write_int_callback(struct urb *urb);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic struct usb_serial_driver cypress_earthmate_device = {
1408c2ecf20Sopenharmony_ci	.driver = {
1418c2ecf20Sopenharmony_ci		.owner =		THIS_MODULE,
1428c2ecf20Sopenharmony_ci		.name =			"earthmate",
1438c2ecf20Sopenharmony_ci	},
1448c2ecf20Sopenharmony_ci	.description =			"DeLorme Earthmate USB",
1458c2ecf20Sopenharmony_ci	.id_table =			id_table_earthmate,
1468c2ecf20Sopenharmony_ci	.num_ports =			1,
1478c2ecf20Sopenharmony_ci	.port_probe =			cypress_earthmate_port_probe,
1488c2ecf20Sopenharmony_ci	.port_remove =			cypress_port_remove,
1498c2ecf20Sopenharmony_ci	.open =				cypress_open,
1508c2ecf20Sopenharmony_ci	.close =			cypress_close,
1518c2ecf20Sopenharmony_ci	.dtr_rts =			cypress_dtr_rts,
1528c2ecf20Sopenharmony_ci	.write =			cypress_write,
1538c2ecf20Sopenharmony_ci	.write_room =			cypress_write_room,
1548c2ecf20Sopenharmony_ci	.init_termios =			cypress_earthmate_init_termios,
1558c2ecf20Sopenharmony_ci	.set_termios =			cypress_set_termios,
1568c2ecf20Sopenharmony_ci	.tiocmget =			cypress_tiocmget,
1578c2ecf20Sopenharmony_ci	.tiocmset =			cypress_tiocmset,
1588c2ecf20Sopenharmony_ci	.tiocmiwait =			usb_serial_generic_tiocmiwait,
1598c2ecf20Sopenharmony_ci	.chars_in_buffer =		cypress_chars_in_buffer,
1608c2ecf20Sopenharmony_ci	.throttle =		 	cypress_throttle,
1618c2ecf20Sopenharmony_ci	.unthrottle =			cypress_unthrottle,
1628c2ecf20Sopenharmony_ci	.read_int_callback =		cypress_read_int_callback,
1638c2ecf20Sopenharmony_ci	.write_int_callback =		cypress_write_int_callback,
1648c2ecf20Sopenharmony_ci};
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic struct usb_serial_driver cypress_hidcom_device = {
1678c2ecf20Sopenharmony_ci	.driver = {
1688c2ecf20Sopenharmony_ci		.owner =		THIS_MODULE,
1698c2ecf20Sopenharmony_ci		.name =			"cyphidcom",
1708c2ecf20Sopenharmony_ci	},
1718c2ecf20Sopenharmony_ci	.description =			"HID->COM RS232 Adapter",
1728c2ecf20Sopenharmony_ci	.id_table =			id_table_cyphidcomrs232,
1738c2ecf20Sopenharmony_ci	.num_ports =			1,
1748c2ecf20Sopenharmony_ci	.port_probe =			cypress_hidcom_port_probe,
1758c2ecf20Sopenharmony_ci	.port_remove =			cypress_port_remove,
1768c2ecf20Sopenharmony_ci	.open =				cypress_open,
1778c2ecf20Sopenharmony_ci	.close =			cypress_close,
1788c2ecf20Sopenharmony_ci	.dtr_rts =			cypress_dtr_rts,
1798c2ecf20Sopenharmony_ci	.write =			cypress_write,
1808c2ecf20Sopenharmony_ci	.write_room =			cypress_write_room,
1818c2ecf20Sopenharmony_ci	.set_termios =			cypress_set_termios,
1828c2ecf20Sopenharmony_ci	.tiocmget =			cypress_tiocmget,
1838c2ecf20Sopenharmony_ci	.tiocmset =			cypress_tiocmset,
1848c2ecf20Sopenharmony_ci	.tiocmiwait =			usb_serial_generic_tiocmiwait,
1858c2ecf20Sopenharmony_ci	.chars_in_buffer =		cypress_chars_in_buffer,
1868c2ecf20Sopenharmony_ci	.throttle =			cypress_throttle,
1878c2ecf20Sopenharmony_ci	.unthrottle =			cypress_unthrottle,
1888c2ecf20Sopenharmony_ci	.read_int_callback =		cypress_read_int_callback,
1898c2ecf20Sopenharmony_ci	.write_int_callback =		cypress_write_int_callback,
1908c2ecf20Sopenharmony_ci};
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic struct usb_serial_driver cypress_ca42v2_device = {
1938c2ecf20Sopenharmony_ci	.driver = {
1948c2ecf20Sopenharmony_ci		.owner =		THIS_MODULE,
1958c2ecf20Sopenharmony_ci		.name =			"nokiaca42v2",
1968c2ecf20Sopenharmony_ci	},
1978c2ecf20Sopenharmony_ci	.description =			"Nokia CA-42 V2 Adapter",
1988c2ecf20Sopenharmony_ci	.id_table =			id_table_nokiaca42v2,
1998c2ecf20Sopenharmony_ci	.num_ports =			1,
2008c2ecf20Sopenharmony_ci	.port_probe =			cypress_ca42v2_port_probe,
2018c2ecf20Sopenharmony_ci	.port_remove =			cypress_port_remove,
2028c2ecf20Sopenharmony_ci	.open =				cypress_open,
2038c2ecf20Sopenharmony_ci	.close =			cypress_close,
2048c2ecf20Sopenharmony_ci	.dtr_rts =			cypress_dtr_rts,
2058c2ecf20Sopenharmony_ci	.write =			cypress_write,
2068c2ecf20Sopenharmony_ci	.write_room =			cypress_write_room,
2078c2ecf20Sopenharmony_ci	.set_termios =			cypress_set_termios,
2088c2ecf20Sopenharmony_ci	.tiocmget =			cypress_tiocmget,
2098c2ecf20Sopenharmony_ci	.tiocmset =			cypress_tiocmset,
2108c2ecf20Sopenharmony_ci	.tiocmiwait =			usb_serial_generic_tiocmiwait,
2118c2ecf20Sopenharmony_ci	.chars_in_buffer =		cypress_chars_in_buffer,
2128c2ecf20Sopenharmony_ci	.throttle =			cypress_throttle,
2138c2ecf20Sopenharmony_ci	.unthrottle =			cypress_unthrottle,
2148c2ecf20Sopenharmony_ci	.read_int_callback =		cypress_read_int_callback,
2158c2ecf20Sopenharmony_ci	.write_int_callback =		cypress_write_int_callback,
2168c2ecf20Sopenharmony_ci};
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = {
2198c2ecf20Sopenharmony_ci	&cypress_earthmate_device, &cypress_hidcom_device,
2208c2ecf20Sopenharmony_ci	&cypress_ca42v2_device, NULL
2218c2ecf20Sopenharmony_ci};
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci/*****************************************************************************
2248c2ecf20Sopenharmony_ci * Cypress serial helper functions
2258c2ecf20Sopenharmony_ci *****************************************************************************/
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci/* FRWD Dongle hidcom needs to skip reset and speed checks */
2288c2ecf20Sopenharmony_cistatic inline bool is_frwd(struct usb_device *dev)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	return ((le16_to_cpu(dev->descriptor.idVendor) == VENDOR_ID_FRWD) &&
2318c2ecf20Sopenharmony_ci		(le16_to_cpu(dev->descriptor.idProduct) == PRODUCT_ID_CYPHIDCOM_FRWD));
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic int analyze_baud_rate(struct usb_serial_port *port, speed_t new_rate)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	struct cypress_private *priv;
2378c2ecf20Sopenharmony_ci	priv = usb_get_serial_port_data(port);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	if (unstable_bauds)
2408c2ecf20Sopenharmony_ci		return new_rate;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	/* FRWD Dongle uses 115200 bps */
2438c2ecf20Sopenharmony_ci	if (is_frwd(port->serial->dev))
2448c2ecf20Sopenharmony_ci		return new_rate;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	/*
2478c2ecf20Sopenharmony_ci	 * The general purpose firmware for the Cypress M8 allows for
2488c2ecf20Sopenharmony_ci	 * a maximum speed of 57600bps (I have no idea whether DeLorme
2498c2ecf20Sopenharmony_ci	 * chose to use the general purpose firmware or not), if you
2508c2ecf20Sopenharmony_ci	 * need to modify this speed setting for your own project
2518c2ecf20Sopenharmony_ci	 * please add your own chiptype and modify the code likewise.
2528c2ecf20Sopenharmony_ci	 * The Cypress HID->COM device will work successfully up to
2538c2ecf20Sopenharmony_ci	 * 115200bps (but the actual throughput is around 3kBps).
2548c2ecf20Sopenharmony_ci	 */
2558c2ecf20Sopenharmony_ci	if (port->serial->dev->speed == USB_SPEED_LOW) {
2568c2ecf20Sopenharmony_ci		/*
2578c2ecf20Sopenharmony_ci		 * Mike Isely <isely@pobox.com> 2-Feb-2008: The
2588c2ecf20Sopenharmony_ci		 * Cypress app note that describes this mechanism
2598c2ecf20Sopenharmony_ci		 * states the the low-speed part can't handle more
2608c2ecf20Sopenharmony_ci		 * than 800 bytes/sec, in which case 4800 baud is the
2618c2ecf20Sopenharmony_ci		 * safest speed for a part like that.
2628c2ecf20Sopenharmony_ci		 */
2638c2ecf20Sopenharmony_ci		if (new_rate > 4800) {
2648c2ecf20Sopenharmony_ci			dev_dbg(&port->dev,
2658c2ecf20Sopenharmony_ci				"%s - failed setting baud rate, device incapable speed %d\n",
2668c2ecf20Sopenharmony_ci				__func__, new_rate);
2678c2ecf20Sopenharmony_ci			return -1;
2688c2ecf20Sopenharmony_ci		}
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci	switch (priv->chiptype) {
2718c2ecf20Sopenharmony_ci	case CT_EARTHMATE:
2728c2ecf20Sopenharmony_ci		if (new_rate <= 600) {
2738c2ecf20Sopenharmony_ci			/* 300 and 600 baud rates are supported under
2748c2ecf20Sopenharmony_ci			 * the generic firmware, but are not used with
2758c2ecf20Sopenharmony_ci			 * NMEA and SiRF protocols */
2768c2ecf20Sopenharmony_ci			dev_dbg(&port->dev,
2778c2ecf20Sopenharmony_ci				"%s - failed setting baud rate, unsupported speed of %d on Earthmate GPS\n",
2788c2ecf20Sopenharmony_ci				__func__, new_rate);
2798c2ecf20Sopenharmony_ci			return -1;
2808c2ecf20Sopenharmony_ci		}
2818c2ecf20Sopenharmony_ci		break;
2828c2ecf20Sopenharmony_ci	default:
2838c2ecf20Sopenharmony_ci		break;
2848c2ecf20Sopenharmony_ci	}
2858c2ecf20Sopenharmony_ci	return new_rate;
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci/* This function can either set or retrieve the current serial line settings */
2908c2ecf20Sopenharmony_cistatic int cypress_serial_control(struct tty_struct *tty,
2918c2ecf20Sopenharmony_ci	struct usb_serial_port *port, speed_t baud_rate, int data_bits,
2928c2ecf20Sopenharmony_ci	int stop_bits, int parity_enable, int parity_type, int reset,
2938c2ecf20Sopenharmony_ci	int cypress_request_type)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	int new_baudrate = 0, retval = 0, tries = 0;
2968c2ecf20Sopenharmony_ci	struct cypress_private *priv;
2978c2ecf20Sopenharmony_ci	struct device *dev = &port->dev;
2988c2ecf20Sopenharmony_ci	u8 *feature_buffer;
2998c2ecf20Sopenharmony_ci	const unsigned int feature_len = 5;
3008c2ecf20Sopenharmony_ci	unsigned long flags;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	priv = usb_get_serial_port_data(port);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	if (!priv->comm_is_ok)
3058c2ecf20Sopenharmony_ci		return -ENODEV;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	feature_buffer = kcalloc(feature_len, sizeof(u8), GFP_KERNEL);
3088c2ecf20Sopenharmony_ci	if (!feature_buffer)
3098c2ecf20Sopenharmony_ci		return -ENOMEM;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	switch (cypress_request_type) {
3128c2ecf20Sopenharmony_ci	case CYPRESS_SET_CONFIG:
3138c2ecf20Sopenharmony_ci		/* 0 means 'Hang up' so doesn't change the true bit rate */
3148c2ecf20Sopenharmony_ci		new_baudrate = priv->baud_rate;
3158c2ecf20Sopenharmony_ci		if (baud_rate && baud_rate != priv->baud_rate) {
3168c2ecf20Sopenharmony_ci			dev_dbg(dev, "%s - baud rate is changing\n", __func__);
3178c2ecf20Sopenharmony_ci			retval = analyze_baud_rate(port, baud_rate);
3188c2ecf20Sopenharmony_ci			if (retval >= 0) {
3198c2ecf20Sopenharmony_ci				new_baudrate = retval;
3208c2ecf20Sopenharmony_ci				dev_dbg(dev, "%s - New baud rate set to %d\n",
3218c2ecf20Sopenharmony_ci					__func__, new_baudrate);
3228c2ecf20Sopenharmony_ci			}
3238c2ecf20Sopenharmony_ci		}
3248c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - baud rate is being sent as %d\n", __func__,
3258c2ecf20Sopenharmony_ci			new_baudrate);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci		/* fill the feature_buffer with new configuration */
3288c2ecf20Sopenharmony_ci		put_unaligned_le32(new_baudrate, feature_buffer);
3298c2ecf20Sopenharmony_ci		feature_buffer[4] |= data_bits;   /* assign data bits in 2 bit space ( max 3 ) */
3308c2ecf20Sopenharmony_ci		/* 1 bit gap */
3318c2ecf20Sopenharmony_ci		feature_buffer[4] |= (stop_bits << 3);   /* assign stop bits in 1 bit space */
3328c2ecf20Sopenharmony_ci		feature_buffer[4] |= (parity_enable << 4);   /* assign parity flag in 1 bit space */
3338c2ecf20Sopenharmony_ci		feature_buffer[4] |= (parity_type << 5);   /* assign parity type in 1 bit space */
3348c2ecf20Sopenharmony_ci		/* 1 bit gap */
3358c2ecf20Sopenharmony_ci		feature_buffer[4] |= (reset << 7);   /* assign reset at end of byte, 1 bit space */
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - device is being sent this feature report:\n", __func__);
3388c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - %02X - %02X - %02X - %02X - %02X\n", __func__,
3398c2ecf20Sopenharmony_ci			feature_buffer[0], feature_buffer[1],
3408c2ecf20Sopenharmony_ci			feature_buffer[2], feature_buffer[3],
3418c2ecf20Sopenharmony_ci			feature_buffer[4]);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci		do {
3448c2ecf20Sopenharmony_ci			retval = usb_control_msg(port->serial->dev,
3458c2ecf20Sopenharmony_ci					usb_sndctrlpipe(port->serial->dev, 0),
3468c2ecf20Sopenharmony_ci					HID_REQ_SET_REPORT,
3478c2ecf20Sopenharmony_ci					USB_DIR_OUT | USB_RECIP_INTERFACE | USB_TYPE_CLASS,
3488c2ecf20Sopenharmony_ci					0x0300, 0, feature_buffer,
3498c2ecf20Sopenharmony_ci					feature_len, 500);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci			if (tries++ >= 3)
3528c2ecf20Sopenharmony_ci				break;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci		} while (retval != feature_len &&
3558c2ecf20Sopenharmony_ci			 retval != -ENODEV);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci		if (retval != feature_len) {
3588c2ecf20Sopenharmony_ci			dev_err(dev, "%s - failed sending serial line settings - %d\n",
3598c2ecf20Sopenharmony_ci				__func__, retval);
3608c2ecf20Sopenharmony_ci			cypress_set_dead(port);
3618c2ecf20Sopenharmony_ci		} else {
3628c2ecf20Sopenharmony_ci			spin_lock_irqsave(&priv->lock, flags);
3638c2ecf20Sopenharmony_ci			priv->baud_rate = new_baudrate;
3648c2ecf20Sopenharmony_ci			priv->current_config = feature_buffer[4];
3658c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&priv->lock, flags);
3668c2ecf20Sopenharmony_ci			/* If we asked for a speed change encode it */
3678c2ecf20Sopenharmony_ci			if (baud_rate)
3688c2ecf20Sopenharmony_ci				tty_encode_baud_rate(tty,
3698c2ecf20Sopenharmony_ci					new_baudrate, new_baudrate);
3708c2ecf20Sopenharmony_ci		}
3718c2ecf20Sopenharmony_ci	break;
3728c2ecf20Sopenharmony_ci	case CYPRESS_GET_CONFIG:
3738c2ecf20Sopenharmony_ci		if (priv->get_cfg_unsafe) {
3748c2ecf20Sopenharmony_ci			/* Not implemented for this device,
3758c2ecf20Sopenharmony_ci			   and if we try to do it we're likely
3768c2ecf20Sopenharmony_ci			   to crash the hardware. */
3778c2ecf20Sopenharmony_ci			retval = -ENOTTY;
3788c2ecf20Sopenharmony_ci			goto out;
3798c2ecf20Sopenharmony_ci		}
3808c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - retrieving serial line settings\n", __func__);
3818c2ecf20Sopenharmony_ci		do {
3828c2ecf20Sopenharmony_ci			retval = usb_control_msg(port->serial->dev,
3838c2ecf20Sopenharmony_ci					usb_rcvctrlpipe(port->serial->dev, 0),
3848c2ecf20Sopenharmony_ci					HID_REQ_GET_REPORT,
3858c2ecf20Sopenharmony_ci					USB_DIR_IN | USB_RECIP_INTERFACE | USB_TYPE_CLASS,
3868c2ecf20Sopenharmony_ci					0x0300, 0, feature_buffer,
3878c2ecf20Sopenharmony_ci					feature_len, 500);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci			if (tries++ >= 3)
3908c2ecf20Sopenharmony_ci				break;
3918c2ecf20Sopenharmony_ci		} while (retval != feature_len
3928c2ecf20Sopenharmony_ci						&& retval != -ENODEV);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci		if (retval != feature_len) {
3958c2ecf20Sopenharmony_ci			dev_err(dev, "%s - failed to retrieve serial line settings - %d\n",
3968c2ecf20Sopenharmony_ci				__func__, retval);
3978c2ecf20Sopenharmony_ci			cypress_set_dead(port);
3988c2ecf20Sopenharmony_ci			goto out;
3998c2ecf20Sopenharmony_ci		} else {
4008c2ecf20Sopenharmony_ci			spin_lock_irqsave(&priv->lock, flags);
4018c2ecf20Sopenharmony_ci			/* store the config in one byte, and later
4028c2ecf20Sopenharmony_ci			   use bit masks to check values */
4038c2ecf20Sopenharmony_ci			priv->current_config = feature_buffer[4];
4048c2ecf20Sopenharmony_ci			priv->baud_rate = get_unaligned_le32(feature_buffer);
4058c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&priv->lock, flags);
4068c2ecf20Sopenharmony_ci		}
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
4098c2ecf20Sopenharmony_ci	++priv->cmd_count;
4108c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
4118c2ecf20Sopenharmony_ciout:
4128c2ecf20Sopenharmony_ci	kfree(feature_buffer);
4138c2ecf20Sopenharmony_ci	return retval;
4148c2ecf20Sopenharmony_ci} /* cypress_serial_control */
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic void cypress_set_dead(struct usb_serial_port *port)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	struct cypress_private *priv = usb_get_serial_port_data(port);
4208c2ecf20Sopenharmony_ci	unsigned long flags;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
4238c2ecf20Sopenharmony_ci	if (!priv->comm_is_ok) {
4248c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&priv->lock, flags);
4258c2ecf20Sopenharmony_ci		return;
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci	priv->comm_is_ok = 0;
4288c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	dev_err(&port->dev, "cypress_m8 suspending failing port %d - "
4318c2ecf20Sopenharmony_ci		"interval might be too short\n", port->port_number);
4328c2ecf20Sopenharmony_ci}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci/*****************************************************************************
4368c2ecf20Sopenharmony_ci * Cypress serial driver functions
4378c2ecf20Sopenharmony_ci *****************************************************************************/
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_cistatic int cypress_generic_port_probe(struct usb_serial_port *port)
4418c2ecf20Sopenharmony_ci{
4428c2ecf20Sopenharmony_ci	struct usb_serial *serial = port->serial;
4438c2ecf20Sopenharmony_ci	struct cypress_private *priv;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	if (!port->interrupt_out_urb || !port->interrupt_in_urb) {
4468c2ecf20Sopenharmony_ci		dev_err(&port->dev, "required endpoint is missing\n");
4478c2ecf20Sopenharmony_ci		return -ENODEV;
4488c2ecf20Sopenharmony_ci	}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	priv = kzalloc(sizeof(struct cypress_private), GFP_KERNEL);
4518c2ecf20Sopenharmony_ci	if (!priv)
4528c2ecf20Sopenharmony_ci		return -ENOMEM;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	priv->comm_is_ok = !0;
4558c2ecf20Sopenharmony_ci	spin_lock_init(&priv->lock);
4568c2ecf20Sopenharmony_ci	if (kfifo_alloc(&priv->write_fifo, CYPRESS_BUF_SIZE, GFP_KERNEL)) {
4578c2ecf20Sopenharmony_ci		kfree(priv);
4588c2ecf20Sopenharmony_ci		return -ENOMEM;
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	/* Skip reset for FRWD device. It is a workaound:
4628c2ecf20Sopenharmony_ci	   device hangs if it receives SET_CONFIGURE in Configured
4638c2ecf20Sopenharmony_ci	   state. */
4648c2ecf20Sopenharmony_ci	if (!is_frwd(serial->dev))
4658c2ecf20Sopenharmony_ci		usb_reset_configuration(serial->dev);
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	priv->cmd_ctrl = 0;
4688c2ecf20Sopenharmony_ci	priv->line_control = 0;
4698c2ecf20Sopenharmony_ci	priv->rx_flags = 0;
4708c2ecf20Sopenharmony_ci	/* Default packet format setting is determined by packet size.
4718c2ecf20Sopenharmony_ci	   Anything with a size larger then 9 must have a separate
4728c2ecf20Sopenharmony_ci	   count field since the 3 bit count field is otherwise too
4738c2ecf20Sopenharmony_ci	   small.  Otherwise we can use the slightly more compact
4748c2ecf20Sopenharmony_ci	   format.  This is in accordance with the cypress_m8 serial
4758c2ecf20Sopenharmony_ci	   converter app note. */
4768c2ecf20Sopenharmony_ci	if (port->interrupt_out_size > 9)
4778c2ecf20Sopenharmony_ci		priv->pkt_fmt = packet_format_1;
4788c2ecf20Sopenharmony_ci	else
4798c2ecf20Sopenharmony_ci		priv->pkt_fmt = packet_format_2;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	if (interval > 0) {
4828c2ecf20Sopenharmony_ci		priv->write_urb_interval = interval;
4838c2ecf20Sopenharmony_ci		priv->read_urb_interval = interval;
4848c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - read & write intervals forced to %d\n",
4858c2ecf20Sopenharmony_ci			__func__, interval);
4868c2ecf20Sopenharmony_ci	} else {
4878c2ecf20Sopenharmony_ci		priv->write_urb_interval = port->interrupt_out_urb->interval;
4888c2ecf20Sopenharmony_ci		priv->read_urb_interval = port->interrupt_in_urb->interval;
4898c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - intervals: read=%d write=%d\n",
4908c2ecf20Sopenharmony_ci			__func__, priv->read_urb_interval,
4918c2ecf20Sopenharmony_ci			priv->write_urb_interval);
4928c2ecf20Sopenharmony_ci	}
4938c2ecf20Sopenharmony_ci	usb_set_serial_port_data(port, priv);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	port->port.drain_delay = 256;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	return 0;
4988c2ecf20Sopenharmony_ci}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_cistatic int cypress_earthmate_port_probe(struct usb_serial_port *port)
5028c2ecf20Sopenharmony_ci{
5038c2ecf20Sopenharmony_ci	struct usb_serial *serial = port->serial;
5048c2ecf20Sopenharmony_ci	struct cypress_private *priv;
5058c2ecf20Sopenharmony_ci	int ret;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	ret = cypress_generic_port_probe(port);
5088c2ecf20Sopenharmony_ci	if (ret) {
5098c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - Failed setting up port\n", __func__);
5108c2ecf20Sopenharmony_ci		return ret;
5118c2ecf20Sopenharmony_ci	}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	priv = usb_get_serial_port_data(port);
5148c2ecf20Sopenharmony_ci	priv->chiptype = CT_EARTHMATE;
5158c2ecf20Sopenharmony_ci	/* All Earthmate devices use the separated-count packet
5168c2ecf20Sopenharmony_ci	   format!  Idiotic. */
5178c2ecf20Sopenharmony_ci	priv->pkt_fmt = packet_format_1;
5188c2ecf20Sopenharmony_ci	if (serial->dev->descriptor.idProduct !=
5198c2ecf20Sopenharmony_ci				cpu_to_le16(PRODUCT_ID_EARTHMATEUSB)) {
5208c2ecf20Sopenharmony_ci		/* The old original USB Earthmate seemed able to
5218c2ecf20Sopenharmony_ci		   handle GET_CONFIG requests; everything they've
5228c2ecf20Sopenharmony_ci		   produced since that time crashes if this command is
5238c2ecf20Sopenharmony_ci		   attempted :-( */
5248c2ecf20Sopenharmony_ci		dev_dbg(&port->dev,
5258c2ecf20Sopenharmony_ci			"%s - Marking this device as unsafe for GET_CONFIG commands\n",
5268c2ecf20Sopenharmony_ci			__func__);
5278c2ecf20Sopenharmony_ci		priv->get_cfg_unsafe = !0;
5288c2ecf20Sopenharmony_ci	}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	return 0;
5318c2ecf20Sopenharmony_ci}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_cistatic int cypress_hidcom_port_probe(struct usb_serial_port *port)
5348c2ecf20Sopenharmony_ci{
5358c2ecf20Sopenharmony_ci	struct cypress_private *priv;
5368c2ecf20Sopenharmony_ci	int ret;
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	ret = cypress_generic_port_probe(port);
5398c2ecf20Sopenharmony_ci	if (ret) {
5408c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - Failed setting up port\n", __func__);
5418c2ecf20Sopenharmony_ci		return ret;
5428c2ecf20Sopenharmony_ci	}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	priv = usb_get_serial_port_data(port);
5458c2ecf20Sopenharmony_ci	priv->chiptype = CT_CYPHIDCOM;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	return 0;
5488c2ecf20Sopenharmony_ci}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic int cypress_ca42v2_port_probe(struct usb_serial_port *port)
5518c2ecf20Sopenharmony_ci{
5528c2ecf20Sopenharmony_ci	struct cypress_private *priv;
5538c2ecf20Sopenharmony_ci	int ret;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	ret = cypress_generic_port_probe(port);
5568c2ecf20Sopenharmony_ci	if (ret) {
5578c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - Failed setting up port\n", __func__);
5588c2ecf20Sopenharmony_ci		return ret;
5598c2ecf20Sopenharmony_ci	}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	priv = usb_get_serial_port_data(port);
5628c2ecf20Sopenharmony_ci	priv->chiptype = CT_CA42V2;
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	return 0;
5658c2ecf20Sopenharmony_ci}
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_cistatic int cypress_port_remove(struct usb_serial_port *port)
5688c2ecf20Sopenharmony_ci{
5698c2ecf20Sopenharmony_ci	struct cypress_private *priv;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	priv = usb_get_serial_port_data(port);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	kfifo_free(&priv->write_fifo);
5748c2ecf20Sopenharmony_ci	kfree(priv);
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	return 0;
5778c2ecf20Sopenharmony_ci}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_cistatic int cypress_open(struct tty_struct *tty, struct usb_serial_port *port)
5808c2ecf20Sopenharmony_ci{
5818c2ecf20Sopenharmony_ci	struct cypress_private *priv = usb_get_serial_port_data(port);
5828c2ecf20Sopenharmony_ci	struct usb_serial *serial = port->serial;
5838c2ecf20Sopenharmony_ci	unsigned long flags;
5848c2ecf20Sopenharmony_ci	int result = 0;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	if (!priv->comm_is_ok)
5878c2ecf20Sopenharmony_ci		return -EIO;
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	/* clear halts before open */
5908c2ecf20Sopenharmony_ci	usb_clear_halt(serial->dev, 0x81);
5918c2ecf20Sopenharmony_ci	usb_clear_halt(serial->dev, 0x02);
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
5948c2ecf20Sopenharmony_ci	/* reset read/write statistics */
5958c2ecf20Sopenharmony_ci	priv->bytes_in = 0;
5968c2ecf20Sopenharmony_ci	priv->bytes_out = 0;
5978c2ecf20Sopenharmony_ci	priv->cmd_count = 0;
5988c2ecf20Sopenharmony_ci	priv->rx_flags = 0;
5998c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	/* Set termios */
6028c2ecf20Sopenharmony_ci	cypress_send(port);
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	if (tty)
6058c2ecf20Sopenharmony_ci		cypress_set_termios(tty, port, NULL);
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	/* setup the port and start reading from the device */
6088c2ecf20Sopenharmony_ci	usb_fill_int_urb(port->interrupt_in_urb, serial->dev,
6098c2ecf20Sopenharmony_ci		usb_rcvintpipe(serial->dev, port->interrupt_in_endpointAddress),
6108c2ecf20Sopenharmony_ci		port->interrupt_in_urb->transfer_buffer,
6118c2ecf20Sopenharmony_ci		port->interrupt_in_urb->transfer_buffer_length,
6128c2ecf20Sopenharmony_ci		cypress_read_int_callback, port, priv->read_urb_interval);
6138c2ecf20Sopenharmony_ci	result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	if (result) {
6168c2ecf20Sopenharmony_ci		dev_err(&port->dev,
6178c2ecf20Sopenharmony_ci			"%s - failed submitting read urb, error %d\n",
6188c2ecf20Sopenharmony_ci							__func__, result);
6198c2ecf20Sopenharmony_ci		cypress_set_dead(port);
6208c2ecf20Sopenharmony_ci	}
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	return result;
6238c2ecf20Sopenharmony_ci} /* cypress_open */
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_cistatic void cypress_dtr_rts(struct usb_serial_port *port, int on)
6268c2ecf20Sopenharmony_ci{
6278c2ecf20Sopenharmony_ci	struct cypress_private *priv = usb_get_serial_port_data(port);
6288c2ecf20Sopenharmony_ci	/* drop dtr and rts */
6298c2ecf20Sopenharmony_ci	spin_lock_irq(&priv->lock);
6308c2ecf20Sopenharmony_ci	if (on == 0)
6318c2ecf20Sopenharmony_ci		priv->line_control = 0;
6328c2ecf20Sopenharmony_ci	else
6338c2ecf20Sopenharmony_ci		priv->line_control = CONTROL_DTR | CONTROL_RTS;
6348c2ecf20Sopenharmony_ci	priv->cmd_ctrl = 1;
6358c2ecf20Sopenharmony_ci	spin_unlock_irq(&priv->lock);
6368c2ecf20Sopenharmony_ci	cypress_write(NULL, port, NULL, 0);
6378c2ecf20Sopenharmony_ci}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_cistatic void cypress_close(struct usb_serial_port *port)
6408c2ecf20Sopenharmony_ci{
6418c2ecf20Sopenharmony_ci	struct cypress_private *priv = usb_get_serial_port_data(port);
6428c2ecf20Sopenharmony_ci	unsigned long flags;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
6458c2ecf20Sopenharmony_ci	kfifo_reset_out(&priv->write_fifo);
6468c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "%s - stopping urbs\n", __func__);
6498c2ecf20Sopenharmony_ci	usb_kill_urb(port->interrupt_in_urb);
6508c2ecf20Sopenharmony_ci	usb_kill_urb(port->interrupt_out_urb);
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	if (stats)
6538c2ecf20Sopenharmony_ci		dev_info(&port->dev, "Statistics: %d Bytes In | %d Bytes Out | %d Commands Issued\n",
6548c2ecf20Sopenharmony_ci			priv->bytes_in, priv->bytes_out, priv->cmd_count);
6558c2ecf20Sopenharmony_ci} /* cypress_close */
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_cistatic int cypress_write(struct tty_struct *tty, struct usb_serial_port *port,
6598c2ecf20Sopenharmony_ci					const unsigned char *buf, int count)
6608c2ecf20Sopenharmony_ci{
6618c2ecf20Sopenharmony_ci	struct cypress_private *priv = usb_get_serial_port_data(port);
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "%s - %d bytes\n", __func__, count);
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	/* line control commands, which need to be executed immediately,
6668c2ecf20Sopenharmony_ci	   are not put into the buffer for obvious reasons.
6678c2ecf20Sopenharmony_ci	 */
6688c2ecf20Sopenharmony_ci	if (priv->cmd_ctrl) {
6698c2ecf20Sopenharmony_ci		count = 0;
6708c2ecf20Sopenharmony_ci		goto finish;
6718c2ecf20Sopenharmony_ci	}
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	if (!count)
6748c2ecf20Sopenharmony_ci		return count;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	count = kfifo_in_locked(&priv->write_fifo, buf, count, &priv->lock);
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_cifinish:
6798c2ecf20Sopenharmony_ci	cypress_send(port);
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	return count;
6828c2ecf20Sopenharmony_ci} /* cypress_write */
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_cistatic void cypress_send(struct usb_serial_port *port)
6868c2ecf20Sopenharmony_ci{
6878c2ecf20Sopenharmony_ci	int count = 0, result, offset, actual_size;
6888c2ecf20Sopenharmony_ci	struct cypress_private *priv = usb_get_serial_port_data(port);
6898c2ecf20Sopenharmony_ci	struct device *dev = &port->dev;
6908c2ecf20Sopenharmony_ci	unsigned long flags;
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	if (!priv->comm_is_ok)
6938c2ecf20Sopenharmony_ci		return;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s - interrupt out size is %d\n", __func__,
6968c2ecf20Sopenharmony_ci		port->interrupt_out_size);
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
6998c2ecf20Sopenharmony_ci	if (priv->write_urb_in_use) {
7008c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - can't write, urb in use\n", __func__);
7018c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&priv->lock, flags);
7028c2ecf20Sopenharmony_ci		return;
7038c2ecf20Sopenharmony_ci	}
7048c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	/* clear buffer */
7078c2ecf20Sopenharmony_ci	memset(port->interrupt_out_urb->transfer_buffer, 0,
7088c2ecf20Sopenharmony_ci						port->interrupt_out_size);
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
7118c2ecf20Sopenharmony_ci	switch (priv->pkt_fmt) {
7128c2ecf20Sopenharmony_ci	default:
7138c2ecf20Sopenharmony_ci	case packet_format_1:
7148c2ecf20Sopenharmony_ci		/* this is for the CY7C64013... */
7158c2ecf20Sopenharmony_ci		offset = 2;
7168c2ecf20Sopenharmony_ci		port->interrupt_out_buffer[0] = priv->line_control;
7178c2ecf20Sopenharmony_ci		break;
7188c2ecf20Sopenharmony_ci	case packet_format_2:
7198c2ecf20Sopenharmony_ci		/* this is for the CY7C63743... */
7208c2ecf20Sopenharmony_ci		offset = 1;
7218c2ecf20Sopenharmony_ci		port->interrupt_out_buffer[0] = priv->line_control;
7228c2ecf20Sopenharmony_ci		break;
7238c2ecf20Sopenharmony_ci	}
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	if (priv->line_control & CONTROL_RESET)
7268c2ecf20Sopenharmony_ci		priv->line_control &= ~CONTROL_RESET;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	if (priv->cmd_ctrl) {
7298c2ecf20Sopenharmony_ci		priv->cmd_count++;
7308c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - line control command being issued\n", __func__);
7318c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&priv->lock, flags);
7328c2ecf20Sopenharmony_ci		goto send;
7338c2ecf20Sopenharmony_ci	} else
7348c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&priv->lock, flags);
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	count = kfifo_out_locked(&priv->write_fifo,
7378c2ecf20Sopenharmony_ci					&port->interrupt_out_buffer[offset],
7388c2ecf20Sopenharmony_ci					port->interrupt_out_size - offset,
7398c2ecf20Sopenharmony_ci					&priv->lock);
7408c2ecf20Sopenharmony_ci	if (count == 0)
7418c2ecf20Sopenharmony_ci		return;
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	switch (priv->pkt_fmt) {
7448c2ecf20Sopenharmony_ci	default:
7458c2ecf20Sopenharmony_ci	case packet_format_1:
7468c2ecf20Sopenharmony_ci		port->interrupt_out_buffer[1] = count;
7478c2ecf20Sopenharmony_ci		break;
7488c2ecf20Sopenharmony_ci	case packet_format_2:
7498c2ecf20Sopenharmony_ci		port->interrupt_out_buffer[0] |= count;
7508c2ecf20Sopenharmony_ci	}
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s - count is %d\n", __func__, count);
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_cisend:
7558c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
7568c2ecf20Sopenharmony_ci	priv->write_urb_in_use = 1;
7578c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	if (priv->cmd_ctrl)
7608c2ecf20Sopenharmony_ci		actual_size = 1;
7618c2ecf20Sopenharmony_ci	else
7628c2ecf20Sopenharmony_ci		actual_size = count +
7638c2ecf20Sopenharmony_ci			      (priv->pkt_fmt == packet_format_1 ? 2 : 1);
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	usb_serial_debug_data(dev, __func__, port->interrupt_out_size,
7668c2ecf20Sopenharmony_ci			      port->interrupt_out_urb->transfer_buffer);
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	usb_fill_int_urb(port->interrupt_out_urb, port->serial->dev,
7698c2ecf20Sopenharmony_ci		usb_sndintpipe(port->serial->dev, port->interrupt_out_endpointAddress),
7708c2ecf20Sopenharmony_ci		port->interrupt_out_buffer, actual_size,
7718c2ecf20Sopenharmony_ci		cypress_write_int_callback, port, priv->write_urb_interval);
7728c2ecf20Sopenharmony_ci	result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC);
7738c2ecf20Sopenharmony_ci	if (result) {
7748c2ecf20Sopenharmony_ci		dev_err_console(port,
7758c2ecf20Sopenharmony_ci				"%s - failed submitting write urb, error %d\n",
7768c2ecf20Sopenharmony_ci							__func__, result);
7778c2ecf20Sopenharmony_ci		priv->write_urb_in_use = 0;
7788c2ecf20Sopenharmony_ci		cypress_set_dead(port);
7798c2ecf20Sopenharmony_ci	}
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
7828c2ecf20Sopenharmony_ci	if (priv->cmd_ctrl)
7838c2ecf20Sopenharmony_ci		priv->cmd_ctrl = 0;
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	/* do not count the line control and size bytes */
7868c2ecf20Sopenharmony_ci	priv->bytes_out += count;
7878c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	usb_serial_port_softint(port);
7908c2ecf20Sopenharmony_ci} /* cypress_send */
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci/* returns how much space is available in the soft buffer */
7948c2ecf20Sopenharmony_cistatic int cypress_write_room(struct tty_struct *tty)
7958c2ecf20Sopenharmony_ci{
7968c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
7978c2ecf20Sopenharmony_ci	struct cypress_private *priv = usb_get_serial_port_data(port);
7988c2ecf20Sopenharmony_ci	int room = 0;
7998c2ecf20Sopenharmony_ci	unsigned long flags;
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
8028c2ecf20Sopenharmony_ci	room = kfifo_avail(&priv->write_fifo);
8038c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "%s - returns %d\n", __func__, room);
8068c2ecf20Sopenharmony_ci	return room;
8078c2ecf20Sopenharmony_ci}
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_cistatic int cypress_tiocmget(struct tty_struct *tty)
8118c2ecf20Sopenharmony_ci{
8128c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
8138c2ecf20Sopenharmony_ci	struct cypress_private *priv = usb_get_serial_port_data(port);
8148c2ecf20Sopenharmony_ci	__u8 status, control;
8158c2ecf20Sopenharmony_ci	unsigned int result = 0;
8168c2ecf20Sopenharmony_ci	unsigned long flags;
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
8198c2ecf20Sopenharmony_ci	control = priv->line_control;
8208c2ecf20Sopenharmony_ci	status = priv->current_status;
8218c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	result = ((control & CONTROL_DTR)        ? TIOCM_DTR : 0)
8248c2ecf20Sopenharmony_ci		| ((control & CONTROL_RTS)       ? TIOCM_RTS : 0)
8258c2ecf20Sopenharmony_ci		| ((status & UART_CTS)        ? TIOCM_CTS : 0)
8268c2ecf20Sopenharmony_ci		| ((status & UART_DSR)        ? TIOCM_DSR : 0)
8278c2ecf20Sopenharmony_ci		| ((status & UART_RI)         ? TIOCM_RI  : 0)
8288c2ecf20Sopenharmony_ci		| ((status & UART_CD)         ? TIOCM_CD  : 0);
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "%s - result = %x\n", __func__, result);
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	return result;
8338c2ecf20Sopenharmony_ci}
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_cistatic int cypress_tiocmset(struct tty_struct *tty,
8378c2ecf20Sopenharmony_ci			       unsigned int set, unsigned int clear)
8388c2ecf20Sopenharmony_ci{
8398c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
8408c2ecf20Sopenharmony_ci	struct cypress_private *priv = usb_get_serial_port_data(port);
8418c2ecf20Sopenharmony_ci	unsigned long flags;
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
8448c2ecf20Sopenharmony_ci	if (set & TIOCM_RTS)
8458c2ecf20Sopenharmony_ci		priv->line_control |= CONTROL_RTS;
8468c2ecf20Sopenharmony_ci	if (set & TIOCM_DTR)
8478c2ecf20Sopenharmony_ci		priv->line_control |= CONTROL_DTR;
8488c2ecf20Sopenharmony_ci	if (clear & TIOCM_RTS)
8498c2ecf20Sopenharmony_ci		priv->line_control &= ~CONTROL_RTS;
8508c2ecf20Sopenharmony_ci	if (clear & TIOCM_DTR)
8518c2ecf20Sopenharmony_ci		priv->line_control &= ~CONTROL_DTR;
8528c2ecf20Sopenharmony_ci	priv->cmd_ctrl = 1;
8538c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	return cypress_write(tty, port, NULL, 0);
8568c2ecf20Sopenharmony_ci}
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_cistatic void cypress_earthmate_init_termios(struct tty_struct *tty)
8598c2ecf20Sopenharmony_ci{
8608c2ecf20Sopenharmony_ci	tty_encode_baud_rate(tty, 4800, 4800);
8618c2ecf20Sopenharmony_ci}
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_cistatic void cypress_set_termios(struct tty_struct *tty,
8648c2ecf20Sopenharmony_ci	struct usb_serial_port *port, struct ktermios *old_termios)
8658c2ecf20Sopenharmony_ci{
8668c2ecf20Sopenharmony_ci	struct cypress_private *priv = usb_get_serial_port_data(port);
8678c2ecf20Sopenharmony_ci	struct device *dev = &port->dev;
8688c2ecf20Sopenharmony_ci	int data_bits, stop_bits, parity_type, parity_enable;
8698c2ecf20Sopenharmony_ci	unsigned int cflag;
8708c2ecf20Sopenharmony_ci	unsigned long flags;
8718c2ecf20Sopenharmony_ci	__u8 oldlines;
8728c2ecf20Sopenharmony_ci	int linechange = 0;
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	/* Unsupported features need clearing */
8758c2ecf20Sopenharmony_ci	tty->termios.c_cflag &= ~(CMSPAR|CRTSCTS);
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	cflag = tty->termios.c_cflag;
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	/* set number of data bits, parity, stop bits */
8808c2ecf20Sopenharmony_ci	/* when parity is disabled the parity type bit is ignored */
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	/* 1 means 2 stop bits, 0 means 1 stop bit */
8838c2ecf20Sopenharmony_ci	stop_bits = cflag & CSTOPB ? 1 : 0;
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	if (cflag & PARENB) {
8868c2ecf20Sopenharmony_ci		parity_enable = 1;
8878c2ecf20Sopenharmony_ci		/* 1 means odd parity, 0 means even parity */
8888c2ecf20Sopenharmony_ci		parity_type = cflag & PARODD ? 1 : 0;
8898c2ecf20Sopenharmony_ci	} else
8908c2ecf20Sopenharmony_ci		parity_enable = parity_type = 0;
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	switch (cflag & CSIZE) {
8938c2ecf20Sopenharmony_ci	case CS5:
8948c2ecf20Sopenharmony_ci		data_bits = 0;
8958c2ecf20Sopenharmony_ci		break;
8968c2ecf20Sopenharmony_ci	case CS6:
8978c2ecf20Sopenharmony_ci		data_bits = 1;
8988c2ecf20Sopenharmony_ci		break;
8998c2ecf20Sopenharmony_ci	case CS7:
9008c2ecf20Sopenharmony_ci		data_bits = 2;
9018c2ecf20Sopenharmony_ci		break;
9028c2ecf20Sopenharmony_ci	case CS8:
9038c2ecf20Sopenharmony_ci		data_bits = 3;
9048c2ecf20Sopenharmony_ci		break;
9058c2ecf20Sopenharmony_ci	default:
9068c2ecf20Sopenharmony_ci		dev_err(dev, "%s - CSIZE was set, but not CS5-CS8\n", __func__);
9078c2ecf20Sopenharmony_ci		data_bits = 3;
9088c2ecf20Sopenharmony_ci	}
9098c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
9108c2ecf20Sopenharmony_ci	oldlines = priv->line_control;
9118c2ecf20Sopenharmony_ci	if ((cflag & CBAUD) == B0) {
9128c2ecf20Sopenharmony_ci		/* drop dtr and rts */
9138c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - dropping the lines, baud rate 0bps\n", __func__);
9148c2ecf20Sopenharmony_ci		priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
9158c2ecf20Sopenharmony_ci	} else
9168c2ecf20Sopenharmony_ci		priv->line_control = (CONTROL_DTR | CONTROL_RTS);
9178c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s - sending %d stop_bits, %d parity_enable, %d parity_type, %d data_bits (+5)\n",
9208c2ecf20Sopenharmony_ci		__func__, stop_bits, parity_enable, parity_type, data_bits);
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	cypress_serial_control(tty, port, tty_get_baud_rate(tty),
9238c2ecf20Sopenharmony_ci			data_bits, stop_bits,
9248c2ecf20Sopenharmony_ci			parity_enable, parity_type,
9258c2ecf20Sopenharmony_ci			0, CYPRESS_SET_CONFIG);
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	/* we perform a CYPRESS_GET_CONFIG so that the current settings are
9288c2ecf20Sopenharmony_ci	 * filled into the private structure this should confirm that all is
9298c2ecf20Sopenharmony_ci	 * working if it returns what we just set */
9308c2ecf20Sopenharmony_ci	cypress_serial_control(tty, port, 0, 0, 0, 0, 0, 0, CYPRESS_GET_CONFIG);
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	/* Here we can define custom tty settings for devices; the main tty
9338c2ecf20Sopenharmony_ci	 * termios flag base comes from empeg.c */
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
9368c2ecf20Sopenharmony_ci	if (priv->chiptype == CT_EARTHMATE && priv->baud_rate == 4800) {
9378c2ecf20Sopenharmony_ci		dev_dbg(dev, "Using custom termios settings for a baud rate of 4800bps.\n");
9388c2ecf20Sopenharmony_ci		/* define custom termios settings for NMEA protocol */
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci		tty->termios.c_iflag /* input modes - */
9418c2ecf20Sopenharmony_ci			&= ~(IGNBRK  /* disable ignore break */
9428c2ecf20Sopenharmony_ci			| BRKINT     /* disable break causes interrupt */
9438c2ecf20Sopenharmony_ci			| PARMRK     /* disable mark parity errors */
9448c2ecf20Sopenharmony_ci			| ISTRIP     /* disable clear high bit of input char */
9458c2ecf20Sopenharmony_ci			| INLCR      /* disable translate NL to CR */
9468c2ecf20Sopenharmony_ci			| IGNCR      /* disable ignore CR */
9478c2ecf20Sopenharmony_ci			| ICRNL      /* disable translate CR to NL */
9488c2ecf20Sopenharmony_ci			| IXON);     /* disable enable XON/XOFF flow control */
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci		tty->termios.c_oflag /* output modes */
9518c2ecf20Sopenharmony_ci			&= ~OPOST;    /* disable postprocess output char */
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci		tty->termios.c_lflag /* line discipline modes */
9548c2ecf20Sopenharmony_ci			&= ~(ECHO     /* disable echo input characters */
9558c2ecf20Sopenharmony_ci			| ECHONL      /* disable echo new line */
9568c2ecf20Sopenharmony_ci			| ICANON      /* disable erase, kill, werase, and rprnt
9578c2ecf20Sopenharmony_ci					 special characters */
9588c2ecf20Sopenharmony_ci			| ISIG        /* disable interrupt, quit, and suspend
9598c2ecf20Sopenharmony_ci					 special characters */
9608c2ecf20Sopenharmony_ci			| IEXTEN);    /* disable non-POSIX special characters */
9618c2ecf20Sopenharmony_ci	} /* CT_CYPHIDCOM: Application should handle this for device */
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	linechange = (priv->line_control != oldlines);
9648c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci	/* if necessary, set lines */
9678c2ecf20Sopenharmony_ci	if (linechange) {
9688c2ecf20Sopenharmony_ci		priv->cmd_ctrl = 1;
9698c2ecf20Sopenharmony_ci		cypress_write(tty, port, NULL, 0);
9708c2ecf20Sopenharmony_ci	}
9718c2ecf20Sopenharmony_ci} /* cypress_set_termios */
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci/* returns amount of data still left in soft buffer */
9758c2ecf20Sopenharmony_cistatic int cypress_chars_in_buffer(struct tty_struct *tty)
9768c2ecf20Sopenharmony_ci{
9778c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
9788c2ecf20Sopenharmony_ci	struct cypress_private *priv = usb_get_serial_port_data(port);
9798c2ecf20Sopenharmony_ci	int chars = 0;
9808c2ecf20Sopenharmony_ci	unsigned long flags;
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
9838c2ecf20Sopenharmony_ci	chars = kfifo_len(&priv->write_fifo);
9848c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars);
9878c2ecf20Sopenharmony_ci	return chars;
9888c2ecf20Sopenharmony_ci}
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_cistatic void cypress_throttle(struct tty_struct *tty)
9928c2ecf20Sopenharmony_ci{
9938c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
9948c2ecf20Sopenharmony_ci	struct cypress_private *priv = usb_get_serial_port_data(port);
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	spin_lock_irq(&priv->lock);
9978c2ecf20Sopenharmony_ci	priv->rx_flags = THROTTLED;
9988c2ecf20Sopenharmony_ci	spin_unlock_irq(&priv->lock);
9998c2ecf20Sopenharmony_ci}
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_cistatic void cypress_unthrottle(struct tty_struct *tty)
10038c2ecf20Sopenharmony_ci{
10048c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
10058c2ecf20Sopenharmony_ci	struct cypress_private *priv = usb_get_serial_port_data(port);
10068c2ecf20Sopenharmony_ci	int actually_throttled, result;
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	spin_lock_irq(&priv->lock);
10098c2ecf20Sopenharmony_ci	actually_throttled = priv->rx_flags & ACTUALLY_THROTTLED;
10108c2ecf20Sopenharmony_ci	priv->rx_flags = 0;
10118c2ecf20Sopenharmony_ci	spin_unlock_irq(&priv->lock);
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	if (!priv->comm_is_ok)
10148c2ecf20Sopenharmony_ci		return;
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci	if (actually_throttled) {
10178c2ecf20Sopenharmony_ci		result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
10188c2ecf20Sopenharmony_ci		if (result) {
10198c2ecf20Sopenharmony_ci			dev_err(&port->dev, "%s - failed submitting read urb, "
10208c2ecf20Sopenharmony_ci					"error %d\n", __func__, result);
10218c2ecf20Sopenharmony_ci			cypress_set_dead(port);
10228c2ecf20Sopenharmony_ci		}
10238c2ecf20Sopenharmony_ci	}
10248c2ecf20Sopenharmony_ci}
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_cistatic void cypress_read_int_callback(struct urb *urb)
10288c2ecf20Sopenharmony_ci{
10298c2ecf20Sopenharmony_ci	struct usb_serial_port *port = urb->context;
10308c2ecf20Sopenharmony_ci	struct cypress_private *priv = usb_get_serial_port_data(port);
10318c2ecf20Sopenharmony_ci	struct device *dev = &urb->dev->dev;
10328c2ecf20Sopenharmony_ci	struct tty_struct *tty;
10338c2ecf20Sopenharmony_ci	unsigned char *data = urb->transfer_buffer;
10348c2ecf20Sopenharmony_ci	unsigned long flags;
10358c2ecf20Sopenharmony_ci	char tty_flag = TTY_NORMAL;
10368c2ecf20Sopenharmony_ci	int bytes = 0;
10378c2ecf20Sopenharmony_ci	int result;
10388c2ecf20Sopenharmony_ci	int i = 0;
10398c2ecf20Sopenharmony_ci	int status = urb->status;
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	switch (status) {
10428c2ecf20Sopenharmony_ci	case 0: /* success */
10438c2ecf20Sopenharmony_ci		break;
10448c2ecf20Sopenharmony_ci	case -ECONNRESET:
10458c2ecf20Sopenharmony_ci	case -ENOENT:
10468c2ecf20Sopenharmony_ci	case -ESHUTDOWN:
10478c2ecf20Sopenharmony_ci		/* precursor to disconnect so just go away */
10488c2ecf20Sopenharmony_ci		return;
10498c2ecf20Sopenharmony_ci	case -EPIPE:
10508c2ecf20Sopenharmony_ci		/* Can't call usb_clear_halt while in_interrupt */
10518c2ecf20Sopenharmony_ci		fallthrough;
10528c2ecf20Sopenharmony_ci	default:
10538c2ecf20Sopenharmony_ci		/* something ugly is going on... */
10548c2ecf20Sopenharmony_ci		dev_err(dev, "%s - unexpected nonzero read status received: %d\n",
10558c2ecf20Sopenharmony_ci			__func__, status);
10568c2ecf20Sopenharmony_ci		cypress_set_dead(port);
10578c2ecf20Sopenharmony_ci		return;
10588c2ecf20Sopenharmony_ci	}
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
10618c2ecf20Sopenharmony_ci	if (priv->rx_flags & THROTTLED) {
10628c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - now throttling\n", __func__);
10638c2ecf20Sopenharmony_ci		priv->rx_flags |= ACTUALLY_THROTTLED;
10648c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&priv->lock, flags);
10658c2ecf20Sopenharmony_ci		return;
10668c2ecf20Sopenharmony_ci	}
10678c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	tty = tty_port_tty_get(&port->port);
10708c2ecf20Sopenharmony_ci	if (!tty) {
10718c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - bad tty pointer - exiting\n", __func__);
10728c2ecf20Sopenharmony_ci		return;
10738c2ecf20Sopenharmony_ci	}
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
10768c2ecf20Sopenharmony_ci	result = urb->actual_length;
10778c2ecf20Sopenharmony_ci	switch (priv->pkt_fmt) {
10788c2ecf20Sopenharmony_ci	default:
10798c2ecf20Sopenharmony_ci	case packet_format_1:
10808c2ecf20Sopenharmony_ci		/* This is for the CY7C64013... */
10818c2ecf20Sopenharmony_ci		priv->current_status = data[0] & 0xF8;
10828c2ecf20Sopenharmony_ci		bytes = data[1] + 2;
10838c2ecf20Sopenharmony_ci		i = 2;
10848c2ecf20Sopenharmony_ci		break;
10858c2ecf20Sopenharmony_ci	case packet_format_2:
10868c2ecf20Sopenharmony_ci		/* This is for the CY7C63743... */
10878c2ecf20Sopenharmony_ci		priv->current_status = data[0] & 0xF8;
10888c2ecf20Sopenharmony_ci		bytes = (data[0] & 0x07) + 1;
10898c2ecf20Sopenharmony_ci		i = 1;
10908c2ecf20Sopenharmony_ci		break;
10918c2ecf20Sopenharmony_ci	}
10928c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
10938c2ecf20Sopenharmony_ci	if (result < bytes) {
10948c2ecf20Sopenharmony_ci		dev_dbg(dev,
10958c2ecf20Sopenharmony_ci			"%s - wrong packet size - received %d bytes but packet said %d bytes\n",
10968c2ecf20Sopenharmony_ci			__func__, result, bytes);
10978c2ecf20Sopenharmony_ci		goto continue_read;
10988c2ecf20Sopenharmony_ci	}
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci	usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
11038c2ecf20Sopenharmony_ci	/* check to see if status has changed */
11048c2ecf20Sopenharmony_ci	if (priv->current_status != priv->prev_status) {
11058c2ecf20Sopenharmony_ci		u8 delta = priv->current_status ^ priv->prev_status;
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci		if (delta & UART_MSR_MASK) {
11088c2ecf20Sopenharmony_ci			if (delta & UART_CTS)
11098c2ecf20Sopenharmony_ci				port->icount.cts++;
11108c2ecf20Sopenharmony_ci			if (delta & UART_DSR)
11118c2ecf20Sopenharmony_ci				port->icount.dsr++;
11128c2ecf20Sopenharmony_ci			if (delta & UART_RI)
11138c2ecf20Sopenharmony_ci				port->icount.rng++;
11148c2ecf20Sopenharmony_ci			if (delta & UART_CD)
11158c2ecf20Sopenharmony_ci				port->icount.dcd++;
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci			wake_up_interruptible(&port->port.delta_msr_wait);
11188c2ecf20Sopenharmony_ci		}
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci		priv->prev_status = priv->current_status;
11218c2ecf20Sopenharmony_ci	}
11228c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	/* hangup, as defined in acm.c... this might be a bad place for it
11258c2ecf20Sopenharmony_ci	 * though */
11268c2ecf20Sopenharmony_ci	if (tty && !C_CLOCAL(tty) && !(priv->current_status & UART_CD)) {
11278c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - calling hangup\n", __func__);
11288c2ecf20Sopenharmony_ci		tty_hangup(tty);
11298c2ecf20Sopenharmony_ci		goto continue_read;
11308c2ecf20Sopenharmony_ci	}
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci	/* There is one error bit... I'm assuming it is a parity error
11338c2ecf20Sopenharmony_ci	 * indicator as the generic firmware will set this bit to 1 if a
11348c2ecf20Sopenharmony_ci	 * parity error occurs.
11358c2ecf20Sopenharmony_ci	 * I can not find reference to any other error events. */
11368c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
11378c2ecf20Sopenharmony_ci	if (priv->current_status & CYP_ERROR) {
11388c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&priv->lock, flags);
11398c2ecf20Sopenharmony_ci		tty_flag = TTY_PARITY;
11408c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - Parity Error detected\n", __func__);
11418c2ecf20Sopenharmony_ci	} else
11428c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&priv->lock, flags);
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci	/* process read if there is data other than line status */
11458c2ecf20Sopenharmony_ci	if (bytes > i) {
11468c2ecf20Sopenharmony_ci		tty_insert_flip_string_fixed_flag(&port->port, data + i,
11478c2ecf20Sopenharmony_ci				tty_flag, bytes - i);
11488c2ecf20Sopenharmony_ci		tty_flip_buffer_push(&port->port);
11498c2ecf20Sopenharmony_ci	}
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
11528c2ecf20Sopenharmony_ci	/* control and status byte(s) are also counted */
11538c2ecf20Sopenharmony_ci	priv->bytes_in += bytes;
11548c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_cicontinue_read:
11578c2ecf20Sopenharmony_ci	tty_kref_put(tty);
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	/* Continue trying to always read */
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	if (priv->comm_is_ok) {
11628c2ecf20Sopenharmony_ci		usb_fill_int_urb(port->interrupt_in_urb, port->serial->dev,
11638c2ecf20Sopenharmony_ci				usb_rcvintpipe(port->serial->dev,
11648c2ecf20Sopenharmony_ci					port->interrupt_in_endpointAddress),
11658c2ecf20Sopenharmony_ci				port->interrupt_in_urb->transfer_buffer,
11668c2ecf20Sopenharmony_ci				port->interrupt_in_urb->transfer_buffer_length,
11678c2ecf20Sopenharmony_ci				cypress_read_int_callback, port,
11688c2ecf20Sopenharmony_ci				priv->read_urb_interval);
11698c2ecf20Sopenharmony_ci		result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
11708c2ecf20Sopenharmony_ci		if (result && result != -EPERM) {
11718c2ecf20Sopenharmony_ci			dev_err(dev, "%s - failed resubmitting read urb, error %d\n",
11728c2ecf20Sopenharmony_ci				__func__, result);
11738c2ecf20Sopenharmony_ci			cypress_set_dead(port);
11748c2ecf20Sopenharmony_ci		}
11758c2ecf20Sopenharmony_ci	}
11768c2ecf20Sopenharmony_ci} /* cypress_read_int_callback */
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_cistatic void cypress_write_int_callback(struct urb *urb)
11808c2ecf20Sopenharmony_ci{
11818c2ecf20Sopenharmony_ci	struct usb_serial_port *port = urb->context;
11828c2ecf20Sopenharmony_ci	struct cypress_private *priv = usb_get_serial_port_data(port);
11838c2ecf20Sopenharmony_ci	struct device *dev = &urb->dev->dev;
11848c2ecf20Sopenharmony_ci	int status = urb->status;
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_ci	switch (status) {
11878c2ecf20Sopenharmony_ci	case 0:
11888c2ecf20Sopenharmony_ci		/* success */
11898c2ecf20Sopenharmony_ci		break;
11908c2ecf20Sopenharmony_ci	case -ECONNRESET:
11918c2ecf20Sopenharmony_ci	case -ENOENT:
11928c2ecf20Sopenharmony_ci	case -ESHUTDOWN:
11938c2ecf20Sopenharmony_ci		/* this urb is terminated, clean up */
11948c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - urb shutting down with status: %d\n",
11958c2ecf20Sopenharmony_ci			__func__, status);
11968c2ecf20Sopenharmony_ci		priv->write_urb_in_use = 0;
11978c2ecf20Sopenharmony_ci		return;
11988c2ecf20Sopenharmony_ci	case -EPIPE:
11998c2ecf20Sopenharmony_ci		/* Cannot call usb_clear_halt while in_interrupt */
12008c2ecf20Sopenharmony_ci		fallthrough;
12018c2ecf20Sopenharmony_ci	default:
12028c2ecf20Sopenharmony_ci		dev_err(dev, "%s - unexpected nonzero write status received: %d\n",
12038c2ecf20Sopenharmony_ci			__func__, status);
12048c2ecf20Sopenharmony_ci		cypress_set_dead(port);
12058c2ecf20Sopenharmony_ci		break;
12068c2ecf20Sopenharmony_ci	}
12078c2ecf20Sopenharmony_ci	priv->write_urb_in_use = 0;
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci	/* send any buffered data */
12108c2ecf20Sopenharmony_ci	cypress_send(port);
12118c2ecf20Sopenharmony_ci}
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table_combined);
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
12168c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
12178c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_cimodule_param(stats, bool, S_IRUGO | S_IWUSR);
12208c2ecf20Sopenharmony_ciMODULE_PARM_DESC(stats, "Enable statistics or not");
12218c2ecf20Sopenharmony_cimodule_param(interval, int, S_IRUGO | S_IWUSR);
12228c2ecf20Sopenharmony_ciMODULE_PARM_DESC(interval, "Overrides interrupt interval");
12238c2ecf20Sopenharmony_cimodule_param(unstable_bauds, bool, S_IRUGO | S_IWUSR);
12248c2ecf20Sopenharmony_ciMODULE_PARM_DESC(unstable_bauds, "Allow unstable baud rates");
1225