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