cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

whiteheat.c (23941B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * USB ConnectTech WhiteHEAT driver
      4 *
      5 *	Copyright (C) 2002
      6 *	    Connect Tech Inc.
      7 *
      8 *	Copyright (C) 1999 - 2001
      9 *	    Greg Kroah-Hartman (greg@kroah.com)
     10 *
     11 * See Documentation/usb/usb-serial.rst for more information on using this
     12 * driver
     13 */
     14
     15#include <linux/kernel.h>
     16#include <linux/errno.h>
     17#include <linux/slab.h>
     18#include <linux/tty.h>
     19#include <linux/tty_driver.h>
     20#include <linux/tty_flip.h>
     21#include <linux/module.h>
     22#include <linux/spinlock.h>
     23#include <linux/mutex.h>
     24#include <linux/uaccess.h>
     25#include <asm/termbits.h>
     26#include <linux/usb.h>
     27#include <linux/serial_reg.h>
     28#include <linux/serial.h>
     29#include <linux/usb/serial.h>
     30#include <linux/usb/ezusb.h>
     31#include "whiteheat.h"			/* WhiteHEAT specific commands */
     32
     33/*
     34 * Version Information
     35 */
     36#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Stuart MacDonald <stuartm@connecttech.com>"
     37#define DRIVER_DESC "USB ConnectTech WhiteHEAT driver"
     38
     39#define CONNECT_TECH_VENDOR_ID		0x0710
     40#define CONNECT_TECH_FAKE_WHITE_HEAT_ID	0x0001
     41#define CONNECT_TECH_WHITE_HEAT_ID	0x8001
     42
     43/*
     44   ID tables for whiteheat are unusual, because we want to different
     45   things for different versions of the device.  Eventually, this
     46   will be doable from a single table.  But, for now, we define two
     47   separate ID tables, and then a third table that combines them
     48   just for the purpose of exporting the autoloading information.
     49*/
     50static const struct usb_device_id id_table_std[] = {
     51	{ USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_WHITE_HEAT_ID) },
     52	{ }						/* Terminating entry */
     53};
     54
     55static const struct usb_device_id id_table_prerenumeration[] = {
     56	{ USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_FAKE_WHITE_HEAT_ID) },
     57	{ }						/* Terminating entry */
     58};
     59
     60static const struct usb_device_id id_table_combined[] = {
     61	{ USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_WHITE_HEAT_ID) },
     62	{ USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_FAKE_WHITE_HEAT_ID) },
     63	{ }						/* Terminating entry */
     64};
     65
     66MODULE_DEVICE_TABLE(usb, id_table_combined);
     67
     68
     69/* function prototypes for the Connect Tech WhiteHEAT prerenumeration device */
     70static int  whiteheat_firmware_download(struct usb_serial *serial,
     71					const struct usb_device_id *id);
     72static int  whiteheat_firmware_attach(struct usb_serial *serial);
     73
     74/* function prototypes for the Connect Tech WhiteHEAT serial converter */
     75static int  whiteheat_attach(struct usb_serial *serial);
     76static void whiteheat_release(struct usb_serial *serial);
     77static int  whiteheat_port_probe(struct usb_serial_port *port);
     78static void whiteheat_port_remove(struct usb_serial_port *port);
     79static int  whiteheat_open(struct tty_struct *tty,
     80			struct usb_serial_port *port);
     81static void whiteheat_close(struct usb_serial_port *port);
     82static void whiteheat_get_serial(struct tty_struct *tty,
     83			struct serial_struct *ss);
     84static void whiteheat_set_termios(struct tty_struct *tty,
     85			struct usb_serial_port *port, struct ktermios *old);
     86static int  whiteheat_tiocmget(struct tty_struct *tty);
     87static int  whiteheat_tiocmset(struct tty_struct *tty,
     88			unsigned int set, unsigned int clear);
     89static void whiteheat_break_ctl(struct tty_struct *tty, int break_state);
     90
     91static struct usb_serial_driver whiteheat_fake_device = {
     92	.driver = {
     93		.owner =	THIS_MODULE,
     94		.name =		"whiteheatnofirm",
     95	},
     96	.description =		"Connect Tech - WhiteHEAT - (prerenumeration)",
     97	.id_table =		id_table_prerenumeration,
     98	.num_ports =		1,
     99	.probe =		whiteheat_firmware_download,
    100	.attach =		whiteheat_firmware_attach,
    101};
    102
    103static struct usb_serial_driver whiteheat_device = {
    104	.driver = {
    105		.owner =	THIS_MODULE,
    106		.name =		"whiteheat",
    107	},
    108	.description =		"Connect Tech - WhiteHEAT",
    109	.id_table =		id_table_std,
    110	.num_ports =		4,
    111	.num_bulk_in =		5,
    112	.num_bulk_out =		5,
    113	.attach =		whiteheat_attach,
    114	.release =		whiteheat_release,
    115	.port_probe =		whiteheat_port_probe,
    116	.port_remove =		whiteheat_port_remove,
    117	.open =			whiteheat_open,
    118	.close =		whiteheat_close,
    119	.get_serial =		whiteheat_get_serial,
    120	.set_termios =		whiteheat_set_termios,
    121	.break_ctl =		whiteheat_break_ctl,
    122	.tiocmget =		whiteheat_tiocmget,
    123	.tiocmset =		whiteheat_tiocmset,
    124	.throttle =		usb_serial_generic_throttle,
    125	.unthrottle =		usb_serial_generic_unthrottle,
    126};
    127
    128static struct usb_serial_driver * const serial_drivers[] = {
    129	&whiteheat_fake_device, &whiteheat_device, NULL
    130};
    131
    132struct whiteheat_command_private {
    133	struct mutex		mutex;
    134	__u8			port_running;
    135	__u8			command_finished;
    136	wait_queue_head_t	wait_command; /* for handling sleeping whilst
    137						 waiting for a command to
    138						 finish */
    139	__u8			result_buffer[64];
    140};
    141
    142struct whiteheat_private {
    143	__u8			mcr;		/* FIXME: no locking on mcr */
    144};
    145
    146
    147/* local function prototypes */
    148static int start_command_port(struct usb_serial *serial);
    149static void stop_command_port(struct usb_serial *serial);
    150static void command_port_write_callback(struct urb *urb);
    151static void command_port_read_callback(struct urb *urb);
    152
    153static int firm_send_command(struct usb_serial_port *port, __u8 command,
    154						__u8 *data, __u8 datasize);
    155static int firm_open(struct usb_serial_port *port);
    156static int firm_close(struct usb_serial_port *port);
    157static void firm_setup_port(struct tty_struct *tty);
    158static int firm_set_rts(struct usb_serial_port *port, __u8 onoff);
    159static int firm_set_dtr(struct usb_serial_port *port, __u8 onoff);
    160static int firm_set_break(struct usb_serial_port *port, __u8 onoff);
    161static int firm_purge(struct usb_serial_port *port, __u8 rxtx);
    162static int firm_get_dtr_rts(struct usb_serial_port *port);
    163static int firm_report_tx_done(struct usb_serial_port *port);
    164
    165
    166#define COMMAND_PORT		4
    167#define COMMAND_TIMEOUT		(2*HZ)	/* 2 second timeout for a command */
    168#define	COMMAND_TIMEOUT_MS	2000
    169
    170
    171/*****************************************************************************
    172 * Connect Tech's White Heat prerenumeration driver functions
    173 *****************************************************************************/
    174
    175/* steps to download the firmware to the WhiteHEAT device:
    176 - hold the reset (by writing to the reset bit of the CPUCS register)
    177 - download the VEND_AX.HEX file to the chip using VENDOR_REQUEST-ANCHOR_LOAD
    178 - release the reset (by writing to the CPUCS register)
    179 - download the WH.HEX file for all addresses greater than 0x1b3f using
    180   VENDOR_REQUEST-ANCHOR_EXTERNAL_RAM_LOAD
    181 - hold the reset
    182 - download the WH.HEX file for all addresses less than 0x1b40 using
    183   VENDOR_REQUEST_ANCHOR_LOAD
    184 - release the reset
    185 - device renumerated itself and comes up as new device id with all
    186   firmware download completed.
    187*/
    188static int whiteheat_firmware_download(struct usb_serial *serial,
    189					const struct usb_device_id *id)
    190{
    191	int response;
    192
    193	response = ezusb_fx1_ihex_firmware_download(serial->dev, "whiteheat_loader.fw");
    194	if (response >= 0) {
    195		response = ezusb_fx1_ihex_firmware_download(serial->dev, "whiteheat.fw");
    196		if (response >= 0)
    197			return 0;
    198	}
    199	return -ENOENT;
    200}
    201
    202
    203static int whiteheat_firmware_attach(struct usb_serial *serial)
    204{
    205	/* We want this device to fail to have a driver assigned to it */
    206	return 1;
    207}
    208
    209
    210/*****************************************************************************
    211 * Connect Tech's White Heat serial driver functions
    212 *****************************************************************************/
    213
    214static int whiteheat_attach(struct usb_serial *serial)
    215{
    216	struct usb_serial_port *command_port;
    217	struct whiteheat_command_private *command_info;
    218	struct whiteheat_hw_info *hw_info;
    219	int pipe;
    220	int ret;
    221	int alen;
    222	__u8 *command;
    223	__u8 *result;
    224
    225	command_port = serial->port[COMMAND_PORT];
    226
    227	pipe = usb_sndbulkpipe(serial->dev,
    228			command_port->bulk_out_endpointAddress);
    229	command = kmalloc(2, GFP_KERNEL);
    230	if (!command)
    231		goto no_command_buffer;
    232	command[0] = WHITEHEAT_GET_HW_INFO;
    233	command[1] = 0;
    234
    235	result = kmalloc(sizeof(*hw_info) + 1, GFP_KERNEL);
    236	if (!result)
    237		goto no_result_buffer;
    238	/*
    239	 * When the module is reloaded the firmware is still there and
    240	 * the endpoints are still in the usb core unchanged. This is the
    241	 * unlinking bug in disguise. Same for the call below.
    242	 */
    243	usb_clear_halt(serial->dev, pipe);
    244	ret = usb_bulk_msg(serial->dev, pipe, command, 2,
    245						&alen, COMMAND_TIMEOUT_MS);
    246	if (ret) {
    247		dev_err(&serial->dev->dev, "%s: Couldn't send command [%d]\n",
    248			serial->type->description, ret);
    249		goto no_firmware;
    250	} else if (alen != 2) {
    251		dev_err(&serial->dev->dev, "%s: Send command incomplete [%d]\n",
    252			serial->type->description, alen);
    253		goto no_firmware;
    254	}
    255
    256	pipe = usb_rcvbulkpipe(serial->dev,
    257				command_port->bulk_in_endpointAddress);
    258	/* See the comment on the usb_clear_halt() above */
    259	usb_clear_halt(serial->dev, pipe);
    260	ret = usb_bulk_msg(serial->dev, pipe, result,
    261			sizeof(*hw_info) + 1, &alen, COMMAND_TIMEOUT_MS);
    262	if (ret) {
    263		dev_err(&serial->dev->dev, "%s: Couldn't get results [%d]\n",
    264			serial->type->description, ret);
    265		goto no_firmware;
    266	} else if (alen != sizeof(*hw_info) + 1) {
    267		dev_err(&serial->dev->dev, "%s: Get results incomplete [%d]\n",
    268			serial->type->description, alen);
    269		goto no_firmware;
    270	} else if (result[0] != command[0]) {
    271		dev_err(&serial->dev->dev, "%s: Command failed [%d]\n",
    272			serial->type->description, result[0]);
    273		goto no_firmware;
    274	}
    275
    276	hw_info = (struct whiteheat_hw_info *)&result[1];
    277
    278	dev_info(&serial->dev->dev, "%s: Firmware v%d.%02d\n",
    279		 serial->type->description,
    280		 hw_info->sw_major_rev, hw_info->sw_minor_rev);
    281
    282	command_info = kmalloc(sizeof(struct whiteheat_command_private),
    283								GFP_KERNEL);
    284	if (!command_info)
    285		goto no_command_private;
    286
    287	mutex_init(&command_info->mutex);
    288	command_info->port_running = 0;
    289	init_waitqueue_head(&command_info->wait_command);
    290	usb_set_serial_port_data(command_port, command_info);
    291	command_port->write_urb->complete = command_port_write_callback;
    292	command_port->read_urb->complete = command_port_read_callback;
    293	kfree(result);
    294	kfree(command);
    295
    296	return 0;
    297
    298no_firmware:
    299	/* Firmware likely not running */
    300	dev_err(&serial->dev->dev,
    301		"%s: Unable to retrieve firmware version, try replugging\n",
    302		serial->type->description);
    303	dev_err(&serial->dev->dev,
    304		"%s: If the firmware is not running (status led not blinking)\n",
    305		serial->type->description);
    306	dev_err(&serial->dev->dev,
    307		"%s: please contact support@connecttech.com\n",
    308		serial->type->description);
    309	kfree(result);
    310	kfree(command);
    311	return -ENODEV;
    312
    313no_command_private:
    314	kfree(result);
    315no_result_buffer:
    316	kfree(command);
    317no_command_buffer:
    318	return -ENOMEM;
    319}
    320
    321static void whiteheat_release(struct usb_serial *serial)
    322{
    323	struct usb_serial_port *command_port;
    324
    325	/* free up our private data for our command port */
    326	command_port = serial->port[COMMAND_PORT];
    327	kfree(usb_get_serial_port_data(command_port));
    328}
    329
    330static int whiteheat_port_probe(struct usb_serial_port *port)
    331{
    332	struct whiteheat_private *info;
    333
    334	info = kzalloc(sizeof(*info), GFP_KERNEL);
    335	if (!info)
    336		return -ENOMEM;
    337
    338	usb_set_serial_port_data(port, info);
    339
    340	return 0;
    341}
    342
    343static void whiteheat_port_remove(struct usb_serial_port *port)
    344{
    345	struct whiteheat_private *info;
    346
    347	info = usb_get_serial_port_data(port);
    348	kfree(info);
    349}
    350
    351static int whiteheat_open(struct tty_struct *tty, struct usb_serial_port *port)
    352{
    353	int retval;
    354
    355	retval = start_command_port(port->serial);
    356	if (retval)
    357		goto exit;
    358
    359	/* send an open port command */
    360	retval = firm_open(port);
    361	if (retval) {
    362		stop_command_port(port->serial);
    363		goto exit;
    364	}
    365
    366	retval = firm_purge(port, WHITEHEAT_PURGE_RX | WHITEHEAT_PURGE_TX);
    367	if (retval) {
    368		firm_close(port);
    369		stop_command_port(port->serial);
    370		goto exit;
    371	}
    372
    373	if (tty)
    374		firm_setup_port(tty);
    375
    376	/* Work around HCD bugs */
    377	usb_clear_halt(port->serial->dev, port->read_urb->pipe);
    378	usb_clear_halt(port->serial->dev, port->write_urb->pipe);
    379
    380	retval = usb_serial_generic_open(tty, port);
    381	if (retval) {
    382		firm_close(port);
    383		stop_command_port(port->serial);
    384		goto exit;
    385	}
    386exit:
    387	return retval;
    388}
    389
    390
    391static void whiteheat_close(struct usb_serial_port *port)
    392{
    393	firm_report_tx_done(port);
    394	firm_close(port);
    395
    396	usb_serial_generic_close(port);
    397
    398	stop_command_port(port->serial);
    399}
    400
    401static int whiteheat_tiocmget(struct tty_struct *tty)
    402{
    403	struct usb_serial_port *port = tty->driver_data;
    404	struct whiteheat_private *info = usb_get_serial_port_data(port);
    405	unsigned int modem_signals = 0;
    406
    407	firm_get_dtr_rts(port);
    408	if (info->mcr & UART_MCR_DTR)
    409		modem_signals |= TIOCM_DTR;
    410	if (info->mcr & UART_MCR_RTS)
    411		modem_signals |= TIOCM_RTS;
    412
    413	return modem_signals;
    414}
    415
    416static int whiteheat_tiocmset(struct tty_struct *tty,
    417			       unsigned int set, unsigned int clear)
    418{
    419	struct usb_serial_port *port = tty->driver_data;
    420	struct whiteheat_private *info = usb_get_serial_port_data(port);
    421
    422	if (set & TIOCM_RTS)
    423		info->mcr |= UART_MCR_RTS;
    424	if (set & TIOCM_DTR)
    425		info->mcr |= UART_MCR_DTR;
    426
    427	if (clear & TIOCM_RTS)
    428		info->mcr &= ~UART_MCR_RTS;
    429	if (clear & TIOCM_DTR)
    430		info->mcr &= ~UART_MCR_DTR;
    431
    432	firm_set_dtr(port, info->mcr & UART_MCR_DTR);
    433	firm_set_rts(port, info->mcr & UART_MCR_RTS);
    434	return 0;
    435}
    436
    437
    438static void whiteheat_get_serial(struct tty_struct *tty, struct serial_struct *ss)
    439{
    440	ss->baud_base = 460800;
    441}
    442
    443
    444static void whiteheat_set_termios(struct tty_struct *tty,
    445	struct usb_serial_port *port, struct ktermios *old_termios)
    446{
    447	firm_setup_port(tty);
    448}
    449
    450static void whiteheat_break_ctl(struct tty_struct *tty, int break_state)
    451{
    452	struct usb_serial_port *port = tty->driver_data;
    453	firm_set_break(port, break_state);
    454}
    455
    456
    457/*****************************************************************************
    458 * Connect Tech's White Heat callback routines
    459 *****************************************************************************/
    460static void command_port_write_callback(struct urb *urb)
    461{
    462	int status = urb->status;
    463
    464	if (status) {
    465		dev_dbg(&urb->dev->dev, "nonzero urb status: %d\n", status);
    466		return;
    467	}
    468}
    469
    470
    471static void command_port_read_callback(struct urb *urb)
    472{
    473	struct usb_serial_port *command_port = urb->context;
    474	struct whiteheat_command_private *command_info;
    475	int status = urb->status;
    476	unsigned char *data = urb->transfer_buffer;
    477	int result;
    478
    479	command_info = usb_get_serial_port_data(command_port);
    480	if (!command_info) {
    481		dev_dbg(&urb->dev->dev, "%s - command_info is NULL, exiting.\n", __func__);
    482		return;
    483	}
    484	if (!urb->actual_length) {
    485		dev_dbg(&urb->dev->dev, "%s - empty response, exiting.\n", __func__);
    486		return;
    487	}
    488	if (status) {
    489		dev_dbg(&urb->dev->dev, "%s - nonzero urb status: %d\n", __func__, status);
    490		if (status != -ENOENT)
    491			command_info->command_finished = WHITEHEAT_CMD_FAILURE;
    492		wake_up(&command_info->wait_command);
    493		return;
    494	}
    495
    496	usb_serial_debug_data(&command_port->dev, __func__, urb->actual_length, data);
    497
    498	if (data[0] == WHITEHEAT_CMD_COMPLETE) {
    499		command_info->command_finished = WHITEHEAT_CMD_COMPLETE;
    500		wake_up(&command_info->wait_command);
    501	} else if (data[0] == WHITEHEAT_CMD_FAILURE) {
    502		command_info->command_finished = WHITEHEAT_CMD_FAILURE;
    503		wake_up(&command_info->wait_command);
    504	} else if (data[0] == WHITEHEAT_EVENT) {
    505		/* These are unsolicited reports from the firmware, hence no
    506		   waiting command to wakeup */
    507		dev_dbg(&urb->dev->dev, "%s - event received\n", __func__);
    508	} else if ((data[0] == WHITEHEAT_GET_DTR_RTS) &&
    509		(urb->actual_length - 1 <= sizeof(command_info->result_buffer))) {
    510		memcpy(command_info->result_buffer, &data[1],
    511						urb->actual_length - 1);
    512		command_info->command_finished = WHITEHEAT_CMD_COMPLETE;
    513		wake_up(&command_info->wait_command);
    514	} else
    515		dev_dbg(&urb->dev->dev, "%s - bad reply from firmware\n", __func__);
    516
    517	/* Continue trying to always read */
    518	result = usb_submit_urb(command_port->read_urb, GFP_ATOMIC);
    519	if (result)
    520		dev_dbg(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n",
    521			__func__, result);
    522}
    523
    524
    525/*****************************************************************************
    526 * Connect Tech's White Heat firmware interface
    527 *****************************************************************************/
    528static int firm_send_command(struct usb_serial_port *port, __u8 command,
    529						__u8 *data, __u8 datasize)
    530{
    531	struct usb_serial_port *command_port;
    532	struct whiteheat_command_private *command_info;
    533	struct whiteheat_private *info;
    534	struct device *dev = &port->dev;
    535	__u8 *transfer_buffer;
    536	int retval = 0;
    537	int t;
    538
    539	dev_dbg(dev, "%s - command %d\n", __func__, command);
    540
    541	command_port = port->serial->port[COMMAND_PORT];
    542	command_info = usb_get_serial_port_data(command_port);
    543
    544	if (command_port->bulk_out_size < datasize + 1)
    545		return -EIO;
    546
    547	mutex_lock(&command_info->mutex);
    548	command_info->command_finished = false;
    549
    550	transfer_buffer = (__u8 *)command_port->write_urb->transfer_buffer;
    551	transfer_buffer[0] = command;
    552	memcpy(&transfer_buffer[1], data, datasize);
    553	command_port->write_urb->transfer_buffer_length = datasize + 1;
    554	retval = usb_submit_urb(command_port->write_urb, GFP_NOIO);
    555	if (retval) {
    556		dev_dbg(dev, "%s - submit urb failed\n", __func__);
    557		goto exit;
    558	}
    559
    560	/* wait for the command to complete */
    561	t = wait_event_timeout(command_info->wait_command,
    562		(bool)command_info->command_finished, COMMAND_TIMEOUT);
    563	if (!t)
    564		usb_kill_urb(command_port->write_urb);
    565
    566	if (command_info->command_finished == false) {
    567		dev_dbg(dev, "%s - command timed out.\n", __func__);
    568		retval = -ETIMEDOUT;
    569		goto exit;
    570	}
    571
    572	if (command_info->command_finished == WHITEHEAT_CMD_FAILURE) {
    573		dev_dbg(dev, "%s - command failed.\n", __func__);
    574		retval = -EIO;
    575		goto exit;
    576	}
    577
    578	if (command_info->command_finished == WHITEHEAT_CMD_COMPLETE) {
    579		dev_dbg(dev, "%s - command completed.\n", __func__);
    580		switch (command) {
    581		case WHITEHEAT_GET_DTR_RTS:
    582			info = usb_get_serial_port_data(port);
    583			info->mcr = command_info->result_buffer[0];
    584			break;
    585		}
    586	}
    587exit:
    588	mutex_unlock(&command_info->mutex);
    589	return retval;
    590}
    591
    592
    593static int firm_open(struct usb_serial_port *port)
    594{
    595	struct whiteheat_simple open_command;
    596
    597	open_command.port = port->port_number + 1;
    598	return firm_send_command(port, WHITEHEAT_OPEN,
    599		(__u8 *)&open_command, sizeof(open_command));
    600}
    601
    602
    603static int firm_close(struct usb_serial_port *port)
    604{
    605	struct whiteheat_simple close_command;
    606
    607	close_command.port = port->port_number + 1;
    608	return firm_send_command(port, WHITEHEAT_CLOSE,
    609			(__u8 *)&close_command, sizeof(close_command));
    610}
    611
    612
    613static void firm_setup_port(struct tty_struct *tty)
    614{
    615	struct usb_serial_port *port = tty->driver_data;
    616	struct device *dev = &port->dev;
    617	struct whiteheat_port_settings port_settings;
    618	unsigned int cflag = tty->termios.c_cflag;
    619	speed_t baud;
    620
    621	port_settings.port = port->port_number + 1;
    622
    623	port_settings.bits = tty_get_char_size(cflag);
    624	dev_dbg(dev, "%s - data bits = %d\n", __func__, port_settings.bits);
    625
    626	/* determine the parity */
    627	if (cflag & PARENB)
    628		if (cflag & CMSPAR)
    629			if (cflag & PARODD)
    630				port_settings.parity = WHITEHEAT_PAR_MARK;
    631			else
    632				port_settings.parity = WHITEHEAT_PAR_SPACE;
    633		else
    634			if (cflag & PARODD)
    635				port_settings.parity = WHITEHEAT_PAR_ODD;
    636			else
    637				port_settings.parity = WHITEHEAT_PAR_EVEN;
    638	else
    639		port_settings.parity = WHITEHEAT_PAR_NONE;
    640	dev_dbg(dev, "%s - parity = %c\n", __func__, port_settings.parity);
    641
    642	/* figure out the stop bits requested */
    643	if (cflag & CSTOPB)
    644		port_settings.stop = 2;
    645	else
    646		port_settings.stop = 1;
    647	dev_dbg(dev, "%s - stop bits = %d\n", __func__, port_settings.stop);
    648
    649	/* figure out the flow control settings */
    650	if (cflag & CRTSCTS)
    651		port_settings.hflow = (WHITEHEAT_HFLOW_CTS |
    652						WHITEHEAT_HFLOW_RTS);
    653	else
    654		port_settings.hflow = WHITEHEAT_HFLOW_NONE;
    655	dev_dbg(dev, "%s - hardware flow control = %s %s %s %s\n", __func__,
    656	    (port_settings.hflow & WHITEHEAT_HFLOW_CTS) ? "CTS" : "",
    657	    (port_settings.hflow & WHITEHEAT_HFLOW_RTS) ? "RTS" : "",
    658	    (port_settings.hflow & WHITEHEAT_HFLOW_DSR) ? "DSR" : "",
    659	    (port_settings.hflow & WHITEHEAT_HFLOW_DTR) ? "DTR" : "");
    660
    661	/* determine software flow control */
    662	if (I_IXOFF(tty))
    663		port_settings.sflow = WHITEHEAT_SFLOW_RXTX;
    664	else
    665		port_settings.sflow = WHITEHEAT_SFLOW_NONE;
    666	dev_dbg(dev, "%s - software flow control = %c\n", __func__, port_settings.sflow);
    667
    668	port_settings.xon = START_CHAR(tty);
    669	port_settings.xoff = STOP_CHAR(tty);
    670	dev_dbg(dev, "%s - XON = %2x, XOFF = %2x\n", __func__, port_settings.xon, port_settings.xoff);
    671
    672	/* get the baud rate wanted */
    673	baud = tty_get_baud_rate(tty);
    674	port_settings.baud = cpu_to_le32(baud);
    675	dev_dbg(dev, "%s - baud rate = %u\n", __func__, baud);
    676
    677	/* fixme: should set validated settings */
    678	tty_encode_baud_rate(tty, baud, baud);
    679
    680	/* handle any settings that aren't specified in the tty structure */
    681	port_settings.lloop = 0;
    682
    683	/* now send the message to the device */
    684	firm_send_command(port, WHITEHEAT_SETUP_PORT,
    685			(__u8 *)&port_settings, sizeof(port_settings));
    686}
    687
    688
    689static int firm_set_rts(struct usb_serial_port *port, __u8 onoff)
    690{
    691	struct whiteheat_set_rdb rts_command;
    692
    693	rts_command.port = port->port_number + 1;
    694	rts_command.state = onoff;
    695	return firm_send_command(port, WHITEHEAT_SET_RTS,
    696			(__u8 *)&rts_command, sizeof(rts_command));
    697}
    698
    699
    700static int firm_set_dtr(struct usb_serial_port *port, __u8 onoff)
    701{
    702	struct whiteheat_set_rdb dtr_command;
    703
    704	dtr_command.port = port->port_number + 1;
    705	dtr_command.state = onoff;
    706	return firm_send_command(port, WHITEHEAT_SET_DTR,
    707			(__u8 *)&dtr_command, sizeof(dtr_command));
    708}
    709
    710
    711static int firm_set_break(struct usb_serial_port *port, __u8 onoff)
    712{
    713	struct whiteheat_set_rdb break_command;
    714
    715	break_command.port = port->port_number + 1;
    716	break_command.state = onoff;
    717	return firm_send_command(port, WHITEHEAT_SET_BREAK,
    718			(__u8 *)&break_command, sizeof(break_command));
    719}
    720
    721
    722static int firm_purge(struct usb_serial_port *port, __u8 rxtx)
    723{
    724	struct whiteheat_purge purge_command;
    725
    726	purge_command.port = port->port_number + 1;
    727	purge_command.what = rxtx;
    728	return firm_send_command(port, WHITEHEAT_PURGE,
    729			(__u8 *)&purge_command, sizeof(purge_command));
    730}
    731
    732
    733static int firm_get_dtr_rts(struct usb_serial_port *port)
    734{
    735	struct whiteheat_simple get_dr_command;
    736
    737	get_dr_command.port = port->port_number + 1;
    738	return firm_send_command(port, WHITEHEAT_GET_DTR_RTS,
    739			(__u8 *)&get_dr_command, sizeof(get_dr_command));
    740}
    741
    742
    743static int firm_report_tx_done(struct usb_serial_port *port)
    744{
    745	struct whiteheat_simple close_command;
    746
    747	close_command.port = port->port_number + 1;
    748	return firm_send_command(port, WHITEHEAT_REPORT_TX_DONE,
    749			(__u8 *)&close_command, sizeof(close_command));
    750}
    751
    752
    753/*****************************************************************************
    754 * Connect Tech's White Heat utility functions
    755 *****************************************************************************/
    756static int start_command_port(struct usb_serial *serial)
    757{
    758	struct usb_serial_port *command_port;
    759	struct whiteheat_command_private *command_info;
    760	int retval = 0;
    761
    762	command_port = serial->port[COMMAND_PORT];
    763	command_info = usb_get_serial_port_data(command_port);
    764	mutex_lock(&command_info->mutex);
    765	if (!command_info->port_running) {
    766		/* Work around HCD bugs */
    767		usb_clear_halt(serial->dev, command_port->read_urb->pipe);
    768
    769		retval = usb_submit_urb(command_port->read_urb, GFP_KERNEL);
    770		if (retval) {
    771			dev_err(&serial->dev->dev,
    772				"%s - failed submitting read urb, error %d\n",
    773				__func__, retval);
    774			goto exit;
    775		}
    776	}
    777	command_info->port_running++;
    778
    779exit:
    780	mutex_unlock(&command_info->mutex);
    781	return retval;
    782}
    783
    784
    785static void stop_command_port(struct usb_serial *serial)
    786{
    787	struct usb_serial_port *command_port;
    788	struct whiteheat_command_private *command_info;
    789
    790	command_port = serial->port[COMMAND_PORT];
    791	command_info = usb_get_serial_port_data(command_port);
    792	mutex_lock(&command_info->mutex);
    793	command_info->port_running--;
    794	if (!command_info->port_running)
    795		usb_kill_urb(command_port->read_urb);
    796	mutex_unlock(&command_info->mutex);
    797}
    798
    799module_usb_serial_driver(serial_drivers, id_table_combined);
    800
    801MODULE_AUTHOR(DRIVER_AUTHOR);
    802MODULE_DESCRIPTION(DRIVER_DESC);
    803MODULE_LICENSE("GPL");
    804
    805MODULE_FIRMWARE("whiteheat.fw");
    806MODULE_FIRMWARE("whiteheat_loader.fw");