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