162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * USB ConnectTech WhiteHEAT driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2002 662306a36Sopenharmony_ci * Connect Tech Inc. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright (C) 1999 - 2001 962306a36Sopenharmony_ci * Greg Kroah-Hartman (greg@kroah.com) 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * See Documentation/usb/usb-serial.rst for more information on using this 1262306a36Sopenharmony_ci * driver 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/errno.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/tty.h> 1962306a36Sopenharmony_ci#include <linux/tty_driver.h> 2062306a36Sopenharmony_ci#include <linux/tty_flip.h> 2162306a36Sopenharmony_ci#include <linux/module.h> 2262306a36Sopenharmony_ci#include <linux/spinlock.h> 2362306a36Sopenharmony_ci#include <linux/mutex.h> 2462306a36Sopenharmony_ci#include <linux/uaccess.h> 2562306a36Sopenharmony_ci#include <asm/termbits.h> 2662306a36Sopenharmony_ci#include <linux/usb.h> 2762306a36Sopenharmony_ci#include <linux/serial_reg.h> 2862306a36Sopenharmony_ci#include <linux/serial.h> 2962306a36Sopenharmony_ci#include <linux/usb/serial.h> 3062306a36Sopenharmony_ci#include <linux/usb/ezusb.h> 3162306a36Sopenharmony_ci#include "whiteheat.h" /* WhiteHEAT specific commands */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * Version Information 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ci#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Stuart MacDonald <stuartm@connecttech.com>" 3762306a36Sopenharmony_ci#define DRIVER_DESC "USB ConnectTech WhiteHEAT driver" 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define CONNECT_TECH_VENDOR_ID 0x0710 4062306a36Sopenharmony_ci#define CONNECT_TECH_FAKE_WHITE_HEAT_ID 0x0001 4162306a36Sopenharmony_ci#define CONNECT_TECH_WHITE_HEAT_ID 0x8001 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* 4462306a36Sopenharmony_ci ID tables for whiteheat are unusual, because we want to different 4562306a36Sopenharmony_ci things for different versions of the device. Eventually, this 4662306a36Sopenharmony_ci will be doable from a single table. But, for now, we define two 4762306a36Sopenharmony_ci separate ID tables, and then a third table that combines them 4862306a36Sopenharmony_ci just for the purpose of exporting the autoloading information. 4962306a36Sopenharmony_ci*/ 5062306a36Sopenharmony_cistatic const struct usb_device_id id_table_std[] = { 5162306a36Sopenharmony_ci { USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_WHITE_HEAT_ID) }, 5262306a36Sopenharmony_ci { } /* Terminating entry */ 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic const struct usb_device_id id_table_prerenumeration[] = { 5662306a36Sopenharmony_ci { USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_FAKE_WHITE_HEAT_ID) }, 5762306a36Sopenharmony_ci { } /* Terminating entry */ 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic const struct usb_device_id id_table_combined[] = { 6162306a36Sopenharmony_ci { USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_WHITE_HEAT_ID) }, 6262306a36Sopenharmony_ci { USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_FAKE_WHITE_HEAT_ID) }, 6362306a36Sopenharmony_ci { } /* Terminating entry */ 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table_combined); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* function prototypes for the Connect Tech WhiteHEAT prerenumeration device */ 7062306a36Sopenharmony_cistatic int whiteheat_firmware_download(struct usb_serial *serial, 7162306a36Sopenharmony_ci const struct usb_device_id *id); 7262306a36Sopenharmony_cistatic int whiteheat_firmware_attach(struct usb_serial *serial); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* function prototypes for the Connect Tech WhiteHEAT serial converter */ 7562306a36Sopenharmony_cistatic int whiteheat_attach(struct usb_serial *serial); 7662306a36Sopenharmony_cistatic void whiteheat_release(struct usb_serial *serial); 7762306a36Sopenharmony_cistatic int whiteheat_port_probe(struct usb_serial_port *port); 7862306a36Sopenharmony_cistatic void whiteheat_port_remove(struct usb_serial_port *port); 7962306a36Sopenharmony_cistatic int whiteheat_open(struct tty_struct *tty, 8062306a36Sopenharmony_ci struct usb_serial_port *port); 8162306a36Sopenharmony_cistatic void whiteheat_close(struct usb_serial_port *port); 8262306a36Sopenharmony_cistatic void whiteheat_get_serial(struct tty_struct *tty, 8362306a36Sopenharmony_ci struct serial_struct *ss); 8462306a36Sopenharmony_cistatic void whiteheat_set_termios(struct tty_struct *tty, 8562306a36Sopenharmony_ci struct usb_serial_port *port, 8662306a36Sopenharmony_ci const struct ktermios *old_termios); 8762306a36Sopenharmony_cistatic int whiteheat_tiocmget(struct tty_struct *tty); 8862306a36Sopenharmony_cistatic int whiteheat_tiocmset(struct tty_struct *tty, 8962306a36Sopenharmony_ci unsigned int set, unsigned int clear); 9062306a36Sopenharmony_cistatic int whiteheat_break_ctl(struct tty_struct *tty, int break_state); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic struct usb_serial_driver whiteheat_fake_device = { 9362306a36Sopenharmony_ci .driver = { 9462306a36Sopenharmony_ci .owner = THIS_MODULE, 9562306a36Sopenharmony_ci .name = "whiteheatnofirm", 9662306a36Sopenharmony_ci }, 9762306a36Sopenharmony_ci .description = "Connect Tech - WhiteHEAT - (prerenumeration)", 9862306a36Sopenharmony_ci .id_table = id_table_prerenumeration, 9962306a36Sopenharmony_ci .num_ports = 1, 10062306a36Sopenharmony_ci .probe = whiteheat_firmware_download, 10162306a36Sopenharmony_ci .attach = whiteheat_firmware_attach, 10262306a36Sopenharmony_ci}; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic struct usb_serial_driver whiteheat_device = { 10562306a36Sopenharmony_ci .driver = { 10662306a36Sopenharmony_ci .owner = THIS_MODULE, 10762306a36Sopenharmony_ci .name = "whiteheat", 10862306a36Sopenharmony_ci }, 10962306a36Sopenharmony_ci .description = "Connect Tech - WhiteHEAT", 11062306a36Sopenharmony_ci .id_table = id_table_std, 11162306a36Sopenharmony_ci .num_ports = 4, 11262306a36Sopenharmony_ci .num_bulk_in = 5, 11362306a36Sopenharmony_ci .num_bulk_out = 5, 11462306a36Sopenharmony_ci .attach = whiteheat_attach, 11562306a36Sopenharmony_ci .release = whiteheat_release, 11662306a36Sopenharmony_ci .port_probe = whiteheat_port_probe, 11762306a36Sopenharmony_ci .port_remove = whiteheat_port_remove, 11862306a36Sopenharmony_ci .open = whiteheat_open, 11962306a36Sopenharmony_ci .close = whiteheat_close, 12062306a36Sopenharmony_ci .get_serial = whiteheat_get_serial, 12162306a36Sopenharmony_ci .set_termios = whiteheat_set_termios, 12262306a36Sopenharmony_ci .break_ctl = whiteheat_break_ctl, 12362306a36Sopenharmony_ci .tiocmget = whiteheat_tiocmget, 12462306a36Sopenharmony_ci .tiocmset = whiteheat_tiocmset, 12562306a36Sopenharmony_ci .throttle = usb_serial_generic_throttle, 12662306a36Sopenharmony_ci .unthrottle = usb_serial_generic_unthrottle, 12762306a36Sopenharmony_ci}; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = { 13062306a36Sopenharmony_ci &whiteheat_fake_device, &whiteheat_device, NULL 13162306a36Sopenharmony_ci}; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistruct whiteheat_command_private { 13462306a36Sopenharmony_ci struct mutex mutex; 13562306a36Sopenharmony_ci __u8 port_running; 13662306a36Sopenharmony_ci __u8 command_finished; 13762306a36Sopenharmony_ci wait_queue_head_t wait_command; /* for handling sleeping whilst 13862306a36Sopenharmony_ci waiting for a command to 13962306a36Sopenharmony_ci finish */ 14062306a36Sopenharmony_ci __u8 result_buffer[64]; 14162306a36Sopenharmony_ci}; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistruct whiteheat_private { 14462306a36Sopenharmony_ci __u8 mcr; /* FIXME: no locking on mcr */ 14562306a36Sopenharmony_ci}; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci/* local function prototypes */ 14962306a36Sopenharmony_cistatic int start_command_port(struct usb_serial *serial); 15062306a36Sopenharmony_cistatic void stop_command_port(struct usb_serial *serial); 15162306a36Sopenharmony_cistatic void command_port_write_callback(struct urb *urb); 15262306a36Sopenharmony_cistatic void command_port_read_callback(struct urb *urb); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic int firm_send_command(struct usb_serial_port *port, __u8 command, 15562306a36Sopenharmony_ci __u8 *data, __u8 datasize); 15662306a36Sopenharmony_cistatic int firm_open(struct usb_serial_port *port); 15762306a36Sopenharmony_cistatic int firm_close(struct usb_serial_port *port); 15862306a36Sopenharmony_cistatic void firm_setup_port(struct tty_struct *tty); 15962306a36Sopenharmony_cistatic int firm_set_rts(struct usb_serial_port *port, __u8 onoff); 16062306a36Sopenharmony_cistatic int firm_set_dtr(struct usb_serial_port *port, __u8 onoff); 16162306a36Sopenharmony_cistatic int firm_set_break(struct usb_serial_port *port, __u8 onoff); 16262306a36Sopenharmony_cistatic int firm_purge(struct usb_serial_port *port, __u8 rxtx); 16362306a36Sopenharmony_cistatic int firm_get_dtr_rts(struct usb_serial_port *port); 16462306a36Sopenharmony_cistatic int firm_report_tx_done(struct usb_serial_port *port); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci#define COMMAND_PORT 4 16862306a36Sopenharmony_ci#define COMMAND_TIMEOUT (2*HZ) /* 2 second timeout for a command */ 16962306a36Sopenharmony_ci#define COMMAND_TIMEOUT_MS 2000 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci/***************************************************************************** 17362306a36Sopenharmony_ci * Connect Tech's White Heat prerenumeration driver functions 17462306a36Sopenharmony_ci *****************************************************************************/ 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci/* steps to download the firmware to the WhiteHEAT device: 17762306a36Sopenharmony_ci - hold the reset (by writing to the reset bit of the CPUCS register) 17862306a36Sopenharmony_ci - download the VEND_AX.HEX file to the chip using VENDOR_REQUEST-ANCHOR_LOAD 17962306a36Sopenharmony_ci - release the reset (by writing to the CPUCS register) 18062306a36Sopenharmony_ci - download the WH.HEX file for all addresses greater than 0x1b3f using 18162306a36Sopenharmony_ci VENDOR_REQUEST-ANCHOR_EXTERNAL_RAM_LOAD 18262306a36Sopenharmony_ci - hold the reset 18362306a36Sopenharmony_ci - download the WH.HEX file for all addresses less than 0x1b40 using 18462306a36Sopenharmony_ci VENDOR_REQUEST_ANCHOR_LOAD 18562306a36Sopenharmony_ci - release the reset 18662306a36Sopenharmony_ci - device renumerated itself and comes up as new device id with all 18762306a36Sopenharmony_ci firmware download completed. 18862306a36Sopenharmony_ci*/ 18962306a36Sopenharmony_cistatic int whiteheat_firmware_download(struct usb_serial *serial, 19062306a36Sopenharmony_ci const struct usb_device_id *id) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci int response; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci response = ezusb_fx1_ihex_firmware_download(serial->dev, "whiteheat_loader.fw"); 19562306a36Sopenharmony_ci if (response >= 0) { 19662306a36Sopenharmony_ci response = ezusb_fx1_ihex_firmware_download(serial->dev, "whiteheat.fw"); 19762306a36Sopenharmony_ci if (response >= 0) 19862306a36Sopenharmony_ci return 0; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci return -ENOENT; 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic int whiteheat_firmware_attach(struct usb_serial *serial) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci /* We want this device to fail to have a driver assigned to it */ 20762306a36Sopenharmony_ci return 1; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci/***************************************************************************** 21262306a36Sopenharmony_ci * Connect Tech's White Heat serial driver functions 21362306a36Sopenharmony_ci *****************************************************************************/ 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic int whiteheat_attach(struct usb_serial *serial) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct usb_serial_port *command_port; 21862306a36Sopenharmony_ci struct whiteheat_command_private *command_info; 21962306a36Sopenharmony_ci struct whiteheat_hw_info *hw_info; 22062306a36Sopenharmony_ci int pipe; 22162306a36Sopenharmony_ci int ret; 22262306a36Sopenharmony_ci int alen; 22362306a36Sopenharmony_ci __u8 *command; 22462306a36Sopenharmony_ci __u8 *result; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci command_port = serial->port[COMMAND_PORT]; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci pipe = usb_sndbulkpipe(serial->dev, 22962306a36Sopenharmony_ci command_port->bulk_out_endpointAddress); 23062306a36Sopenharmony_ci command = kmalloc(2, GFP_KERNEL); 23162306a36Sopenharmony_ci if (!command) 23262306a36Sopenharmony_ci goto no_command_buffer; 23362306a36Sopenharmony_ci command[0] = WHITEHEAT_GET_HW_INFO; 23462306a36Sopenharmony_ci command[1] = 0; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci result = kmalloc(sizeof(*hw_info) + 1, GFP_KERNEL); 23762306a36Sopenharmony_ci if (!result) 23862306a36Sopenharmony_ci goto no_result_buffer; 23962306a36Sopenharmony_ci /* 24062306a36Sopenharmony_ci * When the module is reloaded the firmware is still there and 24162306a36Sopenharmony_ci * the endpoints are still in the usb core unchanged. This is the 24262306a36Sopenharmony_ci * unlinking bug in disguise. Same for the call below. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci usb_clear_halt(serial->dev, pipe); 24562306a36Sopenharmony_ci ret = usb_bulk_msg(serial->dev, pipe, command, 2, 24662306a36Sopenharmony_ci &alen, COMMAND_TIMEOUT_MS); 24762306a36Sopenharmony_ci if (ret) { 24862306a36Sopenharmony_ci dev_err(&serial->dev->dev, "%s: Couldn't send command [%d]\n", 24962306a36Sopenharmony_ci serial->type->description, ret); 25062306a36Sopenharmony_ci goto no_firmware; 25162306a36Sopenharmony_ci } else if (alen != 2) { 25262306a36Sopenharmony_ci dev_err(&serial->dev->dev, "%s: Send command incomplete [%d]\n", 25362306a36Sopenharmony_ci serial->type->description, alen); 25462306a36Sopenharmony_ci goto no_firmware; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci pipe = usb_rcvbulkpipe(serial->dev, 25862306a36Sopenharmony_ci command_port->bulk_in_endpointAddress); 25962306a36Sopenharmony_ci /* See the comment on the usb_clear_halt() above */ 26062306a36Sopenharmony_ci usb_clear_halt(serial->dev, pipe); 26162306a36Sopenharmony_ci ret = usb_bulk_msg(serial->dev, pipe, result, 26262306a36Sopenharmony_ci sizeof(*hw_info) + 1, &alen, COMMAND_TIMEOUT_MS); 26362306a36Sopenharmony_ci if (ret) { 26462306a36Sopenharmony_ci dev_err(&serial->dev->dev, "%s: Couldn't get results [%d]\n", 26562306a36Sopenharmony_ci serial->type->description, ret); 26662306a36Sopenharmony_ci goto no_firmware; 26762306a36Sopenharmony_ci } else if (alen != sizeof(*hw_info) + 1) { 26862306a36Sopenharmony_ci dev_err(&serial->dev->dev, "%s: Get results incomplete [%d]\n", 26962306a36Sopenharmony_ci serial->type->description, alen); 27062306a36Sopenharmony_ci goto no_firmware; 27162306a36Sopenharmony_ci } else if (result[0] != command[0]) { 27262306a36Sopenharmony_ci dev_err(&serial->dev->dev, "%s: Command failed [%d]\n", 27362306a36Sopenharmony_ci serial->type->description, result[0]); 27462306a36Sopenharmony_ci goto no_firmware; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci hw_info = (struct whiteheat_hw_info *)&result[1]; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci dev_info(&serial->dev->dev, "%s: Firmware v%d.%02d\n", 28062306a36Sopenharmony_ci serial->type->description, 28162306a36Sopenharmony_ci hw_info->sw_major_rev, hw_info->sw_minor_rev); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci command_info = kmalloc(sizeof(struct whiteheat_command_private), 28462306a36Sopenharmony_ci GFP_KERNEL); 28562306a36Sopenharmony_ci if (!command_info) 28662306a36Sopenharmony_ci goto no_command_private; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci mutex_init(&command_info->mutex); 28962306a36Sopenharmony_ci command_info->port_running = 0; 29062306a36Sopenharmony_ci init_waitqueue_head(&command_info->wait_command); 29162306a36Sopenharmony_ci usb_set_serial_port_data(command_port, command_info); 29262306a36Sopenharmony_ci command_port->write_urb->complete = command_port_write_callback; 29362306a36Sopenharmony_ci command_port->read_urb->complete = command_port_read_callback; 29462306a36Sopenharmony_ci kfree(result); 29562306a36Sopenharmony_ci kfree(command); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci return 0; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cino_firmware: 30062306a36Sopenharmony_ci /* Firmware likely not running */ 30162306a36Sopenharmony_ci dev_err(&serial->dev->dev, 30262306a36Sopenharmony_ci "%s: Unable to retrieve firmware version, try replugging\n", 30362306a36Sopenharmony_ci serial->type->description); 30462306a36Sopenharmony_ci dev_err(&serial->dev->dev, 30562306a36Sopenharmony_ci "%s: If the firmware is not running (status led not blinking)\n", 30662306a36Sopenharmony_ci serial->type->description); 30762306a36Sopenharmony_ci dev_err(&serial->dev->dev, 30862306a36Sopenharmony_ci "%s: please contact support@connecttech.com\n", 30962306a36Sopenharmony_ci serial->type->description); 31062306a36Sopenharmony_ci kfree(result); 31162306a36Sopenharmony_ci kfree(command); 31262306a36Sopenharmony_ci return -ENODEV; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cino_command_private: 31562306a36Sopenharmony_ci kfree(result); 31662306a36Sopenharmony_cino_result_buffer: 31762306a36Sopenharmony_ci kfree(command); 31862306a36Sopenharmony_cino_command_buffer: 31962306a36Sopenharmony_ci return -ENOMEM; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic void whiteheat_release(struct usb_serial *serial) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci struct usb_serial_port *command_port; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci /* free up our private data for our command port */ 32762306a36Sopenharmony_ci command_port = serial->port[COMMAND_PORT]; 32862306a36Sopenharmony_ci kfree(usb_get_serial_port_data(command_port)); 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic int whiteheat_port_probe(struct usb_serial_port *port) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci struct whiteheat_private *info; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci info = kzalloc(sizeof(*info), GFP_KERNEL); 33662306a36Sopenharmony_ci if (!info) 33762306a36Sopenharmony_ci return -ENOMEM; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci usb_set_serial_port_data(port, info); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci return 0; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic void whiteheat_port_remove(struct usb_serial_port *port) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci struct whiteheat_private *info; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci info = usb_get_serial_port_data(port); 34962306a36Sopenharmony_ci kfree(info); 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic int whiteheat_open(struct tty_struct *tty, struct usb_serial_port *port) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci int retval; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci retval = start_command_port(port->serial); 35762306a36Sopenharmony_ci if (retval) 35862306a36Sopenharmony_ci goto exit; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* send an open port command */ 36162306a36Sopenharmony_ci retval = firm_open(port); 36262306a36Sopenharmony_ci if (retval) { 36362306a36Sopenharmony_ci stop_command_port(port->serial); 36462306a36Sopenharmony_ci goto exit; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci retval = firm_purge(port, WHITEHEAT_PURGE_RX | WHITEHEAT_PURGE_TX); 36862306a36Sopenharmony_ci if (retval) { 36962306a36Sopenharmony_ci firm_close(port); 37062306a36Sopenharmony_ci stop_command_port(port->serial); 37162306a36Sopenharmony_ci goto exit; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (tty) 37562306a36Sopenharmony_ci firm_setup_port(tty); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci /* Work around HCD bugs */ 37862306a36Sopenharmony_ci usb_clear_halt(port->serial->dev, port->read_urb->pipe); 37962306a36Sopenharmony_ci usb_clear_halt(port->serial->dev, port->write_urb->pipe); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci retval = usb_serial_generic_open(tty, port); 38262306a36Sopenharmony_ci if (retval) { 38362306a36Sopenharmony_ci firm_close(port); 38462306a36Sopenharmony_ci stop_command_port(port->serial); 38562306a36Sopenharmony_ci goto exit; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ciexit: 38862306a36Sopenharmony_ci return retval; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic void whiteheat_close(struct usb_serial_port *port) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci firm_report_tx_done(port); 39562306a36Sopenharmony_ci firm_close(port); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci usb_serial_generic_close(port); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci stop_command_port(port->serial); 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic int whiteheat_tiocmget(struct tty_struct *tty) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 40562306a36Sopenharmony_ci struct whiteheat_private *info = usb_get_serial_port_data(port); 40662306a36Sopenharmony_ci unsigned int modem_signals = 0; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci firm_get_dtr_rts(port); 40962306a36Sopenharmony_ci if (info->mcr & UART_MCR_DTR) 41062306a36Sopenharmony_ci modem_signals |= TIOCM_DTR; 41162306a36Sopenharmony_ci if (info->mcr & UART_MCR_RTS) 41262306a36Sopenharmony_ci modem_signals |= TIOCM_RTS; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci return modem_signals; 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic int whiteheat_tiocmset(struct tty_struct *tty, 41862306a36Sopenharmony_ci unsigned int set, unsigned int clear) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 42162306a36Sopenharmony_ci struct whiteheat_private *info = usb_get_serial_port_data(port); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (set & TIOCM_RTS) 42462306a36Sopenharmony_ci info->mcr |= UART_MCR_RTS; 42562306a36Sopenharmony_ci if (set & TIOCM_DTR) 42662306a36Sopenharmony_ci info->mcr |= UART_MCR_DTR; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (clear & TIOCM_RTS) 42962306a36Sopenharmony_ci info->mcr &= ~UART_MCR_RTS; 43062306a36Sopenharmony_ci if (clear & TIOCM_DTR) 43162306a36Sopenharmony_ci info->mcr &= ~UART_MCR_DTR; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci firm_set_dtr(port, info->mcr & UART_MCR_DTR); 43462306a36Sopenharmony_ci firm_set_rts(port, info->mcr & UART_MCR_RTS); 43562306a36Sopenharmony_ci return 0; 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic void whiteheat_get_serial(struct tty_struct *tty, struct serial_struct *ss) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci ss->baud_base = 460800; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic void whiteheat_set_termios(struct tty_struct *tty, 44662306a36Sopenharmony_ci struct usb_serial_port *port, 44762306a36Sopenharmony_ci const struct ktermios *old_termios) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci firm_setup_port(tty); 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic int whiteheat_break_ctl(struct tty_struct *tty, int break_state) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci return firm_set_break(port, break_state); 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci/***************************************************************************** 46162306a36Sopenharmony_ci * Connect Tech's White Heat callback routines 46262306a36Sopenharmony_ci *****************************************************************************/ 46362306a36Sopenharmony_cistatic void command_port_write_callback(struct urb *urb) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci int status = urb->status; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (status) { 46862306a36Sopenharmony_ci dev_dbg(&urb->dev->dev, "nonzero urb status: %d\n", status); 46962306a36Sopenharmony_ci return; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic void command_port_read_callback(struct urb *urb) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci struct usb_serial_port *command_port = urb->context; 47762306a36Sopenharmony_ci struct whiteheat_command_private *command_info; 47862306a36Sopenharmony_ci int status = urb->status; 47962306a36Sopenharmony_ci unsigned char *data = urb->transfer_buffer; 48062306a36Sopenharmony_ci int result; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci command_info = usb_get_serial_port_data(command_port); 48362306a36Sopenharmony_ci if (!command_info) { 48462306a36Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s - command_info is NULL, exiting.\n", __func__); 48562306a36Sopenharmony_ci return; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci if (!urb->actual_length) { 48862306a36Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s - empty response, exiting.\n", __func__); 48962306a36Sopenharmony_ci return; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci if (status) { 49262306a36Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s - nonzero urb status: %d\n", __func__, status); 49362306a36Sopenharmony_ci if (status != -ENOENT) 49462306a36Sopenharmony_ci command_info->command_finished = WHITEHEAT_CMD_FAILURE; 49562306a36Sopenharmony_ci wake_up(&command_info->wait_command); 49662306a36Sopenharmony_ci return; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci usb_serial_debug_data(&command_port->dev, __func__, urb->actual_length, data); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (data[0] == WHITEHEAT_CMD_COMPLETE) { 50262306a36Sopenharmony_ci command_info->command_finished = WHITEHEAT_CMD_COMPLETE; 50362306a36Sopenharmony_ci wake_up(&command_info->wait_command); 50462306a36Sopenharmony_ci } else if (data[0] == WHITEHEAT_CMD_FAILURE) { 50562306a36Sopenharmony_ci command_info->command_finished = WHITEHEAT_CMD_FAILURE; 50662306a36Sopenharmony_ci wake_up(&command_info->wait_command); 50762306a36Sopenharmony_ci } else if (data[0] == WHITEHEAT_EVENT) { 50862306a36Sopenharmony_ci /* These are unsolicited reports from the firmware, hence no 50962306a36Sopenharmony_ci waiting command to wakeup */ 51062306a36Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s - event received\n", __func__); 51162306a36Sopenharmony_ci } else if ((data[0] == WHITEHEAT_GET_DTR_RTS) && 51262306a36Sopenharmony_ci (urb->actual_length - 1 <= sizeof(command_info->result_buffer))) { 51362306a36Sopenharmony_ci memcpy(command_info->result_buffer, &data[1], 51462306a36Sopenharmony_ci urb->actual_length - 1); 51562306a36Sopenharmony_ci command_info->command_finished = WHITEHEAT_CMD_COMPLETE; 51662306a36Sopenharmony_ci wake_up(&command_info->wait_command); 51762306a36Sopenharmony_ci } else 51862306a36Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s - bad reply from firmware\n", __func__); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* Continue trying to always read */ 52162306a36Sopenharmony_ci result = usb_submit_urb(command_port->read_urb, GFP_ATOMIC); 52262306a36Sopenharmony_ci if (result) 52362306a36Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n", 52462306a36Sopenharmony_ci __func__, result); 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci/***************************************************************************** 52962306a36Sopenharmony_ci * Connect Tech's White Heat firmware interface 53062306a36Sopenharmony_ci *****************************************************************************/ 53162306a36Sopenharmony_cistatic int firm_send_command(struct usb_serial_port *port, __u8 command, 53262306a36Sopenharmony_ci __u8 *data, __u8 datasize) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci struct usb_serial_port *command_port; 53562306a36Sopenharmony_ci struct whiteheat_command_private *command_info; 53662306a36Sopenharmony_ci struct whiteheat_private *info; 53762306a36Sopenharmony_ci struct device *dev = &port->dev; 53862306a36Sopenharmony_ci __u8 *transfer_buffer; 53962306a36Sopenharmony_ci int retval = 0; 54062306a36Sopenharmony_ci int t; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci dev_dbg(dev, "%s - command %d\n", __func__, command); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci command_port = port->serial->port[COMMAND_PORT]; 54562306a36Sopenharmony_ci command_info = usb_get_serial_port_data(command_port); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (command_port->bulk_out_size < datasize + 1) 54862306a36Sopenharmony_ci return -EIO; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci mutex_lock(&command_info->mutex); 55162306a36Sopenharmony_ci command_info->command_finished = false; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci transfer_buffer = (__u8 *)command_port->write_urb->transfer_buffer; 55462306a36Sopenharmony_ci transfer_buffer[0] = command; 55562306a36Sopenharmony_ci memcpy(&transfer_buffer[1], data, datasize); 55662306a36Sopenharmony_ci command_port->write_urb->transfer_buffer_length = datasize + 1; 55762306a36Sopenharmony_ci retval = usb_submit_urb(command_port->write_urb, GFP_NOIO); 55862306a36Sopenharmony_ci if (retval) { 55962306a36Sopenharmony_ci dev_dbg(dev, "%s - submit urb failed\n", __func__); 56062306a36Sopenharmony_ci goto exit; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci /* wait for the command to complete */ 56462306a36Sopenharmony_ci t = wait_event_timeout(command_info->wait_command, 56562306a36Sopenharmony_ci (bool)command_info->command_finished, COMMAND_TIMEOUT); 56662306a36Sopenharmony_ci if (!t) 56762306a36Sopenharmony_ci usb_kill_urb(command_port->write_urb); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (command_info->command_finished == false) { 57062306a36Sopenharmony_ci dev_dbg(dev, "%s - command timed out.\n", __func__); 57162306a36Sopenharmony_ci retval = -ETIMEDOUT; 57262306a36Sopenharmony_ci goto exit; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci if (command_info->command_finished == WHITEHEAT_CMD_FAILURE) { 57662306a36Sopenharmony_ci dev_dbg(dev, "%s - command failed.\n", __func__); 57762306a36Sopenharmony_ci retval = -EIO; 57862306a36Sopenharmony_ci goto exit; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci if (command_info->command_finished == WHITEHEAT_CMD_COMPLETE) { 58262306a36Sopenharmony_ci dev_dbg(dev, "%s - command completed.\n", __func__); 58362306a36Sopenharmony_ci switch (command) { 58462306a36Sopenharmony_ci case WHITEHEAT_GET_DTR_RTS: 58562306a36Sopenharmony_ci info = usb_get_serial_port_data(port); 58662306a36Sopenharmony_ci info->mcr = command_info->result_buffer[0]; 58762306a36Sopenharmony_ci break; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ciexit: 59162306a36Sopenharmony_ci mutex_unlock(&command_info->mutex); 59262306a36Sopenharmony_ci return retval; 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic int firm_open(struct usb_serial_port *port) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci struct whiteheat_simple open_command; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci open_command.port = port->port_number + 1; 60162306a36Sopenharmony_ci return firm_send_command(port, WHITEHEAT_OPEN, 60262306a36Sopenharmony_ci (__u8 *)&open_command, sizeof(open_command)); 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cistatic int firm_close(struct usb_serial_port *port) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci struct whiteheat_simple close_command; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci close_command.port = port->port_number + 1; 61162306a36Sopenharmony_ci return firm_send_command(port, WHITEHEAT_CLOSE, 61262306a36Sopenharmony_ci (__u8 *)&close_command, sizeof(close_command)); 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cistatic void firm_setup_port(struct tty_struct *tty) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 61962306a36Sopenharmony_ci struct device *dev = &port->dev; 62062306a36Sopenharmony_ci struct whiteheat_port_settings port_settings; 62162306a36Sopenharmony_ci unsigned int cflag = tty->termios.c_cflag; 62262306a36Sopenharmony_ci speed_t baud; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci port_settings.port = port->port_number + 1; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci port_settings.bits = tty_get_char_size(cflag); 62762306a36Sopenharmony_ci dev_dbg(dev, "%s - data bits = %d\n", __func__, port_settings.bits); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci /* determine the parity */ 63062306a36Sopenharmony_ci if (cflag & PARENB) 63162306a36Sopenharmony_ci if (cflag & CMSPAR) 63262306a36Sopenharmony_ci if (cflag & PARODD) 63362306a36Sopenharmony_ci port_settings.parity = WHITEHEAT_PAR_MARK; 63462306a36Sopenharmony_ci else 63562306a36Sopenharmony_ci port_settings.parity = WHITEHEAT_PAR_SPACE; 63662306a36Sopenharmony_ci else 63762306a36Sopenharmony_ci if (cflag & PARODD) 63862306a36Sopenharmony_ci port_settings.parity = WHITEHEAT_PAR_ODD; 63962306a36Sopenharmony_ci else 64062306a36Sopenharmony_ci port_settings.parity = WHITEHEAT_PAR_EVEN; 64162306a36Sopenharmony_ci else 64262306a36Sopenharmony_ci port_settings.parity = WHITEHEAT_PAR_NONE; 64362306a36Sopenharmony_ci dev_dbg(dev, "%s - parity = %c\n", __func__, port_settings.parity); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci /* figure out the stop bits requested */ 64662306a36Sopenharmony_ci if (cflag & CSTOPB) 64762306a36Sopenharmony_ci port_settings.stop = 2; 64862306a36Sopenharmony_ci else 64962306a36Sopenharmony_ci port_settings.stop = 1; 65062306a36Sopenharmony_ci dev_dbg(dev, "%s - stop bits = %d\n", __func__, port_settings.stop); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci /* figure out the flow control settings */ 65362306a36Sopenharmony_ci if (cflag & CRTSCTS) 65462306a36Sopenharmony_ci port_settings.hflow = (WHITEHEAT_HFLOW_CTS | 65562306a36Sopenharmony_ci WHITEHEAT_HFLOW_RTS); 65662306a36Sopenharmony_ci else 65762306a36Sopenharmony_ci port_settings.hflow = WHITEHEAT_HFLOW_NONE; 65862306a36Sopenharmony_ci dev_dbg(dev, "%s - hardware flow control = %s %s %s %s\n", __func__, 65962306a36Sopenharmony_ci (port_settings.hflow & WHITEHEAT_HFLOW_CTS) ? "CTS" : "", 66062306a36Sopenharmony_ci (port_settings.hflow & WHITEHEAT_HFLOW_RTS) ? "RTS" : "", 66162306a36Sopenharmony_ci (port_settings.hflow & WHITEHEAT_HFLOW_DSR) ? "DSR" : "", 66262306a36Sopenharmony_ci (port_settings.hflow & WHITEHEAT_HFLOW_DTR) ? "DTR" : ""); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci /* determine software flow control */ 66562306a36Sopenharmony_ci if (I_IXOFF(tty)) 66662306a36Sopenharmony_ci port_settings.sflow = WHITEHEAT_SFLOW_RXTX; 66762306a36Sopenharmony_ci else 66862306a36Sopenharmony_ci port_settings.sflow = WHITEHEAT_SFLOW_NONE; 66962306a36Sopenharmony_ci dev_dbg(dev, "%s - software flow control = %c\n", __func__, port_settings.sflow); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci port_settings.xon = START_CHAR(tty); 67262306a36Sopenharmony_ci port_settings.xoff = STOP_CHAR(tty); 67362306a36Sopenharmony_ci dev_dbg(dev, "%s - XON = %2x, XOFF = %2x\n", __func__, port_settings.xon, port_settings.xoff); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci /* get the baud rate wanted */ 67662306a36Sopenharmony_ci baud = tty_get_baud_rate(tty); 67762306a36Sopenharmony_ci port_settings.baud = cpu_to_le32(baud); 67862306a36Sopenharmony_ci dev_dbg(dev, "%s - baud rate = %u\n", __func__, baud); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci /* fixme: should set validated settings */ 68162306a36Sopenharmony_ci tty_encode_baud_rate(tty, baud, baud); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci /* handle any settings that aren't specified in the tty structure */ 68462306a36Sopenharmony_ci port_settings.lloop = 0; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci /* now send the message to the device */ 68762306a36Sopenharmony_ci firm_send_command(port, WHITEHEAT_SETUP_PORT, 68862306a36Sopenharmony_ci (__u8 *)&port_settings, sizeof(port_settings)); 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic int firm_set_rts(struct usb_serial_port *port, __u8 onoff) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci struct whiteheat_set_rdb rts_command; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci rts_command.port = port->port_number + 1; 69762306a36Sopenharmony_ci rts_command.state = onoff; 69862306a36Sopenharmony_ci return firm_send_command(port, WHITEHEAT_SET_RTS, 69962306a36Sopenharmony_ci (__u8 *)&rts_command, sizeof(rts_command)); 70062306a36Sopenharmony_ci} 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_cistatic int firm_set_dtr(struct usb_serial_port *port, __u8 onoff) 70462306a36Sopenharmony_ci{ 70562306a36Sopenharmony_ci struct whiteheat_set_rdb dtr_command; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci dtr_command.port = port->port_number + 1; 70862306a36Sopenharmony_ci dtr_command.state = onoff; 70962306a36Sopenharmony_ci return firm_send_command(port, WHITEHEAT_SET_DTR, 71062306a36Sopenharmony_ci (__u8 *)&dtr_command, sizeof(dtr_command)); 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_cistatic int firm_set_break(struct usb_serial_port *port, __u8 onoff) 71562306a36Sopenharmony_ci{ 71662306a36Sopenharmony_ci struct whiteheat_set_rdb break_command; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci break_command.port = port->port_number + 1; 71962306a36Sopenharmony_ci break_command.state = onoff; 72062306a36Sopenharmony_ci return firm_send_command(port, WHITEHEAT_SET_BREAK, 72162306a36Sopenharmony_ci (__u8 *)&break_command, sizeof(break_command)); 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_cistatic int firm_purge(struct usb_serial_port *port, __u8 rxtx) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci struct whiteheat_purge purge_command; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci purge_command.port = port->port_number + 1; 73062306a36Sopenharmony_ci purge_command.what = rxtx; 73162306a36Sopenharmony_ci return firm_send_command(port, WHITEHEAT_PURGE, 73262306a36Sopenharmony_ci (__u8 *)&purge_command, sizeof(purge_command)); 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_cistatic int firm_get_dtr_rts(struct usb_serial_port *port) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci struct whiteheat_simple get_dr_command; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci get_dr_command.port = port->port_number + 1; 74162306a36Sopenharmony_ci return firm_send_command(port, WHITEHEAT_GET_DTR_RTS, 74262306a36Sopenharmony_ci (__u8 *)&get_dr_command, sizeof(get_dr_command)); 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic int firm_report_tx_done(struct usb_serial_port *port) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci struct whiteheat_simple close_command; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci close_command.port = port->port_number + 1; 75162306a36Sopenharmony_ci return firm_send_command(port, WHITEHEAT_REPORT_TX_DONE, 75262306a36Sopenharmony_ci (__u8 *)&close_command, sizeof(close_command)); 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci/***************************************************************************** 75762306a36Sopenharmony_ci * Connect Tech's White Heat utility functions 75862306a36Sopenharmony_ci *****************************************************************************/ 75962306a36Sopenharmony_cistatic int start_command_port(struct usb_serial *serial) 76062306a36Sopenharmony_ci{ 76162306a36Sopenharmony_ci struct usb_serial_port *command_port; 76262306a36Sopenharmony_ci struct whiteheat_command_private *command_info; 76362306a36Sopenharmony_ci int retval = 0; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci command_port = serial->port[COMMAND_PORT]; 76662306a36Sopenharmony_ci command_info = usb_get_serial_port_data(command_port); 76762306a36Sopenharmony_ci mutex_lock(&command_info->mutex); 76862306a36Sopenharmony_ci if (!command_info->port_running) { 76962306a36Sopenharmony_ci /* Work around HCD bugs */ 77062306a36Sopenharmony_ci usb_clear_halt(serial->dev, command_port->read_urb->pipe); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci retval = usb_submit_urb(command_port->read_urb, GFP_KERNEL); 77362306a36Sopenharmony_ci if (retval) { 77462306a36Sopenharmony_ci dev_err(&serial->dev->dev, 77562306a36Sopenharmony_ci "%s - failed submitting read urb, error %d\n", 77662306a36Sopenharmony_ci __func__, retval); 77762306a36Sopenharmony_ci goto exit; 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci command_info->port_running++; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ciexit: 78362306a36Sopenharmony_ci mutex_unlock(&command_info->mutex); 78462306a36Sopenharmony_ci return retval; 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_cistatic void stop_command_port(struct usb_serial *serial) 78962306a36Sopenharmony_ci{ 79062306a36Sopenharmony_ci struct usb_serial_port *command_port; 79162306a36Sopenharmony_ci struct whiteheat_command_private *command_info; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci command_port = serial->port[COMMAND_PORT]; 79462306a36Sopenharmony_ci command_info = usb_get_serial_port_data(command_port); 79562306a36Sopenharmony_ci mutex_lock(&command_info->mutex); 79662306a36Sopenharmony_ci command_info->port_running--; 79762306a36Sopenharmony_ci if (!command_info->port_running) 79862306a36Sopenharmony_ci usb_kill_urb(command_port->read_urb); 79962306a36Sopenharmony_ci mutex_unlock(&command_info->mutex); 80062306a36Sopenharmony_ci} 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table_combined); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 80562306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 80662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ciMODULE_FIRMWARE("whiteheat.fw"); 80962306a36Sopenharmony_ciMODULE_FIRMWARE("whiteheat_loader.fw"); 810