162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * USB Cypress M8 driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2004 662306a36Sopenharmony_ci * Lonnie Mendez (dignome@gmail.com) 762306a36Sopenharmony_ci * Copyright (C) 2003,2004 862306a36Sopenharmony_ci * Neil Whelchel (koyama@firstlight.net) 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * See Documentation/usb/usb-serial.rst for more information on using this 1162306a36Sopenharmony_ci * driver 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * See http://geocities.com/i0xox0i for information on this driver and the 1462306a36Sopenharmony_ci * earthmate usb device. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* Thanks to Neil Whelchel for writing the first cypress m8 implementation 1862306a36Sopenharmony_ci for linux. */ 1962306a36Sopenharmony_ci/* Thanks to cypress for providing references for the hid reports. */ 2062306a36Sopenharmony_ci/* Thanks to Jiang Zhang for providing links and for general help. */ 2162306a36Sopenharmony_ci/* Code originates and was built up from ftdi_sio, belkin, pl2303 and others.*/ 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <linux/kernel.h> 2562306a36Sopenharmony_ci#include <linux/errno.h> 2662306a36Sopenharmony_ci#include <linux/slab.h> 2762306a36Sopenharmony_ci#include <linux/tty.h> 2862306a36Sopenharmony_ci#include <linux/tty_driver.h> 2962306a36Sopenharmony_ci#include <linux/tty_flip.h> 3062306a36Sopenharmony_ci#include <linux/module.h> 3162306a36Sopenharmony_ci#include <linux/moduleparam.h> 3262306a36Sopenharmony_ci#include <linux/spinlock.h> 3362306a36Sopenharmony_ci#include <linux/usb.h> 3462306a36Sopenharmony_ci#include <linux/usb/serial.h> 3562306a36Sopenharmony_ci#include <linux/serial.h> 3662306a36Sopenharmony_ci#include <linux/kfifo.h> 3762306a36Sopenharmony_ci#include <linux/delay.h> 3862306a36Sopenharmony_ci#include <linux/uaccess.h> 3962306a36Sopenharmony_ci#include <asm/unaligned.h> 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#include "cypress_m8.h" 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic bool stats; 4562306a36Sopenharmony_cistatic int interval; 4662306a36Sopenharmony_cistatic bool unstable_bauds; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define DRIVER_AUTHOR "Lonnie Mendez <dignome@gmail.com>, Neil Whelchel <koyama@firstlight.net>" 4962306a36Sopenharmony_ci#define DRIVER_DESC "Cypress USB to Serial Driver" 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* write buffer size defines */ 5262306a36Sopenharmony_ci#define CYPRESS_BUF_SIZE 1024 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic const struct usb_device_id id_table_earthmate[] = { 5562306a36Sopenharmony_ci { USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB) }, 5662306a36Sopenharmony_ci { USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB_LT20) }, 5762306a36Sopenharmony_ci { } /* Terminating entry */ 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic const struct usb_device_id id_table_cyphidcomrs232[] = { 6162306a36Sopenharmony_ci { USB_DEVICE(VENDOR_ID_CYPRESS, PRODUCT_ID_CYPHIDCOM) }, 6262306a36Sopenharmony_ci { USB_DEVICE(VENDOR_ID_SAI, PRODUCT_ID_CYPHIDCOM) }, 6362306a36Sopenharmony_ci { USB_DEVICE(VENDOR_ID_POWERCOM, PRODUCT_ID_UPS) }, 6462306a36Sopenharmony_ci { USB_DEVICE(VENDOR_ID_FRWD, PRODUCT_ID_CYPHIDCOM_FRWD) }, 6562306a36Sopenharmony_ci { } /* Terminating entry */ 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic const struct usb_device_id id_table_nokiaca42v2[] = { 6962306a36Sopenharmony_ci { USB_DEVICE(VENDOR_ID_DAZZLE, PRODUCT_ID_CA42) }, 7062306a36Sopenharmony_ci { } /* Terminating entry */ 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic const struct usb_device_id id_table_combined[] = { 7462306a36Sopenharmony_ci { USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB) }, 7562306a36Sopenharmony_ci { USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB_LT20) }, 7662306a36Sopenharmony_ci { USB_DEVICE(VENDOR_ID_CYPRESS, PRODUCT_ID_CYPHIDCOM) }, 7762306a36Sopenharmony_ci { USB_DEVICE(VENDOR_ID_SAI, PRODUCT_ID_CYPHIDCOM) }, 7862306a36Sopenharmony_ci { USB_DEVICE(VENDOR_ID_POWERCOM, PRODUCT_ID_UPS) }, 7962306a36Sopenharmony_ci { USB_DEVICE(VENDOR_ID_FRWD, PRODUCT_ID_CYPHIDCOM_FRWD) }, 8062306a36Sopenharmony_ci { USB_DEVICE(VENDOR_ID_DAZZLE, PRODUCT_ID_CA42) }, 8162306a36Sopenharmony_ci { } /* Terminating entry */ 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table_combined); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cienum packet_format { 8762306a36Sopenharmony_ci packet_format_1, /* b0:status, b1:payload count */ 8862306a36Sopenharmony_ci packet_format_2 /* b0[7:3]:status, b0[2:0]:payload count */ 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistruct cypress_private { 9262306a36Sopenharmony_ci spinlock_t lock; /* private lock */ 9362306a36Sopenharmony_ci int chiptype; /* identifier of device, for quirks/etc */ 9462306a36Sopenharmony_ci int bytes_in; /* used for statistics */ 9562306a36Sopenharmony_ci int bytes_out; /* used for statistics */ 9662306a36Sopenharmony_ci int cmd_count; /* used for statistics */ 9762306a36Sopenharmony_ci int cmd_ctrl; /* always set this to 1 before issuing a command */ 9862306a36Sopenharmony_ci struct kfifo write_fifo; /* write fifo */ 9962306a36Sopenharmony_ci int write_urb_in_use; /* write urb in use indicator */ 10062306a36Sopenharmony_ci int write_urb_interval; /* interval to use for write urb */ 10162306a36Sopenharmony_ci int read_urb_interval; /* interval to use for read urb */ 10262306a36Sopenharmony_ci int comm_is_ok; /* true if communication is (still) ok */ 10362306a36Sopenharmony_ci __u8 line_control; /* holds dtr / rts value */ 10462306a36Sopenharmony_ci __u8 current_status; /* received from last read - info on dsr,cts,cd,ri,etc */ 10562306a36Sopenharmony_ci __u8 current_config; /* stores the current configuration byte */ 10662306a36Sopenharmony_ci __u8 rx_flags; /* throttling - used from whiteheat/ftdi_sio */ 10762306a36Sopenharmony_ci enum packet_format pkt_fmt; /* format to use for packet send / receive */ 10862306a36Sopenharmony_ci int get_cfg_unsafe; /* If true, the CYPRESS_GET_CONFIG is unsafe */ 10962306a36Sopenharmony_ci int baud_rate; /* stores current baud rate in 11062306a36Sopenharmony_ci integer form */ 11162306a36Sopenharmony_ci char prev_status; /* used for TIOCMIWAIT */ 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* function prototypes for the Cypress USB to serial device */ 11562306a36Sopenharmony_cistatic int cypress_earthmate_port_probe(struct usb_serial_port *port); 11662306a36Sopenharmony_cistatic int cypress_hidcom_port_probe(struct usb_serial_port *port); 11762306a36Sopenharmony_cistatic int cypress_ca42v2_port_probe(struct usb_serial_port *port); 11862306a36Sopenharmony_cistatic void cypress_port_remove(struct usb_serial_port *port); 11962306a36Sopenharmony_cistatic int cypress_open(struct tty_struct *tty, struct usb_serial_port *port); 12062306a36Sopenharmony_cistatic void cypress_close(struct usb_serial_port *port); 12162306a36Sopenharmony_cistatic void cypress_dtr_rts(struct usb_serial_port *port, int on); 12262306a36Sopenharmony_cistatic int cypress_write(struct tty_struct *tty, struct usb_serial_port *port, 12362306a36Sopenharmony_ci const unsigned char *buf, int count); 12462306a36Sopenharmony_cistatic void cypress_send(struct usb_serial_port *port); 12562306a36Sopenharmony_cistatic unsigned int cypress_write_room(struct tty_struct *tty); 12662306a36Sopenharmony_cistatic void cypress_earthmate_init_termios(struct tty_struct *tty); 12762306a36Sopenharmony_cistatic void cypress_set_termios(struct tty_struct *tty, 12862306a36Sopenharmony_ci struct usb_serial_port *port, 12962306a36Sopenharmony_ci const struct ktermios *old_termios); 13062306a36Sopenharmony_cistatic int cypress_tiocmget(struct tty_struct *tty); 13162306a36Sopenharmony_cistatic int cypress_tiocmset(struct tty_struct *tty, 13262306a36Sopenharmony_ci unsigned int set, unsigned int clear); 13362306a36Sopenharmony_cistatic unsigned int cypress_chars_in_buffer(struct tty_struct *tty); 13462306a36Sopenharmony_cistatic void cypress_throttle(struct tty_struct *tty); 13562306a36Sopenharmony_cistatic void cypress_unthrottle(struct tty_struct *tty); 13662306a36Sopenharmony_cistatic void cypress_set_dead(struct usb_serial_port *port); 13762306a36Sopenharmony_cistatic void cypress_read_int_callback(struct urb *urb); 13862306a36Sopenharmony_cistatic void cypress_write_int_callback(struct urb *urb); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic struct usb_serial_driver cypress_earthmate_device = { 14162306a36Sopenharmony_ci .driver = { 14262306a36Sopenharmony_ci .owner = THIS_MODULE, 14362306a36Sopenharmony_ci .name = "earthmate", 14462306a36Sopenharmony_ci }, 14562306a36Sopenharmony_ci .description = "DeLorme Earthmate USB", 14662306a36Sopenharmony_ci .id_table = id_table_earthmate, 14762306a36Sopenharmony_ci .num_ports = 1, 14862306a36Sopenharmony_ci .port_probe = cypress_earthmate_port_probe, 14962306a36Sopenharmony_ci .port_remove = cypress_port_remove, 15062306a36Sopenharmony_ci .open = cypress_open, 15162306a36Sopenharmony_ci .close = cypress_close, 15262306a36Sopenharmony_ci .dtr_rts = cypress_dtr_rts, 15362306a36Sopenharmony_ci .write = cypress_write, 15462306a36Sopenharmony_ci .write_room = cypress_write_room, 15562306a36Sopenharmony_ci .init_termios = cypress_earthmate_init_termios, 15662306a36Sopenharmony_ci .set_termios = cypress_set_termios, 15762306a36Sopenharmony_ci .tiocmget = cypress_tiocmget, 15862306a36Sopenharmony_ci .tiocmset = cypress_tiocmset, 15962306a36Sopenharmony_ci .tiocmiwait = usb_serial_generic_tiocmiwait, 16062306a36Sopenharmony_ci .chars_in_buffer = cypress_chars_in_buffer, 16162306a36Sopenharmony_ci .throttle = cypress_throttle, 16262306a36Sopenharmony_ci .unthrottle = cypress_unthrottle, 16362306a36Sopenharmony_ci .read_int_callback = cypress_read_int_callback, 16462306a36Sopenharmony_ci .write_int_callback = cypress_write_int_callback, 16562306a36Sopenharmony_ci}; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic struct usb_serial_driver cypress_hidcom_device = { 16862306a36Sopenharmony_ci .driver = { 16962306a36Sopenharmony_ci .owner = THIS_MODULE, 17062306a36Sopenharmony_ci .name = "cyphidcom", 17162306a36Sopenharmony_ci }, 17262306a36Sopenharmony_ci .description = "HID->COM RS232 Adapter", 17362306a36Sopenharmony_ci .id_table = id_table_cyphidcomrs232, 17462306a36Sopenharmony_ci .num_ports = 1, 17562306a36Sopenharmony_ci .port_probe = cypress_hidcom_port_probe, 17662306a36Sopenharmony_ci .port_remove = cypress_port_remove, 17762306a36Sopenharmony_ci .open = cypress_open, 17862306a36Sopenharmony_ci .close = cypress_close, 17962306a36Sopenharmony_ci .dtr_rts = cypress_dtr_rts, 18062306a36Sopenharmony_ci .write = cypress_write, 18162306a36Sopenharmony_ci .write_room = cypress_write_room, 18262306a36Sopenharmony_ci .set_termios = cypress_set_termios, 18362306a36Sopenharmony_ci .tiocmget = cypress_tiocmget, 18462306a36Sopenharmony_ci .tiocmset = cypress_tiocmset, 18562306a36Sopenharmony_ci .tiocmiwait = usb_serial_generic_tiocmiwait, 18662306a36Sopenharmony_ci .chars_in_buffer = cypress_chars_in_buffer, 18762306a36Sopenharmony_ci .throttle = cypress_throttle, 18862306a36Sopenharmony_ci .unthrottle = cypress_unthrottle, 18962306a36Sopenharmony_ci .read_int_callback = cypress_read_int_callback, 19062306a36Sopenharmony_ci .write_int_callback = cypress_write_int_callback, 19162306a36Sopenharmony_ci}; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic struct usb_serial_driver cypress_ca42v2_device = { 19462306a36Sopenharmony_ci .driver = { 19562306a36Sopenharmony_ci .owner = THIS_MODULE, 19662306a36Sopenharmony_ci .name = "nokiaca42v2", 19762306a36Sopenharmony_ci }, 19862306a36Sopenharmony_ci .description = "Nokia CA-42 V2 Adapter", 19962306a36Sopenharmony_ci .id_table = id_table_nokiaca42v2, 20062306a36Sopenharmony_ci .num_ports = 1, 20162306a36Sopenharmony_ci .port_probe = cypress_ca42v2_port_probe, 20262306a36Sopenharmony_ci .port_remove = cypress_port_remove, 20362306a36Sopenharmony_ci .open = cypress_open, 20462306a36Sopenharmony_ci .close = cypress_close, 20562306a36Sopenharmony_ci .dtr_rts = cypress_dtr_rts, 20662306a36Sopenharmony_ci .write = cypress_write, 20762306a36Sopenharmony_ci .write_room = cypress_write_room, 20862306a36Sopenharmony_ci .set_termios = cypress_set_termios, 20962306a36Sopenharmony_ci .tiocmget = cypress_tiocmget, 21062306a36Sopenharmony_ci .tiocmset = cypress_tiocmset, 21162306a36Sopenharmony_ci .tiocmiwait = usb_serial_generic_tiocmiwait, 21262306a36Sopenharmony_ci .chars_in_buffer = cypress_chars_in_buffer, 21362306a36Sopenharmony_ci .throttle = cypress_throttle, 21462306a36Sopenharmony_ci .unthrottle = cypress_unthrottle, 21562306a36Sopenharmony_ci .read_int_callback = cypress_read_int_callback, 21662306a36Sopenharmony_ci .write_int_callback = cypress_write_int_callback, 21762306a36Sopenharmony_ci}; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = { 22062306a36Sopenharmony_ci &cypress_earthmate_device, &cypress_hidcom_device, 22162306a36Sopenharmony_ci &cypress_ca42v2_device, NULL 22262306a36Sopenharmony_ci}; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci/***************************************************************************** 22562306a36Sopenharmony_ci * Cypress serial helper functions 22662306a36Sopenharmony_ci *****************************************************************************/ 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci/* FRWD Dongle hidcom needs to skip reset and speed checks */ 22962306a36Sopenharmony_cistatic inline bool is_frwd(struct usb_device *dev) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci return ((le16_to_cpu(dev->descriptor.idVendor) == VENDOR_ID_FRWD) && 23262306a36Sopenharmony_ci (le16_to_cpu(dev->descriptor.idProduct) == PRODUCT_ID_CYPHIDCOM_FRWD)); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic int analyze_baud_rate(struct usb_serial_port *port, speed_t new_rate) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci struct cypress_private *priv; 23862306a36Sopenharmony_ci priv = usb_get_serial_port_data(port); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (unstable_bauds) 24162306a36Sopenharmony_ci return new_rate; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* FRWD Dongle uses 115200 bps */ 24462306a36Sopenharmony_ci if (is_frwd(port->serial->dev)) 24562306a36Sopenharmony_ci return new_rate; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* 24862306a36Sopenharmony_ci * The general purpose firmware for the Cypress M8 allows for 24962306a36Sopenharmony_ci * a maximum speed of 57600bps (I have no idea whether DeLorme 25062306a36Sopenharmony_ci * chose to use the general purpose firmware or not), if you 25162306a36Sopenharmony_ci * need to modify this speed setting for your own project 25262306a36Sopenharmony_ci * please add your own chiptype and modify the code likewise. 25362306a36Sopenharmony_ci * The Cypress HID->COM device will work successfully up to 25462306a36Sopenharmony_ci * 115200bps (but the actual throughput is around 3kBps). 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_ci if (port->serial->dev->speed == USB_SPEED_LOW) { 25762306a36Sopenharmony_ci /* 25862306a36Sopenharmony_ci * Mike Isely <isely@pobox.com> 2-Feb-2008: The 25962306a36Sopenharmony_ci * Cypress app note that describes this mechanism 26062306a36Sopenharmony_ci * states that the low-speed part can't handle more 26162306a36Sopenharmony_ci * than 800 bytes/sec, in which case 4800 baud is the 26262306a36Sopenharmony_ci * safest speed for a part like that. 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_ci if (new_rate > 4800) { 26562306a36Sopenharmony_ci dev_dbg(&port->dev, 26662306a36Sopenharmony_ci "%s - failed setting baud rate, device incapable speed %d\n", 26762306a36Sopenharmony_ci __func__, new_rate); 26862306a36Sopenharmony_ci return -1; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci switch (priv->chiptype) { 27262306a36Sopenharmony_ci case CT_EARTHMATE: 27362306a36Sopenharmony_ci if (new_rate <= 600) { 27462306a36Sopenharmony_ci /* 300 and 600 baud rates are supported under 27562306a36Sopenharmony_ci * the generic firmware, but are not used with 27662306a36Sopenharmony_ci * NMEA and SiRF protocols */ 27762306a36Sopenharmony_ci dev_dbg(&port->dev, 27862306a36Sopenharmony_ci "%s - failed setting baud rate, unsupported speed of %d on Earthmate GPS\n", 27962306a36Sopenharmony_ci __func__, new_rate); 28062306a36Sopenharmony_ci return -1; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci break; 28362306a36Sopenharmony_ci default: 28462306a36Sopenharmony_ci break; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci return new_rate; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci/* This function can either set or retrieve the current serial line settings */ 29162306a36Sopenharmony_cistatic int cypress_serial_control(struct tty_struct *tty, 29262306a36Sopenharmony_ci struct usb_serial_port *port, speed_t baud_rate, int data_bits, 29362306a36Sopenharmony_ci int stop_bits, int parity_enable, int parity_type, int reset, 29462306a36Sopenharmony_ci int cypress_request_type) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci int new_baudrate = 0, retval = 0, tries = 0; 29762306a36Sopenharmony_ci struct cypress_private *priv; 29862306a36Sopenharmony_ci struct device *dev = &port->dev; 29962306a36Sopenharmony_ci u8 *feature_buffer; 30062306a36Sopenharmony_ci const unsigned int feature_len = 5; 30162306a36Sopenharmony_ci unsigned long flags; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci priv = usb_get_serial_port_data(port); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (!priv->comm_is_ok) 30662306a36Sopenharmony_ci return -ENODEV; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci feature_buffer = kcalloc(feature_len, sizeof(u8), GFP_KERNEL); 30962306a36Sopenharmony_ci if (!feature_buffer) 31062306a36Sopenharmony_ci return -ENOMEM; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci switch (cypress_request_type) { 31362306a36Sopenharmony_ci case CYPRESS_SET_CONFIG: 31462306a36Sopenharmony_ci /* 0 means 'Hang up' so doesn't change the true bit rate */ 31562306a36Sopenharmony_ci new_baudrate = priv->baud_rate; 31662306a36Sopenharmony_ci if (baud_rate && baud_rate != priv->baud_rate) { 31762306a36Sopenharmony_ci dev_dbg(dev, "%s - baud rate is changing\n", __func__); 31862306a36Sopenharmony_ci retval = analyze_baud_rate(port, baud_rate); 31962306a36Sopenharmony_ci if (retval >= 0) { 32062306a36Sopenharmony_ci new_baudrate = retval; 32162306a36Sopenharmony_ci dev_dbg(dev, "%s - New baud rate set to %d\n", 32262306a36Sopenharmony_ci __func__, new_baudrate); 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci dev_dbg(dev, "%s - baud rate is being sent as %d\n", __func__, 32662306a36Sopenharmony_ci new_baudrate); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* fill the feature_buffer with new configuration */ 32962306a36Sopenharmony_ci put_unaligned_le32(new_baudrate, feature_buffer); 33062306a36Sopenharmony_ci feature_buffer[4] |= data_bits - 5; /* assign data bits in 2 bit space ( max 3 ) */ 33162306a36Sopenharmony_ci /* 1 bit gap */ 33262306a36Sopenharmony_ci feature_buffer[4] |= (stop_bits << 3); /* assign stop bits in 1 bit space */ 33362306a36Sopenharmony_ci feature_buffer[4] |= (parity_enable << 4); /* assign parity flag in 1 bit space */ 33462306a36Sopenharmony_ci feature_buffer[4] |= (parity_type << 5); /* assign parity type in 1 bit space */ 33562306a36Sopenharmony_ci /* 1 bit gap */ 33662306a36Sopenharmony_ci feature_buffer[4] |= (reset << 7); /* assign reset at end of byte, 1 bit space */ 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci dev_dbg(dev, "%s - device is being sent this feature report:\n", __func__); 33962306a36Sopenharmony_ci dev_dbg(dev, "%s - %02X - %02X - %02X - %02X - %02X\n", __func__, 34062306a36Sopenharmony_ci feature_buffer[0], feature_buffer[1], 34162306a36Sopenharmony_ci feature_buffer[2], feature_buffer[3], 34262306a36Sopenharmony_ci feature_buffer[4]); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci do { 34562306a36Sopenharmony_ci retval = usb_control_msg(port->serial->dev, 34662306a36Sopenharmony_ci usb_sndctrlpipe(port->serial->dev, 0), 34762306a36Sopenharmony_ci HID_REQ_SET_REPORT, 34862306a36Sopenharmony_ci USB_DIR_OUT | USB_RECIP_INTERFACE | USB_TYPE_CLASS, 34962306a36Sopenharmony_ci 0x0300, 0, feature_buffer, 35062306a36Sopenharmony_ci feature_len, 500); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (tries++ >= 3) 35362306a36Sopenharmony_ci break; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci } while (retval != feature_len && 35662306a36Sopenharmony_ci retval != -ENODEV); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (retval != feature_len) { 35962306a36Sopenharmony_ci dev_err(dev, "%s - failed sending serial line settings - %d\n", 36062306a36Sopenharmony_ci __func__, retval); 36162306a36Sopenharmony_ci cypress_set_dead(port); 36262306a36Sopenharmony_ci } else { 36362306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 36462306a36Sopenharmony_ci priv->baud_rate = new_baudrate; 36562306a36Sopenharmony_ci priv->current_config = feature_buffer[4]; 36662306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 36762306a36Sopenharmony_ci /* If we asked for a speed change encode it */ 36862306a36Sopenharmony_ci if (baud_rate) 36962306a36Sopenharmony_ci tty_encode_baud_rate(tty, 37062306a36Sopenharmony_ci new_baudrate, new_baudrate); 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci break; 37362306a36Sopenharmony_ci case CYPRESS_GET_CONFIG: 37462306a36Sopenharmony_ci if (priv->get_cfg_unsafe) { 37562306a36Sopenharmony_ci /* Not implemented for this device, 37662306a36Sopenharmony_ci and if we try to do it we're likely 37762306a36Sopenharmony_ci to crash the hardware. */ 37862306a36Sopenharmony_ci retval = -ENOTTY; 37962306a36Sopenharmony_ci goto out; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci dev_dbg(dev, "%s - retrieving serial line settings\n", __func__); 38262306a36Sopenharmony_ci do { 38362306a36Sopenharmony_ci retval = usb_control_msg(port->serial->dev, 38462306a36Sopenharmony_ci usb_rcvctrlpipe(port->serial->dev, 0), 38562306a36Sopenharmony_ci HID_REQ_GET_REPORT, 38662306a36Sopenharmony_ci USB_DIR_IN | USB_RECIP_INTERFACE | USB_TYPE_CLASS, 38762306a36Sopenharmony_ci 0x0300, 0, feature_buffer, 38862306a36Sopenharmony_ci feature_len, 500); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (tries++ >= 3) 39162306a36Sopenharmony_ci break; 39262306a36Sopenharmony_ci } while (retval != feature_len 39362306a36Sopenharmony_ci && retval != -ENODEV); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (retval != feature_len) { 39662306a36Sopenharmony_ci dev_err(dev, "%s - failed to retrieve serial line settings - %d\n", 39762306a36Sopenharmony_ci __func__, retval); 39862306a36Sopenharmony_ci cypress_set_dead(port); 39962306a36Sopenharmony_ci goto out; 40062306a36Sopenharmony_ci } else { 40162306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 40262306a36Sopenharmony_ci /* store the config in one byte, and later 40362306a36Sopenharmony_ci use bit masks to check values */ 40462306a36Sopenharmony_ci priv->current_config = feature_buffer[4]; 40562306a36Sopenharmony_ci priv->baud_rate = get_unaligned_le32(feature_buffer); 40662306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 41062306a36Sopenharmony_ci ++priv->cmd_count; 41162306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 41262306a36Sopenharmony_ciout: 41362306a36Sopenharmony_ci kfree(feature_buffer); 41462306a36Sopenharmony_ci return retval; 41562306a36Sopenharmony_ci} /* cypress_serial_control */ 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic void cypress_set_dead(struct usb_serial_port *port) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci struct cypress_private *priv = usb_get_serial_port_data(port); 42162306a36Sopenharmony_ci unsigned long flags; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 42462306a36Sopenharmony_ci if (!priv->comm_is_ok) { 42562306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 42662306a36Sopenharmony_ci return; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci priv->comm_is_ok = 0; 42962306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci dev_err(&port->dev, "cypress_m8 suspending failing port %d - " 43262306a36Sopenharmony_ci "interval might be too short\n", port->port_number); 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci/***************************************************************************** 43762306a36Sopenharmony_ci * Cypress serial driver functions 43862306a36Sopenharmony_ci *****************************************************************************/ 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_cistatic int cypress_generic_port_probe(struct usb_serial_port *port) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 44462306a36Sopenharmony_ci struct cypress_private *priv; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (!port->interrupt_out_urb || !port->interrupt_in_urb) { 44762306a36Sopenharmony_ci dev_err(&port->dev, "required endpoint is missing\n"); 44862306a36Sopenharmony_ci return -ENODEV; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci priv = kzalloc(sizeof(struct cypress_private), GFP_KERNEL); 45262306a36Sopenharmony_ci if (!priv) 45362306a36Sopenharmony_ci return -ENOMEM; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci priv->comm_is_ok = !0; 45662306a36Sopenharmony_ci spin_lock_init(&priv->lock); 45762306a36Sopenharmony_ci if (kfifo_alloc(&priv->write_fifo, CYPRESS_BUF_SIZE, GFP_KERNEL)) { 45862306a36Sopenharmony_ci kfree(priv); 45962306a36Sopenharmony_ci return -ENOMEM; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* Skip reset for FRWD device. It is a workaound: 46362306a36Sopenharmony_ci device hangs if it receives SET_CONFIGURE in Configured 46462306a36Sopenharmony_ci state. */ 46562306a36Sopenharmony_ci if (!is_frwd(serial->dev)) 46662306a36Sopenharmony_ci usb_reset_configuration(serial->dev); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci priv->cmd_ctrl = 0; 46962306a36Sopenharmony_ci priv->line_control = 0; 47062306a36Sopenharmony_ci priv->rx_flags = 0; 47162306a36Sopenharmony_ci /* Default packet format setting is determined by packet size. 47262306a36Sopenharmony_ci Anything with a size larger then 9 must have a separate 47362306a36Sopenharmony_ci count field since the 3 bit count field is otherwise too 47462306a36Sopenharmony_ci small. Otherwise we can use the slightly more compact 47562306a36Sopenharmony_ci format. This is in accordance with the cypress_m8 serial 47662306a36Sopenharmony_ci converter app note. */ 47762306a36Sopenharmony_ci if (port->interrupt_out_size > 9) 47862306a36Sopenharmony_ci priv->pkt_fmt = packet_format_1; 47962306a36Sopenharmony_ci else 48062306a36Sopenharmony_ci priv->pkt_fmt = packet_format_2; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (interval > 0) { 48362306a36Sopenharmony_ci priv->write_urb_interval = interval; 48462306a36Sopenharmony_ci priv->read_urb_interval = interval; 48562306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - read & write intervals forced to %d\n", 48662306a36Sopenharmony_ci __func__, interval); 48762306a36Sopenharmony_ci } else { 48862306a36Sopenharmony_ci priv->write_urb_interval = port->interrupt_out_urb->interval; 48962306a36Sopenharmony_ci priv->read_urb_interval = port->interrupt_in_urb->interval; 49062306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - intervals: read=%d write=%d\n", 49162306a36Sopenharmony_ci __func__, priv->read_urb_interval, 49262306a36Sopenharmony_ci priv->write_urb_interval); 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci usb_set_serial_port_data(port, priv); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci port->port.drain_delay = 256; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci return 0; 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic int cypress_earthmate_port_probe(struct usb_serial_port *port) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 50562306a36Sopenharmony_ci struct cypress_private *priv; 50662306a36Sopenharmony_ci int ret; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci ret = cypress_generic_port_probe(port); 50962306a36Sopenharmony_ci if (ret) { 51062306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - Failed setting up port\n", __func__); 51162306a36Sopenharmony_ci return ret; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci priv = usb_get_serial_port_data(port); 51562306a36Sopenharmony_ci priv->chiptype = CT_EARTHMATE; 51662306a36Sopenharmony_ci /* All Earthmate devices use the separated-count packet 51762306a36Sopenharmony_ci format! Idiotic. */ 51862306a36Sopenharmony_ci priv->pkt_fmt = packet_format_1; 51962306a36Sopenharmony_ci if (serial->dev->descriptor.idProduct != 52062306a36Sopenharmony_ci cpu_to_le16(PRODUCT_ID_EARTHMATEUSB)) { 52162306a36Sopenharmony_ci /* The old original USB Earthmate seemed able to 52262306a36Sopenharmony_ci handle GET_CONFIG requests; everything they've 52362306a36Sopenharmony_ci produced since that time crashes if this command is 52462306a36Sopenharmony_ci attempted :-( */ 52562306a36Sopenharmony_ci dev_dbg(&port->dev, 52662306a36Sopenharmony_ci "%s - Marking this device as unsafe for GET_CONFIG commands\n", 52762306a36Sopenharmony_ci __func__); 52862306a36Sopenharmony_ci priv->get_cfg_unsafe = !0; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci return 0; 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic int cypress_hidcom_port_probe(struct usb_serial_port *port) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci struct cypress_private *priv; 53762306a36Sopenharmony_ci int ret; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci ret = cypress_generic_port_probe(port); 54062306a36Sopenharmony_ci if (ret) { 54162306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - Failed setting up port\n", __func__); 54262306a36Sopenharmony_ci return ret; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci priv = usb_get_serial_port_data(port); 54662306a36Sopenharmony_ci priv->chiptype = CT_CYPHIDCOM; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci return 0; 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cistatic int cypress_ca42v2_port_probe(struct usb_serial_port *port) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci struct cypress_private *priv; 55462306a36Sopenharmony_ci int ret; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci ret = cypress_generic_port_probe(port); 55762306a36Sopenharmony_ci if (ret) { 55862306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - Failed setting up port\n", __func__); 55962306a36Sopenharmony_ci return ret; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci priv = usb_get_serial_port_data(port); 56362306a36Sopenharmony_ci priv->chiptype = CT_CA42V2; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci return 0; 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic void cypress_port_remove(struct usb_serial_port *port) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci struct cypress_private *priv; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci priv = usb_get_serial_port_data(port); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci kfifo_free(&priv->write_fifo); 57562306a36Sopenharmony_ci kfree(priv); 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistatic int cypress_open(struct tty_struct *tty, struct usb_serial_port *port) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci struct cypress_private *priv = usb_get_serial_port_data(port); 58162306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 58262306a36Sopenharmony_ci unsigned long flags; 58362306a36Sopenharmony_ci int result = 0; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (!priv->comm_is_ok) 58662306a36Sopenharmony_ci return -EIO; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci /* clear halts before open */ 58962306a36Sopenharmony_ci usb_clear_halt(serial->dev, 0x81); 59062306a36Sopenharmony_ci usb_clear_halt(serial->dev, 0x02); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 59362306a36Sopenharmony_ci /* reset read/write statistics */ 59462306a36Sopenharmony_ci priv->bytes_in = 0; 59562306a36Sopenharmony_ci priv->bytes_out = 0; 59662306a36Sopenharmony_ci priv->cmd_count = 0; 59762306a36Sopenharmony_ci priv->rx_flags = 0; 59862306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci /* Set termios */ 60162306a36Sopenharmony_ci cypress_send(port); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci if (tty) 60462306a36Sopenharmony_ci cypress_set_termios(tty, port, NULL); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci /* setup the port and start reading from the device */ 60762306a36Sopenharmony_ci usb_fill_int_urb(port->interrupt_in_urb, serial->dev, 60862306a36Sopenharmony_ci usb_rcvintpipe(serial->dev, port->interrupt_in_endpointAddress), 60962306a36Sopenharmony_ci port->interrupt_in_urb->transfer_buffer, 61062306a36Sopenharmony_ci port->interrupt_in_urb->transfer_buffer_length, 61162306a36Sopenharmony_ci cypress_read_int_callback, port, priv->read_urb_interval); 61262306a36Sopenharmony_ci result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (result) { 61562306a36Sopenharmony_ci dev_err(&port->dev, 61662306a36Sopenharmony_ci "%s - failed submitting read urb, error %d\n", 61762306a36Sopenharmony_ci __func__, result); 61862306a36Sopenharmony_ci cypress_set_dead(port); 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci return result; 62262306a36Sopenharmony_ci} /* cypress_open */ 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic void cypress_dtr_rts(struct usb_serial_port *port, int on) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci struct cypress_private *priv = usb_get_serial_port_data(port); 62762306a36Sopenharmony_ci /* drop dtr and rts */ 62862306a36Sopenharmony_ci spin_lock_irq(&priv->lock); 62962306a36Sopenharmony_ci if (on == 0) 63062306a36Sopenharmony_ci priv->line_control = 0; 63162306a36Sopenharmony_ci else 63262306a36Sopenharmony_ci priv->line_control = CONTROL_DTR | CONTROL_RTS; 63362306a36Sopenharmony_ci priv->cmd_ctrl = 1; 63462306a36Sopenharmony_ci spin_unlock_irq(&priv->lock); 63562306a36Sopenharmony_ci cypress_write(NULL, port, NULL, 0); 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cistatic void cypress_close(struct usb_serial_port *port) 63962306a36Sopenharmony_ci{ 64062306a36Sopenharmony_ci struct cypress_private *priv = usb_get_serial_port_data(port); 64162306a36Sopenharmony_ci unsigned long flags; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 64462306a36Sopenharmony_ci kfifo_reset_out(&priv->write_fifo); 64562306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - stopping urbs\n", __func__); 64862306a36Sopenharmony_ci usb_kill_urb(port->interrupt_in_urb); 64962306a36Sopenharmony_ci usb_kill_urb(port->interrupt_out_urb); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci if (stats) 65262306a36Sopenharmony_ci dev_info(&port->dev, "Statistics: %d Bytes In | %d Bytes Out | %d Commands Issued\n", 65362306a36Sopenharmony_ci priv->bytes_in, priv->bytes_out, priv->cmd_count); 65462306a36Sopenharmony_ci} /* cypress_close */ 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_cistatic int cypress_write(struct tty_struct *tty, struct usb_serial_port *port, 65862306a36Sopenharmony_ci const unsigned char *buf, int count) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci struct cypress_private *priv = usb_get_serial_port_data(port); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - %d bytes\n", __func__, count); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci /* line control commands, which need to be executed immediately, 66562306a36Sopenharmony_ci are not put into the buffer for obvious reasons. 66662306a36Sopenharmony_ci */ 66762306a36Sopenharmony_ci if (priv->cmd_ctrl) { 66862306a36Sopenharmony_ci count = 0; 66962306a36Sopenharmony_ci goto finish; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci if (!count) 67362306a36Sopenharmony_ci return count; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci count = kfifo_in_locked(&priv->write_fifo, buf, count, &priv->lock); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cifinish: 67862306a36Sopenharmony_ci cypress_send(port); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci return count; 68162306a36Sopenharmony_ci} /* cypress_write */ 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_cistatic void cypress_send(struct usb_serial_port *port) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci int count = 0, result, offset, actual_size; 68762306a36Sopenharmony_ci struct cypress_private *priv = usb_get_serial_port_data(port); 68862306a36Sopenharmony_ci struct device *dev = &port->dev; 68962306a36Sopenharmony_ci unsigned long flags; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci if (!priv->comm_is_ok) 69262306a36Sopenharmony_ci return; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci dev_dbg(dev, "%s - interrupt out size is %d\n", __func__, 69562306a36Sopenharmony_ci port->interrupt_out_size); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 69862306a36Sopenharmony_ci if (priv->write_urb_in_use) { 69962306a36Sopenharmony_ci dev_dbg(dev, "%s - can't write, urb in use\n", __func__); 70062306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 70162306a36Sopenharmony_ci return; 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci /* clear buffer */ 70662306a36Sopenharmony_ci memset(port->interrupt_out_urb->transfer_buffer, 0, 70762306a36Sopenharmony_ci port->interrupt_out_size); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 71062306a36Sopenharmony_ci switch (priv->pkt_fmt) { 71162306a36Sopenharmony_ci default: 71262306a36Sopenharmony_ci case packet_format_1: 71362306a36Sopenharmony_ci /* this is for the CY7C64013... */ 71462306a36Sopenharmony_ci offset = 2; 71562306a36Sopenharmony_ci port->interrupt_out_buffer[0] = priv->line_control; 71662306a36Sopenharmony_ci break; 71762306a36Sopenharmony_ci case packet_format_2: 71862306a36Sopenharmony_ci /* this is for the CY7C63743... */ 71962306a36Sopenharmony_ci offset = 1; 72062306a36Sopenharmony_ci port->interrupt_out_buffer[0] = priv->line_control; 72162306a36Sopenharmony_ci break; 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci if (priv->line_control & CONTROL_RESET) 72562306a36Sopenharmony_ci priv->line_control &= ~CONTROL_RESET; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci if (priv->cmd_ctrl) { 72862306a36Sopenharmony_ci priv->cmd_count++; 72962306a36Sopenharmony_ci dev_dbg(dev, "%s - line control command being issued\n", __func__); 73062306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 73162306a36Sopenharmony_ci goto send; 73262306a36Sopenharmony_ci } else 73362306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci count = kfifo_out_locked(&priv->write_fifo, 73662306a36Sopenharmony_ci &port->interrupt_out_buffer[offset], 73762306a36Sopenharmony_ci port->interrupt_out_size - offset, 73862306a36Sopenharmony_ci &priv->lock); 73962306a36Sopenharmony_ci if (count == 0) 74062306a36Sopenharmony_ci return; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci switch (priv->pkt_fmt) { 74362306a36Sopenharmony_ci default: 74462306a36Sopenharmony_ci case packet_format_1: 74562306a36Sopenharmony_ci port->interrupt_out_buffer[1] = count; 74662306a36Sopenharmony_ci break; 74762306a36Sopenharmony_ci case packet_format_2: 74862306a36Sopenharmony_ci port->interrupt_out_buffer[0] |= count; 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci dev_dbg(dev, "%s - count is %d\n", __func__, count); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_cisend: 75462306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 75562306a36Sopenharmony_ci priv->write_urb_in_use = 1; 75662306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci if (priv->cmd_ctrl) 75962306a36Sopenharmony_ci actual_size = 1; 76062306a36Sopenharmony_ci else 76162306a36Sopenharmony_ci actual_size = count + 76262306a36Sopenharmony_ci (priv->pkt_fmt == packet_format_1 ? 2 : 1); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci usb_serial_debug_data(dev, __func__, port->interrupt_out_size, 76562306a36Sopenharmony_ci port->interrupt_out_urb->transfer_buffer); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci usb_fill_int_urb(port->interrupt_out_urb, port->serial->dev, 76862306a36Sopenharmony_ci usb_sndintpipe(port->serial->dev, port->interrupt_out_endpointAddress), 76962306a36Sopenharmony_ci port->interrupt_out_buffer, actual_size, 77062306a36Sopenharmony_ci cypress_write_int_callback, port, priv->write_urb_interval); 77162306a36Sopenharmony_ci result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC); 77262306a36Sopenharmony_ci if (result) { 77362306a36Sopenharmony_ci dev_err_console(port, 77462306a36Sopenharmony_ci "%s - failed submitting write urb, error %d\n", 77562306a36Sopenharmony_ci __func__, result); 77662306a36Sopenharmony_ci priv->write_urb_in_use = 0; 77762306a36Sopenharmony_ci cypress_set_dead(port); 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 78162306a36Sopenharmony_ci if (priv->cmd_ctrl) 78262306a36Sopenharmony_ci priv->cmd_ctrl = 0; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci /* do not count the line control and size bytes */ 78562306a36Sopenharmony_ci priv->bytes_out += count; 78662306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci usb_serial_port_softint(port); 78962306a36Sopenharmony_ci} /* cypress_send */ 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci/* returns how much space is available in the soft buffer */ 79362306a36Sopenharmony_cistatic unsigned int cypress_write_room(struct tty_struct *tty) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 79662306a36Sopenharmony_ci struct cypress_private *priv = usb_get_serial_port_data(port); 79762306a36Sopenharmony_ci unsigned int room; 79862306a36Sopenharmony_ci unsigned long flags; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 80162306a36Sopenharmony_ci room = kfifo_avail(&priv->write_fifo); 80262306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - returns %u\n", __func__, room); 80562306a36Sopenharmony_ci return room; 80662306a36Sopenharmony_ci} 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_cistatic int cypress_tiocmget(struct tty_struct *tty) 81062306a36Sopenharmony_ci{ 81162306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 81262306a36Sopenharmony_ci struct cypress_private *priv = usb_get_serial_port_data(port); 81362306a36Sopenharmony_ci __u8 status, control; 81462306a36Sopenharmony_ci unsigned int result = 0; 81562306a36Sopenharmony_ci unsigned long flags; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 81862306a36Sopenharmony_ci control = priv->line_control; 81962306a36Sopenharmony_ci status = priv->current_status; 82062306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci result = ((control & CONTROL_DTR) ? TIOCM_DTR : 0) 82362306a36Sopenharmony_ci | ((control & CONTROL_RTS) ? TIOCM_RTS : 0) 82462306a36Sopenharmony_ci | ((status & UART_CTS) ? TIOCM_CTS : 0) 82562306a36Sopenharmony_ci | ((status & UART_DSR) ? TIOCM_DSR : 0) 82662306a36Sopenharmony_ci | ((status & UART_RI) ? TIOCM_RI : 0) 82762306a36Sopenharmony_ci | ((status & UART_CD) ? TIOCM_CD : 0); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - result = %x\n", __func__, result); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci return result; 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_cistatic int cypress_tiocmset(struct tty_struct *tty, 83662306a36Sopenharmony_ci unsigned int set, unsigned int clear) 83762306a36Sopenharmony_ci{ 83862306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 83962306a36Sopenharmony_ci struct cypress_private *priv = usb_get_serial_port_data(port); 84062306a36Sopenharmony_ci unsigned long flags; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 84362306a36Sopenharmony_ci if (set & TIOCM_RTS) 84462306a36Sopenharmony_ci priv->line_control |= CONTROL_RTS; 84562306a36Sopenharmony_ci if (set & TIOCM_DTR) 84662306a36Sopenharmony_ci priv->line_control |= CONTROL_DTR; 84762306a36Sopenharmony_ci if (clear & TIOCM_RTS) 84862306a36Sopenharmony_ci priv->line_control &= ~CONTROL_RTS; 84962306a36Sopenharmony_ci if (clear & TIOCM_DTR) 85062306a36Sopenharmony_ci priv->line_control &= ~CONTROL_DTR; 85162306a36Sopenharmony_ci priv->cmd_ctrl = 1; 85262306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci return cypress_write(tty, port, NULL, 0); 85562306a36Sopenharmony_ci} 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_cistatic void cypress_earthmate_init_termios(struct tty_struct *tty) 85862306a36Sopenharmony_ci{ 85962306a36Sopenharmony_ci tty_encode_baud_rate(tty, 4800, 4800); 86062306a36Sopenharmony_ci} 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_cistatic void cypress_set_termios(struct tty_struct *tty, 86362306a36Sopenharmony_ci struct usb_serial_port *port, 86462306a36Sopenharmony_ci const struct ktermios *old_termios) 86562306a36Sopenharmony_ci{ 86662306a36Sopenharmony_ci struct cypress_private *priv = usb_get_serial_port_data(port); 86762306a36Sopenharmony_ci struct device *dev = &port->dev; 86862306a36Sopenharmony_ci int data_bits, stop_bits, parity_type, parity_enable; 86962306a36Sopenharmony_ci unsigned int cflag; 87062306a36Sopenharmony_ci unsigned long flags; 87162306a36Sopenharmony_ci __u8 oldlines; 87262306a36Sopenharmony_ci int linechange = 0; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci /* Unsupported features need clearing */ 87562306a36Sopenharmony_ci tty->termios.c_cflag &= ~(CMSPAR|CRTSCTS); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci cflag = tty->termios.c_cflag; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci /* set number of data bits, parity, stop bits */ 88062306a36Sopenharmony_ci /* when parity is disabled the parity type bit is ignored */ 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci /* 1 means 2 stop bits, 0 means 1 stop bit */ 88362306a36Sopenharmony_ci stop_bits = cflag & CSTOPB ? 1 : 0; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci if (cflag & PARENB) { 88662306a36Sopenharmony_ci parity_enable = 1; 88762306a36Sopenharmony_ci /* 1 means odd parity, 0 means even parity */ 88862306a36Sopenharmony_ci parity_type = cflag & PARODD ? 1 : 0; 88962306a36Sopenharmony_ci } else 89062306a36Sopenharmony_ci parity_enable = parity_type = 0; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci data_bits = tty_get_char_size(cflag); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 89562306a36Sopenharmony_ci oldlines = priv->line_control; 89662306a36Sopenharmony_ci if ((cflag & CBAUD) == B0) { 89762306a36Sopenharmony_ci /* drop dtr and rts */ 89862306a36Sopenharmony_ci dev_dbg(dev, "%s - dropping the lines, baud rate 0bps\n", __func__); 89962306a36Sopenharmony_ci priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS); 90062306a36Sopenharmony_ci } else 90162306a36Sopenharmony_ci priv->line_control = (CONTROL_DTR | CONTROL_RTS); 90262306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci dev_dbg(dev, "%s - sending %d stop_bits, %d parity_enable, %d parity_type, %d data_bits (+5)\n", 90562306a36Sopenharmony_ci __func__, stop_bits, parity_enable, parity_type, data_bits); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci cypress_serial_control(tty, port, tty_get_baud_rate(tty), 90862306a36Sopenharmony_ci data_bits, stop_bits, 90962306a36Sopenharmony_ci parity_enable, parity_type, 91062306a36Sopenharmony_ci 0, CYPRESS_SET_CONFIG); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci /* we perform a CYPRESS_GET_CONFIG so that the current settings are 91362306a36Sopenharmony_ci * filled into the private structure this should confirm that all is 91462306a36Sopenharmony_ci * working if it returns what we just set */ 91562306a36Sopenharmony_ci cypress_serial_control(tty, port, 0, 0, 0, 0, 0, 0, CYPRESS_GET_CONFIG); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci /* Here we can define custom tty settings for devices; the main tty 91862306a36Sopenharmony_ci * termios flag base comes from empeg.c */ 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 92162306a36Sopenharmony_ci if (priv->chiptype == CT_EARTHMATE && priv->baud_rate == 4800) { 92262306a36Sopenharmony_ci dev_dbg(dev, "Using custom termios settings for a baud rate of 4800bps.\n"); 92362306a36Sopenharmony_ci /* define custom termios settings for NMEA protocol */ 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci tty->termios.c_iflag /* input modes - */ 92662306a36Sopenharmony_ci &= ~(IGNBRK /* disable ignore break */ 92762306a36Sopenharmony_ci | BRKINT /* disable break causes interrupt */ 92862306a36Sopenharmony_ci | PARMRK /* disable mark parity errors */ 92962306a36Sopenharmony_ci | ISTRIP /* disable clear high bit of input char */ 93062306a36Sopenharmony_ci | INLCR /* disable translate NL to CR */ 93162306a36Sopenharmony_ci | IGNCR /* disable ignore CR */ 93262306a36Sopenharmony_ci | ICRNL /* disable translate CR to NL */ 93362306a36Sopenharmony_ci | IXON); /* disable enable XON/XOFF flow control */ 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci tty->termios.c_oflag /* output modes */ 93662306a36Sopenharmony_ci &= ~OPOST; /* disable postprocess output char */ 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci tty->termios.c_lflag /* line discipline modes */ 93962306a36Sopenharmony_ci &= ~(ECHO /* disable echo input characters */ 94062306a36Sopenharmony_ci | ECHONL /* disable echo new line */ 94162306a36Sopenharmony_ci | ICANON /* disable erase, kill, werase, and rprnt 94262306a36Sopenharmony_ci special characters */ 94362306a36Sopenharmony_ci | ISIG /* disable interrupt, quit, and suspend 94462306a36Sopenharmony_ci special characters */ 94562306a36Sopenharmony_ci | IEXTEN); /* disable non-POSIX special characters */ 94662306a36Sopenharmony_ci } /* CT_CYPHIDCOM: Application should handle this for device */ 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci linechange = (priv->line_control != oldlines); 94962306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci /* if necessary, set lines */ 95262306a36Sopenharmony_ci if (linechange) { 95362306a36Sopenharmony_ci priv->cmd_ctrl = 1; 95462306a36Sopenharmony_ci cypress_write(tty, port, NULL, 0); 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci} /* cypress_set_termios */ 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci/* returns amount of data still left in soft buffer */ 96062306a36Sopenharmony_cistatic unsigned int cypress_chars_in_buffer(struct tty_struct *tty) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 96362306a36Sopenharmony_ci struct cypress_private *priv = usb_get_serial_port_data(port); 96462306a36Sopenharmony_ci unsigned int chars; 96562306a36Sopenharmony_ci unsigned long flags; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 96862306a36Sopenharmony_ci chars = kfifo_len(&priv->write_fifo); 96962306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - returns %u\n", __func__, chars); 97262306a36Sopenharmony_ci return chars; 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_cistatic void cypress_throttle(struct tty_struct *tty) 97762306a36Sopenharmony_ci{ 97862306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 97962306a36Sopenharmony_ci struct cypress_private *priv = usb_get_serial_port_data(port); 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci spin_lock_irq(&priv->lock); 98262306a36Sopenharmony_ci priv->rx_flags = THROTTLED; 98362306a36Sopenharmony_ci spin_unlock_irq(&priv->lock); 98462306a36Sopenharmony_ci} 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_cistatic void cypress_unthrottle(struct tty_struct *tty) 98862306a36Sopenharmony_ci{ 98962306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 99062306a36Sopenharmony_ci struct cypress_private *priv = usb_get_serial_port_data(port); 99162306a36Sopenharmony_ci int actually_throttled, result; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci spin_lock_irq(&priv->lock); 99462306a36Sopenharmony_ci actually_throttled = priv->rx_flags & ACTUALLY_THROTTLED; 99562306a36Sopenharmony_ci priv->rx_flags = 0; 99662306a36Sopenharmony_ci spin_unlock_irq(&priv->lock); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci if (!priv->comm_is_ok) 99962306a36Sopenharmony_ci return; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci if (actually_throttled) { 100262306a36Sopenharmony_ci result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); 100362306a36Sopenharmony_ci if (result) { 100462306a36Sopenharmony_ci dev_err(&port->dev, "%s - failed submitting read urb, " 100562306a36Sopenharmony_ci "error %d\n", __func__, result); 100662306a36Sopenharmony_ci cypress_set_dead(port); 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci } 100962306a36Sopenharmony_ci} 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_cistatic void cypress_read_int_callback(struct urb *urb) 101362306a36Sopenharmony_ci{ 101462306a36Sopenharmony_ci struct usb_serial_port *port = urb->context; 101562306a36Sopenharmony_ci struct cypress_private *priv = usb_get_serial_port_data(port); 101662306a36Sopenharmony_ci struct device *dev = &urb->dev->dev; 101762306a36Sopenharmony_ci struct tty_struct *tty; 101862306a36Sopenharmony_ci unsigned char *data = urb->transfer_buffer; 101962306a36Sopenharmony_ci unsigned long flags; 102062306a36Sopenharmony_ci char tty_flag = TTY_NORMAL; 102162306a36Sopenharmony_ci int bytes = 0; 102262306a36Sopenharmony_ci int result; 102362306a36Sopenharmony_ci int i = 0; 102462306a36Sopenharmony_ci int status = urb->status; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci switch (status) { 102762306a36Sopenharmony_ci case 0: /* success */ 102862306a36Sopenharmony_ci break; 102962306a36Sopenharmony_ci case -ECONNRESET: 103062306a36Sopenharmony_ci case -ENOENT: 103162306a36Sopenharmony_ci case -ESHUTDOWN: 103262306a36Sopenharmony_ci /* precursor to disconnect so just go away */ 103362306a36Sopenharmony_ci return; 103462306a36Sopenharmony_ci case -EPIPE: 103562306a36Sopenharmony_ci /* Can't call usb_clear_halt while in_interrupt */ 103662306a36Sopenharmony_ci fallthrough; 103762306a36Sopenharmony_ci default: 103862306a36Sopenharmony_ci /* something ugly is going on... */ 103962306a36Sopenharmony_ci dev_err(dev, "%s - unexpected nonzero read status received: %d\n", 104062306a36Sopenharmony_ci __func__, status); 104162306a36Sopenharmony_ci cypress_set_dead(port); 104262306a36Sopenharmony_ci return; 104362306a36Sopenharmony_ci } 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 104662306a36Sopenharmony_ci if (priv->rx_flags & THROTTLED) { 104762306a36Sopenharmony_ci dev_dbg(dev, "%s - now throttling\n", __func__); 104862306a36Sopenharmony_ci priv->rx_flags |= ACTUALLY_THROTTLED; 104962306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 105062306a36Sopenharmony_ci return; 105162306a36Sopenharmony_ci } 105262306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci tty = tty_port_tty_get(&port->port); 105562306a36Sopenharmony_ci if (!tty) { 105662306a36Sopenharmony_ci dev_dbg(dev, "%s - bad tty pointer - exiting\n", __func__); 105762306a36Sopenharmony_ci return; 105862306a36Sopenharmony_ci } 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 106162306a36Sopenharmony_ci result = urb->actual_length; 106262306a36Sopenharmony_ci switch (priv->pkt_fmt) { 106362306a36Sopenharmony_ci default: 106462306a36Sopenharmony_ci case packet_format_1: 106562306a36Sopenharmony_ci /* This is for the CY7C64013... */ 106662306a36Sopenharmony_ci priv->current_status = data[0] & 0xF8; 106762306a36Sopenharmony_ci bytes = data[1] + 2; 106862306a36Sopenharmony_ci i = 2; 106962306a36Sopenharmony_ci break; 107062306a36Sopenharmony_ci case packet_format_2: 107162306a36Sopenharmony_ci /* This is for the CY7C63743... */ 107262306a36Sopenharmony_ci priv->current_status = data[0] & 0xF8; 107362306a36Sopenharmony_ci bytes = (data[0] & 0x07) + 1; 107462306a36Sopenharmony_ci i = 1; 107562306a36Sopenharmony_ci break; 107662306a36Sopenharmony_ci } 107762306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 107862306a36Sopenharmony_ci if (result < bytes) { 107962306a36Sopenharmony_ci dev_dbg(dev, 108062306a36Sopenharmony_ci "%s - wrong packet size - received %d bytes but packet said %d bytes\n", 108162306a36Sopenharmony_ci __func__, result, bytes); 108262306a36Sopenharmony_ci goto continue_read; 108362306a36Sopenharmony_ci } 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 108862306a36Sopenharmony_ci /* check to see if status has changed */ 108962306a36Sopenharmony_ci if (priv->current_status != priv->prev_status) { 109062306a36Sopenharmony_ci u8 delta = priv->current_status ^ priv->prev_status; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci if (delta & UART_MSR_MASK) { 109362306a36Sopenharmony_ci if (delta & UART_CTS) 109462306a36Sopenharmony_ci port->icount.cts++; 109562306a36Sopenharmony_ci if (delta & UART_DSR) 109662306a36Sopenharmony_ci port->icount.dsr++; 109762306a36Sopenharmony_ci if (delta & UART_RI) 109862306a36Sopenharmony_ci port->icount.rng++; 109962306a36Sopenharmony_ci if (delta & UART_CD) 110062306a36Sopenharmony_ci port->icount.dcd++; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci wake_up_interruptible(&port->port.delta_msr_wait); 110362306a36Sopenharmony_ci } 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci priv->prev_status = priv->current_status; 110662306a36Sopenharmony_ci } 110762306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci /* hangup, as defined in acm.c... this might be a bad place for it 111062306a36Sopenharmony_ci * though */ 111162306a36Sopenharmony_ci if (tty && !C_CLOCAL(tty) && !(priv->current_status & UART_CD)) { 111262306a36Sopenharmony_ci dev_dbg(dev, "%s - calling hangup\n", __func__); 111362306a36Sopenharmony_ci tty_hangup(tty); 111462306a36Sopenharmony_ci goto continue_read; 111562306a36Sopenharmony_ci } 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci /* There is one error bit... I'm assuming it is a parity error 111862306a36Sopenharmony_ci * indicator as the generic firmware will set this bit to 1 if a 111962306a36Sopenharmony_ci * parity error occurs. 112062306a36Sopenharmony_ci * I can not find reference to any other error events. */ 112162306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 112262306a36Sopenharmony_ci if (priv->current_status & CYP_ERROR) { 112362306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 112462306a36Sopenharmony_ci tty_flag = TTY_PARITY; 112562306a36Sopenharmony_ci dev_dbg(dev, "%s - Parity Error detected\n", __func__); 112662306a36Sopenharmony_ci } else 112762306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci /* process read if there is data other than line status */ 113062306a36Sopenharmony_ci if (bytes > i) { 113162306a36Sopenharmony_ci tty_insert_flip_string_fixed_flag(&port->port, data + i, 113262306a36Sopenharmony_ci tty_flag, bytes - i); 113362306a36Sopenharmony_ci tty_flip_buffer_push(&port->port); 113462306a36Sopenharmony_ci } 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 113762306a36Sopenharmony_ci /* control and status byte(s) are also counted */ 113862306a36Sopenharmony_ci priv->bytes_in += bytes; 113962306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_cicontinue_read: 114262306a36Sopenharmony_ci tty_kref_put(tty); 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci /* Continue trying to always read */ 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci if (priv->comm_is_ok) { 114762306a36Sopenharmony_ci usb_fill_int_urb(port->interrupt_in_urb, port->serial->dev, 114862306a36Sopenharmony_ci usb_rcvintpipe(port->serial->dev, 114962306a36Sopenharmony_ci port->interrupt_in_endpointAddress), 115062306a36Sopenharmony_ci port->interrupt_in_urb->transfer_buffer, 115162306a36Sopenharmony_ci port->interrupt_in_urb->transfer_buffer_length, 115262306a36Sopenharmony_ci cypress_read_int_callback, port, 115362306a36Sopenharmony_ci priv->read_urb_interval); 115462306a36Sopenharmony_ci result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); 115562306a36Sopenharmony_ci if (result && result != -EPERM) { 115662306a36Sopenharmony_ci dev_err(dev, "%s - failed resubmitting read urb, error %d\n", 115762306a36Sopenharmony_ci __func__, result); 115862306a36Sopenharmony_ci cypress_set_dead(port); 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci } 116162306a36Sopenharmony_ci} /* cypress_read_int_callback */ 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_cistatic void cypress_write_int_callback(struct urb *urb) 116562306a36Sopenharmony_ci{ 116662306a36Sopenharmony_ci struct usb_serial_port *port = urb->context; 116762306a36Sopenharmony_ci struct cypress_private *priv = usb_get_serial_port_data(port); 116862306a36Sopenharmony_ci struct device *dev = &urb->dev->dev; 116962306a36Sopenharmony_ci int status = urb->status; 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci switch (status) { 117262306a36Sopenharmony_ci case 0: 117362306a36Sopenharmony_ci /* success */ 117462306a36Sopenharmony_ci break; 117562306a36Sopenharmony_ci case -ECONNRESET: 117662306a36Sopenharmony_ci case -ENOENT: 117762306a36Sopenharmony_ci case -ESHUTDOWN: 117862306a36Sopenharmony_ci /* this urb is terminated, clean up */ 117962306a36Sopenharmony_ci dev_dbg(dev, "%s - urb shutting down with status: %d\n", 118062306a36Sopenharmony_ci __func__, status); 118162306a36Sopenharmony_ci priv->write_urb_in_use = 0; 118262306a36Sopenharmony_ci return; 118362306a36Sopenharmony_ci case -EPIPE: 118462306a36Sopenharmony_ci /* Cannot call usb_clear_halt while in_interrupt */ 118562306a36Sopenharmony_ci fallthrough; 118662306a36Sopenharmony_ci default: 118762306a36Sopenharmony_ci dev_err(dev, "%s - unexpected nonzero write status received: %d\n", 118862306a36Sopenharmony_ci __func__, status); 118962306a36Sopenharmony_ci cypress_set_dead(port); 119062306a36Sopenharmony_ci break; 119162306a36Sopenharmony_ci } 119262306a36Sopenharmony_ci priv->write_urb_in_use = 0; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci /* send any buffered data */ 119562306a36Sopenharmony_ci cypress_send(port); 119662306a36Sopenharmony_ci} 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table_combined); 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 120162306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 120262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_cimodule_param(stats, bool, 0644); 120562306a36Sopenharmony_ciMODULE_PARM_DESC(stats, "Enable statistics or not"); 120662306a36Sopenharmony_cimodule_param(interval, int, 0644); 120762306a36Sopenharmony_ciMODULE_PARM_DESC(interval, "Overrides interrupt interval"); 120862306a36Sopenharmony_cimodule_param(unstable_bauds, bool, 0644); 120962306a36Sopenharmony_ciMODULE_PARM_DESC(unstable_bauds, "Allow unstable baud rates"); 1210