18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * USB ConnectTech WhiteHEAT driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2002 68c2ecf20Sopenharmony_ci * Connect Tech Inc. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (C) 1999 - 2001 98c2ecf20Sopenharmony_ci * Greg Kroah-Hartman (greg@kroah.com) 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * See Documentation/usb/usb-serial.rst for more information on using this 128c2ecf20Sopenharmony_ci * driver 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/errno.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/tty.h> 198c2ecf20Sopenharmony_ci#include <linux/tty_driver.h> 208c2ecf20Sopenharmony_ci#include <linux/tty_flip.h> 218c2ecf20Sopenharmony_ci#include <linux/module.h> 228c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 238c2ecf20Sopenharmony_ci#include <linux/mutex.h> 248c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 258c2ecf20Sopenharmony_ci#include <asm/termbits.h> 268c2ecf20Sopenharmony_ci#include <linux/usb.h> 278c2ecf20Sopenharmony_ci#include <linux/serial_reg.h> 288c2ecf20Sopenharmony_ci#include <linux/serial.h> 298c2ecf20Sopenharmony_ci#include <linux/usb/serial.h> 308c2ecf20Sopenharmony_ci#include <linux/usb/ezusb.h> 318c2ecf20Sopenharmony_ci#include "whiteheat.h" /* WhiteHEAT specific commands */ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#ifndef CMSPAR 348c2ecf20Sopenharmony_ci#define CMSPAR 0 358c2ecf20Sopenharmony_ci#endif 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* 388c2ecf20Sopenharmony_ci * Version Information 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Stuart MacDonald <stuartm@connecttech.com>" 418c2ecf20Sopenharmony_ci#define DRIVER_DESC "USB ConnectTech WhiteHEAT driver" 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define CONNECT_TECH_VENDOR_ID 0x0710 448c2ecf20Sopenharmony_ci#define CONNECT_TECH_FAKE_WHITE_HEAT_ID 0x0001 458c2ecf20Sopenharmony_ci#define CONNECT_TECH_WHITE_HEAT_ID 0x8001 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* 488c2ecf20Sopenharmony_ci ID tables for whiteheat are unusual, because we want to different 498c2ecf20Sopenharmony_ci things for different versions of the device. Eventually, this 508c2ecf20Sopenharmony_ci will be doable from a single table. But, for now, we define two 518c2ecf20Sopenharmony_ci separate ID tables, and then a third table that combines them 528c2ecf20Sopenharmony_ci just for the purpose of exporting the autoloading information. 538c2ecf20Sopenharmony_ci*/ 548c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table_std[] = { 558c2ecf20Sopenharmony_ci { USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_WHITE_HEAT_ID) }, 568c2ecf20Sopenharmony_ci { } /* Terminating entry */ 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table_prerenumeration[] = { 608c2ecf20Sopenharmony_ci { USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_FAKE_WHITE_HEAT_ID) }, 618c2ecf20Sopenharmony_ci { } /* Terminating entry */ 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table_combined[] = { 658c2ecf20Sopenharmony_ci { USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_WHITE_HEAT_ID) }, 668c2ecf20Sopenharmony_ci { USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_FAKE_WHITE_HEAT_ID) }, 678c2ecf20Sopenharmony_ci { } /* Terminating entry */ 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table_combined); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/* function prototypes for the Connect Tech WhiteHEAT prerenumeration device */ 748c2ecf20Sopenharmony_cistatic int whiteheat_firmware_download(struct usb_serial *serial, 758c2ecf20Sopenharmony_ci const struct usb_device_id *id); 768c2ecf20Sopenharmony_cistatic int whiteheat_firmware_attach(struct usb_serial *serial); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* function prototypes for the Connect Tech WhiteHEAT serial converter */ 798c2ecf20Sopenharmony_cistatic int whiteheat_attach(struct usb_serial *serial); 808c2ecf20Sopenharmony_cistatic void whiteheat_release(struct usb_serial *serial); 818c2ecf20Sopenharmony_cistatic int whiteheat_port_probe(struct usb_serial_port *port); 828c2ecf20Sopenharmony_cistatic int whiteheat_port_remove(struct usb_serial_port *port); 838c2ecf20Sopenharmony_cistatic int whiteheat_open(struct tty_struct *tty, 848c2ecf20Sopenharmony_ci struct usb_serial_port *port); 858c2ecf20Sopenharmony_cistatic void whiteheat_close(struct usb_serial_port *port); 868c2ecf20Sopenharmony_cistatic int whiteheat_get_serial(struct tty_struct *tty, 878c2ecf20Sopenharmony_ci struct serial_struct *ss); 888c2ecf20Sopenharmony_cistatic void whiteheat_set_termios(struct tty_struct *tty, 898c2ecf20Sopenharmony_ci struct usb_serial_port *port, struct ktermios *old); 908c2ecf20Sopenharmony_cistatic int whiteheat_tiocmget(struct tty_struct *tty); 918c2ecf20Sopenharmony_cistatic int whiteheat_tiocmset(struct tty_struct *tty, 928c2ecf20Sopenharmony_ci unsigned int set, unsigned int clear); 938c2ecf20Sopenharmony_cistatic void whiteheat_break_ctl(struct tty_struct *tty, int break_state); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic struct usb_serial_driver whiteheat_fake_device = { 968c2ecf20Sopenharmony_ci .driver = { 978c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 988c2ecf20Sopenharmony_ci .name = "whiteheatnofirm", 998c2ecf20Sopenharmony_ci }, 1008c2ecf20Sopenharmony_ci .description = "Connect Tech - WhiteHEAT - (prerenumeration)", 1018c2ecf20Sopenharmony_ci .id_table = id_table_prerenumeration, 1028c2ecf20Sopenharmony_ci .num_ports = 1, 1038c2ecf20Sopenharmony_ci .probe = whiteheat_firmware_download, 1048c2ecf20Sopenharmony_ci .attach = whiteheat_firmware_attach, 1058c2ecf20Sopenharmony_ci}; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic struct usb_serial_driver whiteheat_device = { 1088c2ecf20Sopenharmony_ci .driver = { 1098c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1108c2ecf20Sopenharmony_ci .name = "whiteheat", 1118c2ecf20Sopenharmony_ci }, 1128c2ecf20Sopenharmony_ci .description = "Connect Tech - WhiteHEAT", 1138c2ecf20Sopenharmony_ci .id_table = id_table_std, 1148c2ecf20Sopenharmony_ci .num_ports = 4, 1158c2ecf20Sopenharmony_ci .num_bulk_in = 5, 1168c2ecf20Sopenharmony_ci .num_bulk_out = 5, 1178c2ecf20Sopenharmony_ci .attach = whiteheat_attach, 1188c2ecf20Sopenharmony_ci .release = whiteheat_release, 1198c2ecf20Sopenharmony_ci .port_probe = whiteheat_port_probe, 1208c2ecf20Sopenharmony_ci .port_remove = whiteheat_port_remove, 1218c2ecf20Sopenharmony_ci .open = whiteheat_open, 1228c2ecf20Sopenharmony_ci .close = whiteheat_close, 1238c2ecf20Sopenharmony_ci .get_serial = whiteheat_get_serial, 1248c2ecf20Sopenharmony_ci .set_termios = whiteheat_set_termios, 1258c2ecf20Sopenharmony_ci .break_ctl = whiteheat_break_ctl, 1268c2ecf20Sopenharmony_ci .tiocmget = whiteheat_tiocmget, 1278c2ecf20Sopenharmony_ci .tiocmset = whiteheat_tiocmset, 1288c2ecf20Sopenharmony_ci .throttle = usb_serial_generic_throttle, 1298c2ecf20Sopenharmony_ci .unthrottle = usb_serial_generic_unthrottle, 1308c2ecf20Sopenharmony_ci}; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = { 1338c2ecf20Sopenharmony_ci &whiteheat_fake_device, &whiteheat_device, NULL 1348c2ecf20Sopenharmony_ci}; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistruct whiteheat_command_private { 1378c2ecf20Sopenharmony_ci struct mutex mutex; 1388c2ecf20Sopenharmony_ci __u8 port_running; 1398c2ecf20Sopenharmony_ci __u8 command_finished; 1408c2ecf20Sopenharmony_ci wait_queue_head_t wait_command; /* for handling sleeping whilst 1418c2ecf20Sopenharmony_ci waiting for a command to 1428c2ecf20Sopenharmony_ci finish */ 1438c2ecf20Sopenharmony_ci __u8 result_buffer[64]; 1448c2ecf20Sopenharmony_ci}; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistruct whiteheat_private { 1478c2ecf20Sopenharmony_ci __u8 mcr; /* FIXME: no locking on mcr */ 1488c2ecf20Sopenharmony_ci}; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/* local function prototypes */ 1528c2ecf20Sopenharmony_cistatic int start_command_port(struct usb_serial *serial); 1538c2ecf20Sopenharmony_cistatic void stop_command_port(struct usb_serial *serial); 1548c2ecf20Sopenharmony_cistatic void command_port_write_callback(struct urb *urb); 1558c2ecf20Sopenharmony_cistatic void command_port_read_callback(struct urb *urb); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic int firm_send_command(struct usb_serial_port *port, __u8 command, 1588c2ecf20Sopenharmony_ci __u8 *data, __u8 datasize); 1598c2ecf20Sopenharmony_cistatic int firm_open(struct usb_serial_port *port); 1608c2ecf20Sopenharmony_cistatic int firm_close(struct usb_serial_port *port); 1618c2ecf20Sopenharmony_cistatic void firm_setup_port(struct tty_struct *tty); 1628c2ecf20Sopenharmony_cistatic int firm_set_rts(struct usb_serial_port *port, __u8 onoff); 1638c2ecf20Sopenharmony_cistatic int firm_set_dtr(struct usb_serial_port *port, __u8 onoff); 1648c2ecf20Sopenharmony_cistatic int firm_set_break(struct usb_serial_port *port, __u8 onoff); 1658c2ecf20Sopenharmony_cistatic int firm_purge(struct usb_serial_port *port, __u8 rxtx); 1668c2ecf20Sopenharmony_cistatic int firm_get_dtr_rts(struct usb_serial_port *port); 1678c2ecf20Sopenharmony_cistatic int firm_report_tx_done(struct usb_serial_port *port); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci#define COMMAND_PORT 4 1718c2ecf20Sopenharmony_ci#define COMMAND_TIMEOUT (2*HZ) /* 2 second timeout for a command */ 1728c2ecf20Sopenharmony_ci#define COMMAND_TIMEOUT_MS 2000 1738c2ecf20Sopenharmony_ci#define CLOSING_DELAY (30 * HZ) 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci/***************************************************************************** 1778c2ecf20Sopenharmony_ci * Connect Tech's White Heat prerenumeration driver functions 1788c2ecf20Sopenharmony_ci *****************************************************************************/ 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci/* steps to download the firmware to the WhiteHEAT device: 1818c2ecf20Sopenharmony_ci - hold the reset (by writing to the reset bit of the CPUCS register) 1828c2ecf20Sopenharmony_ci - download the VEND_AX.HEX file to the chip using VENDOR_REQUEST-ANCHOR_LOAD 1838c2ecf20Sopenharmony_ci - release the reset (by writing to the CPUCS register) 1848c2ecf20Sopenharmony_ci - download the WH.HEX file for all addresses greater than 0x1b3f using 1858c2ecf20Sopenharmony_ci VENDOR_REQUEST-ANCHOR_EXTERNAL_RAM_LOAD 1868c2ecf20Sopenharmony_ci - hold the reset 1878c2ecf20Sopenharmony_ci - download the WH.HEX file for all addresses less than 0x1b40 using 1888c2ecf20Sopenharmony_ci VENDOR_REQUEST_ANCHOR_LOAD 1898c2ecf20Sopenharmony_ci - release the reset 1908c2ecf20Sopenharmony_ci - device renumerated itself and comes up as new device id with all 1918c2ecf20Sopenharmony_ci firmware download completed. 1928c2ecf20Sopenharmony_ci*/ 1938c2ecf20Sopenharmony_cistatic int whiteheat_firmware_download(struct usb_serial *serial, 1948c2ecf20Sopenharmony_ci const struct usb_device_id *id) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci int response; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci response = ezusb_fx1_ihex_firmware_download(serial->dev, "whiteheat_loader.fw"); 1998c2ecf20Sopenharmony_ci if (response >= 0) { 2008c2ecf20Sopenharmony_ci response = ezusb_fx1_ihex_firmware_download(serial->dev, "whiteheat.fw"); 2018c2ecf20Sopenharmony_ci if (response >= 0) 2028c2ecf20Sopenharmony_ci return 0; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci return -ENOENT; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic int whiteheat_firmware_attach(struct usb_serial *serial) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci /* We want this device to fail to have a driver assigned to it */ 2118c2ecf20Sopenharmony_ci return 1; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci/***************************************************************************** 2168c2ecf20Sopenharmony_ci * Connect Tech's White Heat serial driver functions 2178c2ecf20Sopenharmony_ci *****************************************************************************/ 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic int whiteheat_attach(struct usb_serial *serial) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci struct usb_serial_port *command_port; 2228c2ecf20Sopenharmony_ci struct whiteheat_command_private *command_info; 2238c2ecf20Sopenharmony_ci struct whiteheat_hw_info *hw_info; 2248c2ecf20Sopenharmony_ci int pipe; 2258c2ecf20Sopenharmony_ci int ret; 2268c2ecf20Sopenharmony_ci int alen; 2278c2ecf20Sopenharmony_ci __u8 *command; 2288c2ecf20Sopenharmony_ci __u8 *result; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci command_port = serial->port[COMMAND_PORT]; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci pipe = usb_sndbulkpipe(serial->dev, 2338c2ecf20Sopenharmony_ci command_port->bulk_out_endpointAddress); 2348c2ecf20Sopenharmony_ci command = kmalloc(2, GFP_KERNEL); 2358c2ecf20Sopenharmony_ci if (!command) 2368c2ecf20Sopenharmony_ci goto no_command_buffer; 2378c2ecf20Sopenharmony_ci command[0] = WHITEHEAT_GET_HW_INFO; 2388c2ecf20Sopenharmony_ci command[1] = 0; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci result = kmalloc(sizeof(*hw_info) + 1, GFP_KERNEL); 2418c2ecf20Sopenharmony_ci if (!result) 2428c2ecf20Sopenharmony_ci goto no_result_buffer; 2438c2ecf20Sopenharmony_ci /* 2448c2ecf20Sopenharmony_ci * When the module is reloaded the firmware is still there and 2458c2ecf20Sopenharmony_ci * the endpoints are still in the usb core unchanged. This is the 2468c2ecf20Sopenharmony_ci * unlinking bug in disguise. Same for the call below. 2478c2ecf20Sopenharmony_ci */ 2488c2ecf20Sopenharmony_ci usb_clear_halt(serial->dev, pipe); 2498c2ecf20Sopenharmony_ci ret = usb_bulk_msg(serial->dev, pipe, command, 2, 2508c2ecf20Sopenharmony_ci &alen, COMMAND_TIMEOUT_MS); 2518c2ecf20Sopenharmony_ci if (ret) { 2528c2ecf20Sopenharmony_ci dev_err(&serial->dev->dev, "%s: Couldn't send command [%d]\n", 2538c2ecf20Sopenharmony_ci serial->type->description, ret); 2548c2ecf20Sopenharmony_ci goto no_firmware; 2558c2ecf20Sopenharmony_ci } else if (alen != 2) { 2568c2ecf20Sopenharmony_ci dev_err(&serial->dev->dev, "%s: Send command incomplete [%d]\n", 2578c2ecf20Sopenharmony_ci serial->type->description, alen); 2588c2ecf20Sopenharmony_ci goto no_firmware; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci pipe = usb_rcvbulkpipe(serial->dev, 2628c2ecf20Sopenharmony_ci command_port->bulk_in_endpointAddress); 2638c2ecf20Sopenharmony_ci /* See the comment on the usb_clear_halt() above */ 2648c2ecf20Sopenharmony_ci usb_clear_halt(serial->dev, pipe); 2658c2ecf20Sopenharmony_ci ret = usb_bulk_msg(serial->dev, pipe, result, 2668c2ecf20Sopenharmony_ci sizeof(*hw_info) + 1, &alen, COMMAND_TIMEOUT_MS); 2678c2ecf20Sopenharmony_ci if (ret) { 2688c2ecf20Sopenharmony_ci dev_err(&serial->dev->dev, "%s: Couldn't get results [%d]\n", 2698c2ecf20Sopenharmony_ci serial->type->description, ret); 2708c2ecf20Sopenharmony_ci goto no_firmware; 2718c2ecf20Sopenharmony_ci } else if (alen != sizeof(*hw_info) + 1) { 2728c2ecf20Sopenharmony_ci dev_err(&serial->dev->dev, "%s: Get results incomplete [%d]\n", 2738c2ecf20Sopenharmony_ci serial->type->description, alen); 2748c2ecf20Sopenharmony_ci goto no_firmware; 2758c2ecf20Sopenharmony_ci } else if (result[0] != command[0]) { 2768c2ecf20Sopenharmony_ci dev_err(&serial->dev->dev, "%s: Command failed [%d]\n", 2778c2ecf20Sopenharmony_ci serial->type->description, result[0]); 2788c2ecf20Sopenharmony_ci goto no_firmware; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci hw_info = (struct whiteheat_hw_info *)&result[1]; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci dev_info(&serial->dev->dev, "%s: Firmware v%d.%02d\n", 2848c2ecf20Sopenharmony_ci serial->type->description, 2858c2ecf20Sopenharmony_ci hw_info->sw_major_rev, hw_info->sw_minor_rev); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci command_info = kmalloc(sizeof(struct whiteheat_command_private), 2888c2ecf20Sopenharmony_ci GFP_KERNEL); 2898c2ecf20Sopenharmony_ci if (!command_info) 2908c2ecf20Sopenharmony_ci goto no_command_private; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci mutex_init(&command_info->mutex); 2938c2ecf20Sopenharmony_ci command_info->port_running = 0; 2948c2ecf20Sopenharmony_ci init_waitqueue_head(&command_info->wait_command); 2958c2ecf20Sopenharmony_ci usb_set_serial_port_data(command_port, command_info); 2968c2ecf20Sopenharmony_ci command_port->write_urb->complete = command_port_write_callback; 2978c2ecf20Sopenharmony_ci command_port->read_urb->complete = command_port_read_callback; 2988c2ecf20Sopenharmony_ci kfree(result); 2998c2ecf20Sopenharmony_ci kfree(command); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci return 0; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cino_firmware: 3048c2ecf20Sopenharmony_ci /* Firmware likely not running */ 3058c2ecf20Sopenharmony_ci dev_err(&serial->dev->dev, 3068c2ecf20Sopenharmony_ci "%s: Unable to retrieve firmware version, try replugging\n", 3078c2ecf20Sopenharmony_ci serial->type->description); 3088c2ecf20Sopenharmony_ci dev_err(&serial->dev->dev, 3098c2ecf20Sopenharmony_ci "%s: If the firmware is not running (status led not blinking)\n", 3108c2ecf20Sopenharmony_ci serial->type->description); 3118c2ecf20Sopenharmony_ci dev_err(&serial->dev->dev, 3128c2ecf20Sopenharmony_ci "%s: please contact support@connecttech.com\n", 3138c2ecf20Sopenharmony_ci serial->type->description); 3148c2ecf20Sopenharmony_ci kfree(result); 3158c2ecf20Sopenharmony_ci kfree(command); 3168c2ecf20Sopenharmony_ci return -ENODEV; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cino_command_private: 3198c2ecf20Sopenharmony_ci kfree(result); 3208c2ecf20Sopenharmony_cino_result_buffer: 3218c2ecf20Sopenharmony_ci kfree(command); 3228c2ecf20Sopenharmony_cino_command_buffer: 3238c2ecf20Sopenharmony_ci return -ENOMEM; 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic void whiteheat_release(struct usb_serial *serial) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci struct usb_serial_port *command_port; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* free up our private data for our command port */ 3318c2ecf20Sopenharmony_ci command_port = serial->port[COMMAND_PORT]; 3328c2ecf20Sopenharmony_ci kfree(usb_get_serial_port_data(command_port)); 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic int whiteheat_port_probe(struct usb_serial_port *port) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci struct whiteheat_private *info; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci info = kzalloc(sizeof(*info), GFP_KERNEL); 3408c2ecf20Sopenharmony_ci if (!info) 3418c2ecf20Sopenharmony_ci return -ENOMEM; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci usb_set_serial_port_data(port, info); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci return 0; 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic int whiteheat_port_remove(struct usb_serial_port *port) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci struct whiteheat_private *info; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci info = usb_get_serial_port_data(port); 3538c2ecf20Sopenharmony_ci kfree(info); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci return 0; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic int whiteheat_open(struct tty_struct *tty, struct usb_serial_port *port) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci int retval; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci retval = start_command_port(port->serial); 3638c2ecf20Sopenharmony_ci if (retval) 3648c2ecf20Sopenharmony_ci goto exit; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci /* send an open port command */ 3678c2ecf20Sopenharmony_ci retval = firm_open(port); 3688c2ecf20Sopenharmony_ci if (retval) { 3698c2ecf20Sopenharmony_ci stop_command_port(port->serial); 3708c2ecf20Sopenharmony_ci goto exit; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci retval = firm_purge(port, WHITEHEAT_PURGE_RX | WHITEHEAT_PURGE_TX); 3748c2ecf20Sopenharmony_ci if (retval) { 3758c2ecf20Sopenharmony_ci firm_close(port); 3768c2ecf20Sopenharmony_ci stop_command_port(port->serial); 3778c2ecf20Sopenharmony_ci goto exit; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (tty) 3818c2ecf20Sopenharmony_ci firm_setup_port(tty); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* Work around HCD bugs */ 3848c2ecf20Sopenharmony_ci usb_clear_halt(port->serial->dev, port->read_urb->pipe); 3858c2ecf20Sopenharmony_ci usb_clear_halt(port->serial->dev, port->write_urb->pipe); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci retval = usb_serial_generic_open(tty, port); 3888c2ecf20Sopenharmony_ci if (retval) { 3898c2ecf20Sopenharmony_ci firm_close(port); 3908c2ecf20Sopenharmony_ci stop_command_port(port->serial); 3918c2ecf20Sopenharmony_ci goto exit; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ciexit: 3948c2ecf20Sopenharmony_ci return retval; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic void whiteheat_close(struct usb_serial_port *port) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci firm_report_tx_done(port); 4018c2ecf20Sopenharmony_ci firm_close(port); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci usb_serial_generic_close(port); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci stop_command_port(port->serial); 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic int whiteheat_tiocmget(struct tty_struct *tty) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 4118c2ecf20Sopenharmony_ci struct whiteheat_private *info = usb_get_serial_port_data(port); 4128c2ecf20Sopenharmony_ci unsigned int modem_signals = 0; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci firm_get_dtr_rts(port); 4158c2ecf20Sopenharmony_ci if (info->mcr & UART_MCR_DTR) 4168c2ecf20Sopenharmony_ci modem_signals |= TIOCM_DTR; 4178c2ecf20Sopenharmony_ci if (info->mcr & UART_MCR_RTS) 4188c2ecf20Sopenharmony_ci modem_signals |= TIOCM_RTS; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci return modem_signals; 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic int whiteheat_tiocmset(struct tty_struct *tty, 4248c2ecf20Sopenharmony_ci unsigned int set, unsigned int clear) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 4278c2ecf20Sopenharmony_ci struct whiteheat_private *info = usb_get_serial_port_data(port); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci if (set & TIOCM_RTS) 4308c2ecf20Sopenharmony_ci info->mcr |= UART_MCR_RTS; 4318c2ecf20Sopenharmony_ci if (set & TIOCM_DTR) 4328c2ecf20Sopenharmony_ci info->mcr |= UART_MCR_DTR; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (clear & TIOCM_RTS) 4358c2ecf20Sopenharmony_ci info->mcr &= ~UART_MCR_RTS; 4368c2ecf20Sopenharmony_ci if (clear & TIOCM_DTR) 4378c2ecf20Sopenharmony_ci info->mcr &= ~UART_MCR_DTR; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci firm_set_dtr(port, info->mcr & UART_MCR_DTR); 4408c2ecf20Sopenharmony_ci firm_set_rts(port, info->mcr & UART_MCR_RTS); 4418c2ecf20Sopenharmony_ci return 0; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic int whiteheat_get_serial(struct tty_struct *tty, 4468c2ecf20Sopenharmony_ci struct serial_struct *ss) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci ss->type = PORT_16654; 4518c2ecf20Sopenharmony_ci ss->line = port->minor; 4528c2ecf20Sopenharmony_ci ss->port = port->port_number; 4538c2ecf20Sopenharmony_ci ss->xmit_fifo_size = kfifo_size(&port->write_fifo); 4548c2ecf20Sopenharmony_ci ss->custom_divisor = 0; 4558c2ecf20Sopenharmony_ci ss->baud_base = 460800; 4568c2ecf20Sopenharmony_ci ss->close_delay = CLOSING_DELAY; 4578c2ecf20Sopenharmony_ci ss->closing_wait = CLOSING_DELAY; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci return 0; 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic void whiteheat_set_termios(struct tty_struct *tty, 4648c2ecf20Sopenharmony_ci struct usb_serial_port *port, struct ktermios *old_termios) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci firm_setup_port(tty); 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_cistatic void whiteheat_break_ctl(struct tty_struct *tty, int break_state) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 4728c2ecf20Sopenharmony_ci firm_set_break(port, break_state); 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci/***************************************************************************** 4778c2ecf20Sopenharmony_ci * Connect Tech's White Heat callback routines 4788c2ecf20Sopenharmony_ci *****************************************************************************/ 4798c2ecf20Sopenharmony_cistatic void command_port_write_callback(struct urb *urb) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci int status = urb->status; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (status) { 4848c2ecf20Sopenharmony_ci dev_dbg(&urb->dev->dev, "nonzero urb status: %d\n", status); 4858c2ecf20Sopenharmony_ci return; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cistatic void command_port_read_callback(struct urb *urb) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci struct usb_serial_port *command_port = urb->context; 4938c2ecf20Sopenharmony_ci struct whiteheat_command_private *command_info; 4948c2ecf20Sopenharmony_ci int status = urb->status; 4958c2ecf20Sopenharmony_ci unsigned char *data = urb->transfer_buffer; 4968c2ecf20Sopenharmony_ci int result; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci command_info = usb_get_serial_port_data(command_port); 4998c2ecf20Sopenharmony_ci if (!command_info) { 5008c2ecf20Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s - command_info is NULL, exiting.\n", __func__); 5018c2ecf20Sopenharmony_ci return; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci if (!urb->actual_length) { 5048c2ecf20Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s - empty response, exiting.\n", __func__); 5058c2ecf20Sopenharmony_ci return; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci if (status) { 5088c2ecf20Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s - nonzero urb status: %d\n", __func__, status); 5098c2ecf20Sopenharmony_ci if (status != -ENOENT) 5108c2ecf20Sopenharmony_ci command_info->command_finished = WHITEHEAT_CMD_FAILURE; 5118c2ecf20Sopenharmony_ci wake_up(&command_info->wait_command); 5128c2ecf20Sopenharmony_ci return; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci usb_serial_debug_data(&command_port->dev, __func__, urb->actual_length, data); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci if (data[0] == WHITEHEAT_CMD_COMPLETE) { 5188c2ecf20Sopenharmony_ci command_info->command_finished = WHITEHEAT_CMD_COMPLETE; 5198c2ecf20Sopenharmony_ci wake_up(&command_info->wait_command); 5208c2ecf20Sopenharmony_ci } else if (data[0] == WHITEHEAT_CMD_FAILURE) { 5218c2ecf20Sopenharmony_ci command_info->command_finished = WHITEHEAT_CMD_FAILURE; 5228c2ecf20Sopenharmony_ci wake_up(&command_info->wait_command); 5238c2ecf20Sopenharmony_ci } else if (data[0] == WHITEHEAT_EVENT) { 5248c2ecf20Sopenharmony_ci /* These are unsolicited reports from the firmware, hence no 5258c2ecf20Sopenharmony_ci waiting command to wakeup */ 5268c2ecf20Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s - event received\n", __func__); 5278c2ecf20Sopenharmony_ci } else if ((data[0] == WHITEHEAT_GET_DTR_RTS) && 5288c2ecf20Sopenharmony_ci (urb->actual_length - 1 <= sizeof(command_info->result_buffer))) { 5298c2ecf20Sopenharmony_ci memcpy(command_info->result_buffer, &data[1], 5308c2ecf20Sopenharmony_ci urb->actual_length - 1); 5318c2ecf20Sopenharmony_ci command_info->command_finished = WHITEHEAT_CMD_COMPLETE; 5328c2ecf20Sopenharmony_ci wake_up(&command_info->wait_command); 5338c2ecf20Sopenharmony_ci } else 5348c2ecf20Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s - bad reply from firmware\n", __func__); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci /* Continue trying to always read */ 5378c2ecf20Sopenharmony_ci result = usb_submit_urb(command_port->read_urb, GFP_ATOMIC); 5388c2ecf20Sopenharmony_ci if (result) 5398c2ecf20Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n", 5408c2ecf20Sopenharmony_ci __func__, result); 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci/***************************************************************************** 5458c2ecf20Sopenharmony_ci * Connect Tech's White Heat firmware interface 5468c2ecf20Sopenharmony_ci *****************************************************************************/ 5478c2ecf20Sopenharmony_cistatic int firm_send_command(struct usb_serial_port *port, __u8 command, 5488c2ecf20Sopenharmony_ci __u8 *data, __u8 datasize) 5498c2ecf20Sopenharmony_ci{ 5508c2ecf20Sopenharmony_ci struct usb_serial_port *command_port; 5518c2ecf20Sopenharmony_ci struct whiteheat_command_private *command_info; 5528c2ecf20Sopenharmony_ci struct whiteheat_private *info; 5538c2ecf20Sopenharmony_ci struct device *dev = &port->dev; 5548c2ecf20Sopenharmony_ci __u8 *transfer_buffer; 5558c2ecf20Sopenharmony_ci int retval = 0; 5568c2ecf20Sopenharmony_ci int t; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - command %d\n", __func__, command); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci command_port = port->serial->port[COMMAND_PORT]; 5618c2ecf20Sopenharmony_ci command_info = usb_get_serial_port_data(command_port); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci if (command_port->bulk_out_size < datasize + 1) 5648c2ecf20Sopenharmony_ci return -EIO; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci mutex_lock(&command_info->mutex); 5678c2ecf20Sopenharmony_ci command_info->command_finished = false; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci transfer_buffer = (__u8 *)command_port->write_urb->transfer_buffer; 5708c2ecf20Sopenharmony_ci transfer_buffer[0] = command; 5718c2ecf20Sopenharmony_ci memcpy(&transfer_buffer[1], data, datasize); 5728c2ecf20Sopenharmony_ci command_port->write_urb->transfer_buffer_length = datasize + 1; 5738c2ecf20Sopenharmony_ci retval = usb_submit_urb(command_port->write_urb, GFP_NOIO); 5748c2ecf20Sopenharmony_ci if (retval) { 5758c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - submit urb failed\n", __func__); 5768c2ecf20Sopenharmony_ci goto exit; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci /* wait for the command to complete */ 5808c2ecf20Sopenharmony_ci t = wait_event_timeout(command_info->wait_command, 5818c2ecf20Sopenharmony_ci (bool)command_info->command_finished, COMMAND_TIMEOUT); 5828c2ecf20Sopenharmony_ci if (!t) 5838c2ecf20Sopenharmony_ci usb_kill_urb(command_port->write_urb); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci if (command_info->command_finished == false) { 5868c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - command timed out.\n", __func__); 5878c2ecf20Sopenharmony_ci retval = -ETIMEDOUT; 5888c2ecf20Sopenharmony_ci goto exit; 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci if (command_info->command_finished == WHITEHEAT_CMD_FAILURE) { 5928c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - command failed.\n", __func__); 5938c2ecf20Sopenharmony_ci retval = -EIO; 5948c2ecf20Sopenharmony_ci goto exit; 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (command_info->command_finished == WHITEHEAT_CMD_COMPLETE) { 5988c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - command completed.\n", __func__); 5998c2ecf20Sopenharmony_ci switch (command) { 6008c2ecf20Sopenharmony_ci case WHITEHEAT_GET_DTR_RTS: 6018c2ecf20Sopenharmony_ci info = usb_get_serial_port_data(port); 6028c2ecf20Sopenharmony_ci info->mcr = command_info->result_buffer[0]; 6038c2ecf20Sopenharmony_ci break; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ciexit: 6078c2ecf20Sopenharmony_ci mutex_unlock(&command_info->mutex); 6088c2ecf20Sopenharmony_ci return retval; 6098c2ecf20Sopenharmony_ci} 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_cistatic int firm_open(struct usb_serial_port *port) 6138c2ecf20Sopenharmony_ci{ 6148c2ecf20Sopenharmony_ci struct whiteheat_simple open_command; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci open_command.port = port->port_number + 1; 6178c2ecf20Sopenharmony_ci return firm_send_command(port, WHITEHEAT_OPEN, 6188c2ecf20Sopenharmony_ci (__u8 *)&open_command, sizeof(open_command)); 6198c2ecf20Sopenharmony_ci} 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cistatic int firm_close(struct usb_serial_port *port) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci struct whiteheat_simple close_command; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci close_command.port = port->port_number + 1; 6278c2ecf20Sopenharmony_ci return firm_send_command(port, WHITEHEAT_CLOSE, 6288c2ecf20Sopenharmony_ci (__u8 *)&close_command, sizeof(close_command)); 6298c2ecf20Sopenharmony_ci} 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cistatic void firm_setup_port(struct tty_struct *tty) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 6358c2ecf20Sopenharmony_ci struct device *dev = &port->dev; 6368c2ecf20Sopenharmony_ci struct whiteheat_port_settings port_settings; 6378c2ecf20Sopenharmony_ci unsigned int cflag = tty->termios.c_cflag; 6388c2ecf20Sopenharmony_ci speed_t baud; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci port_settings.port = port->port_number + 1; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci /* get the byte size */ 6438c2ecf20Sopenharmony_ci switch (cflag & CSIZE) { 6448c2ecf20Sopenharmony_ci case CS5: port_settings.bits = 5; break; 6458c2ecf20Sopenharmony_ci case CS6: port_settings.bits = 6; break; 6468c2ecf20Sopenharmony_ci case CS7: port_settings.bits = 7; break; 6478c2ecf20Sopenharmony_ci default: 6488c2ecf20Sopenharmony_ci case CS8: port_settings.bits = 8; break; 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - data bits = %d\n", __func__, port_settings.bits); 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci /* determine the parity */ 6538c2ecf20Sopenharmony_ci if (cflag & PARENB) 6548c2ecf20Sopenharmony_ci if (cflag & CMSPAR) 6558c2ecf20Sopenharmony_ci if (cflag & PARODD) 6568c2ecf20Sopenharmony_ci port_settings.parity = WHITEHEAT_PAR_MARK; 6578c2ecf20Sopenharmony_ci else 6588c2ecf20Sopenharmony_ci port_settings.parity = WHITEHEAT_PAR_SPACE; 6598c2ecf20Sopenharmony_ci else 6608c2ecf20Sopenharmony_ci if (cflag & PARODD) 6618c2ecf20Sopenharmony_ci port_settings.parity = WHITEHEAT_PAR_ODD; 6628c2ecf20Sopenharmony_ci else 6638c2ecf20Sopenharmony_ci port_settings.parity = WHITEHEAT_PAR_EVEN; 6648c2ecf20Sopenharmony_ci else 6658c2ecf20Sopenharmony_ci port_settings.parity = WHITEHEAT_PAR_NONE; 6668c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - parity = %c\n", __func__, port_settings.parity); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci /* figure out the stop bits requested */ 6698c2ecf20Sopenharmony_ci if (cflag & CSTOPB) 6708c2ecf20Sopenharmony_ci port_settings.stop = 2; 6718c2ecf20Sopenharmony_ci else 6728c2ecf20Sopenharmony_ci port_settings.stop = 1; 6738c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - stop bits = %d\n", __func__, port_settings.stop); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci /* figure out the flow control settings */ 6768c2ecf20Sopenharmony_ci if (cflag & CRTSCTS) 6778c2ecf20Sopenharmony_ci port_settings.hflow = (WHITEHEAT_HFLOW_CTS | 6788c2ecf20Sopenharmony_ci WHITEHEAT_HFLOW_RTS); 6798c2ecf20Sopenharmony_ci else 6808c2ecf20Sopenharmony_ci port_settings.hflow = WHITEHEAT_HFLOW_NONE; 6818c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - hardware flow control = %s %s %s %s\n", __func__, 6828c2ecf20Sopenharmony_ci (port_settings.hflow & WHITEHEAT_HFLOW_CTS) ? "CTS" : "", 6838c2ecf20Sopenharmony_ci (port_settings.hflow & WHITEHEAT_HFLOW_RTS) ? "RTS" : "", 6848c2ecf20Sopenharmony_ci (port_settings.hflow & WHITEHEAT_HFLOW_DSR) ? "DSR" : "", 6858c2ecf20Sopenharmony_ci (port_settings.hflow & WHITEHEAT_HFLOW_DTR) ? "DTR" : ""); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci /* determine software flow control */ 6888c2ecf20Sopenharmony_ci if (I_IXOFF(tty)) 6898c2ecf20Sopenharmony_ci port_settings.sflow = WHITEHEAT_SFLOW_RXTX; 6908c2ecf20Sopenharmony_ci else 6918c2ecf20Sopenharmony_ci port_settings.sflow = WHITEHEAT_SFLOW_NONE; 6928c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - software flow control = %c\n", __func__, port_settings.sflow); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci port_settings.xon = START_CHAR(tty); 6958c2ecf20Sopenharmony_ci port_settings.xoff = STOP_CHAR(tty); 6968c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - XON = %2x, XOFF = %2x\n", __func__, port_settings.xon, port_settings.xoff); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci /* get the baud rate wanted */ 6998c2ecf20Sopenharmony_ci baud = tty_get_baud_rate(tty); 7008c2ecf20Sopenharmony_ci port_settings.baud = cpu_to_le32(baud); 7018c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - baud rate = %u\n", __func__, baud); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci /* fixme: should set validated settings */ 7048c2ecf20Sopenharmony_ci tty_encode_baud_rate(tty, baud, baud); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci /* handle any settings that aren't specified in the tty structure */ 7078c2ecf20Sopenharmony_ci port_settings.lloop = 0; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci /* now send the message to the device */ 7108c2ecf20Sopenharmony_ci firm_send_command(port, WHITEHEAT_SETUP_PORT, 7118c2ecf20Sopenharmony_ci (__u8 *)&port_settings, sizeof(port_settings)); 7128c2ecf20Sopenharmony_ci} 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_cistatic int firm_set_rts(struct usb_serial_port *port, __u8 onoff) 7168c2ecf20Sopenharmony_ci{ 7178c2ecf20Sopenharmony_ci struct whiteheat_set_rdb rts_command; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci rts_command.port = port->port_number + 1; 7208c2ecf20Sopenharmony_ci rts_command.state = onoff; 7218c2ecf20Sopenharmony_ci return firm_send_command(port, WHITEHEAT_SET_RTS, 7228c2ecf20Sopenharmony_ci (__u8 *)&rts_command, sizeof(rts_command)); 7238c2ecf20Sopenharmony_ci} 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_cistatic int firm_set_dtr(struct usb_serial_port *port, __u8 onoff) 7278c2ecf20Sopenharmony_ci{ 7288c2ecf20Sopenharmony_ci struct whiteheat_set_rdb dtr_command; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci dtr_command.port = port->port_number + 1; 7318c2ecf20Sopenharmony_ci dtr_command.state = onoff; 7328c2ecf20Sopenharmony_ci return firm_send_command(port, WHITEHEAT_SET_DTR, 7338c2ecf20Sopenharmony_ci (__u8 *)&dtr_command, sizeof(dtr_command)); 7348c2ecf20Sopenharmony_ci} 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_cistatic int firm_set_break(struct usb_serial_port *port, __u8 onoff) 7388c2ecf20Sopenharmony_ci{ 7398c2ecf20Sopenharmony_ci struct whiteheat_set_rdb break_command; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci break_command.port = port->port_number + 1; 7428c2ecf20Sopenharmony_ci break_command.state = onoff; 7438c2ecf20Sopenharmony_ci return firm_send_command(port, WHITEHEAT_SET_BREAK, 7448c2ecf20Sopenharmony_ci (__u8 *)&break_command, sizeof(break_command)); 7458c2ecf20Sopenharmony_ci} 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_cistatic int firm_purge(struct usb_serial_port *port, __u8 rxtx) 7498c2ecf20Sopenharmony_ci{ 7508c2ecf20Sopenharmony_ci struct whiteheat_purge purge_command; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci purge_command.port = port->port_number + 1; 7538c2ecf20Sopenharmony_ci purge_command.what = rxtx; 7548c2ecf20Sopenharmony_ci return firm_send_command(port, WHITEHEAT_PURGE, 7558c2ecf20Sopenharmony_ci (__u8 *)&purge_command, sizeof(purge_command)); 7568c2ecf20Sopenharmony_ci} 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_cistatic int firm_get_dtr_rts(struct usb_serial_port *port) 7608c2ecf20Sopenharmony_ci{ 7618c2ecf20Sopenharmony_ci struct whiteheat_simple get_dr_command; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci get_dr_command.port = port->port_number + 1; 7648c2ecf20Sopenharmony_ci return firm_send_command(port, WHITEHEAT_GET_DTR_RTS, 7658c2ecf20Sopenharmony_ci (__u8 *)&get_dr_command, sizeof(get_dr_command)); 7668c2ecf20Sopenharmony_ci} 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_cistatic int firm_report_tx_done(struct usb_serial_port *port) 7708c2ecf20Sopenharmony_ci{ 7718c2ecf20Sopenharmony_ci struct whiteheat_simple close_command; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci close_command.port = port->port_number + 1; 7748c2ecf20Sopenharmony_ci return firm_send_command(port, WHITEHEAT_REPORT_TX_DONE, 7758c2ecf20Sopenharmony_ci (__u8 *)&close_command, sizeof(close_command)); 7768c2ecf20Sopenharmony_ci} 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci/***************************************************************************** 7808c2ecf20Sopenharmony_ci * Connect Tech's White Heat utility functions 7818c2ecf20Sopenharmony_ci *****************************************************************************/ 7828c2ecf20Sopenharmony_cistatic int start_command_port(struct usb_serial *serial) 7838c2ecf20Sopenharmony_ci{ 7848c2ecf20Sopenharmony_ci struct usb_serial_port *command_port; 7858c2ecf20Sopenharmony_ci struct whiteheat_command_private *command_info; 7868c2ecf20Sopenharmony_ci int retval = 0; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci command_port = serial->port[COMMAND_PORT]; 7898c2ecf20Sopenharmony_ci command_info = usb_get_serial_port_data(command_port); 7908c2ecf20Sopenharmony_ci mutex_lock(&command_info->mutex); 7918c2ecf20Sopenharmony_ci if (!command_info->port_running) { 7928c2ecf20Sopenharmony_ci /* Work around HCD bugs */ 7938c2ecf20Sopenharmony_ci usb_clear_halt(serial->dev, command_port->read_urb->pipe); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci retval = usb_submit_urb(command_port->read_urb, GFP_KERNEL); 7968c2ecf20Sopenharmony_ci if (retval) { 7978c2ecf20Sopenharmony_ci dev_err(&serial->dev->dev, 7988c2ecf20Sopenharmony_ci "%s - failed submitting read urb, error %d\n", 7998c2ecf20Sopenharmony_ci __func__, retval); 8008c2ecf20Sopenharmony_ci goto exit; 8018c2ecf20Sopenharmony_ci } 8028c2ecf20Sopenharmony_ci } 8038c2ecf20Sopenharmony_ci command_info->port_running++; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ciexit: 8068c2ecf20Sopenharmony_ci mutex_unlock(&command_info->mutex); 8078c2ecf20Sopenharmony_ci return retval; 8088c2ecf20Sopenharmony_ci} 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_cistatic void stop_command_port(struct usb_serial *serial) 8128c2ecf20Sopenharmony_ci{ 8138c2ecf20Sopenharmony_ci struct usb_serial_port *command_port; 8148c2ecf20Sopenharmony_ci struct whiteheat_command_private *command_info; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci command_port = serial->port[COMMAND_PORT]; 8178c2ecf20Sopenharmony_ci command_info = usb_get_serial_port_data(command_port); 8188c2ecf20Sopenharmony_ci mutex_lock(&command_info->mutex); 8198c2ecf20Sopenharmony_ci command_info->port_running--; 8208c2ecf20Sopenharmony_ci if (!command_info->port_running) 8218c2ecf20Sopenharmony_ci usb_kill_urb(command_port->read_urb); 8228c2ecf20Sopenharmony_ci mutex_unlock(&command_info->mutex); 8238c2ecf20Sopenharmony_ci} 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table_combined); 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 8288c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 8298c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ciMODULE_FIRMWARE("whiteheat.fw"); 8328c2ecf20Sopenharmony_ciMODULE_FIRMWARE("whiteheat_loader.fw"); 833