Commit 97338442 authored by Stuart MacDonald's avatar Stuart MacDonald Committed by Greg Kroah-Hartman

[PATCH] usb whiteheat driver update

Update to full working driver status. Latest firmware 4.06 too. Driver
now officially supported.
parent e185597b
...@@ -41,17 +41,13 @@ SPECIFIC DEVICES SUPPORTED ...@@ -41,17 +41,13 @@ SPECIFIC DEVICES SUPPORTED
ConnectTech WhiteHEAT 4 port converter ConnectTech WhiteHEAT 4 port converter
ConnectTech has been very forthcoming with information about their ConnectTech has been very forthcoming with information about their
device, including providing a unit to test with. This driver will end up device, including providing a unit to test with.
being fully supported.
Current status: The driver is officially supported by Connect Tech Inc.
The device's firmware is downloaded on connection, the new firmware http://www.connecttech.com
runs properly and all four ports are successfully recognized and connected.
Data can be sent and received through the device on all ports.
Hardware flow control needs to be implemented.
For any questions or problems with this driver, please contact Greg For any questions or problems with this driver, please contact
Kroah-Hartman at greg@kroah.com Stuart MacDonald at stuartm@connecttech.com
HandSpring Visor, Palm USB, and Clié USB driver HandSpring Visor, Palm USB, and Clié USB driver
......
...@@ -1830,6 +1830,14 @@ L: linux-usb-devel@lists.sourceforge.net ...@@ -1830,6 +1830,14 @@ L: linux-usb-devel@lists.sourceforge.net
W: http://www.kroah.com/linux/ W: http://www.kroah.com/linux/
S: Maintained S: Maintained
USB SERIAL WHITEHEAT DRIVER
P: Stuart MacDonald
M: stuartm@connecttech.com
L: linux-usb-users@lists.sourceforge.net
L: linux-usb-devel@lists.sourceforge.net
W: http://www.connecttech.com
S: Supported
USB SUBSYSTEM USB SUBSYSTEM
P: Greg Kroah-Hartman P: Greg Kroah-Hartman
M: greg@kroah.com M: greg@kroah.com
......
/* /*
* USB ConnectTech WhiteHEAT driver * USB ConnectTech WhiteHEAT driver
* *
* Copyright (C) 2002
* Connect Tech Inc.
*
* Copyright (C) 1999 - 2001 * Copyright (C) 1999 - 2001
* Greg Kroah-Hartman (greg@kroah.com) * Greg Kroah-Hartman (greg@kroah.com)
* *
...@@ -11,6 +14,9 @@ ...@@ -11,6 +14,9 @@
* *
* See Documentation/usb/usb-serial.txt for more information on using this driver * See Documentation/usb/usb-serial.txt for more information on using this driver
* *
* (10/09/2002) Stuart MacDonald (stuartm@connecttech.com)
* Upgrade to full working driver
*
* (05/30/2001) gkh * (05/30/2001) gkh
* switched from using spinlock to a semaphore, which fixes lots of problems. * switched from using spinlock to a semaphore, which fixes lots of problems.
* *
...@@ -71,6 +77,8 @@ ...@@ -71,6 +77,8 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/serial_reg.h>
#include <linux/serial.h>
#ifdef CONFIG_USB_SERIAL_DEBUG #ifdef CONFIG_USB_SERIAL_DEBUG
static int debug = 1; static int debug = 1;
...@@ -85,8 +93,8 @@ ...@@ -85,8 +93,8 @@
/* /*
* Version Information * Version Information
*/ */
#define DRIVER_VERSION "v1.2" #define DRIVER_VERSION "v2.0"
#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>" #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Stuart MacDonald <stuartm@connecttech.com>"
#define DRIVER_DESC "USB ConnectTech WhiteHEAT driver" #define DRIVER_DESC "USB ConnectTech WhiteHEAT driver"
#define CONNECT_TECH_VENDOR_ID 0x0710 #define CONNECT_TECH_VENDOR_ID 0x0710
...@@ -125,16 +133,25 @@ static struct usb_driver whiteheat_driver = { ...@@ -125,16 +133,25 @@ static struct usb_driver whiteheat_driver = {
.id_table = id_table_combined, .id_table = id_table_combined,
}; };
/* function prototypes for the Connect Tech WhiteHEAT prerenumeration device */
static int whiteheat_firmware_download (struct usb_serial *serial);
static int whiteheat_firmware_attach (struct usb_serial *serial);
/* function prototypes for the Connect Tech WhiteHEAT serial converter */ /* function prototypes for the Connect Tech WhiteHEAT serial converter */
static int whiteheat_attach (struct usb_serial *serial);
static void whiteheat_shutdown (struct usb_serial *serial);
static int whiteheat_open (struct usb_serial_port *port, struct file *filp); static int whiteheat_open (struct usb_serial_port *port, struct file *filp);
static void whiteheat_close (struct usb_serial_port *port, struct file *filp); static void whiteheat_close (struct usb_serial_port *port, struct file *filp);
static int whiteheat_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
static int whiteheat_write_room (struct usb_serial_port *port);
static int whiteheat_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); static int whiteheat_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg);
static void whiteheat_set_termios (struct usb_serial_port *port, struct termios * old); static void whiteheat_set_termios (struct usb_serial_port *port, struct termios * old);
static void whiteheat_break_ctl (struct usb_serial_port *port, int break_state);
static int whiteheat_chars_in_buffer (struct usb_serial_port *port);
static void whiteheat_throttle (struct usb_serial_port *port); static void whiteheat_throttle (struct usb_serial_port *port);
static void whiteheat_unthrottle (struct usb_serial_port *port); static void whiteheat_unthrottle (struct usb_serial_port *port);
static int whiteheat_firmware_download (struct usb_serial *serial); static void whiteheat_read_callback (struct urb *urb);
static int whiteheat_attach (struct usb_serial *serial); static void whiteheat_write_callback (struct urb *urb);
static void whiteheat_shutdown (struct usb_serial *serial);
static struct usb_serial_device_type whiteheat_fake_device = { static struct usb_serial_device_type whiteheat_fake_device = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
...@@ -145,6 +162,7 @@ static struct usb_serial_device_type whiteheat_fake_device = { ...@@ -145,6 +162,7 @@ static struct usb_serial_device_type whiteheat_fake_device = {
.num_bulk_out = NUM_DONT_CARE, .num_bulk_out = NUM_DONT_CARE,
.num_ports = 1, .num_ports = 1,
.probe = whiteheat_firmware_download, .probe = whiteheat_firmware_download,
.attach = whiteheat_firmware_attach,
}; };
static struct usb_serial_device_type whiteheat_device = { static struct usb_serial_device_type whiteheat_device = {
...@@ -155,201 +173,311 @@ static struct usb_serial_device_type whiteheat_device = { ...@@ -155,201 +173,311 @@ static struct usb_serial_device_type whiteheat_device = {
.num_bulk_in = NUM_DONT_CARE, .num_bulk_in = NUM_DONT_CARE,
.num_bulk_out = NUM_DONT_CARE, .num_bulk_out = NUM_DONT_CARE,
.num_ports = 4, .num_ports = 4,
.attach = whiteheat_attach,
.shutdown = whiteheat_shutdown,
.open = whiteheat_open, .open = whiteheat_open,
.close = whiteheat_close, .close = whiteheat_close,
.throttle = whiteheat_throttle, .write = whiteheat_write,
.unthrottle = whiteheat_unthrottle, .write_room = whiteheat_write_room,
.ioctl = whiteheat_ioctl, .ioctl = whiteheat_ioctl,
.set_termios = whiteheat_set_termios, .set_termios = whiteheat_set_termios,
.attach = whiteheat_attach, .break_ctl = whiteheat_break_ctl,
.shutdown = whiteheat_shutdown, .chars_in_buffer = whiteheat_chars_in_buffer,
.throttle = whiteheat_throttle,
.unthrottle = whiteheat_unthrottle,
.read_bulk_callback = whiteheat_read_callback,
.write_bulk_callback = whiteheat_write_callback,
}; };
struct whiteheat_private {
struct whiteheat_command_private {
spinlock_t lock;
__u8 port_running;
__u8 command_finished; __u8 command_finished;
wait_queue_head_t wait_command; /* for handling sleeping while waiting for a command to finish */ wait_queue_head_t wait_command; /* for handling sleeping while waiting for a command to finish */
__u8 result_buffer[64];
}; };
/* local function prototypes */ #define THROTTLED 0x01
static inline void set_rts (struct usb_serial_port *port, unsigned char rts); #define ACTUALLY_THROTTLED 0x02
static inline void set_dtr (struct usb_serial_port *port, unsigned char dtr);
static inline void set_break (struct usb_serial_port *port, unsigned char brk); struct whiteheat_private {
spinlock_t lock;
__u8 flags;
__u8 mcr;
};
/* local function prototypes */
static int start_command_port(struct usb_serial *serial);
static void stop_command_port(struct usb_serial *serial);
static void command_port_write_callback(struct urb *urb);
static void command_port_read_callback(struct urb *urb);
static int firm_send_command(struct usb_serial_port *port, __u8 command, __u8 *data, __u8 datasize);
static int firm_open(struct usb_serial_port *port);
static int firm_close(struct usb_serial_port *port);
static int firm_setup_port(struct usb_serial_port *port);
static int firm_set_rts(struct usb_serial_port *port, __u8 onoff);
static int firm_set_dtr(struct usb_serial_port *port, __u8 onoff);
static int firm_set_break(struct usb_serial_port *port, __u8 onoff);
static int firm_purge(struct usb_serial_port *port, __u8 rxtx);
static int firm_get_dtr_rts(struct usb_serial_port *port);
static int firm_report_tx_done(struct usb_serial_port *port);
#define COMMAND_PORT 4 #define COMMAND_PORT 4
#define COMMAND_TIMEOUT (2*HZ) /* 2 second timeout for a command */ #define COMMAND_TIMEOUT (2*HZ) /* 2 second timeout for a command */
#define CLOSING_DELAY (30 * HZ)
/***************************************************************************** /*****************************************************************************
* Connect Tech's White Heat specific driver functions * Connect Tech's White Heat prerenumeration driver functions
*****************************************************************************/ *****************************************************************************/
static void command_port_write_callback (struct urb *urb)
/* steps to download the firmware to the WhiteHEAT device:
- hold the reset (by writing to the reset bit of the CPUCS register)
- download the VEND_AX.HEX file to the chip using VENDOR_REQUEST-ANCHOR_LOAD
- release the reset (by writing to the CPUCS register)
- download the WH.HEX file for all addresses greater than 0x1b3f using
VENDOR_REQUEST-ANCHOR_EXTERNAL_RAM_LOAD
- hold the reset
- download the WH.HEX file for all addresses less than 0x1b40 using
VENDOR_REQUEST_ANCHOR_LOAD
- release the reset
- device renumerated itself and comes up as new device id with all
firmware download completed.
*/
static int whiteheat_firmware_download (struct usb_serial *serial)
{ {
int response;
const struct whiteheat_hex_record *record;
dbg("%s", __FUNCTION__); dbg("%s", __FUNCTION__);
if (urb->status) { response = ezusb_set_reset (serial, 1);
dbg ("nonzero urb status: %d", urb->status);
return; record = &whiteheat_loader[0];
while (record->address != 0xffff) {
response = ezusb_writememory (serial, record->address,
(unsigned char *)record->data, record->data_size, 0xa0);
if (response < 0) {
err("%s - ezusb_writememory failed for loader (%d %04X %p %d)",
__FUNCTION__, response, record->address, record->data, record->data_size);
break;
}
++record;
} }
usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, urb->transfer_buffer); response = ezusb_set_reset (serial, 0);
return; record = &whiteheat_firmware[0];
while (record->address < 0x1b40) {
++record;
}
while (record->address != 0xffff) {
response = ezusb_writememory (serial, record->address,
(unsigned char *)record->data, record->data_size, 0xa3);
if (response < 0) {
err("%s - ezusb_writememory failed for first firmware step (%d %04X %p %d)",
__FUNCTION__, response, record->address, record->data, record->data_size);
break;
}
++record;
}
response = ezusb_set_reset (serial, 1);
record = &whiteheat_firmware[0];
while (record->address < 0x1b40) {
response = ezusb_writememory (serial, record->address,
(unsigned char *)record->data, record->data_size, 0xa0);
if (response < 0) {
err("%s - ezusb_writememory failed for second firmware step (%d %04X %p %d)",
__FUNCTION__, response, record->address, record->data, record->data_size);
break;
}
++record;
}
response = ezusb_set_reset (serial, 0);
return 0;
} }
static void command_port_read_callback (struct urb *urb) static int whiteheat_firmware_attach (struct usb_serial *serial)
{ {
struct usb_serial_port *port = (struct usb_serial_port *)urb->context; /* We want this device to fail to have a driver assigned to it */
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); return 1;
}
/*****************************************************************************
* Connect Tech's White Heat serial driver functions
*****************************************************************************/
static int whiteheat_attach (struct usb_serial *serial)
{
struct usb_serial_port *command_port;
struct whiteheat_command_private *command_info;
struct usb_serial_port *port;
struct whiteheat_private *info; struct whiteheat_private *info;
unsigned char *data = urb->transfer_buffer; struct whiteheat_hw_info *hw_info;
int result; int pipe;
int ret;
int alen;
__u8 command[2] = { WHITEHEAT_GET_HW_INFO, 0 };
__u8 result[sizeof(*hw_info) + 1];
int i;
dbg("%s", __FUNCTION__); command_port = &serial->port[COMMAND_PORT];
if (urb->status) { pipe = usb_sndbulkpipe (serial->dev, command_port->bulk_out_endpointAddress);
dbg("%s - nonzero urb status: %d", __FUNCTION__, urb->status); ret = usb_bulk_msg (serial->dev, pipe, command, sizeof(command), &alen, COMMAND_TIMEOUT);
return; if (ret) {
err("%s: Couldn't send command [%d]", serial->type->name, ret);
goto no_firmware;
} else if (alen != sizeof(command)) {
err("%s: Send command incomplete [%d]", serial->type->name, alen);
goto no_firmware;
} }
if (!serial) { pipe = usb_rcvbulkpipe (serial->dev, command_port->bulk_in_endpointAddress);
dbg("%s - bad serial pointer, exiting", __FUNCTION__); ret = usb_bulk_msg (serial->dev, pipe, result, sizeof(result), &alen, COMMAND_TIMEOUT);
return; if (ret) {
err("%s: Couldn't get results [%d]", serial->type->name, ret);
goto no_firmware;
} else if (alen != sizeof(result)) {
err("%s: Get results incomplete [%d]", serial->type->name, alen);
goto no_firmware;
} else if (result[0] != command[0]) {
err("%s: Command failed [%d]", serial->type->name, result[0]);
goto no_firmware;
} }
usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data); hw_info = (struct whiteheat_hw_info *)&result[1];
info = (struct whiteheat_private *)port->private; info("%s: Driver %s: Firmware v%d.%02d", serial->type->name,
if (!info) { DRIVER_VERSION, hw_info->sw_major_rev, hw_info->sw_minor_rev);
dbg("%s - info is NULL, exiting.", __FUNCTION__);
return;
}
/* right now, if the command is COMMAND_COMPLETE, just flip the bit saying the command finished */ for (i = 0; i < serial->num_ports; i++) {
/* in the future we're going to have to pay attention to the actual command that completed */ port = &serial->port[i];
if (data[0] == WHITEHEAT_CMD_COMPLETE) {
info->command_finished = WHITEHEAT_CMD_COMPLETE; info = (struct whiteheat_private *)kmalloc(sizeof(struct whiteheat_private), GFP_KERNEL);
wake_up_interruptible(&info->wait_command); if (info == NULL)
} goto no_memory;
if (data[0] == WHITEHEAT_CMD_FAILURE) { spin_lock_init(&info->lock);
info->command_finished = WHITEHEAT_CMD_FAILURE; info->flags = 0;
wake_up_interruptible(&info->wait_command); info->mcr = 0;
port->private = info;
} }
/* Continue trying to always read */ command_info = (struct whiteheat_command_private *)kmalloc(sizeof(struct whiteheat_command_private), GFP_KERNEL);
FILL_BULK_URB(port->read_urb, serial->dev, if (command_info == NULL)
usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), goto no_memory;
port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
command_port_read_callback, port);
result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
if (result)
dbg("%s - failed resubmitting read urb, error %d", __FUNCTION__, result);
}
spin_lock_init(&command_info->lock);
command_info->port_running = 0;
init_waitqueue_head(&command_info->wait_command);
command_port->private = command_info;
command_port->write_urb->complete = command_port_write_callback;
command_port->read_urb->complete = command_port_read_callback;
static int whiteheat_send_cmd (struct usb_serial *serial, __u8 command, __u8 *data, __u8 datasize) return 0;
{
struct whiteheat_private *info;
struct usb_serial_port *port;
int timeout;
__u8 *transfer_buffer;
int retval = 0;
dbg("%s - command %d", __FUNCTION__, command); no_firmware:
/* Firmware likely not running */
err("%s: Unable to retrieve firmware version, try replugging\n", serial->type->name);
err("%s: If the firmware is not running (status led not blinking)\n", serial->type->name);
err("%s: please contact support@connecttech.com\n", serial->type->name);
return -ENODEV;
no_memory:
for (i--; i >= 0; i--) {
port = &serial->port[i];
kfree(port->private);
}
err("%s: Out of memory for port structures\n", serial->type->name);
return -ENOMEM;
}
port = &serial->port[COMMAND_PORT];
info = (struct whiteheat_private *)port->private;
info->command_finished = FALSE;
transfer_buffer = (__u8 *)port->write_urb->transfer_buffer; static void whiteheat_shutdown (struct usb_serial *serial)
transfer_buffer[0] = command; {
memcpy (&transfer_buffer[1], data, datasize); struct usb_serial_port *command_port;
port->write_urb->transfer_buffer_length = datasize + 1; struct usb_serial_port *port;
port->write_urb->dev = serial->dev; int i;
retval = usb_submit_urb (port->write_urb, GFP_KERNEL);
if (retval) {
dbg("%s - submit urb failed", __FUNCTION__);
goto exit;
}
/* wait for the command to complete */ dbg("%s", __FUNCTION__);
timeout = COMMAND_TIMEOUT;
while (timeout && (info->command_finished == FALSE)) {
timeout = interruptible_sleep_on_timeout (&info->wait_command, timeout);
}
if (info->command_finished == FALSE) { /* free up our private data for our command port */
dbg("%s - command timed out.", __FUNCTION__); command_port = &serial->port[COMMAND_PORT];
retval = -ETIMEDOUT; kfree (command_port->private);
goto exit; command_port->private = NULL;
}
if (info->command_finished == WHITEHEAT_CMD_FAILURE) { for (i = 0; i < serial->num_ports; i++) {
dbg("%s - command failed.", __FUNCTION__); port = &serial->port[i];
retval = -EIO; kfree(port->private);
goto exit; port->private = NULL;
} }
if (info->command_finished == WHITEHEAT_CMD_COMPLETE) return;
dbg("%s - command completed.", __FUNCTION__);
exit:
return retval;
} }
static int whiteheat_open (struct usb_serial_port *port, struct file *filp) static int whiteheat_open (struct usb_serial_port *port, struct file *filp)
{ {
struct whiteheat_min_set open_command;
struct usb_serial_port *command_port;
struct whiteheat_private *info;
int retval = 0; int retval = 0;
struct termios old_term;
dbg("%s - port %d", __FUNCTION__, port->number); dbg("%s - port %d", __FUNCTION__, port->number);
/* set up some stuff for our command port */ retval = start_command_port(port->serial);
command_port = &port->serial->port[COMMAND_PORT]; if (retval)
if (command_port->private == NULL) {
info = (struct whiteheat_private *)kmalloc (sizeof(struct whiteheat_private), GFP_KERNEL);
if (info == NULL) {
err("%s - out of memory", __FUNCTION__);
retval = -ENOMEM;
goto exit;
}
init_waitqueue_head(&info->wait_command);
command_port->private = info;
command_port->write_urb->complete = command_port_write_callback;
command_port->read_urb->complete = command_port_read_callback;
command_port->read_urb->dev = port->serial->dev;
command_port->tty = port->tty; /* need this to "fake" our our sanity check macros */
retval = usb_submit_urb (command_port->read_urb, GFP_KERNEL);
if (retval) {
err("%s - failed submitting read urb, error %d", __FUNCTION__, retval);
goto exit; goto exit;
}
}
/* Start reading from the device */ /* Start reading from the device */
port->read_urb->dev = port->serial->dev; port->read_urb->dev = port->serial->dev;
retval = usb_submit_urb(port->read_urb, GFP_KERNEL); retval = usb_submit_urb(port->read_urb, GFP_KERNEL);
if (retval) { if (retval) {
err("%s - failed submitting read urb, error %d", __FUNCTION__, retval); err("%s - failed submitting read urb, error %d", __FUNCTION__, retval);
stop_command_port(port->serial);
goto exit; goto exit;
} }
/* send an open port command */ /* send an open port command */
/* firmware uses 1 based port numbering */ retval = firm_open(port);
open_command.port = port->number - port->serial->minor + 1; if (retval) {
retval = whiteheat_send_cmd (port->serial, WHITEHEAT_OPEN, (__u8 *)&open_command, sizeof(open_command)); stop_command_port(port->serial);
if (retval) goto exit;
}
retval = firm_purge(port, WHITEHEAT_PURGE_RX | WHITEHEAT_PURGE_TX);
if (retval) {
firm_close(port);
stop_command_port(port->serial);
goto exit; goto exit;
}
old_term.c_cflag = ~port->tty->termios->c_cflag;
old_term.c_iflag = ~port->tty->termios->c_iflag;
whiteheat_set_termios(port, &old_term);
/* Need to do device specific setup here (control lines, baud rate, etc.) */ retval = firm_set_rts(port, WHITEHEAT_RTS_ON);
/* FIXME!!! */ if (retval) {
firm_close(port);
stop_command_port(port->serial);
goto exit;
}
retval = firm_set_dtr(port, WHITEHEAT_DTR_ON);
if (retval) {
firm_set_rts(port, WHITEHEAT_RTS_OFF);
firm_close(port);
stop_command_port(port->serial);
goto exit;
}
exit: exit:
dbg("%s - exit, retval = %d", __FUNCTION__, retval); dbg("%s - exit, retval = %d", __FUNCTION__, retval);
...@@ -359,129 +487,259 @@ static int whiteheat_open (struct usb_serial_port *port, struct file *filp) ...@@ -359,129 +487,259 @@ static int whiteheat_open (struct usb_serial_port *port, struct file *filp)
static void whiteheat_close(struct usb_serial_port *port, struct file * filp) static void whiteheat_close(struct usb_serial_port *port, struct file * filp)
{ {
struct whiteheat_min_set close_command;
dbg("%s - port %d", __FUNCTION__, port->number); dbg("%s - port %d", __FUNCTION__, port->number);
/* send a close command to the port */ if (tty_hung_up_p(filp)) {
/* firmware uses 1 based port numbering */ return;
close_command.port = port->number - port->serial->minor + 1; }
whiteheat_send_cmd (port->serial, WHITEHEAT_CLOSE, (__u8 *)&close_command, sizeof(close_command));
port->tty->closing = 1;
/*
* Not currently in use; tty_wait_until_sent() calls
* serial_chars_in_buffer() which deadlocks on the second semaphore
* acquisition. This should be fixed at some point. Greg's been
* notified.
if ((filp->f_flags & (O_NDELAY | O_NONBLOCK)) == 0) {
tty_wait_until_sent(port->tty, CLOSING_DELAY);
}
*/
if (port->tty->driver.flush_buffer)
port->tty->driver.flush_buffer(port->tty);
if (port->tty->ldisc.flush_buffer)
port->tty->ldisc.flush_buffer(port->tty);
/* Need to change the control lines here */ firm_report_tx_done(port);
/* FIXME */
firm_set_dtr(port, WHITEHEAT_DTR_OFF);
firm_set_rts(port, WHITEHEAT_RTS_OFF);
firm_close(port);
/* shutdown our bulk reads and writes */ /* shutdown our bulk reads and writes */
usb_unlink_urb (port->write_urb); usb_unlink_urb (port->write_urb);
usb_unlink_urb (port->read_urb); usb_unlink_urb (port->read_urb);
}
static int whiteheat_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) stop_command_port(port->serial);
{
dbg("%s - port %d, cmd 0x%.4x", __FUNCTION__, port->number, cmd);
return -ENOIOCTLCMD; port->tty->closing = 0;
} }
static void whiteheat_set_termios (struct usb_serial_port *port, struct termios *old_termios) static int whiteheat_write(struct usb_serial_port *port, int from_user, const unsigned char *buf, int count)
{ {
unsigned int cflag; struct usb_serial *serial = port->serial;
struct whiteheat_port_settings port_settings; int result;
dbg("%s -port %d", __FUNCTION__, port->number); dbg("%s - port %d", __FUNCTION__, port->number);
if ((!port->tty) || (!port->tty->termios)) { if (count == 0) {
dbg("%s - no tty structures", __FUNCTION__); dbg("%s - write request of 0 bytes", __FUNCTION__);
goto exit; return (0);
} }
cflag = port->tty->termios->c_cflag; if (port->write_urb->status == -EINPROGRESS) {
/* check that they really want us to change something */ dbg ("%s - already writing", __FUNCTION__);
if (old_termios) { return (0);
if ((cflag == old_termios->c_cflag) &&
(RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) {
dbg("%s - nothing to change...", __FUNCTION__);
goto exit;
}
} }
/* set the port number */ count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
/* firmware uses 1 based port numbering */
port_settings.port = port->number + 1;
/* get the byte size */ if (from_user) {
switch (cflag & CSIZE) { if (copy_from_user(port->write_urb->transfer_buffer, buf, count))
case CS5: port_settings.bits = 5; break; return -EFAULT;
case CS6: port_settings.bits = 6; break; }
case CS7: port_settings.bits = 7; break; else {
default: memcpy (port->write_urb->transfer_buffer, buf, count);
case CS8: port_settings.bits = 8; break;
} }
dbg("%s - data bits = %d", __FUNCTION__, port_settings.bits);
/* determine the parity */ usb_serial_debug_data (__FILE__, __FUNCTION__, count, port->write_urb->transfer_buffer);
if (cflag & PARENB)
if (cflag & PARODD)
port_settings.parity = 'o';
else
port_settings.parity = 'e';
else
port_settings.parity = 'n';
dbg("%s - parity = %c", __FUNCTION__, port_settings.parity);
/* figure out the stop bits requested */ port->write_urb->dev = serial->dev;
if (cflag & CSTOPB) port->write_urb->transfer_buffer_length = count;
port_settings.stop = 2; result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
if (result)
err("%s - failed submitting write urb, error %d", __FUNCTION__, result);
else else
port_settings.stop = 1; result = count;
dbg("%s - stop bits = %d", __FUNCTION__, port_settings.stop);
return result;
}
/* figure out the flow control settings */
if (cflag & CRTSCTS)
port_settings.hflow = (WHITEHEAT_CTS_FLOW | WHITEHEAT_RTS_FLOW);
else
port_settings.hflow = 0;
dbg("%s - hardware flow control = %s %s %s %s", __FUNCTION__,
(port_settings.hflow & WHITEHEAT_CTS_FLOW) ? "CTS" : "",
(port_settings.hflow & WHITEHEAT_RTS_FLOW) ? "RTS" : "",
(port_settings.hflow & WHITEHEAT_DSR_FLOW) ? "DSR" : "",
(port_settings.hflow & WHITEHEAT_DTR_FLOW) ? "DTR" : "");
/* determine software flow control */ static int whiteheat_write_room(struct usb_serial_port *port)
if (I_IXOFF(port->tty)) {
port_settings.sflow = 'b'; int room = 0;
dbg("%s - port %d", __FUNCTION__, port->number);
if (port->write_urb->status != -EINPROGRESS)
room = port->bulk_out_size;
dbg("%s - returns %d", __FUNCTION__, room);
return (room);
}
static int whiteheat_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg)
{
struct whiteheat_private *info = (struct whiteheat_private *)port->private;
unsigned int modem_signals = 0;
struct serial_struct serstruct;
dbg("%s - port %d, cmd 0x%.4x", __FUNCTION__, port->number, cmd);
switch (cmd) {
case TIOCMGET:
firm_get_dtr_rts(port);
if (info->mcr & UART_MCR_DTR)
modem_signals |= TIOCM_DTR;
if (info->mcr & UART_MCR_RTS)
modem_signals |= TIOCM_RTS;
if (copy_to_user((unsigned int *)arg, &modem_signals, sizeof(unsigned int)));
return -EFAULT;
break;
case TIOCMSET:
if (copy_from_user(&modem_signals, (unsigned int *)arg, sizeof(unsigned int)))
return -EFAULT;
if (modem_signals & TIOCM_DTR)
info->mcr |= UART_MCR_DTR;
else else
port_settings.sflow = 'n'; info->mcr &= ~UART_MCR_DTR;
dbg("%s - software flow control = %c", __FUNCTION__, port_settings.sflow); if (modem_signals & TIOCM_RTS)
info->mcr |= UART_MCR_RTS;
else
info->mcr &= ~UART_MCR_RTS;
port_settings.xon = START_CHAR(port->tty); firm_set_dtr(port, info->mcr & UART_MCR_DTR);
port_settings.xoff = STOP_CHAR(port->tty); firm_set_rts(port, info->mcr & UART_MCR_RTS);
dbg("%s - XON = %2x, XOFF = %2x", __FUNCTION__, port_settings.xon, port_settings.xoff);
/* get the baud rate wanted */ break;
port_settings.baud = tty_get_baud_rate(port->tty);
dbg("%s - baud rate = %d", __FUNCTION__, port_settings.baud);
/* handle any settings that aren't specified in the tty structure */ case TIOCMBIS:
port_settings.lloop = 0; if (copy_from_user(&modem_signals, (unsigned int *)arg, sizeof(unsigned int)))
return -EFAULT;
/* now send the message to the device */ if (modem_signals & TIOCM_DTR)
whiteheat_send_cmd (port->serial, WHITEHEAT_SETUP_PORT, (__u8 *)&port_settings, sizeof(port_settings)); info->mcr |= UART_MCR_DTR;
if (modem_signals & TIOCM_RTS)
info->mcr |= UART_MCR_RTS;
firm_set_dtr(port, info->mcr & UART_MCR_DTR);
firm_set_rts(port, info->mcr & UART_MCR_RTS);
break;
case TIOCMBIC:
if (copy_from_user(&modem_signals, (unsigned int *)arg, sizeof(unsigned int)))
return -EFAULT;
if (modem_signals & TIOCM_DTR)
info->mcr &= ~UART_MCR_DTR;
if (modem_signals & TIOCM_RTS)
info->mcr &= ~UART_MCR_RTS;
firm_set_dtr(port, info->mcr & UART_MCR_DTR);
firm_set_rts(port, info->mcr & UART_MCR_RTS);
break;
case TIOCGSERIAL:
memset(&serstruct, 0, sizeof(serstruct));
serstruct.type = PORT_16654;
serstruct.line = port->serial->minor;
serstruct.port = port->number;
serstruct.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
serstruct.xmit_fifo_size = port->bulk_out_size;
serstruct.custom_divisor = 0;
serstruct.baud_base = 460800;
serstruct.close_delay = CLOSING_DELAY;
serstruct.closing_wait = CLOSING_DELAY;
if (copy_to_user((void *)arg, &serstruct, sizeof(serstruct)))
return -EFAULT;
break;
case TIOCSSERIAL:
if (copy_from_user(&serstruct, (void *)arg, sizeof(serstruct)))
return -EFAULT;
/*
* For now this is ignored. dip sets the ASYNC_[V]HI flags
* but this isn't used by us at all. Maybe someone somewhere
* will need the custom_divisor setting.
*/
break;
default:
return -ENOIOCTLCMD;
}
return 0;
}
static void whiteheat_set_termios (struct usb_serial_port *port, struct termios *old_termios)
{
dbg("%s -port %d", __FUNCTION__, port->number);
if ((!port->tty) || (!port->tty->termios)) {
dbg("%s - no tty structures", __FUNCTION__);
goto exit;
}
/* check that they really want us to change something */
if (old_termios) {
if ((port->tty->termios->c_cflag == old_termios->c_cflag) &&
(RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) {
dbg("%s - nothing to change...", __FUNCTION__);
goto exit;
}
}
firm_setup_port(port);
exit: exit:
return; return;
} }
static void whiteheat_break_ctl(struct usb_serial_port *port, int break_state) {
firm_set_break(port, break_state);
}
static int whiteheat_chars_in_buffer(struct usb_serial_port *port)
{
int chars = 0;
dbg("%s - port %d", __FUNCTION__, port->number);
if (port->write_urb->status == -EINPROGRESS)
chars = port->write_urb->transfer_buffer_length;
dbg ("%s - returns %d", __FUNCTION__, chars);
return (chars);
}
static void whiteheat_throttle (struct usb_serial_port *port) static void whiteheat_throttle (struct usb_serial_port *port)
{ {
struct whiteheat_private *info = (struct whiteheat_private *)port->private;
unsigned long flags;
dbg("%s - port %d", __FUNCTION__, port->number); dbg("%s - port %d", __FUNCTION__, port->number);
/* Change the control signals */ spin_lock_irqsave(&info->lock, flags);
/* FIXME!!! */ info->flags |= THROTTLED;
spin_unlock_irqrestore(&info->lock, flags);
return; return;
} }
...@@ -489,194 +747,441 @@ static void whiteheat_throttle (struct usb_serial_port *port) ...@@ -489,194 +747,441 @@ static void whiteheat_throttle (struct usb_serial_port *port)
static void whiteheat_unthrottle (struct usb_serial_port *port) static void whiteheat_unthrottle (struct usb_serial_port *port)
{ {
struct whiteheat_private *info = (struct whiteheat_private *)port->private;
int result;
unsigned long flags;
dbg("%s - port %d", __FUNCTION__, port->number); dbg("%s - port %d", __FUNCTION__, port->number);
/* Change the control signals */ spin_lock_irqsave(&info->lock, flags);
/* FIXME!!! */
if (info->flags & ACTUALLY_THROTTLED) {
/* Continue trying to always read */
port->read_urb->dev = port->serial->dev;
result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
if (result)
err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result);
}
info->flags &= ~(THROTTLED | ACTUALLY_THROTTLED);
spin_unlock_irqrestore(&info->lock, flags);
return; return;
} }
/* steps to download the firmware to the WhiteHEAT device: /*****************************************************************************
- hold the reset (by writing to the reset bit of the CPUCS register) * Connect Tech's White Heat callback routines
- download the VEND_AX.HEX file to the chip using VENDOR_REQUEST-ANCHOR_LOAD *****************************************************************************/
- release the reset (by writing to the CPUCS register) static void command_port_write_callback (struct urb *urb)
- download the WH.HEX file for all addresses greater than 0x1b3f using
VENDOR_REQUEST-ANCHOR_EXTERNAL_RAM_LOAD
- hold the reset
- download the WH.HEX file for all addresses less than 0x1b40 using
VENDOR_REQUEST_ANCHOR_LOAD
- release the reset
- device renumerated itself and comes up as new device id with all
firmware download completed.
*/
static int whiteheat_firmware_download (struct usb_serial *serial)
{ {
int response;
const struct whiteheat_hex_record *record;
dbg("%s", __FUNCTION__); dbg("%s", __FUNCTION__);
response = ezusb_set_reset (serial, 1); if (urb->status) {
dbg ("nonzero urb status: %d", urb->status);
return;
}
record = &whiteheat_loader[0]; usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, urb->transfer_buffer);
while (record->address != 0xffff) {
response = ezusb_writememory (serial, record->address, return;
(unsigned char *)record->data, record->data_size, 0xa0); }
if (response < 0) {
err("%s - ezusb_writememory failed for loader (%d %04X %p %d)",
__FUNCTION__, response, record->address, record->data, record->data_size); static void command_port_read_callback (struct urb *urb)
break; {
struct usb_serial_port *command_port = (struct usb_serial_port *)urb->context;
struct usb_serial *serial = get_usb_serial (command_port, __FUNCTION__);
struct whiteheat_command_private *command_info;
unsigned char *data = urb->transfer_buffer;
int result;
unsigned long flags;
dbg("%s", __FUNCTION__);
if (urb->status) {
dbg("%s - nonzero urb status: %d", __FUNCTION__, urb->status);
return;
} }
++record;
if (!serial) {
dbg("%s - bad serial pointer, exiting", __FUNCTION__);
return;
} }
response = ezusb_set_reset (serial, 0); usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
record = &whiteheat_firmware[0]; command_info = (struct whiteheat_command_private *)command_port->private;
while (record->address < 0x1b40) { if (!command_info) {
++record; dbg ("%s - command_info is NULL, exiting.", __FUNCTION__);
return;
} }
while (record->address != 0xffff) { spin_lock_irqsave(&command_info->lock, flags);
response = ezusb_writememory (serial, record->address,
(unsigned char *)record->data, record->data_size, 0xa3); if (data[0] == WHITEHEAT_CMD_COMPLETE) {
if (response < 0) { command_info->command_finished = WHITEHEAT_CMD_COMPLETE;
err("%s - ezusb_writememory failed for first firmware step (%d %04X %p %d)", wake_up_interruptible(&command_info->wait_command);
__FUNCTION__, response, record->address, record->data, record->data_size); } else if (data[0] == WHITEHEAT_CMD_FAILURE) {
break; command_info->command_finished = WHITEHEAT_CMD_FAILURE;
wake_up_interruptible(&command_info->wait_command);
} else if (data[0] == WHITEHEAT_EVENT) {
/* These are unsolicited reports from the firmware, hence no waiting command to wakeup */
dbg("%s - event received", __FUNCTION__);
} else if (data[0] == WHITEHEAT_GET_DTR_RTS) {
memcpy(command_info->result_buffer, &data[1], urb->actual_length - 1);
command_info->command_finished = WHITEHEAT_CMD_COMPLETE;
wake_up_interruptible(&command_info->wait_command);
} else {
dbg("%s - bad reply from firmware", __FUNCTION__);
} }
++record;
/* Continue trying to always read */
command_port->read_urb->dev = serial->dev;
result = usb_submit_urb(command_port->read_urb, GFP_ATOMIC);
spin_unlock_irqrestore(&command_info->lock, flags);
if (result)
dbg("%s - failed resubmitting read urb, error %d", __FUNCTION__, result);
}
static void whiteheat_read_callback(struct urb *urb)
{
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
struct tty_struct *tty;
unsigned char *data = urb->transfer_buffer;
int i;
int result;
struct whiteheat_private *info = (struct whiteheat_private *)port->private;
unsigned long flags;
dbg("%s - port %d", __FUNCTION__, port->number);
if (!serial) {
dbg("%s - bad serial pointer, exiting", __FUNCTION__);
return;
} }
response = ezusb_set_reset (serial, 1); if (urb->status) {
dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
return;
}
record = &whiteheat_firmware[0]; usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
while (record->address < 0x1b40) {
response = ezusb_writememory (serial, record->address, tty = port->tty;
(unsigned char *)record->data, record->data_size, 0xa0); if (tty && urb->actual_length) {
if (response < 0) { for (i = 0; i < urb->actual_length ; ++i) {
err("%s - ezusb_writememory failed for second firmware step (%d %04X %p %d)", /* if we insert more than TTY_FLIPBUF_SIZE characters, we drop them. */
__FUNCTION__, response, record->address, record->data, record->data_size); if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
break; tty_flip_buffer_push(tty);
} }
++record; /* this doesn't actually push the data through unless tty->low_latency is set */
tty_insert_flip_char(tty, data[i], 0);
}
tty_flip_buffer_push(tty);
} }
response = ezusb_set_reset (serial, 0); spin_lock_irqsave(&info->lock, flags);
if (info->flags & THROTTLED) {
info->flags |= ACTUALLY_THROTTLED;
spin_unlock_irqrestore(&info->lock, flags);
return;
}
spin_unlock_irqrestore(&info->lock, flags);
/* we want this device to fail to have a driver assigned to it. */ /* Continue trying to always read */
return 1; port->read_urb->dev = serial->dev;
result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
if (result)
err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result);
} }
static int whiteheat_attach (struct usb_serial *serial) static void whiteheat_write_callback(struct urb *urb)
{ {
struct whiteheat_hw_info *hw_info; struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
int pipe; struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
int ret;
int alen;
__u8 command[2] = { WHITEHEAT_GET_HW_INFO, 0 };
__u8 result[sizeof(*hw_info) + 1];
pipe = usb_rcvbulkpipe (serial->dev, 7); dbg("%s - port %d", __FUNCTION__, port->number);
usb_bulk_msg (serial->dev, pipe, result, sizeof(result), &alen, 2 * HZ);
/*
* We ignore the return code. In the case where rmmod/insmod is
* performed with a WhiteHEAT connected, the above times out
* because the endpoint is already prepped, meaning the below succeeds
* regardless. All other cases the above succeeds.
*/
pipe = usb_sndbulkpipe (serial->dev, 7); if (!serial) {
ret = usb_bulk_msg (serial->dev, pipe, command, sizeof(command), &alen, 2 * HZ); dbg("%s - bad serial pointer, exiting", __FUNCTION__);
if (ret) { return;
err("%s: Couldn't send command [%d]", serial->type->name, ret);
goto error_out;
} else if (alen != sizeof(command)) {
err("%s: Send command incomplete [%d]", serial->type->name, alen);
goto error_out;
} }
pipe = usb_rcvbulkpipe (serial->dev, 7); if (urb->status) {
ret = usb_bulk_msg (serial->dev, pipe, result, sizeof(result), &alen, 2 * HZ); dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
if (ret) { return;
err("%s: Couldn't get results [%d]", serial->type->name, ret);
goto error_out;
} else if (alen != sizeof(result)) {
err("%s: Get results incomplete [%d]", serial->type->name, alen);
goto error_out;
} else if (result[0] != command[0]) {
err("%s: Command failed [%d]", serial->type->name, result[0]);
goto error_out;
} }
hw_info = (struct whiteheat_hw_info *)&result[1]; usb_serial_port_softint((void *)port);
info("%s: Driver %s: Firmware v%d.%02d", serial->type->name, queue_task(&port->tqueue, &tq_immediate);
DRIVER_VERSION, hw_info->sw_major_rev, hw_info->sw_minor_rev); mark_bh(IMMEDIATE_BH);
return 0; return;
error_out:
err("%s: Unable to retrieve firmware version, try replugging\n", serial->type->name);
/*
* Return that we've claimed the interface. A failure here may be
* due to interception by the command_callback routine or other
* causes that don't mean that the firmware isn't running. This may
* change in the future. Probably should actually.
*/
return 0;
} }
static void whiteheat_shutdown (struct usb_serial *serial)
/*****************************************************************************
* Connect Tech's White Heat firmware interface
*****************************************************************************/
static int firm_send_command (struct usb_serial_port *port, __u8 command, __u8 *data, __u8 datasize)
{ {
struct usb_serial_port *command_port; struct usb_serial_port *command_port;
struct whiteheat_command_private *command_info;
struct whiteheat_private *info;
int timeout;
__u8 *transfer_buffer;
int retval = 0;
unsigned long flags;
dbg("%s", __FUNCTION__); dbg("%s - command %d", __FUNCTION__, command);
/* free up our private data for our command port */ command_port = &port->serial->port[COMMAND_PORT];
command_port = &serial->port[COMMAND_PORT]; command_info = (struct whiteheat_command_private *)command_port->private;
if (command_port->private != NULL) { spin_lock_irqsave(&command_info->lock, flags);
kfree (command_port->private); command_info->command_finished = FALSE;
command_port->private = NULL;
transfer_buffer = (__u8 *)command_port->write_urb->transfer_buffer;
transfer_buffer[0] = command;
memcpy (&transfer_buffer[1], data, datasize);
command_port->write_urb->transfer_buffer_length = datasize + 1;
command_port->write_urb->dev = port->serial->dev;
retval = usb_submit_urb (command_port->write_urb, GFP_KERNEL);
spin_unlock_irqrestore(&command_info->lock, flags);
if (retval) {
dbg("%s - submit urb failed", __FUNCTION__);
goto exit;
} }
return; /* wait for the command to complete */
timeout = COMMAND_TIMEOUT;
while (timeout && (command_info->command_finished == FALSE)) {
timeout = interruptible_sleep_on_timeout (&command_info->wait_command, timeout);
}
spin_lock_irqsave(&command_info->lock, flags);
if (command_info->command_finished == FALSE) {
dbg("%s - command timed out.", __FUNCTION__);
retval = -ETIMEDOUT;
goto exit;
}
if (command_info->command_finished == WHITEHEAT_CMD_FAILURE) {
dbg("%s - command failed.", __FUNCTION__);
retval = -EIO;
goto exit;
}
if (command_info->command_finished == WHITEHEAT_CMD_COMPLETE) {
dbg("%s - command completed.", __FUNCTION__);
switch (command) {
case WHITEHEAT_GET_DTR_RTS:
info = (struct whiteheat_private *)port->private;
memcpy(&info->mcr, command_info->result_buffer, sizeof(struct whiteheat_dr_info));
break;
}
}
exit:
spin_unlock_irqrestore(&command_info->lock, flags);
return retval;
} }
static void set_command (struct usb_serial_port *port, unsigned char state, unsigned char command) static int firm_open(struct usb_serial_port *port) {
{ struct whiteheat_simple open_command;
struct whiteheat_rdb_set rdb_command;
open_command.port = port->number - port->serial->minor + 1;
return firm_send_command(port, WHITEHEAT_OPEN, (__u8 *)&open_command, sizeof(open_command));
}
/* send a set rts command to the port */
/* firmware uses 1 based port numbering */
rdb_command.port = port->number - port->serial->minor + 1;
rdb_command.state = state;
whiteheat_send_cmd (port->serial, command, (__u8 *)&rdb_command, sizeof(rdb_command)); static int firm_close(struct usb_serial_port *port) {
struct whiteheat_simple close_command;
close_command.port = port->number - port->serial->minor + 1;
return firm_send_command(port, WHITEHEAT_CLOSE, (__u8 *)&close_command, sizeof(close_command));
} }
static inline void set_rts (struct usb_serial_port *port, unsigned char rts) static int firm_setup_port(struct usb_serial_port *port) {
{ struct whiteheat_port_settings port_settings;
set_command (port, rts, WHITEHEAT_SET_RTS); unsigned int cflag = port->tty->termios->c_cflag;
port_settings.port = port->number + 1;
/* get the byte size */
switch (cflag & CSIZE) {
case CS5: port_settings.bits = 5; break;
case CS6: port_settings.bits = 6; break;
case CS7: port_settings.bits = 7; break;
default:
case CS8: port_settings.bits = 8; break;
}
dbg("%s - data bits = %d", __FUNCTION__, port_settings.bits);
/* determine the parity */
if (cflag & PARENB)
if (cflag & CMSPAR)
if (cflag & PARODD)
port_settings.parity = WHITEHEAT_PAR_MARK;
else
port_settings.parity = WHITEHEAT_PAR_SPACE;
else
if (cflag & PARODD)
port_settings.parity = WHITEHEAT_PAR_ODD;
else
port_settings.parity = WHITEHEAT_PAR_EVEN;
else
port_settings.parity = WHITEHEAT_PAR_NONE;
dbg("%s - parity = %c", __FUNCTION__, port_settings.parity);
/* figure out the stop bits requested */
if (cflag & CSTOPB)
port_settings.stop = 2;
else
port_settings.stop = 1;
dbg("%s - stop bits = %d", __FUNCTION__, port_settings.stop);
/* figure out the flow control settings */
if (cflag & CRTSCTS)
port_settings.hflow = (WHITEHEAT_HFLOW_CTS | WHITEHEAT_HFLOW_RTS);
else
port_settings.hflow = WHITEHEAT_HFLOW_NONE;
dbg("%s - hardware flow control = %s %s %s %s", __FUNCTION__,
(port_settings.hflow & WHITEHEAT_HFLOW_CTS) ? "CTS" : "",
(port_settings.hflow & WHITEHEAT_HFLOW_RTS) ? "RTS" : "",
(port_settings.hflow & WHITEHEAT_HFLOW_DSR) ? "DSR" : "",
(port_settings.hflow & WHITEHEAT_HFLOW_DTR) ? "DTR" : "");
/* determine software flow control */
if (I_IXOFF(port->tty))
port_settings.sflow = WHITEHEAT_SFLOW_RXTX;
else
port_settings.sflow = WHITEHEAT_SFLOW_NONE;
dbg("%s - software flow control = %c", __FUNCTION__, port_settings.sflow);
port_settings.xon = START_CHAR(port->tty);
port_settings.xoff = STOP_CHAR(port->tty);
dbg("%s - XON = %2x, XOFF = %2x", __FUNCTION__, port_settings.xon, port_settings.xoff);
/* get the baud rate wanted */
port_settings.baud = tty_get_baud_rate(port->tty);
dbg("%s - baud rate = %d", __FUNCTION__, port_settings.baud);
/* handle any settings that aren't specified in the tty structure */
port_settings.lloop = 0;
/* now send the message to the device */
return firm_send_command(port, WHITEHEAT_SETUP_PORT, (__u8 *)&port_settings, sizeof(port_settings));
}
static int firm_set_rts(struct usb_serial_port *port, __u8 onoff) {
struct whiteheat_set_rdb rts_command;
rts_command.port = port->number - port->serial->minor + 1;
rts_command.state = onoff;
return firm_send_command(port, WHITEHEAT_SET_RTS, (__u8 *)&rts_command, sizeof(rts_command));
}
static int firm_set_dtr(struct usb_serial_port *port, __u8 onoff) {
struct whiteheat_set_rdb dtr_command;
dtr_command.port = port->number - port->serial->minor + 1;
dtr_command.state = onoff;
return firm_send_command(port, WHITEHEAT_SET_RTS, (__u8 *)&dtr_command, sizeof(dtr_command));
}
static int firm_set_break(struct usb_serial_port *port, __u8 onoff) {
struct whiteheat_set_rdb break_command;
break_command.port = port->number - port->serial->minor + 1;
break_command.state = onoff;
return firm_send_command(port, WHITEHEAT_SET_RTS, (__u8 *)&break_command, sizeof(break_command));
}
static int firm_purge(struct usb_serial_port *port, __u8 rxtx) {
struct whiteheat_purge purge_command;
purge_command.port = port->number - port->serial->minor + 1;
purge_command.what = rxtx;
return firm_send_command(port, WHITEHEAT_PURGE, (__u8 *)&purge_command, sizeof(purge_command));
}
static int firm_get_dtr_rts(struct usb_serial_port *port) {
struct whiteheat_simple get_dr_command;
get_dr_command.port = port->number - port->serial->minor + 1;
return firm_send_command(port, WHITEHEAT_GET_DTR_RTS, (__u8 *)&get_dr_command, sizeof(get_dr_command));
} }
static inline void set_dtr (struct usb_serial_port *port, unsigned char dtr) static int firm_report_tx_done(struct usb_serial_port *port) {
struct whiteheat_simple close_command;
close_command.port = port->number - port->serial->minor + 1;
return firm_send_command(port, WHITEHEAT_REPORT_TX_DONE, (__u8 *)&close_command, sizeof(close_command));
}
/*****************************************************************************
* Connect Tech's White Heat utility functions
*****************************************************************************/
static int start_command_port(struct usb_serial *serial)
{ {
set_command (port, dtr, WHITEHEAT_SET_DTR); struct usb_serial_port *command_port;
struct whiteheat_command_private *command_info;
unsigned long flags;
int retval = 0;
command_port = &serial->port[COMMAND_PORT];
command_info = (struct whiteheat_command_private *)command_port->private;
spin_lock_irqsave(&command_info->lock, flags);
if (!command_info->port_running) {
command_port->read_urb->dev = serial->dev;
retval = usb_submit_urb(command_port->read_urb, GFP_KERNEL);
if (retval) {
err("%s - failed submitting read urb, error %d", __FUNCTION__, retval);
goto exit;
}
}
command_info->port_running++;
exit:
spin_unlock_irqrestore(&command_info->lock, flags);
return retval;
} }
static inline void set_break (struct usb_serial_port *port, unsigned char brk) static void stop_command_port(struct usb_serial *serial)
{ {
set_command (port, brk, WHITEHEAT_SET_BREAK); struct usb_serial_port *command_port;
struct whiteheat_command_private *command_info;
unsigned long flags;
command_port = &serial->port[COMMAND_PORT];
command_info = (struct whiteheat_command_private *)command_port->private;
spin_lock_irqsave(&command_info->lock, flags);
command_info->port_running--;
if (!command_info->port_running)
usb_unlink_urb(command_port->read_urb);
spin_unlock_irqrestore(&command_info->lock, flags);
} }
/*****************************************************************************
* Connect Tech's White Heat module functions
*****************************************************************************/
static int __init whiteheat_init (void) static int __init whiteheat_init (void)
{ {
usb_serial_register (&whiteheat_fake_device); usb_serial_register (&whiteheat_fake_device);
...@@ -704,4 +1209,3 @@ MODULE_LICENSE("GPL"); ...@@ -704,4 +1209,3 @@ MODULE_LICENSE("GPL");
MODULE_PARM(debug, "i"); MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Debug enabled or not"); MODULE_PARM_DESC(debug, "Debug enabled or not");
/* /*
* USB ConnectTech WhiteHEAT driver * USB ConnectTech WhiteHEAT driver
* *
* Copyright (C) 2002
* Connect Tech Inc.
*
* Copyright (C) 1999, 2000 * Copyright (C) 1999, 2000
* Greg Kroah-Hartman (greg@kroah.com) * Greg Kroah-Hartman (greg@kroah.com)
* *
...@@ -20,6 +23,7 @@ ...@@ -20,6 +23,7 @@
#define FALSE 0 #define FALSE 0
#define TRUE 1 #define TRUE 1
/* WhiteHEAT commands */ /* WhiteHEAT commands */
#define WHITEHEAT_OPEN 1 /* open the port */ #define WHITEHEAT_OPEN 1 /* open the port */
#define WHITEHEAT_CLOSE 2 /* close the port */ #define WHITEHEAT_CLOSE 2 /* close the port */
...@@ -39,109 +43,192 @@ ...@@ -39,109 +43,192 @@
#define WHITEHEAT_CMD_COMPLETE 16 /* reply for certain commands */ #define WHITEHEAT_CMD_COMPLETE 16 /* reply for certain commands */
#define WHITEHEAT_CMD_FAILURE 17 /* reply for failed commands */ #define WHITEHEAT_CMD_FAILURE 17 /* reply for failed commands */
/* Data for the WHITEHEAT_SETUP_PORT command */
#define WHITEHEAT_CTS_FLOW 0x08 /*
#define WHITEHEAT_RTS_FLOW 0x80 * Commands to the firmware
#define WHITEHEAT_DSR_FLOW 0x10 */
#define WHITEHEAT_DTR_FLOW 0x02
/*
* WHITEHEAT_OPEN
* WHITEHEAT_CLOSE
* WHITEHEAT_STATUS
* WHITEHEAT_GET_DTR_RTS
* WHITEHEAT_REPORT_TX_DONE
*/
struct whiteheat_simple {
__u8 port; /* port number (1 to N) */
};
/*
* WHITEHEAT_SETUP_PORT
*/
#define WHITEHEAT_PAR_NONE 'n' /* no parity */
#define WHITEHEAT_PAR_EVEN 'e' /* even parity */
#define WHITEHEAT_PAR_ODD 'o' /* odd parity */
#define WHITEHEAT_PAR_SPACE '0' /* space (force 0) parity */
#define WHITEHEAT_PAR_MARK '1' /* mark (force 1) parity */
#define WHITEHEAT_SFLOW_NONE 'n' /* no software flow control */
#define WHITEHEAT_SFLOW_RX 'r' /* XOFF/ON is sent when RX fills/empties */
#define WHITEHEAT_SFLOW_TX 't' /* when received XOFF/ON will stop/start TX */
#define WHITEHEAT_SFLOW_RXTX 'b' /* both SFLOW_RX and SFLOW_TX */
#define WHITEHEAT_HFLOW_NONE 0x00 /* no hardware flow control */
#define WHITEHEAT_HFLOW_RTS_TOGGLE 0x01 /* RTS is on during transmit, off otherwise */
#define WHITEHEAT_HFLOW_DTR 0x02 /* DTR is off/on when RX fills/empties */
#define WHITEHEAT_HFLOW_CTS 0x08 /* when received CTS off/on will stop/start TX */
#define WHITEHEAT_HFLOW_DSR 0x10 /* when received DSR off/on will stop/start TX */
#define WHITEHEAT_HFLOW_RTS 0x80 /* RTS is off/on when RX fills/empties */
struct whiteheat_port_settings { struct whiteheat_port_settings {
__u8 port; /* port number (1 to N) */ __u8 port; /* port number (1 to N) */
__u32 baud; /* any value allowed, default 9600, arrives little endian, range is 7 - 460800 */ __u32 baud; /* any value 7 - 460800, firmware calculates best fit; arrives little endian */
__u8 bits; /* 5, 6, 7, or 8, default 8 */ __u8 bits; /* 5, 6, 7, or 8 */
__u8 stop; /* 1 or 2, default 1 (2 = 1.5 if bits = 5) */ __u8 stop; /* 1 or 2, default 1 (2 = 1.5 if bits = 5) */
__u8 parity; /* 'n, e, o, 0, or 1' (ascii), default 'n' __u8 parity; /* see WHITEHEAT_PAR_* above */
* n = none e = even o = odd __u8 sflow; /* see WHITEHEAT_SFLOW_* above */
* 0 = force 0 1 = force 1 */ __u8 xoff; /* XOFF byte value */
__u8 sflow; /* 'n, r, t, or b' (ascii), default 'n' __u8 xon; /* XON byte value */
* n = none __u8 hflow; /* see WHITEHEAT_HFLOW_* above */
* r = receive (XOFF/XON transmitted when receiver fills / empties) __u8 lloop; /* 0/1 turns local loopback mode off/on */
* t = transmit (XOFF/XON received will stop/start TX)
* b = both */
__u8 xoff; /* XOFF byte value, default 0x13 */
__u8 xon; /* XON byte value, default 0x11 */
__u8 hflow; /* bits indicate mode as follows:
* CTS (0x08) (CTS off/on will control/cause TX off/on)
* DSR (0x10) (DSR off/on will control/cause TX off/on)
* RTS (0x80) (RTS off/on when receiver fills/empties)
* DTR (0x02) (DTR off/on when receiver fills/empties) */
__u8 lloop; /* local loopback 0 or 1, default 0 */
} __attribute__ ((packed)); } __attribute__ ((packed));
/* data for WHITEHEAT_SET_RTS, WHITEHEAT_SET_DTR, and WHITEHEAT_SET_BREAK commands */
struct whiteheat_rdb_set { /*
* WHITEHEAT_SET_RTS
* WHITEHEAT_SET_DTR
* WHITEHEAT_SET_BREAK
*/
#define WHITEHEAT_RTS_OFF 0x00
#define WHITEHEAT_RTS_ON 0x01
#define WHITEHEAT_DTR_OFF 0x00
#define WHITEHEAT_DTR_ON 0x01
#define WHITEHEAT_BREAK_OFF 0x00
#define WHITEHEAT_BREAK_ON 0x01
struct whiteheat_set_rdb {
__u8 port; /* port number (1 to N) */ __u8 port; /* port number (1 to N) */
__u8 state; /* 0 = off, non-zero = on */ __u8 state; /* 0/1 turns signal off/on */
}; };
/* data for:
WHITEHEAT_OPEN /*
WHITEHEAT_CLOSE * WHITEHEAT_DUMP
WHITEHEAT_STATUS */
WHITEHEAT_GET_DTR_RTS #define WHITEHEAT_DUMP_MEM_DATA 'd' /* data */
WHITEHEAT_REPORT_TX_DONE */ #define WHITEHEAT_DUMP_MEM_IDATA 'i' /* idata */
struct whiteheat_min_set { #define WHITEHEAT_DUMP_MEM_BDATA 'b' /* bdata */
__u8 port; /* port number (1 to N) */ #define WHITEHEAT_DUMP_MEM_XDATA 'x' /* xdata */
/*
* Allowable address ranges (firmware checks address):
* Type DATA: 0x00 - 0xff
* Type IDATA: 0x80 - 0xff
* Type BDATA: 0x20 - 0x2f
* Type XDATA: 0x0000 - 0xffff
*
* B/I/DATA all read the local memory space
* XDATA reads the external memory space
* BDATA returns bits as bytes
*
* NOTE: 0x80 - 0xff (local space) are the Special Function Registers
* of the 8051, and some have on-read side-effects.
*/
struct whiteheat_dump {
__u8 mem_type; /* see WHITEHEAT_DUMP_* above */
__u16 addr; /* address, see restrictions above */
__u16 length; /* number of bytes to dump, max 63 bytes */
}; };
/* data for WHITEHEAT_PURGE command */
#define WHITEHEAT_PURGE_INPUT 0x01 /*
#define WHITEHEAT_PURGE_OUTPUT 0x02 * WHITEHEAT_PURGE
struct whiteheat_purge_set { */
#define WHITEHEAT_PURGE_RX 0x01 /* purge rx fifos */
#define WHITEHEAT_PURGE_TX 0x02 /* purge tx fifos */
struct whiteheat_purge {
__u8 port; /* port number (1 to N) */ __u8 port; /* port number (1 to N) */
__u8 what; /* bit pattern of what to purge */ __u8 what; /* bit pattern of what to purge */
}; };
/* data for WHITEHEAT_DUMP command */
struct whiteheat_dump_info {
__u8 mem_type; /* memory type: 'd' = data, 'i' = idata, 'b' = bdata, 'x' = xdata */
__u16 addr; /* memory address to dump, address range depends on the above mem_type:
* 'd' = 0 to ff (80 to FF is SFR's)
* 'i' = 80 to ff
* 'b' = 20 to 2f (bits returned as bytes)
* 'x' = 0000 to ffff (also code space) */
__u16 length; /* number of bytes to dump, max 64 */
};
/* data for WHITEHEAT_ECHO command */ /*
struct whiteheat_echo_set { * WHITEHEAT_ECHO
*/
struct whiteheat_echo {
__u8 port; /* port number (1 to N) */ __u8 port; /* port number (1 to N) */
__u8 length; /* length of message to echo */ __u8 length; /* length of message to echo, max 61 bytes */
__u8 echo_data[61]; /* data to echo */ __u8 echo_data[61]; /* data to echo */
}; };
/* data returned from WHITEHEAT_STATUS command */
#define WHITEHEAT_OVERRUN_ERROR 0x02 /*
#define WHITEHEAT_PARITY_ERROR 0x04 * WHITEHEAT_DO_TEST
#define WHITEHEAT_FRAMING_ERROR 0x08 */
#define WHITEHEAT_BREAK_ERROR 0x10 #define WHITEHEAT_TEST_UART_RW 0x01 /* read/write uart registers */
#define WHITEHEAT_TEST_UART_INTR 0x02 /* uart interrupt */
#define WHITEHEAT_OHFLOW 0x01 /* TX is stopped by CTS (waiting for CTS to go ON) */ #define WHITEHEAT_TEST_SETUP_CONT 0x03 /* setup for PORT_CONT/PORT_DISCONT */
#define WHITEHEAT_IHFLOW 0x02 /* remote TX is stopped by RTS */ #define WHITEHEAT_TEST_PORT_CONT 0x04 /* port connect */
#define WHITEHEAT_OSFLOW 0x04 /* TX is stopped by XOFF received (waiting for XON to occur) */ #define WHITEHEAT_TEST_PORT_DISCONT 0x05 /* port disconnect */
#define WHITEHEAT_ISFLOW 0x08 /* remote TX is stopped by XOFF transmitted */ #define WHITEHEAT_TEST_UART_CLK_START 0x06 /* uart clock test start */
#define WHITEHEAT_TX_DONE 0x80 /* TX has completed */ #define WHITEHEAT_TEST_UART_CLK_STOP 0x07 /* uart clock test stop */
#define WHITEHEAT_TEST_MODEM_FT 0x08 /* modem signals, requires a loopback cable/connector */
#define WHITEHEAT_MODEM_EVENT 0x01 #define WHITEHEAT_TEST_ERASE_EEPROM 0x09 /* erase eeprom */
#define WHITEHEAT_ERROR_EVENT 0x02 #define WHITEHEAT_TEST_READ_EEPROM 0x0a /* read eeprom */
#define WHITEHEAT_FLOW_EVENT 0x04 #define WHITEHEAT_TEST_PROGRAM_EEPROM 0x0b /* program eeprom */
#define WHITEHEAT_CONNECT_EVENT 0x08
struct whiteheat_test {
__u8 port; /* port number (1 to n) */
__u8 test; /* see WHITEHEAT_TEST_* above*/
__u8 info[32]; /* additional info */
};
/*
* Replies from the firmware
*/
/*
* WHITEHEAT_STATUS
*/
#define WHITEHEAT_EVENT_MODEM 0x01 /* modem field is valid */
#define WHITEHEAT_EVENT_ERROR 0x02 /* error field is valid */
#define WHITEHEAT_EVENT_FLOW 0x04 /* flow field is valid */
#define WHITEHEAT_EVENT_CONNECT 0x08 /* connect field is valid */
#define WHITEHEAT_FLOW_NONE 0x00 /* no flow control active */
#define WHITEHEAT_FLOW_HARD_OUT 0x01 /* TX is stopped by CTS (waiting for CTS to go on) */
#define WHITEHEAT_FLOW_HARD_IN 0x02 /* remote TX is stopped by RTS */
#define WHITEHEAT_FLOW_SOFT_OUT 0x04 /* TX is stopped by XOFF received (waiting for XON) */
#define WHITEHEAT_FLOW_SOFT_IN 0x08 /* remote TX is stopped by XOFF transmitted */
#define WHITEHEAT_FLOW_TX_DONE 0x80 /* TX has completed */
struct whiteheat_status_info { struct whiteheat_status_info {
__u8 port; /* port number (1 to N) */ __u8 port; /* port number (1 to N) */
__u8 event; /* indicates which of the following bytes are the current event */ __u8 event; /* indicates what the current event is, see WHITEHEAT_EVENT_* above */
__u8 modem; /* modem signal status (copy of UART MSR register) */ __u8 modem; /* modem signal status (copy of uart's MSR register) */
__u8 error; /* PFO and RX break (copy of UART LSR register) */ __u8 error; /* line status (copy of uart's LSR register) */
__u8 flow; /* flow control state */ __u8 flow; /* flow control state, see WHITEHEAT_FLOW_* above */
__u8 connect; /* connect state, non-zero value indicates connected */ __u8 connect; /* 0 means not connected, non-zero means connected */
}; };
/* data returned from WHITEHEAT_EVENT command */
struct whiteheat_event { /*
__u8 port; /* port number (1 to N) */ * WHITEHEAT_GET_DTR_RTS
__u8 event; /* indicates which of the following bytes are the current event */ */
__u8 info; /* either modem, error, flow, or connect information */ struct whiteheat_dr_info {
__u8 mcr; /* copy of uart's MCR register */
}; };
/* data retured by the WHITEHEAT_GET_HW_INFO command */
/*
* WHITEHEAT_GET_HW_INFO
*/
struct whiteheat_hw_info { struct whiteheat_hw_info {
__u8 hw_id; /* hardware id number, WhiteHEAT = 0 */ __u8 hw_id; /* hardware id number, WhiteHEAT = 0 */
__u8 sw_major_rev; /* major version number */ __u8 sw_major_rev; /* major version number */
...@@ -166,5 +253,30 @@ struct whiteheat_hw_info { ...@@ -166,5 +253,30 @@ struct whiteheat_hw_info {
} hw_eeprom_info; /* EEPROM contents */ } hw_eeprom_info; /* EEPROM contents */
}; };
#endif
/*
* WHITEHEAT_EVENT
*/
struct whiteheat_event_info {
__u8 port; /* port number (1 to N) */
__u8 event; /* see whiteheat_status_info.event */
__u8 info; /* see whiteheat_status_info.modem, .error, .flow, .connect */
};
/*
* WHITEHEAT_DO_TEST
*/
#define WHITEHEAT_TEST_FAIL 0x00 /* test failed */
#define WHITEHEAT_TEST_UNKNOWN 0x01 /* unknown test requested */
#define WHITEHEAT_TEST_PASS 0xff /* test passed */
struct whiteheat_test_info {
__u8 port; /* port number (1 to N) */
__u8 test; /* indicates which test this is a response for, see WHITEHEAT_DO_TEST above */
__u8 status; /* see WHITEHEAT_TEST_* above */
__u8 results[32]; /* test-dependent results */
};
#endif
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment