static struct usb_device_id id_table_cyphidcomrs232 [] = {
{ USB_DEVICE(VENDOR_ID_CYPRESS, PRODUCT_ID_CYPHIDCOM) },
+ { USB_DEVICE(VENDOR_ID_POWERCOM, PRODUCT_ID_UPS) },
{ } /* Terminating entry */
};
{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB) },
{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB_LT20) },
{ USB_DEVICE(VENDOR_ID_CYPRESS, PRODUCT_ID_CYPHIDCOM) },
+ { USB_DEVICE(VENDOR_ID_POWERCOM, PRODUCT_ID_UPS) },
{ USB_DEVICE(VENDOR_ID_DAZZLE, PRODUCT_ID_CA42) },
{ } /* Terminating entry */
};
.no_dynamic_id = 1,
};
+enum packet_format {
+ packet_format_1, /* b0:status, b1:payload count */
+ packet_format_2 /* b0[7:3]:status, b0[2:0]:payload count */
+};
+
struct cypress_private {
spinlock_t lock; /* private lock */
int chiptype; /* identifier of device, for quirks/etc */
__u8 current_status; /* received from last read - info on dsr,cts,cd,ri,etc */
__u8 current_config; /* stores the current configuration byte */
__u8 rx_flags; /* throttling - used from whiteheat/ftdi_sio */
+ enum packet_format pkt_fmt; /* format to use for packet send / receive */
+ int get_cfg_unsafe; /* If true, the CYPRESS_GET_CONFIG is unsafe */
int baud_rate; /* stores current baud rate in integer form */
- int cbr_mask; /* stores current baud rate in masked form */
int isthrottled; /* if throttled, discard reads */
wait_queue_head_t delta_msr_wait; /* used for TIOCMIWAIT */
char prev_status, diff_status; /* used for TIOCMIWAIT */
static void cypress_set_dead (struct usb_serial_port *port);
static void cypress_read_int_callback (struct urb *urb);
static void cypress_write_int_callback (struct urb *urb);
-/* baud helper functions */
-static int mask_to_rate (unsigned mask);
-static unsigned rate_to_mask (int rate);
/* write buffer functions */
static struct cypress_buf *cypress_buf_alloc(unsigned int size);
static void cypress_buf_free(struct cypress_buf *cb);
*****************************************************************************/
+static int analyze_baud_rate(struct usb_serial_port *port, speed_t new_rate)
+{
+ struct cypress_private *priv;
+ priv = usb_get_serial_port_data(port);
+
+ /*
+ * The general purpose firmware for the Cypress M8 allows for
+ * a maximum speed of 57600bps (I have no idea whether DeLorme
+ * chose to use the general purpose firmware or not), if you
+ * need to modify this speed setting for your own project
+ * please add your own chiptype and modify the code likewise.
+ * The Cypress HID->COM device will work successfully up to
+ * 115200bps (but the actual throughput is around 3kBps).
+ */
+ if (port->serial->dev->speed == USB_SPEED_LOW) {
+ /*
+ * Mike Isely <isely@pobox.com> 2-Feb-2008: The
+ * Cypress app note that describes this mechanism
+ * states the the low-speed part can't handle more
+ * than 800 bytes/sec, in which case 4800 baud is the
+ * safest speed for a part like that.
+ */
+ if (new_rate > 4800) {
+ dbg("%s - failed setting baud rate, device incapable "
+ "speed %d", __func__, new_rate);
+ return -1;
+ }
+ }
+ switch (priv->chiptype) {
+ case CT_EARTHMATE:
+ if (new_rate <= 600) {
+ /* 300 and 600 baud rates are supported under
+ * the generic firmware, but are not used with
+ * NMEA and SiRF protocols */
+ dbg("%s - failed setting baud rate, unsupported speed "
+ "of %d on Earthmate GPS", __func__, new_rate);
+ return -1;
+ }
+ break;
+ default:
+ break;
+ }
+ return new_rate;
+}
+
+
/* This function can either set or retrieve the current serial line settings */
-static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_mask, int data_bits, int stop_bits,
+static int cypress_serial_control (struct usb_serial_port *port, speed_t baud_rate, int data_bits, int stop_bits,
int parity_enable, int parity_type, int reset, int cypress_request_type)
{
int new_baudrate = 0, retval = 0, tries = 0;
struct cypress_private *priv;
- __u8 feature_buffer[8];
+ __u8 feature_buffer[5];
unsigned long flags;
dbg("%s", __FUNCTION__);
switch(cypress_request_type) {
case CYPRESS_SET_CONFIG:
-
- /*
- * The general purpose firmware for the Cypress M8 allows for a maximum speed
- * of 57600bps (I have no idea whether DeLorme chose to use the general purpose
- * firmware or not), if you need to modify this speed setting for your own
- * project please add your own chiptype and modify the code likewise. The
- * Cypress HID->COM device will work successfully up to 115200bps (but the
- * actual throughput is around 3kBps).
- */
- if (baud_mask != priv->cbr_mask) {
+ new_baudrate = priv->baud_rate;
+ /* 0 means 'Hang up' so doesn't change the true bit rate */
+ if (baud_rate == 0)
+ new_baudrate = priv->baud_rate;
+ /* Change of speed ? */
+ else if (baud_rate != priv->baud_rate) {
dbg("%s - baud rate is changing", __FUNCTION__);
- if ( priv->chiptype == CT_EARTHMATE ) {
- /* 300 and 600 baud rates are supported under the generic firmware,
- * but are not used with NMEA and SiRF protocols */
-
- if ( (baud_mask == B300) || (baud_mask == B600) ) {
- err("%s - failed setting baud rate, unsupported speed",
- __FUNCTION__);
- new_baudrate = priv->baud_rate;
- } else if ( (new_baudrate = mask_to_rate(baud_mask)) == -1) {
- err("%s - failed setting baud rate, unsupported speed",
- __FUNCTION__);
- new_baudrate = priv->baud_rate;
- }
- } else if (priv->chiptype == CT_CYPHIDCOM) {
- if ( (new_baudrate = mask_to_rate(baud_mask)) == -1) {
- err("%s - failed setting baud rate, unsupported speed",
- __FUNCTION__);
- new_baudrate = priv->baud_rate;
- }
- } else if (priv->chiptype == CT_CA42V2) {
- if ( (new_baudrate = mask_to_rate(baud_mask)) == -1) {
- err("%s - failed setting baud rate, unsupported speed",
- __FUNCTION__);
- new_baudrate = priv->baud_rate;
- }
- } else if (priv->chiptype == CT_GENERIC) {
- if ( (new_baudrate = mask_to_rate(baud_mask)) == -1) {
- err("%s - failed setting baud rate, unsupported speed",
- __FUNCTION__);
- new_baudrate = priv->baud_rate;
- }
- } else {
- info("%s - please define your chiptype", __FUNCTION__);
- new_baudrate = priv->baud_rate;
+ retval = analyze_baud_rate(port, baud_rate);
+ if (retval >= 0) {
+ new_baudrate = retval;
+ dbg("%s - New baud rate set to %d",
+ __func__, new_baudrate);
}
- } else { /* baud rate not changing, keep the old */
- new_baudrate = priv->baud_rate;
}
dbg("%s - baud rate is being sent as %d", __FUNCTION__, new_baudrate);
- memset(feature_buffer, 0, 8);
+ memset(feature_buffer, 0, sizeof(feature_buffer));
/* fill the feature_buffer with new configuration */
*((u_int32_t *)feature_buffer) = new_baudrate;
feature_buffer[2], feature_buffer[3], feature_buffer[4]);
do {
- retval = usb_control_msg (port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0),
- HID_REQ_SET_REPORT, USB_DIR_OUT | USB_RECIP_INTERFACE | USB_TYPE_CLASS,
- 0x0300, 0, feature_buffer, 8, 500);
+ retval = usb_control_msg(port->serial->dev,
+ usb_sndctrlpipe(port->serial->dev, 0),
+ HID_REQ_SET_REPORT,
+ USB_DIR_OUT | USB_RECIP_INTERFACE | USB_TYPE_CLASS,
+ 0x0300, 0, feature_buffer,
+ sizeof(feature_buffer), 500);
if (tries++ >= 3)
break;
- } while (retval != 8 && retval != -ENODEV);
+ } while (retval != sizeof(feature_buffer) &&
+ retval != -ENODEV);
- if (retval != 8) {
+ if (retval != sizeof(feature_buffer)) {
err("%s - failed sending serial line settings - %d", __FUNCTION__, retval);
cypress_set_dead(port);
} else {
spin_lock_irqsave(&priv->lock, flags);
priv->baud_rate = new_baudrate;
- priv->cbr_mask = baud_mask;
priv->current_config = feature_buffer[4];
spin_unlock_irqrestore(&priv->lock, flags);
+ /* If we asked for a speed change encode it */
+ if (baud_rate)
+ tty_encode_baud_rate(port->tty,
+ new_baudrate, new_baudrate);
}
break;
case CYPRESS_GET_CONFIG:
+ if (priv->get_cfg_unsafe) {
+ /* Not implemented for this device,
+ and if we try to do it we're likely
+ to crash the hardware. */
+ return -ENOTTY;
+ }
dbg("%s - retreiving serial line settings", __FUNCTION__);
/* set initial values in feature buffer */
- memset(feature_buffer, 0, 8);
+ memset(feature_buffer, 0, sizeof(feature_buffer));
do {
- retval = usb_control_msg (port->serial->dev, usb_rcvctrlpipe(port->serial->dev, 0),
- HID_REQ_GET_REPORT, USB_DIR_IN | USB_RECIP_INTERFACE | USB_TYPE_CLASS,
- 0x0300, 0, feature_buffer, 8, 500);
-
+ retval = usb_control_msg(port->serial->dev,
+ usb_rcvctrlpipe(port->serial->dev, 0),
+ HID_REQ_GET_REPORT,
+ USB_DIR_IN | USB_RECIP_INTERFACE | USB_TYPE_CLASS,
+ 0x0300, 0, feature_buffer,
+ sizeof(feature_buffer), 500);
+
if (tries++ >= 3)
break;
- } while (retval != 5 && retval != -ENODEV);
+ } while (retval != sizeof(feature_buffer) &&
+ retval != -ENODEV);
- if (retval != 5) {
+ if (retval != sizeof(feature_buffer)) {
err("%s - failed to retrieve serial line settings - %d", __FUNCTION__, retval);
cypress_set_dead(port);
return retval;
/* store the config in one byte, and later use bit masks to check values */
priv->current_config = feature_buffer[4];
priv->baud_rate = *((u_int32_t *)feature_buffer);
-
- if ( (priv->cbr_mask = rate_to_mask(priv->baud_rate)) == 0x40)
- dbg("%s - failed setting the baud mask (not defined)", __FUNCTION__);
spin_unlock_irqrestore(&priv->lock, flags);
}
}
}
-/* given a baud mask, it will return integer baud on success */
-static int mask_to_rate (unsigned mask)
-{
- int rate;
-
- switch (mask) {
- case B0: rate = 0; break;
- case B300: rate = 300; break;
- case B600: rate = 600; break;
- case B1200: rate = 1200; break;
- case B2400: rate = 2400; break;
- case B4800: rate = 4800; break;
- case B9600: rate = 9600; break;
- case B19200: rate = 19200; break;
- case B38400: rate = 38400; break;
- case B57600: rate = 57600; break;
- case B115200: rate = 115200; break;
- default: rate = -1;
- }
-
- return rate;
-}
-
-
-static unsigned rate_to_mask (int rate)
-{
- unsigned mask;
-
- switch (rate) {
- case 0: mask = B0; break;
- case 300: mask = B300; break;
- case 600: mask = B600; break;
- case 1200: mask = B1200; break;
- case 2400: mask = B2400; break;
- case 4800: mask = B4800; break;
- case 9600: mask = B9600; break;
- case 19200: mask = B19200; break;
- case 38400: mask = B38400; break;
- case 57600: mask = B57600; break;
- case 115200: mask = B115200; break;
- default: mask = 0x40;
- }
-
- return mask;
-}
/*****************************************************************************
* Cypress serial driver functions
*****************************************************************************/
priv->line_control = 0;
priv->termios_initialized = 0;
priv->rx_flags = 0;
- priv->cbr_mask = B300;
+ /* Default packet format setting is determined by packet size.
+ Anything with a size larger then 9 must have a separate
+ count field since the 3 bit count field is otherwise too
+ small. Otherwise we can use the slightly more compact
+ format. This is in accordance with the cypress_m8 serial
+ converter app note. */
+ if (port->interrupt_out_size > 9) {
+ priv->pkt_fmt = packet_format_1;
+ } else {
+ priv->pkt_fmt = packet_format_2;
+ }
if (interval > 0) {
priv->write_urb_interval = interval;
priv->read_urb_interval = interval;
static int cypress_earthmate_startup (struct usb_serial *serial)
{
struct cypress_private *priv;
+ struct usb_serial_port *port = serial->port[0];
dbg("%s", __FUNCTION__);
if (generic_startup(serial)) {
dbg("%s - Failed setting up port %d", __FUNCTION__,
- serial->port[0]->number);
+ port->number);
return 1;
}
- priv = usb_get_serial_port_data(serial->port[0]);
+ priv = usb_get_serial_port_data(port);
priv->chiptype = CT_EARTHMATE;
+ /* All Earthmate devices use the separated-count packet
+ format! Idiotic. */
+ priv->pkt_fmt = packet_format_1;
+ if (serial->dev->descriptor.idProduct != PRODUCT_ID_EARTHMATEUSB) {
+ /* The old original USB Earthmate seemed able to
+ handle GET_CONFIG requests; everything they've
+ produced since that time crashes if this command is
+ attempted :-( */
+ dbg("%s - Marking this device as unsafe for GET_CONFIG "
+ "commands", __func__);
+ priv->get_cfg_unsafe = !0;
+ }
return 0;
} /* cypress_earthmate_startup */
memset(port->interrupt_out_urb->transfer_buffer, 0, port->interrupt_out_size);
spin_lock_irqsave(&priv->lock, flags);
- switch (port->interrupt_out_size) {
- case 32:
- /* this is for the CY7C64013... */
- offset = 2;
- port->interrupt_out_buffer[0] = priv->line_control;
- break;
- case 8:
- /* this is for the CY7C63743... */
- offset = 1;
- port->interrupt_out_buffer[0] = priv->line_control;
- break;
- default:
- dbg("%s - wrong packet size", __FUNCTION__);
- spin_unlock_irqrestore(&priv->lock, flags);
- return;
+ switch (priv->pkt_fmt) {
+ default:
+ case packet_format_1:
+ /* this is for the CY7C64013... */
+ offset = 2;
+ port->interrupt_out_buffer[0] = priv->line_control;
+ break;
+ case packet_format_2:
+ /* this is for the CY7C63743... */
+ offset = 1;
+ port->interrupt_out_buffer[0] = priv->line_control;
+ break;
}
if (priv->line_control & CONTROL_RESET)
return;
}
- switch (port->interrupt_out_size) {
- case 32:
- port->interrupt_out_buffer[1] = count;
- break;
- case 8:
- port->interrupt_out_buffer[0] |= count;
+ switch (priv->pkt_fmt) {
+ default:
+ case packet_format_1:
+ port->interrupt_out_buffer[1] = count;
+ break;
+ case packet_format_2:
+ port->interrupt_out_buffer[0] |= count;
}
dbg("%s - count is %d", __FUNCTION__, count);
if (priv->cmd_ctrl)
actual_size = 1;
else
- actual_size = count + (port->interrupt_out_size == 32 ? 2 : 1);
-
+ actual_size = count +
+ (priv->pkt_fmt == packet_format_1 ? 2 : 1);
+
usb_serial_debug_data(debug, &port->dev, __FUNCTION__, port->interrupt_out_size,
port->interrupt_out_urb->transfer_buffer);
priv->line_control &= ~CONTROL_RTS;
if (clear & TIOCM_DTR)
priv->line_control &= ~CONTROL_DTR;
+ priv->cmd_ctrl = 1;
spin_unlock_irqrestore(&priv->lock, flags);
- priv->cmd_ctrl = 1;
return cypress_write(port, NULL, 0);
}
dbg("%s - port %d, cmd 0x%.4x", __FUNCTION__, port->number, cmd);
switch (cmd) {
- case TIOCGSERIAL:
- if (copy_to_user((void __user *)arg, port->tty->termios, sizeof(struct ktermios))) {
- return -EFAULT;
- }
- return (0);
- break;
- case TIOCSSERIAL:
- if (copy_from_user(port->tty->termios, (void __user *)arg, sizeof(struct ktermios))) {
- return -EFAULT;
- }
- /* here we need to call cypress_set_termios to invoke the new settings */
- cypress_set_termios(port, &priv->tmp_termios);
- return (0);
- break;
/* This code comes from drivers/char/serial.c and ftdi_sio.c */
case TIOCMIWAIT:
while (priv != NULL) {
struct cypress_private *priv = usb_get_serial_port_data(port);
struct tty_struct *tty;
int data_bits, stop_bits, parity_type, parity_enable;
- unsigned cflag, iflag, baud_mask;
+ unsigned cflag, iflag;
unsigned long flags;
__u8 oldlines;
int linechange = 0;
dbg("%s - port %d", __FUNCTION__, port->number);
tty = port->tty;
- if ((!tty) || (!tty->termios)) {
- dbg("%s - no tty structures", __FUNCTION__);
- return;
- }
spin_lock_irqsave(&priv->lock, flags);
if (!priv->termios_initialized) {
*(tty->termios) = tty_std_termios;
tty->termios->c_cflag = B4800 | CS8 | CREAD | HUPCL |
CLOCAL;
+ tty->termios->c_ispeed = 4800;
+ tty->termios->c_ospeed = 4800;
} else if (priv->chiptype == CT_CYPHIDCOM) {
*(tty->termios) = tty_std_termios;
tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL |
CLOCAL;
+ tty->termios->c_ispeed = 9600;
+ tty->termios->c_ospeed = 9600;
} else if (priv->chiptype == CT_CA42V2) {
*(tty->termios) = tty_std_termios;
tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL |
CLOCAL;
+ tty->termios->c_ispeed = 9600;
+ tty->termios->c_ospeed = 9600;
}
priv->termios_initialized = 1;
}
spin_unlock_irqrestore(&priv->lock, flags);
+ /* Unsupported features need clearing */
+ tty->termios->c_cflag &= ~(CMSPAR|CRTSCTS);
+
cflag = tty->termios->c_cflag;
iflag = tty->termios->c_iflag;
/* check if there are new settings */
if (old_termios) {
- if ((cflag != old_termios->c_cflag) ||
- (RELEVANT_IFLAG(iflag) !=
- RELEVANT_IFLAG(old_termios->c_iflag))) {
- dbg("%s - attempting to set new termios settings",
- __FUNCTION__);
- /* should make a copy of this in case something goes
- * wrong in the function, we can restore it */
- spin_lock_irqsave(&priv->lock, flags);
- priv->tmp_termios = *(tty->termios);
- spin_unlock_irqrestore(&priv->lock, flags);
- } else {
- dbg("%s - nothing to do, exiting", __FUNCTION__);
- return;
- }
- } else
- return;
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->tmp_termios = *(tty->termios);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ }
/* set number of data bits, parity, stop bits */
/* when parity is disabled the parity type bit is ignored */
if ((cflag & CBAUD) == B0) {
/* drop dtr and rts */
dbg("%s - dropping the lines, baud rate 0bps", __FUNCTION__);
- baud_mask = B0;
priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
- } else {
- baud_mask = (cflag & CBAUD);
- switch(baud_mask) {
- case B300:
- dbg("%s - setting baud 300bps", __FUNCTION__);
- break;
- case B600:
- dbg("%s - setting baud 600bps", __FUNCTION__);
- break;
- case B1200:
- dbg("%s - setting baud 1200bps", __FUNCTION__);
- break;
- case B2400:
- dbg("%s - setting baud 2400bps", __FUNCTION__);
- break;
- case B4800:
- dbg("%s - setting baud 4800bps", __FUNCTION__);
- break;
- case B9600:
- dbg("%s - setting baud 9600bps", __FUNCTION__);
- break;
- case B19200:
- dbg("%s - setting baud 19200bps", __FUNCTION__);
- break;
- case B38400:
- dbg("%s - setting baud 38400bps", __FUNCTION__);
- break;
- case B57600:
- dbg("%s - setting baud 57600bps", __FUNCTION__);
- break;
- case B115200:
- dbg("%s - setting baud 115200bps", __FUNCTION__);
- break;
- default:
- dbg("%s - unknown masked baud rate", __FUNCTION__);
- }
+ } else
priv->line_control = (CONTROL_DTR | CONTROL_RTS);
- }
spin_unlock_irqrestore(&priv->lock, flags);
dbg("%s - sending %d stop_bits, %d parity_enable, %d parity_type, "
"%d data_bits (+5)", __FUNCTION__, stop_bits,
parity_enable, parity_type, data_bits);
- cypress_serial_control(port, baud_mask, data_bits, stop_bits,
+ cypress_serial_control(port, tty_get_baud_rate(tty), data_bits, stop_bits,
parity_enable, parity_type, 0, CYPRESS_SET_CONFIG);
/* we perform a CYPRESS_GET_CONFIG so that the current settings are
}
spin_lock_irqsave(&priv->lock, flags);
- switch(urb->actual_length) {
- case 32:
- /* This is for the CY7C64013... */
- priv->current_status = data[0] & 0xF8;
- bytes = data[1] + 2;
- i = 2;
- if (bytes > 2)
- havedata = 1;
- break;
- case 8:
- /* This is for the CY7C63743... */
- priv->current_status = data[0] & 0xF8;
- bytes = (data[0] & 0x07) + 1;
- i = 1;
- if (bytes > 1)
- havedata = 1;
- break;
- default:
- dbg("%s - wrong packet size - received %d bytes",
- __FUNCTION__, urb->actual_length);
- spin_unlock_irqrestore(&priv->lock, flags);
- goto continue_read;
+ result = urb->actual_length;
+ switch (priv->pkt_fmt) {
+ default:
+ case packet_format_1:
+ /* This is for the CY7C64013... */
+ priv->current_status = data[0] & 0xF8;
+ bytes = data[1] + 2;
+ i = 2;
+ if (bytes > 2)
+ havedata = 1;
+ break;
+ case packet_format_2:
+ /* This is for the CY7C63743... */
+ priv->current_status = data[0] & 0xF8;
+ bytes = (data[0] & 0x07) + 1;
+ i = 1;
+ if (bytes > 1)
+ havedata = 1;
+ break;
}
spin_unlock_irqrestore(&priv->lock, flags);
+ if (result < bytes) {
+ dbg("%s - wrong packet size - received %d bytes but packet "
+ "said %d bytes", __func__, result, bytes);
+ goto continue_read;
+ }
usb_serial_debug_data (debug, &port->dev, __FUNCTION__,
urb->actual_length, data);
spin_lock_irqsave(&priv->lock, flags);
/* check to see if status has changed */
- if (priv != NULL) {
- if (priv->current_status != priv->prev_status) {
- priv->diff_status |= priv->current_status ^
- priv->prev_status;
- wake_up_interruptible(&priv->delta_msr_wait);
- priv->prev_status = priv->current_status;
- }
+ if (priv->current_status != priv->prev_status) {
+ priv->diff_status |= priv->current_status ^
+ priv->prev_status;
+ wake_up_interruptible(&priv->delta_msr_wait);
+ priv->prev_status = priv->current_status;
}
spin_unlock_irqrestore(&priv->lock, flags);