summaryrefslogtreecommitdiffstats
path: root/drivers/usb/serial
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/serial')
-rw-r--r--drivers/usb/serial/ftdi_sio.c4
-rw-r--r--drivers/usb/serial/ftdi_sio_ids.h10
-rw-r--r--drivers/usb/serial/io_ti.c279
-rw-r--r--drivers/usb/serial/mxuport.c10
-rw-r--r--drivers/usb/serial/option.c4
-rw-r--r--drivers/usb/serial/pl2303.c35
-rw-r--r--drivers/usb/serial/qcserial.c3
-rw-r--r--drivers/usb/serial/sierra.c1
-rw-r--r--drivers/usb/serial/symbolserial.c24
-rw-r--r--drivers/usb/serial/usb_wwan.c2
10 files changed, 273 insertions, 99 deletions
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 4c8b3b8..a5a0376 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -605,6 +605,10 @@ static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE(FTDI_VID, FTDI_NT_ORIONLXM_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(FTDI_VID, FTDI_SYNAPSE_SS200_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_CUSTOMWARE_MINIPLEX_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_CUSTOMWARE_MINIPLEX2_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_CUSTOMWARE_MINIPLEX2WI_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_CUSTOMWARE_MINIPLEX3_PID) },
/*
* ELV devices:
*/
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index 792e054..67c6d44 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -568,6 +568,14 @@
*/
#define FTDI_SYNAPSE_SS200_PID 0x9090 /* SS200 - SNAP Stick 200 */
+/*
+ * CustomWare / ShipModul NMEA multiplexers product ids (FTDI_VID)
+ */
+#define FTDI_CUSTOMWARE_MINIPLEX_PID 0xfd48 /* MiniPlex first generation NMEA Multiplexer */
+#define FTDI_CUSTOMWARE_MINIPLEX2_PID 0xfd49 /* MiniPlex-USB and MiniPlex-2 series */
+#define FTDI_CUSTOMWARE_MINIPLEX2WI_PID 0xfd4a /* MiniPlex-2Wi */
+#define FTDI_CUSTOMWARE_MINIPLEX3_PID 0xfd4b /* MiniPlex-3 series */
+
/********************************/
/** third-party VID/PID combos **/
@@ -1365,7 +1373,7 @@
#define FTDI_CTI_NANO_PID 0xF60B
/*
- * ZeitControl cardsystems GmbH rfid-readers http://zeitconrol.de
+ * ZeitControl cardsystems GmbH rfid-readers http://zeitcontrol.de
*/
/* TagTracer MIFARE*/
#define FTDI_ZEITCONTROL_TAGTRACE_MIFARE_PID 0xF7C0
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
index ddbb8fe..0ac1b10 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -71,6 +71,25 @@ struct product_info {
__u8 hardware_type; /* Type of hardware */
} __attribute__((packed));
+/*
+ * Edgeport firmware header
+ *
+ * "build_number" has been set to 0 in all three of the images I have
+ * seen, and Digi Tech Support suggests that it is safe to ignore it.
+ *
+ * "length" is the number of bytes of actual data following the header.
+ *
+ * "checksum" is the low order byte resulting from adding the values of
+ * all the data bytes.
+ */
+struct edgeport_fw_hdr {
+ u8 major_version;
+ u8 minor_version;
+ __le16 build_number;
+ __le16 length;
+ u8 checksum;
+} __packed;
+
struct edgeport_port {
__u16 uart_base;
__u16 dma_address;
@@ -101,6 +120,9 @@ struct edgeport_serial {
struct mutex es_lock;
int num_ports_open;
struct usb_serial *serial;
+ struct delayed_work heartbeat_work;
+ int fw_version;
+ bool use_heartbeat;
};
@@ -187,10 +209,6 @@ static const struct usb_device_id id_table_combined[] = {
MODULE_DEVICE_TABLE(usb, id_table_combined);
-static unsigned char OperationalMajorVersion;
-static unsigned char OperationalMinorVersion;
-static unsigned short OperationalBuildNumber;
-
static int closing_wait = EDGE_CLOSING_WAIT;
static bool ignore_cpu_rev;
static int default_uart_mode; /* RS232 */
@@ -209,6 +227,26 @@ static void edge_send(struct usb_serial_port *port, struct tty_struct *tty);
static int edge_create_sysfs_attrs(struct usb_serial_port *port);
static int edge_remove_sysfs_attrs(struct usb_serial_port *port);
+/*
+ * Some release of Edgeport firmware "down3.bin" after version 4.80
+ * introduced code to automatically disconnect idle devices on some
+ * Edgeport models after periods of inactivity, typically ~60 seconds.
+ * This occurs without regard to whether ports on the device are open
+ * or not. Digi International Tech Support suggested:
+ *
+ * 1. Adding driver "heartbeat" code to reset the firmware timer by
+ * requesting a descriptor record every 15 seconds, which should be
+ * effective with newer firmware versions that require it, and benign
+ * with older versions that do not. In practice 40 seconds seems often
+ * enough.
+ * 2. The heartbeat code is currently required only on Edgeport/416 models.
+ */
+#define FW_HEARTBEAT_VERSION_CUTOFF ((4 << 8) + 80)
+#define FW_HEARTBEAT_SECS 40
+
+/* Timeouts in msecs: firmware downloads take longer */
+#define TI_VSEND_TIMEOUT_DEFAULT 1000
+#define TI_VSEND_TIMEOUT_FW_DOWNLOAD 10000
static int ti_vread_sync(struct usb_device *dev, __u8 request,
__u16 value, __u16 index, u8 *data, int size)
@@ -228,14 +266,14 @@ static int ti_vread_sync(struct usb_device *dev, __u8 request,
return 0;
}
-static int ti_vsend_sync(struct usb_device *dev, __u8 request,
- __u16 value, __u16 index, u8 *data, int size)
+static int ti_vsend_sync(struct usb_device *dev, u8 request, u16 value,
+ u16 index, u8 *data, int size, int timeout)
{
int status;
status = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
(USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT),
- value, index, data, size, 1000);
+ value, index, data, size, timeout);
if (status < 0)
return status;
if (status != size) {
@@ -250,7 +288,8 @@ static int send_cmd(struct usb_device *dev, __u8 command,
__u8 moduleid, __u16 value, u8 *data,
int size)
{
- return ti_vsend_sync(dev, command, value, moduleid, data, size);
+ return ti_vsend_sync(dev, command, value, moduleid, data, size,
+ TI_VSEND_TIMEOUT_DEFAULT);
}
/* clear tx/rx buffers and fifo in TI UMP */
@@ -378,9 +417,9 @@ static int write_boot_mem(struct edgeport_serial *serial,
}
for (i = 0; i < length; ++i) {
- status = ti_vsend_sync(serial->serial->dev,
- UMPC_MEMORY_WRITE, buffer[i],
- (__u16)(i + start_address), NULL, 0);
+ status = ti_vsend_sync(serial->serial->dev, UMPC_MEMORY_WRITE,
+ buffer[i], (u16)(i + start_address), NULL,
+ 0, TI_VSEND_TIMEOUT_DEFAULT);
if (status)
return status;
}
@@ -421,10 +460,9 @@ static int write_i2c_mem(struct edgeport_serial *serial,
* regardless of host byte order.
*/
be_start_address = swab16((u16)start_address);
- status = ti_vsend_sync(serial->serial->dev,
- UMPC_MEMORY_WRITE, (__u16)address_type,
- be_start_address,
- buffer, write_length);
+ status = ti_vsend_sync(serial->serial->dev, UMPC_MEMORY_WRITE,
+ (u16)address_type, be_start_address,
+ buffer, write_length, TI_VSEND_TIMEOUT_DEFAULT);
if (status) {
dev_dbg(dev, "%s - ERROR %d\n", __func__, status);
return status;
@@ -454,9 +492,8 @@ static int write_i2c_mem(struct edgeport_serial *serial,
*/
be_start_address = swab16((u16)start_address);
status = ti_vsend_sync(serial->serial->dev, UMPC_MEMORY_WRITE,
- (__u16)address_type,
- be_start_address,
- buffer, write_length);
+ (u16)address_type, be_start_address, buffer,
+ write_length, TI_VSEND_TIMEOUT_DEFAULT);
if (status) {
dev_err(dev, "%s - ERROR %d\n", __func__, status);
return status;
@@ -748,18 +785,17 @@ exit:
}
/* Build firmware header used for firmware update */
-static int build_i2c_fw_hdr(__u8 *header, struct device *dev)
+static int build_i2c_fw_hdr(u8 *header, struct device *dev,
+ const struct firmware *fw)
{
__u8 *buffer;
int buffer_size;
int i;
- int err;
__u8 cs = 0;
struct ti_i2c_desc *i2c_header;
struct ti_i2c_image_header *img_header;
struct ti_i2c_firmware_rec *firmware_rec;
- const struct firmware *fw;
- const char *fw_name = "edgeport/down3.bin";
+ struct edgeport_fw_hdr *fw_hdr = (struct edgeport_fw_hdr *)fw->data;
/* In order to update the I2C firmware we must change the type 2 record
* to type 0xF2. This will force the UMP to come up in Boot Mode.
@@ -782,24 +818,11 @@ static int build_i2c_fw_hdr(__u8 *header, struct device *dev)
// Set entire image of 0xffs
memset(buffer, 0xff, buffer_size);
- err = request_firmware(&fw, fw_name, dev);
- if (err) {
- dev_err(dev, "Failed to load image \"%s\" err %d\n",
- fw_name, err);
- kfree(buffer);
- return err;
- }
-
- /* Save Download Version Number */
- OperationalMajorVersion = fw->data[0];
- OperationalMinorVersion = fw->data[1];
- OperationalBuildNumber = fw->data[2] | (fw->data[3] << 8);
-
/* Copy version number into firmware record */
firmware_rec = (struct ti_i2c_firmware_rec *)buffer;
- firmware_rec->Ver_Major = OperationalMajorVersion;
- firmware_rec->Ver_Minor = OperationalMinorVersion;
+ firmware_rec->Ver_Major = fw_hdr->major_version;
+ firmware_rec->Ver_Minor = fw_hdr->minor_version;
/* Pointer to fw_down memory image */
img_header = (struct ti_i2c_image_header *)&fw->data[4];
@@ -808,8 +831,6 @@ static int build_i2c_fw_hdr(__u8 *header, struct device *dev)
&fw->data[4 + sizeof(struct ti_i2c_image_header)],
le16_to_cpu(img_header->Length));
- release_firmware(fw);
-
for (i=0; i < buffer_size; i++) {
cs = (__u8)(cs + buffer[i]);
}
@@ -823,8 +844,8 @@ static int build_i2c_fw_hdr(__u8 *header, struct device *dev)
i2c_header->Type = I2C_DESC_TYPE_FIRMWARE_BLANK;
i2c_header->Size = cpu_to_le16(buffer_size);
i2c_header->CheckSum = cs;
- firmware_rec->Ver_Major = OperationalMajorVersion;
- firmware_rec->Ver_Minor = OperationalMinorVersion;
+ firmware_rec->Ver_Major = fw_hdr->major_version;
+ firmware_rec->Ver_Minor = fw_hdr->minor_version;
return 0;
}
@@ -925,13 +946,49 @@ static int ti_cpu_rev(struct edge_ti_manuf_descriptor *desc)
return TI_GET_CPU_REVISION(desc->CpuRev_BoardRev);
}
+static int check_fw_sanity(struct edgeport_serial *serial,
+ const struct firmware *fw)
+{
+ u16 length_total;
+ u8 checksum = 0;
+ int pos;
+ struct device *dev = &serial->serial->interface->dev;
+ struct edgeport_fw_hdr *fw_hdr = (struct edgeport_fw_hdr *)fw->data;
+
+ if (fw->size < sizeof(struct edgeport_fw_hdr)) {
+ dev_err(dev, "incomplete fw header\n");
+ return -EINVAL;
+ }
+
+ length_total = le16_to_cpu(fw_hdr->length) +
+ sizeof(struct edgeport_fw_hdr);
+
+ if (fw->size != length_total) {
+ dev_err(dev, "bad fw size (expected: %u, got: %zu)\n",
+ length_total, fw->size);
+ return -EINVAL;
+ }
+
+ for (pos = sizeof(struct edgeport_fw_hdr); pos < fw->size; ++pos)
+ checksum += fw->data[pos];
+
+ if (checksum != fw_hdr->checksum) {
+ dev_err(dev, "bad fw checksum (expected: 0x%x, got: 0x%x)\n",
+ fw_hdr->checksum, checksum);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
/**
* DownloadTIFirmware - Download run-time operating firmware to the TI5052
*
* This routine downloads the main operating code into the TI5052, using the
* boot code already burned into E2PROM or ROM.
*/
-static int download_fw(struct edgeport_serial *serial)
+static int download_fw(struct edgeport_serial *serial,
+ const struct firmware *fw)
{
struct device *dev = &serial->serial->dev->dev;
int status = 0;
@@ -940,6 +997,14 @@ static int download_fw(struct edgeport_serial *serial)
struct usb_interface_descriptor *interface;
int download_cur_ver;
int download_new_ver;
+ struct edgeport_fw_hdr *fw_hdr = (struct edgeport_fw_hdr *)fw->data;
+
+ if (check_fw_sanity(serial, fw))
+ return -EINVAL;
+
+ /* If on-board version is newer, "fw_version" will be updated below. */
+ serial->fw_version = (fw_hdr->major_version << 8) +
+ fw_hdr->minor_version;
/* This routine is entered by both the BOOT mode and the Download mode
* We can determine which code is running by the reading the config
@@ -1047,14 +1112,13 @@ static int download_fw(struct edgeport_serial *serial)
version in I2c */
download_cur_ver = (firmware_version->Ver_Major << 8) +
(firmware_version->Ver_Minor);
- download_new_ver = (OperationalMajorVersion << 8) +
- (OperationalMinorVersion);
+ download_new_ver = (fw_hdr->major_version << 8) +
+ (fw_hdr->minor_version);
dev_dbg(dev, "%s - >> FW Versions Device %d.%d Driver %d.%d\n",
__func__, firmware_version->Ver_Major,
firmware_version->Ver_Minor,
- OperationalMajorVersion,
- OperationalMinorVersion);
+ fw_hdr->major_version, fw_hdr->minor_version);
/* Check if we have an old version in the I2C and
update if necessary */
@@ -1063,8 +1127,8 @@ static int download_fw(struct edgeport_serial *serial)
__func__,
firmware_version->Ver_Major,
firmware_version->Ver_Minor,
- OperationalMajorVersion,
- OperationalMinorVersion);
+ fw_hdr->major_version,
+ fw_hdr->minor_version);
record = kmalloc(1, GFP_KERNEL);
if (!record) {
@@ -1129,7 +1193,8 @@ static int download_fw(struct edgeport_serial *serial)
/* Reset UMP -- Back to BOOT MODE */
status = ti_vsend_sync(serial->serial->dev,
UMPC_HARDWARE_RESET,
- 0, 0, NULL, 0);
+ 0, 0, NULL, 0,
+ TI_VSEND_TIMEOUT_DEFAULT);
dev_dbg(dev, "%s - HARDWARE RESET return %d\n", __func__, status);
@@ -1139,6 +1204,9 @@ static int download_fw(struct edgeport_serial *serial)
kfree(rom_desc);
kfree(ti_manuf_desc);
return -ENODEV;
+ } else {
+ /* Same or newer fw version is already loaded */
+ serial->fw_version = download_cur_ver;
}
kfree(firmware_version);
}
@@ -1177,7 +1245,7 @@ static int download_fw(struct edgeport_serial *serial)
* UMP Ram to I2C and the firmware will update the
* record type from 0xf2 to 0x02.
*/
- status = build_i2c_fw_hdr(header, dev);
+ status = build_i2c_fw_hdr(header, dev, fw);
if (status) {
kfree(vheader);
kfree(header);
@@ -1229,7 +1297,9 @@ static int download_fw(struct edgeport_serial *serial)
/* Tell firmware to copy download image into I2C */
status = ti_vsend_sync(serial->serial->dev,
- UMPC_COPY_DNLD_TO_I2C, 0, 0, NULL, 0);
+ UMPC_COPY_DNLD_TO_I2C,
+ 0, 0, NULL, 0,
+ TI_VSEND_TIMEOUT_FW_DOWNLOAD);
dev_dbg(dev, "%s - Update complete 0x%x\n", __func__, status);
if (status) {
@@ -1278,9 +1348,6 @@ static int download_fw(struct edgeport_serial *serial)
__u8 cs = 0;
__u8 *buffer;
int buffer_size;
- int err;
- const struct firmware *fw;
- const char *fw_name = "edgeport/down3.bin";
/* Validate Hardware version number
* Read Manufacturing Descriptor from TI Based Edgeport
@@ -1328,16 +1395,7 @@ static int download_fw(struct edgeport_serial *serial)
/* Initialize the buffer to 0xff (pad the buffer) */
memset(buffer, 0xff, buffer_size);
-
- err = request_firmware(&fw, fw_name, dev);
- if (err) {
- dev_err(dev, "Failed to load image \"%s\" err %d\n",
- fw_name, err);
- kfree(buffer);
- return err;
- }
memcpy(buffer, &fw->data[4], fw->size - 4);
- release_firmware(fw);
for (i = sizeof(struct ti_i2c_image_header);
i < buffer_size; i++) {
@@ -1352,7 +1410,9 @@ static int download_fw(struct edgeport_serial *serial)
header->CheckSum = cs;
/* Download the operational code */
- dev_dbg(dev, "%s - Downloading operational code image (TI UMP)\n", __func__);
+ dev_dbg(dev, "%s - Downloading operational code image version %d.%d (TI UMP)\n",
+ __func__,
+ fw_hdr->major_version, fw_hdr->minor_version);
status = download_code(serial, buffer, buffer_size);
kfree(buffer);
@@ -2373,10 +2433,44 @@ static void edge_break(struct tty_struct *tty, int break_state)
__func__, status);
}
+static void edge_heartbeat_schedule(struct edgeport_serial *edge_serial)
+{
+ if (!edge_serial->use_heartbeat)
+ return;
+
+ schedule_delayed_work(&edge_serial->heartbeat_work,
+ FW_HEARTBEAT_SECS * HZ);
+}
+
+static void edge_heartbeat_work(struct work_struct *work)
+{
+ struct edgeport_serial *serial;
+ struct ti_i2c_desc *rom_desc;
+
+ serial = container_of(work, struct edgeport_serial,
+ heartbeat_work.work);
+
+ rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL);
+
+ /* Descriptor address request is enough to reset the firmware timer */
+ if (!rom_desc || !get_descriptor_addr(serial, I2C_DESC_TYPE_ION,
+ rom_desc)) {
+ dev_err(&serial->serial->interface->dev,
+ "%s - Incomplete heartbeat\n", __func__);
+ }
+ kfree(rom_desc);
+
+ edge_heartbeat_schedule(serial);
+}
+
static int edge_startup(struct usb_serial *serial)
{
struct edgeport_serial *edge_serial;
int status;
+ const struct firmware *fw;
+ const char *fw_name = "edgeport/down3.bin";
+ struct device *dev = &serial->interface->dev;
+ u16 product_id;
/* create our private serial structure */
edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL);
@@ -2387,12 +2481,35 @@ static int edge_startup(struct usb_serial *serial)
edge_serial->serial = serial;
usb_set_serial_data(serial, edge_serial);
- status = download_fw(edge_serial);
+ status = request_firmware(&fw, fw_name, dev);
if (status) {
+ dev_err(dev, "Failed to load image \"%s\" err %d\n",
+ fw_name, status);
kfree(edge_serial);
return status;
}
+ status = download_fw(edge_serial, fw);
+ release_firmware(fw);
+ if (status) {
+ kfree(edge_serial);
+ return status;
+ }
+
+ product_id = le16_to_cpu(
+ edge_serial->serial->dev->descriptor.idProduct);
+
+ /* Currently only the EP/416 models require heartbeat support */
+ if (edge_serial->fw_version > FW_HEARTBEAT_VERSION_CUTOFF) {
+ if (product_id == ION_DEVICE_ID_TI_EDGEPORT_416 ||
+ product_id == ION_DEVICE_ID_TI_EDGEPORT_416B) {
+ edge_serial->use_heartbeat = true;
+ }
+ }
+
+ INIT_DELAYED_WORK(&edge_serial->heartbeat_work, edge_heartbeat_work);
+ edge_heartbeat_schedule(edge_serial);
+
return 0;
}
@@ -2402,7 +2519,10 @@ static void edge_disconnect(struct usb_serial *serial)
static void edge_release(struct usb_serial *serial)
{
- kfree(usb_get_serial_data(serial));
+ struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
+
+ cancel_delayed_work_sync(&edge_serial->heartbeat_work);
+ kfree(edge_serial);
}
static int edge_port_probe(struct usb_serial_port *port)
@@ -2506,6 +2626,25 @@ static int edge_remove_sysfs_attrs(struct usb_serial_port *port)
return 0;
}
+#ifdef CONFIG_PM
+static int edge_suspend(struct usb_serial *serial, pm_message_t message)
+{
+ struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
+
+ cancel_delayed_work_sync(&edge_serial->heartbeat_work);
+
+ return 0;
+}
+
+static int edge_resume(struct usb_serial *serial)
+{
+ struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
+
+ edge_heartbeat_schedule(edge_serial);
+
+ return 0;
+}
+#endif
static struct usb_serial_driver edgeport_1port_device = {
.driver = {
@@ -2538,6 +2677,10 @@ static struct usb_serial_driver edgeport_1port_device = {
.read_int_callback = edge_interrupt_callback,
.read_bulk_callback = edge_bulk_in_callback,
.write_bulk_callback = edge_bulk_out_callback,
+#ifdef CONFIG_PM
+ .suspend = edge_suspend,
+ .resume = edge_resume,
+#endif
};
static struct usb_serial_driver edgeport_2port_device = {
@@ -2571,6 +2714,10 @@ static struct usb_serial_driver edgeport_2port_device = {
.read_int_callback = edge_interrupt_callback,
.read_bulk_callback = edge_bulk_in_callback,
.write_bulk_callback = edge_bulk_out_callback,
+#ifdef CONFIG_PM
+ .suspend = edge_suspend,
+ .resume = edge_resume,
+#endif
};
static struct usb_serial_driver * const serial_drivers[] = {
diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c
index 460a406..31a8b47 100644
--- a/drivers/usb/serial/mxuport.c
+++ b/drivers/usb/serial/mxuport.c
@@ -1137,13 +1137,9 @@ static int mxuport_port_probe(struct usb_serial_port *port)
return err;
/* Set interface (RS-232) */
- err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_INTERFACE,
- MX_INT_RS232,
- port->port_number);
- if (err)
- return err;
-
- return 0;
+ return mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_INTERFACE,
+ MX_INT_RS232,
+ port->port_number);
}
static int mxuport_alloc_write_urb(struct usb_serial *serial,
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 19b85ee..6d1941a 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -1099,6 +1099,8 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x9000)}, /* SIMCom SIM5218 */
{ USB_DEVICE_INTERFACE_CLASS(SIERRA_VENDOR_ID, 0x68c0, 0xff),
.driver_info = (kernel_ulong_t)&sierra_mc73xx_blacklist }, /* MC73xx */
+ { USB_DEVICE_INTERFACE_CLASS(SIERRA_VENDOR_ID, 0x9041, 0xff),
+ .driver_info = (kernel_ulong_t)&sierra_mc73xx_blacklist }, /* MC7305/MC7355 */
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6001) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CMU_300) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6003),
@@ -1939,7 +1941,7 @@ static void option_instat_callback(struct urb *urb)
} else if (status == -ENOENT || status == -ESHUTDOWN) {
dev_dbg(dev, "%s: urb stopped: %d\n", __func__, status);
} else
- dev_err(dev, "%s: error %d\n", __func__, status);
+ dev_dbg(dev, "%s: error %d\n", __func__, status);
/* Resubmit urb so we continue receiving IRQ data */
if (status != -ESHUTDOWN && status != -ENOENT) {
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index f5257af..ae682e4 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -362,21 +362,38 @@ static speed_t pl2303_encode_baud_rate_direct(unsigned char buf[4],
static speed_t pl2303_encode_baud_rate_divisor(unsigned char buf[4],
speed_t baud)
{
- unsigned int tmp;
+ unsigned int baseline, mantissa, exponent;
/*
* Apparently the formula is:
- * baudrate = 12M * 32 / (2^buf[1]) / buf[0]
+ * baudrate = 12M * 32 / (mantissa * 4^exponent)
+ * where
+ * mantissa = buf[8:0]
+ * exponent = buf[11:9]
*/
- tmp = 12000000 * 32 / baud;
+ baseline = 12000000 * 32;
+ mantissa = baseline / baud;
+ if (mantissa == 0)
+ mantissa = 1; /* Avoid dividing by zero if baud > 32*12M. */
+ exponent = 0;
+ while (mantissa >= 512) {
+ if (exponent < 7) {
+ mantissa >>= 2; /* divide by 4 */
+ exponent++;
+ } else {
+ /* Exponent is maxed. Trim mantissa and leave. */
+ mantissa = 511;
+ break;
+ }
+ }
+
buf[3] = 0x80;
buf[2] = 0;
- buf[1] = (tmp >= 256);
- while (tmp >= 256) {
- tmp >>= 2;
- buf[1] <<= 1;
- }
- buf[0] = tmp;
+ buf[1] = exponent << 1 | mantissa >> 8;
+ buf[0] = mantissa & 0xff;
+
+ /* Calculate and return the exact baud rate. */
+ baud = (baseline / mantissa) >> (exponent << 1);
return baud;
}
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index 9c63897..ebcec8c 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -139,13 +139,13 @@ static const struct usb_device_id id_table[] = {
{USB_DEVICE(0x0AF0, 0x8120)}, /* Option GTM681W */
/* non-Gobi Sierra Wireless devices */
+ {DEVICE_SWI(0x03f0, 0x4e1d)}, /* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */
{DEVICE_SWI(0x0f3d, 0x68a2)}, /* Sierra Wireless MC7700 */
{DEVICE_SWI(0x114f, 0x68a2)}, /* Sierra Wireless MC7750 */
{DEVICE_SWI(0x1199, 0x68a2)}, /* Sierra Wireless MC7710 */
{DEVICE_SWI(0x1199, 0x901c)}, /* Sierra Wireless EM7700 */
{DEVICE_SWI(0x1199, 0x901f)}, /* Sierra Wireless EM7355 */
{DEVICE_SWI(0x1199, 0x9040)}, /* Sierra Wireless Modem */
- {DEVICE_SWI(0x1199, 0x9041)}, /* Sierra Wireless MC7305/MC7355 */
{DEVICE_SWI(0x1199, 0x9051)}, /* Netgear AirCard 340U */
{DEVICE_SWI(0x1199, 0x9053)}, /* Sierra Wireless Modem */
{DEVICE_SWI(0x1199, 0x9054)}, /* Sierra Wireless Modem */
@@ -158,6 +158,7 @@ static const struct usb_device_id id_table[] = {
{DEVICE_SWI(0x413c, 0x81a4)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */
{DEVICE_SWI(0x413c, 0x81a8)}, /* Dell Wireless 5808 Gobi(TM) 4G LTE Mobile Broadband Card */
{DEVICE_SWI(0x413c, 0x81a9)}, /* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card */
+ {DEVICE_SWI(0x413c, 0x81b1)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card */
/* Huawei devices */
{DEVICE_HWI(0x03f0, 0x581d)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c
index 46179a0..07d1ecd 100644
--- a/drivers/usb/serial/sierra.c
+++ b/drivers/usb/serial/sierra.c
@@ -289,6 +289,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x68AA, 0xFF, 0xFF, 0xFF),
.driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist
},
+ { USB_DEVICE(0x1199, 0x68AB) }, /* Sierra Wireless AR8550 */
/* AT&T Direct IP LTE modems */
{ USB_DEVICE_AND_INTERFACE_INFO(0x0F3D, 0x68AA, 0xFF, 0xFF, 0xFF),
.driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist
diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c
index 8fceec7..37f3ad1 100644
--- a/drivers/usb/serial/symbolserial.c
+++ b/drivers/usb/serial/symbolserial.c
@@ -60,17 +60,15 @@ static void symbol_int_callback(struct urb *urb)
usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
+ /*
+ * Data from the device comes with a 1 byte header:
+ *
+ * <size of data> <data>...
+ */
if (urb->actual_length > 1) {
- data_length = urb->actual_length - 1;
-
- /*
- * Data from the device comes with a 1 byte header:
- *
- * <size of data>data...
- * This is real data to be sent to the tty layer
- * we pretty much just ignore the size and send everything
- * else to the tty layer.
- */
+ data_length = data[0];
+ if (data_length > (urb->actual_length - 1))
+ data_length = urb->actual_length - 1;
tty_insert_flip_string(&port->port, &data[1], data_length);
tty_flip_buffer_push(&port->port);
} else {
@@ -94,7 +92,7 @@ exit:
static int symbol_open(struct tty_struct *tty, struct usb_serial_port *port)
{
- struct symbol_private *priv = usb_get_serial_data(port->serial);
+ struct symbol_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
int result = 0;
@@ -120,7 +118,7 @@ static void symbol_close(struct usb_serial_port *port)
static void symbol_throttle(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
- struct symbol_private *priv = usb_get_serial_data(port->serial);
+ struct symbol_private *priv = usb_get_serial_port_data(port);
spin_lock_irq(&priv->lock);
priv->throttled = true;
@@ -130,7 +128,7 @@ static void symbol_throttle(struct tty_struct *tty)
static void symbol_unthrottle(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
- struct symbol_private *priv = usb_get_serial_data(port->serial);
+ struct symbol_private *priv = usb_get_serial_port_data(port);
int result;
bool was_throttled;
diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c
index 2f805cb..825305c 100644
--- a/drivers/usb/serial/usb_wwan.c
+++ b/drivers/usb/serial/usb_wwan.c
@@ -282,7 +282,7 @@ static void usb_wwan_indat_callback(struct urb *urb)
/* Resubmit urb so we continue receiving */
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err) {
- if (err != -EPERM) {
+ if (err != -EPERM && err != -ENODEV) {
dev_err(dev, "%s: resubmit read urb failed. (%d)\n",
__func__, err);
/* busy also in error unless we are killed */
OpenPOWER on IntegriCloud