With relation to the Openwrt with WL500GP-v2 test (BCM5354 chipset), I have
obtained a BUG response. After the first secs of the test with usb-serial or
pl2303 with microcom, the system reboot.
I would like to know if the problem is in the driver (usb-uhci, usb-serial)
or if is there some chipset inconsistency with the kernel?.
# Miscellaneous USB options
#
# CONFIG_USB_DEVICEFS is not set
# CONFIG_USB_BANDWIDTH is not set
#
# USB Host Controller Drivers
#
CONFIG_USB_EHCI_HCD=m
CONFIG_USB_UHCI=m
CONFIG_USB_UHCI_HIGH_BANDWIDTH=y
# CONFIG_USB_UHCI_ALT is not set
# CONFIG_USB_OHCI is not set
Find some diff files including the option.c (needing some corrections) GSM
driver adapted to the kernel 2.4 as follows:
diff -uNr --exclude='.*' --exclude='*.o' --exclude='*~'
a/drivers/usb/serial/Config.in drivers/usb/serial/Config.in
--- a/drivers/usb/serial/Config.in 2008-05-06 19:00:29.000000000 -0400
+++ b/drivers/usb/serial/Config.in 2008-05-19 18:29:25.000000000 -0400
@@ -8,6 +8,7 @@
if [ "$CONFIG_USB_SERIAL" != "n" ]; then
dep_bool ' USB Serial Converter verbose debug' CONFIG_USB_SERIAL_DEBUG
$CONFIG_USB_SERIAL
dep_mbool ' USB Generic Serial Driver' CONFIG_USB_SERIAL_GENERIC
$CONFIG_USB_SERIAL
+ dep_tristate ' USB 3G MODEM' CONFIG_USB_SERIAL_OPTION $CONFIG_USB_SERIAL
dep_tristate ' USB Belkin and Peracom Single Port Serial Driver'
CONFIG_USB_SERIAL_BELKIN $CONFIG_USB_SERIAL
dep_tristate ' USB ConnectTech WhiteHEAT Serial Driver'
CONFIG_USB_SERIAL_WHITEHEAT $CONFIG_USB_SERIAL
dep_tristate ' USB Digi International AccelePort USB Serial Driver'
CONFIG_USB_SERIAL_DIGI_ACCELEPORT $CONFIG_USB_SERIAL
diff -uNr --exclude='.*' --exclude='*.o' --exclude='*~'
a/drivers/usb/serial/Makefile drivers/usb/serial/Makefile
--- a/drivers/usb/serial/Makefile 2008-05-06 19:00:29.000000000 -0400
+++ b/drivers/usb/serial/Makefile 2008-05-22 14:29:13.000000000 -0400
@@ -26,7 +26,7 @@
obj-$(CONFIG_USB_SERIAL_IR) += ir-usb.o
obj-$(CONFIG_USB_SERIAL_KLSI) += kl5kusb105.o
obj-$(CONFIG_USB_SERIAL_KOBIL_SCT) += kobil_sct.o
-
+obj-$(CONFIG_USB_SERIAL_OPTION) += option.o
# Objects that export symbols.
export-objs := usbserial.o
diff -uNr --exclude='.*' --exclude='*.o' --exclude='*~'
a/drivers/usb/serial/option.c drivers/usb/serial/option.c
--- a/drivers/usb/serial/option.c 1969-12-31 19:00:00.000000000 -0500
+++ b/drivers/usb/serial/option.c 2008-05-22 14:22:06.000000000 -0400
@@ -0,0 +1,840 @@
+/*
+ USB Driver for GSM modems
+
+ Copyright (C) 2005 Matthias Urlichs <[EMAIL PROTECTED]>
+
+ This driver is free software; you can redistribute it and/or modify
+ it under the terms of Version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ Portions copied from the Keyspan driver by Hugh Blemings <[EMAIL PROTECTED]>
+
+ History: see the git log.
+
+ Work sponsored by: Sigos GmbH, Germany <[EMAIL PROTECTED]>
+
+ This driver exists because the "normal" serial driver doesn't work too well
+ with GSM modems. Issues:
+ - data loss -- one single Receive URB is not nearly enough
+ - nonstandard flow (Option devices) control
+ - controlling the baud rate doesn't make sense
+
+ This driver is named "option" because the most common device it's
+ used for is a PC-Card (with an internal OHCI-USB interface, behind
+ which the GSM interface sits), made by Option Inc.
+
+ Some of the "one port" devices actually exhibit multiple USB instances
+ on the USB bus. This is not a bug, these ports are used for different
+ device features.
+
+ Kernel 2.4.36.4 (2008), Felipe Maya <[EMAIL PROTECTED]>
+
+*/
+
+#define DRIVER_VERSION "v0.7.1"
+#define DRIVER_AUTHOR "Matthias Urlichs <[EMAIL PROTECTED]>"
+#define DRIVER_DESC "USB Driver for GSM modems"
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/usb.h>
+#include <linux/spinlock.h>
+#include <asm/uaccess.h>
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+ static int debug = 1;
+#else
+ static int debug;
+#endif
+
+#include "usb-serial.h"
+
+
+
+/* Function prototypes */
+static int option_open(struct usb_serial_port *port, struct file *filp);
+static void option_close(struct usb_serial_port *port, struct file *filp);
+static int option_startup(struct usb_serial *serial);
+static void option_shutdown(struct usb_serial *serial);
+static void option_rx_throttle(struct usb_serial_port *port);
+static void option_rx_unthrottle(struct usb_serial_port *port);
+static int option_write_room(struct usb_serial_port *port);
+
+static void option_instat_callback(struct urb *urb);
+
+static int option_write(struct usb_serial_port *port, int from_user,
+ const unsigned char *buf, int count);
+
+static int option_chars_in_buffer(struct usb_serial_port *port);
+static int option_ioctl(struct usb_serial_port *port, struct file *file,
+ unsigned int cmd, unsigned long arg);
+static void option_set_termios(struct usb_serial_port *port,
+ struct termios *old);
+static void option_break_ctl(struct usb_serial_port *port, int break_state);
+static int option_tiocmget(struct usb_serial_port *port, struct file *file);
+static int option_tiocmset(struct usb_serial_port *port, struct file *file,
+ unsigned int set, unsigned int clear);
+static int option_send_setup(struct usb_serial_port *port);
+
+/* Vendor and product IDs */
+#define HUAWEI_VENDOR_ID 0x12D1
+#define HUAWEI_PRODUCT_E226 0x1003
+
+#define ZTE_VENDOR_ID 0x19D2
+#define ZTE_PRODUCT_MF622 0x0001
+
+
+#define N_IN_URB 4
+#define N_OUT_URB 1
+#define IN_BUFLEN 4096
+#define OUT_BUFLEN 128
+
+#define UART_STATE 0x08
+#define UART_STATE_TRANSIENT_MASK 0x74
+#define UART_DCD 0x01
+#define UART_DSR 0x02
+#define UART_BREAK_ERROR 0x04
+#define UART_RING 0x08
+#define UART_FRAME_ERROR 0x10
+#define UART_PARITY_ERROR 0x20
+#define UART_OVERRUN_ERROR 0x40
+#define UART_CTS 0x80
+
+struct option_port_private {
+ spinlock_t lock;
+ u8 line_status;
+ wait_queue_head_t delta_msr_wait;
+ /* Input endpoints and buffer for this port */
+ struct urb *in_urbs[N_IN_URB];
+ char in_buffer[N_IN_URB][IN_BUFLEN];
+ /* Output endpoints and buffer for this port */
+ struct urb *out_urbs[N_OUT_URB];
+ char out_buffer[N_OUT_URB][OUT_BUFLEN];
+ unsigned long out_busy; /* Bit vector of URBs in use */
+
+ /* Settings for the port */
+ int rts_state; /* Handshaking pins (outputs) */
+ int dtr_state;
+ int cts_state; /* Handshaking pins (inputs) */
+ int dsr_state;
+ int dcd_state;
+ int ri_state;
+
+ unsigned long tx_start_time[N_OUT_URB];
+};
+
+static struct usb_device_id id_table [] = {
+// { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_ID) },
+// { USB_DEVICE(ZTExx_VENDOR_ID, ZTExx_PRODUCT_ID) },
+ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E226,
0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF622, 0xff,
0xff, 0xff) },
+ { } /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+
+static void option_rx_throttle(struct usb_serial_port *port)
+{
+ dbg("%s", __FUNCTION__);
+}
+
+static void option_rx_unthrottle(struct usb_serial_port *port)
+{
+ dbg("%s", __FUNCTION__);
+}
+
+static void option_set_termios(struct usb_serial_port *port,
+ struct termios *old)
+{
+ dbg("%s", __FUNCTION__);
+
+ option_send_setup(port);
+}
+
+static int option_tiocmget(struct usb_serial_port *port, struct file *file)
+{
+ unsigned int value;
+ struct option_port_private *portdata;
+
+ portdata = usb_get_serial_port_data(port);
+
+ value = ((portdata->rts_state) ? TIOCM_RTS : 0) |
+ ((portdata->dtr_state) ? TIOCM_DTR : 0) |
+ ((portdata->cts_state) ? TIOCM_CTS : 0) |
+ ((portdata->dsr_state) ? TIOCM_DSR : 0) |
+ ((portdata->dcd_state) ? TIOCM_CAR : 0) |
+ ((portdata->ri_state) ? TIOCM_RNG : 0);
+
+ return value;
+}
+
+static int option_tiocmset(struct usb_serial_port *port, struct file *file,
+ unsigned int set, unsigned int clear)
+{
+ struct option_port_private *portdata;
+
+ portdata = usb_get_serial_port_data(port);
+
+ if (set & TIOCM_RTS)
+ portdata->rts_state = 1;
+ if (set & TIOCM_DTR)
+ portdata->dtr_state = 1;
+
+ if (clear & TIOCM_RTS)
+ portdata->rts_state = 0;
+ if (clear & TIOCM_DTR)
+ portdata->dtr_state = 0;
+
+ return option_send_setup(port);
+}
+
+static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)
+{
+ struct option_port_private *portdata = usb_get_serial_port_data(port);
+ unsigned long flags;
+ unsigned int prevstatus;
+ unsigned int status;
+ unsigned int changed;
+
+ spin_lock_irqsave(&portdata->lock, flags);
+ prevstatus = portdata->line_status;
+ spin_unlock_irqrestore(&portdata->lock, flags);
+
+ while (1) {
+ interruptible_sleep_on(&portdata->delta_msr_wait);
+ /* see if a signal did it */
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+
+ spin_lock_irqsave(&portdata->lock, flags);
+ status = portdata->line_status;
+ spin_unlock_irqrestore(&portdata->lock, flags);
+
+ changed=prevstatus^status;
+
+ if (((arg & TIOCM_RNG) && (changed & UART_RING)) ||
+ ((arg & TIOCM_DSR) && (changed & UART_DSR)) ||
+ ((arg & TIOCM_CD) && (changed & UART_DCD)) ||
+ ((arg & TIOCM_CTS) && (changed & UART_CTS)) ) {
+ return 0;
+ }
+ prevstatus = status;
+ }
+ /* NOTREACHED */
+ return 0;
+}
+
+static int option_ioctl(struct usb_serial_port *port, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int value;
+ dbg("%s (%d) cmd = 0x%04x", __FUNCTION__, port->number, cmd);
+
+ switch (cmd) {
+ case TIOCMGET:
+ dbg("%s (%d) TIOCMGET", __FUNCTION__, port->number);
+ value = option_tiocmget(port, file);
+ if (copy_to_user((unsigned int *)arg, &value,
sizeof(int)))
+ return -EFAULT;
+ return 0;
+ case TIOCMSET:
+ dbg("%s (%d) TIOCMSET", __FUNCTION__, port->number);
+ if (copy_from_user(&value, (unsigned int *)arg,
sizeof(int)))
+ return -EFAULT;
+ /* turn off RTS and DTR and then only turn
+ on what was asked to */
+ return option_tiocmset(port, file, value,
value^(TIOCM_RTS|TIOCM_DTR));
+ case TIOCMIWAIT:
+ dbg("%s (%d) TIOCMIWAIT", __FUNCTION__, port->number);
+ return wait_modem_info(port, arg);
+ default:
+ dbg("%s not supported = 0x%04x", __FUNCTION__, cmd);
+ break;
+ }
+ return -ENOIOCTLCMD;
+}
+
+static void option_break_ctl(struct usb_serial_port *port, int break_state)
+{
+ dbg("%s", __FUNCTION__);
+}
+
+static int option_write(struct usb_serial_port *port, int from_user,
+ const unsigned char *buf, int count)
+{
+ struct option_port_private *portdata;
+ int i;
+ int left, todo;
+ struct urb *this_urb = NULL; /* spurious */
+ int err;
+
+ portdata = usb_get_serial_port_data(port);
+
+ dbg("%s: write (%d chars)", __FUNCTION__, count);
+
+ i = 0;
+ left = count;
+ for (i=0; left > 0 && i < N_OUT_URB; i++) {
+ todo = left;
+ if (todo > OUT_BUFLEN)
+ todo = OUT_BUFLEN;
+
+ this_urb = portdata->out_urbs[i];
+ if (test_and_set_bit(i, &portdata->out_busy)) {
+ if (time_before(jiffies,
+ portdata->tx_start_time[i] + 10 * HZ))
+ continue;
+ usb_unlink_urb(this_urb);
+ continue;
+ }
+ if (this_urb->status != 0)
+ dbg("usb_write %p failed (err=%d)",
+ this_urb, this_urb->status);
+
+ dbg("%s: endpoint %d buf %d", __FUNCTION__,
+ usb_pipeendpoint(this_urb->pipe), i);
+
+ /* send the data */
+ memcpy (this_urb->transfer_buffer, buf, todo);
+ this_urb->transfer_buffer_length = todo;
+
+ this_urb->dev = port->serial->dev;
+ err = usb_submit_urb(this_urb);
+ if (err) {
+ dbg("usb_submit_urb %p (write bulk) failed "
+ "(%d, has %d)", this_urb,
+ err, this_urb->status);
+ clear_bit(i, &portdata->out_busy);
+ continue;
+ }
+ portdata->tx_start_time[i] = jiffies;
+ buf += todo;
+ left -= todo;
+ }
+
+ count -= left;
+ dbg("%s: wrote (did %d)", __FUNCTION__, count);
+ return count;
+}
+
+static void option_indat_callback(struct urb *urb)
+{
+ int i, err;
+ int endpoint;
+ struct usb_serial_port *port;
+ struct option_port_private *portdata = usb_get_serial_port_data(port);
+ struct tty_struct *tty;
+ unsigned char *data = urb->transfer_buffer;
+ unsigned long flags;
+ int status = urb->status;
+ char tty_flag;
+ u8 line_status;
+
+ dbg("%s: %p", __FUNCTION__, urb);
+
+ endpoint = usb_pipeendpoint(urb->pipe);
+ port = (struct usb_serial_port *) urb->context;
+
+ switch (status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d", __FUNCTION__,
+ status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d", __FUNCTION__,
+ status);
+ return;
+ }
+
+ /* get tty_flag from status */
+ tty_flag = TTY_NORMAL;
+
+ spin_lock_irqsave(&portdata->lock, flags);
+ line_status = portdata->line_status;
+ portdata->line_status &= ~UART_STATE_TRANSIENT_MASK;
+ spin_unlock_irqrestore(&portdata->lock, flags);
+ wake_up_interruptible(&portdata->delta_msr_wait);
+
+ /* break takes precedence over parity, */
+ /* which takes precedence over framing errors */
+ if (line_status & UART_BREAK_ERROR )
+ tty_flag = TTY_BREAK;
+ else if (line_status & UART_PARITY_ERROR)
+ tty_flag = TTY_PARITY;
+ else if (line_status & UART_FRAME_ERROR)
+ tty_flag = TTY_FRAME;
+ dbg("%s - tty_flag = %d", __FUNCTION__, tty_flag);
+
+ tty = port->tty;
+ if (tty && urb->actual_length) {
+ /* overrun is special, not associated with a char */
+ if (line_status & UART_OVERRUN_ERROR)
+ tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+
+ for (i = 0; i < urb->actual_length; ++i) {
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+ tty_flip_buffer_push(tty);
+ tty_insert_flip_char(tty, data[i], tty_flag);
+ }
+ tty_flip_buffer_push(tty);
+ }
+
+ /* Resubmit urb so we continue receiving */
+ if (port->open_count && status != -ESHUTDOWN) {
+ err = usb_submit_urb(urb);
+ if (err)
+ printk(KERN_ERR "%s: resubmit read urb failed. "
+ "(%d)", __FUNCTION__, err);
+ }
+
+ if(UART_STATE+1 < urb->actual_length)
+ return;
+
+ //update line status
+ /* Save off the uart status for others to look at */
+ spin_lock_irqsave(&portdata->lock, flags);
+ portdata->line_status = data[UART_STATE];
+ spin_unlock_irqrestore(&portdata->lock, flags);
+ wake_up_interruptible(&portdata->delta_msr_wait);
+
+ return;
+}
+
+static void option_outdat_callback(struct urb *urb)
+{
+ struct usb_serial_port *port;
+ struct option_port_private *portdata;
+ int i;
+
+ dbg("%s", __FUNCTION__);
+
+ port = (struct usb_serial_port *) urb->context;
+ //FIXME
+ //schedule_work(&port->work);
+
+ portdata = usb_get_serial_port_data(port);
+ for (i = 0; i < N_OUT_URB; ++i) {
+ if (portdata->out_urbs[i] == urb) {
+ smp_mb__before_clear_bit();
+ clear_bit(i, &portdata->out_busy);
+ break;
+ }
+ }
+}
+
+static void option_instat_callback(struct urb *urb)
+{
+ int err;
+ int status = urb->status;
+ struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
+ struct option_port_private *portdata = usb_get_serial_port_data(port);
+ struct usb_serial *serial = port->serial;
+
+ dbg("%s", __FUNCTION__);
+ dbg("%s: urb %p port %p has data %p", __FUNCTION__,urb,port,portdata);
+
+ if (status == 0) {
+ struct usb_ctrlrequest *req_pkt =
+ (struct usb_ctrlrequest *)urb->transfer_buffer;
+
+ if (!req_pkt) {
+ dbg("%s: NULL req_pkt\n", __FUNCTION__);
+ return;
+ }
+ if ((req_pkt->bRequestType == 0xA1) &&
+ (req_pkt->bRequest == 0x20)) {
+ int old_dcd_state;
+ unsigned char signals = *((unsigned char *)
+ urb->transfer_buffer +
+ sizeof(struct usb_ctrlrequest));
+
+ dbg("%s: signal x%x", __FUNCTION__, signals);
+
+ old_dcd_state = portdata->dcd_state;
+ portdata->cts_state = 1;
+ portdata->dcd_state = ((signals & 0x01) ? 1 : 0);
+ portdata->dsr_state = ((signals & 0x02) ? 1 : 0);
+ portdata->ri_state = ((signals & 0x08) ? 1 : 0);
+
+ if (port->tty && !C_CLOCAL(port->tty) &&
+ old_dcd_state && !portdata->dcd_state)
+ tty_hangup(port->tty);
+ } else {
+ dbg("%s: type %x req %x", __FUNCTION__,
+ req_pkt->bRequestType,req_pkt->bRequest);
+ }
+ } else
+ dbg("%s: error %d", __FUNCTION__, status);
+
+ /* Resubmit urb so we continue receiving IRQ data */
+ if (status != -ESHUTDOWN) {
+ urb->dev = serial->dev;
+ err = usb_submit_urb(urb);
+ if (err)
+ dbg("%s: resubmit intr urb failed. (%d)",
+ __FUNCTION__, err);
+ }
+
+}
+
+static int option_write_room(struct usb_serial_port *port)
+{
+ struct option_port_private *portdata;
+ int i;
+ int data_len = 0;
+ struct urb *this_urb;
+
+ portdata = usb_get_serial_port_data(port);
+
+ for (i=0; i < N_OUT_URB; i++) {
+ this_urb = portdata->out_urbs[i];
+ if (this_urb && !test_bit(i, &portdata->out_busy))
+ data_len += OUT_BUFLEN;
+ }
+
+ dbg("%s: %d", __FUNCTION__, data_len);
+ return data_len;
+}
+
+static int option_chars_in_buffer(struct usb_serial_port *port)
+{
+ struct option_port_private *portdata;
+ int i;
+ int data_len = 0;
+ struct urb *this_urb;
+
+ portdata = usb_get_serial_port_data(port);
+
+ for (i=0; i < N_OUT_URB; i++) {
+ this_urb = portdata->out_urbs[i];
+ if (this_urb && test_bit(i, &portdata->out_busy))
+ data_len += this_urb->transfer_buffer_length;
+ }
+ dbg("%s: %d", __FUNCTION__, data_len);
+ return data_len;
+}
+
+static int option_open(struct usb_serial_port *port, struct file *filp)
+{
+ struct option_port_private *portdata;
+ struct usb_serial *serial = port->serial;
+ int i, err;
+ struct urb *urb;
+
+ portdata = usb_get_serial_port_data(port);
+
+ dbg("%s", __FUNCTION__);
+
+ /* Set some sane defaults */
+ portdata->rts_state = 1;
+ portdata->dtr_state = 1;
+
+ /* Reset low level data toggle and start reading from endpoints */
+ for (i = 0; i < N_IN_URB; i++) {
+ urb = portdata->in_urbs[i];
+ if (! urb)
+ continue;
+ if (urb->dev != serial->dev) {
+ dbg("%s: dev %p != %p", __FUNCTION__,
+ urb->dev, serial->dev);
+ continue;
+ }
+
+ /*
+ * make sure endpoint data toggle is synchronized with the
+ * device
+ */
+ usb_clear_halt(urb->dev, urb->pipe);
+
+ err = usb_submit_urb(urb);
+ if (err) {
+ dbg("%s: submit urb %d failed (%d) %d",
+ __FUNCTION__, i, err,
+ urb->transfer_buffer_length);
+ }
+ }
+
+ /* Reset low level data toggle on out endpoints */
+ for (i = 0; i < N_OUT_URB; i++) {
+ urb = portdata->out_urbs[i];
+ if (! urb)
+ continue;
+ urb->dev = serial->dev;
+ /* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe), 0); */
+ }
+
+ port->tty->low_latency = 1;
+
+ option_send_setup(port);
+
+ return (0);
+}
+
+static void option_close(struct usb_serial_port *port, struct file *filp)
+{
+ int i, result;
+ struct usb_serial *serial = port->serial;
+ struct option_port_private *portdata;
+
+ dbg("%s", __FUNCTION__);
+ portdata = usb_get_serial_port_data(port);
+
+ portdata->rts_state = 0;
+ portdata->dtr_state = 0;
+
+ if (serial->dev) {
+ option_send_setup(port);
+ dbg("%s - shutting down urbs", __FUNCTION__);
+
+ /* Stop reading/writing urbs */
+ for (i = 0; i < N_IN_URB; i++){
+ result = usb_unlink_urb (portdata->in_urbs[i]);
+ if (result)
+ dbg("%s - usb_unlink_urb (in_urbs)"
+ " failed with reason: %d", __FUNCTION__,
+ result);
+ }
+ for (i = 0; i < N_OUT_URB; i++){
+ result = usb_unlink_urb (portdata->out_urbs[i]);
+ if (result)
+ dbg("%s - usb_unlink_urb (out_urbs)"
+ " failed with reason: %d", __FUNCTION__,
+ result);
+ }
+ }
+ port->tty = NULL;
+}
+
+//Helper functions used by option_setup_urbs
+static struct urb *option_setup_urb(struct usb_serial *serial, int endpoint,
+ int dir, void *ctx, char *buf, int len,
+ void (*callback)(struct urb *))
+{
+ struct urb *urb;
+
+ if (endpoint == -1)
+ return NULL; // endpoint not needed
+
+ urb = usb_alloc_urb(0); // No ISO
+ if (urb == NULL) {
+ dbg("%s: alloc for endpoint %d failed.", __FUNCTION__,
endpoint);
+ return NULL;
+ }
+
+ // Fill URB using supplied data.
+ usb_fill_bulk_urb(urb, serial->dev,
+ usb_sndbulkpipe(serial->dev, endpoint) | dir,
+ buf, len, callback, ctx);
+
+ return urb;
+}
+
+/* Setup urbs */
+static void option_setup_urbs(struct usb_serial *serial)
+{
+ int i,j;
+ struct option_port_private *portdata;
+
+ dbg("%s", __FUNCTION__);
+
+ for (i = 0; i < serial->num_ports; i++) {
+ portdata = usb_get_serial_port_data(&serial->port[i]);
+
+ /* Do indat endpoints first */
+ for (j = 0; j < N_IN_URB; ++j) {
+ portdata->in_urbs[j] = option_setup_urb (serial,
+ (serial->port[i]).bulk_in_endpointAddress, USB_DIR_IN,
&serial->port[i],
+ portdata->in_buffer[j], IN_BUFLEN,
option_indat_callback);
+ }
+
+ /* outdat endpoints */
+ for (j = 0; j < N_OUT_URB; ++j) {
+ portdata->out_urbs[j] = option_setup_urb (serial,
+ (serial->port[i]).bulk_out_endpointAddress, USB_DIR_OUT,
&serial->port[i],
+ portdata->out_buffer[j], OUT_BUFLEN,
option_outdat_callback);
+ }
+ }
+}
+
+
+static int option_send_setup(struct usb_serial_port *port)
+{
+ struct usb_serial *serial = port->serial;
+ struct option_port_private *portdata;
+
+ dbg("%s", __FUNCTION__);
+
+ if (port->number != 0)
+ return 0;
+
+ portdata = usb_get_serial_port_data(port);
+
+ if (port->tty) {
+ int val = 0;
+ if (portdata->dtr_state)
+ val |= 0x01;
+ if (portdata->rts_state)
+ val |= 0x02;
+
+ return usb_control_msg(serial->dev,
+ usb_rcvctrlpipe(serial->dev, 0),
+ 0x22,0x21,val,0,NULL,0,USB_CTRL_SET_TIMEOUT);
+ }
+
+ return 0;
+}
+
+static int option_startup(struct usb_serial *serial)
+{
+ int i, err;
+ struct option_port_private *portdata;
+
+ dbg("%s", __FUNCTION__);
+
+ // Now setup per port private data
+ for (i = 0; i < serial->num_ports; i++) {
+ portdata = kmalloc(sizeof(*portdata), GFP_KERNEL);
+ if (!portdata) {
+ dbg("%s: kmalloc for option_port_private (%d) failed!.",
+ __FUNCTION__, i);
+ return (1);
+ }
+
+ init_waitqueue_head(&portdata->delta_msr_wait);
+ usb_set_serial_port_data(&serial->port[i], portdata);
+
+ if (! (serial->port[i]).interrupt_in_urb)
+ continue;
+ err = usb_submit_urb((serial->port[i]).interrupt_in_urb);
+ if (err)
+ dbg("%s: submit irq_in urb failed %d",
+ __FUNCTION__, err);
+ }
+
+ option_setup_urbs(serial);
+
+ return (0);
+}
+
+static void option_shutdown(struct usb_serial *serial)
+{
+ int i, j;
+ struct option_port_private *portdata;
+
+ dbg("%s", __FUNCTION__);
+
+ /* Stop reading/writing urbs */
+ for (i = 0; i < serial->num_ports; ++i) {
+ portdata = usb_get_serial_port_data(&serial->port[i]);
+ for (j = 0; j < N_IN_URB; j++)
+ usb_unlink_urb (portdata->in_urbs[j]);
+ for (j = 0; j < N_OUT_URB; j++)
+ usb_unlink_urb (portdata->out_urbs[j]);
+ }
+
+ /* Now free them */
+ for (i = 0; i < serial->num_ports; ++i) {
+ portdata = usb_get_serial_port_data(&serial->port[i]);
+
+ for (j = 0; j < N_IN_URB; j++) {
+ if (portdata->in_urbs[j]) {
+ usb_free_urb(portdata->in_urbs[j]);
+ portdata->in_urbs[j] = NULL;
+ }
+ }
+ for (j = 0; j < N_OUT_URB; j++) {
+ if (portdata->out_urbs[j]) {
+ usb_free_urb(portdata->out_urbs[j]);
+ portdata->out_urbs[j] = NULL;
+ }
+ }
+ }
+
+ /* Now free per port private data */
+ for (i = 0; i < serial->num_ports; i++) {
+ kfree(usb_get_serial_port_data(&serial->port[i]));
+ }
+}
+
+static struct usb_driver option_driver = {
+ .name = "option",
+ .probe = usb_serial_probe,
+ .disconnect = usb_serial_disconnect,
+ .id_table = id_table,
+};
+
+static struct usb_serial_device_type option_device = {
+ .owner = THIS_MODULE,
+ .name = "GSM modem (1-port)",
+ .id_table = id_table,
+ .num_interrupt_in = NUM_DONT_CARE,//1
+ .num_bulk_in = NUM_DONT_CARE,//1
+ .num_bulk_out = NUM_DONT_CARE,//1,
+ .num_ports = 1,
+ .open = option_open,
+ .close = option_close,
+ .write = option_write,
+ .ioctl = option_ioctl,
+ .break_ctl = option_break_ctl,
+ .set_termios = option_set_termios,
+// 2.6 .tiocmget = option_tiocmget,
+// 2.6 .tiocmset = option_tiocmset,
+ .throttle = option_rx_throttle,
+ .unthrottle = option_rx_unthrottle,
+ .read_int_callback = option_instat_callback,
+ .write_room = option_write_room,
+ .chars_in_buffer = option_chars_in_buffer,
+ .startup = option_startup,
+ .shutdown = option_shutdown,
+};
+
+static int __init option_init(void)
+{
+ int retval=0;
+ retval = usb_serial_register(&option_device);
+ if (retval)
+ goto failed_1port_device_register;
+ retval = usb_register(&option_driver);
+ if (retval)
+ goto failed_driver_register;
+
+ info(DRIVER_DESC ": " DRIVER_VERSION);
+
+ return 0;
+
+failed_driver_register:
+ usb_serial_deregister (&option_device);
+failed_1port_device_register:
+ return retval;
+}
+
+static void __exit option_exit(void)
+{
+ usb_deregister (&option_driver);
+ usb_serial_deregister (&option_device);
+}
+
+module_init(option_init);
+module_exit(option_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
diff -uNr --exclude='.*' --exclude='*.o' --exclude='*~'
a/drivers/usb/serial/pl2303.c drivers/usb/serial/pl2303.c
--- a/drivers/usb/serial/pl2303.c 2008-05-06 19:00:29.000000000 -0400
+++ b/drivers/usb/serial/pl2303.c 2008-05-22 15:33:28.000000000 -0400
@@ -67,10 +67,10 @@
*/
#define DRIVER_DESC "Prolific PL2303 USB to serial adaptor driver"
-#define PL2303_CLOSING_WAIT (30*HZ)
+#define PL2303_CLOSING_WAIT (300*HZ)
-#define PL2303_BUF_SIZE 1024
-#define PL2303_TMP_BUF_SIZE 1024
+#define PL2303_BUF_SIZE 4096
+#define PL2303_TMP_BUF_SIZE 4096
struct pl2303_buf {
unsigned int buf_size;
@@ -115,6 +115,8 @@
{ USB_DEVICE(DATAPILOT_U2_VENDOR_ID, DATAPILOT_U2_PRODUCT_ID) },
{ USB_DEVICE(BELKIN_VENDOR_ID, BELKIN_PRODUCT_ID) },
{ USB_DEVICE(ALCOR_VENDOR_ID, ALCOR_PRODUCT_ID) },
+ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_ID,
0xff,
0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTExx_VENDOR_ID, ZTExx_PRODUCT_ID,
0xff,
0xff, 0xff) },
{ USB_DEVICE(WS002IN_VENDOR_ID, WS002IN_PRODUCT_ID) },
{ USB_DEVICE(COREGA_VENDOR_ID, COREGA_PRODUCT_ID) },
{ USB_DEVICE(HL340_VENDOR_ID, HL340_PRODUCT_ID) },
@@ -1081,7 +1083,7 @@
u8 line_status;
char tty_flag;
- dbg("%s - port %d", __FUNCTION__, port->number);
+ //dbg("%s - port %d", __FUNCTION__, port->number);
if (!get_usb_serial (port, __FUNCTION__)) {
dbg("%s - bad serial pointer, exiting", __FUNCTION__);
@@ -1164,7 +1166,7 @@
if (port_paranoia_check (port, __FUNCTION__))
return;
-
+
dbg("%s - port %d", __FUNCTION__, port->number);
switch (status) {
@@ -1204,11 +1206,11 @@
/* All of the device info needed for the PL2303 SIO serial converter */
static struct usb_serial_device_type pl2303_device = {
.owner = THIS_MODULE,
- .name = "PL-2303",
+ .name = "PL-2303 HSDPA modem",
.id_table = id_table,
.num_interrupt_in = NUM_DONT_CARE,
- .num_bulk_in = 1,
- .num_bulk_out = 1,
+ .num_bulk_in = NUM_DONT_CARE,
+ .num_bulk_out = NUM_DONT_CARE,
.num_ports = 1,
.open = pl2303_open,
.close = pl2303_close,
diff -uNr --exclude='.*' --exclude='*.o' --exclude='*~'
a/drivers/usb/serial/pl2303.h drivers/usb/serial/pl2303.h
--- a/drivers/usb/serial/pl2303.h 2008-05-06 19:00:29.000000000 -0400
+++ b/drivers/usb/serial/pl2303.h 2008-05-19 08:20:24.000000000 -0400
@@ -112,3 +112,13 @@
/* Y.C. Cable U.S.A., Inc - USB to RS-232 */
#define YCCABLE_VENDOR_ID 0x05ad
#define YCCABLE_PRODUCT_ID 0x0fba
+
+/* Huawei E226 HSDPA card (ID: 12d1:1003) --fmay*/
+#define HUAWEI_VENDOR_ID 0x12d1
+#define HUAWEI_PRODUCT_ID 0x1003
+
+/*ZTExx HSDPA card (0x19d2:0x0001) --fmay*/
+#define ZTExx_VENDOR_ID 0x19d2
+#define ZTExx_PRODUCT_ID 0x0001
+
+
diff -uNr --exclude='.*' --exclude='*.o' --exclude='*~'
a/drivers/usb/serial/usbserial.c drivers/usb/serial/usbserial.c
--- a/drivers/usb/serial/usbserial.c 2008-05-19 11:19:57.000000000 -0400
+++ drivers/usb/serial/usbserial.c 2008-05-21 16:35:48.000000000 -0400
@@ -333,7 +333,7 @@
static __u16 product = 0xffff;
static int maxSize = 0;
-static struct usb_device_id generic_device_ids[10]; /* Initially all zeroes. */
+static struct usb_device_id generic_device_ids[11]; /* Initially all zeroes. */
/* All of the device info needed for the Generic Serial Converter */
static struct usb_serial_device_type generic_device = {
@@ -1558,11 +1558,18 @@
err("No free urbs available");
goto probe_error;
}
+
#ifdef CONFIG_USB_SERIAL_GENERIC
buffer_size = (endpoint->wMaxPacketSize > maxSize) ?
endpoint->wMaxPacketSize : maxSize;
+ endpoint->wMaxPacketSize = buffer_size;
+ //HSDPA--fmay
+ buffer_size = ( (serial->vendor==0x12d1 &&
serial->product==0x1003) ||
+ (serial->vendor==0x19d2 &&
serial->product==0x0001) ) ? 4096 :
buffer_size;
+ //---
#else
buffer_size = endpoint->wMaxPacketSize;
#endif
+ printk("KERNEL DEBUG => USBSERIAL.O buffer_size = %d",
buffer_size);
port->bulk_in_endpointAddress = endpoint->bEndpointAddress;
port->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
if (!port->bulk_in_buffer) {
@@ -1864,6 +1871,14 @@
generic_device_ids[7].idVendor = 0x1410;
generic_device_ids[7].idProduct = 0x1430;
generic_device_ids[7].match_flags = USB_DEVICE_ID_MATCH_VENDOR |
USB_DEVICE_ID_MATCH_PRODUCT;
+ /* Huawei E226 *////--fmay
+ generic_device_ids[8].idVendor = 0x12d1;
+ generic_device_ids[8].idProduct = 0x1003;
+ generic_device_ids[8].match_flags = USB_DEVICE_ID_MATCH_VENDOR |
USB_DEVICE_ID_MATCH_PRODUCT;
+ /* ZTExx MF622 *////--fmay
+ generic_device_ids[9].idVendor = 0x19d2;
+ generic_device_ids[9].idProduct = 0x0001;
+ generic_device_ids[9].match_flags = USB_DEVICE_ID_MATCH_VENDOR |
USB_DEVICE_ID_MATCH_PRODUCT;
/* register our generic driver with ourselves */
usb_serial_register (&generic_device);
#endif
@@ -1934,6 +1949,8 @@
need these symbols to load properly as modules. */
EXPORT_SYMBOL(usb_serial_register);
EXPORT_SYMBOL(usb_serial_deregister);
+EXPORT_SYMBOL(usb_serial_probe);
+EXPORT_SYMBOL(usb_serial_disconnect);
#ifdef USES_EZUSB_FUNCTIONS
EXPORT_SYMBOL(ezusb_writememory);
EXPORT_SYMBOL(ezusb_set_reset);
diff -uNr --exclude='.*' --exclude='*.o' --exclude='*~'
a/drivers/usb/serial/usb-serial.h drivers/usb/serial/usb-serial.h
--- a/drivers/usb/serial/usb-serial.h 2008-05-06 19:00:29.000000000 -0400
+++ b/drivers/usb/serial/usb-serial.h 2008-05-20 17:27:37.000000000 -0400
@@ -245,6 +245,8 @@
extern int usb_serial_register(struct usb_serial_device_type *new_device);
extern void usb_serial_deregister(struct usb_serial_device_type *device);
+extern void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum,
const struct usb_device_id *id);
+extern void usb_serial_disconnect(struct usb_device *dev, void *ptr);
/* determine if we should include the EzUSB loader functions */
#undef USES_EZUSB_FUNCTIONS
diff -uNr --exclude='.*' --exclude='*.o' --exclude='*~'
a/drivers/usb/storage/initializers.c drivers/usb/storage/initializers.c
--- a/drivers/usb/storage/initializers.c 2008-05-06 19:00:29.000000000
-0400
+++ b/drivers/usb/storage/initializers.c 2008-05-19 17:01:18.000000000
-0400
@@ -41,13 +41,34 @@
#include "debug.h"
#include "transport.h"
+
+/* This places the HSDPA with storage devices in multi-port mode *///--fmay
+int usb_stor_hsdpa_init(struct us_data *us)
+{
+ int result = 0;
+ unsigned char data = 0x1;
+ unsigned long flags;
+
+ printk("HSDPA storage init performing...\n");
+
+ result = usb_stor_control_msg(us, usb_sndctrlpipe(us->pusb_dev, 0),
+ USB_REQ_SET_FEATURE, USB_TYPE_STANDARD
| USB_RECIP_DEVICE,
+ 0x01, 0x0, &data, 0x1);
+
+
+
+ US_DEBUGP("HSDPA storage init performing result is %d\n", result);
+
+ return (result ? 0 : -1);
+}
+
/* This places the Shuttle/SCM USB<->SCSI bridge devices in multi-target
* mode */
int usb_stor_euscsi_init(struct us_data *us)
{
unsigned char data = 0x1;
int result;
-
+
US_DEBUGP("Attempting to init eUSCSI bridge...\n");
result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev, 0),
0x0C, USB_RECIP_INTERFACE | USB_TYPE_VENDOR,
diff -uNr --exclude='.*' --exclude='*.o' --exclude='*~'
a/drivers/usb/storage/initializers.h drivers/usb/storage/initializers.h
--- a/drivers/usb/storage/initializers.h 2008-05-06 19:00:29.000000000
-0400
+++ b/drivers/usb/storage/initializers.h 2008-05-21 16:03:32.000000000
-0400
@@ -53,3 +53,7 @@
/* This function is required to activate all four slots on the UCR-61S2B
* flash reader */
int usb_stor_ucr61s2b_init(struct us_data *us);
+
+/* This places the HSDPA devices in multi-port mode --fmay*/
+int usb_stor_hsdpa_init(struct us_data *us);
+
diff -uNr --exclude='.*' --exclude='*.o' --exclude='*~'
a/drivers/usb/storage/unusual_devs.h drivers/usb/storage/unusual_devs.h
--- a/drivers/usb/storage/unusual_devs.h 2008-05-06 19:00:29.000000000
-0400
+++ b/drivers/usb/storage/unusual_devs.h 2008-05-19 09:45:35.000000000
-0400
@@ -1012,3 +1012,18 @@
"Finecam L3",
US_SC_SCSI, US_PR_BULK, NULL,
US_FL_FIX_INQUIRY),
+
+/*HUAWEI HSDPA with storage device support --fmay*/
+UNUSUAL_DEV( 0x12d1, 0x1003, 0x0000, 0x0000,
+ "HSDPA Support",
+ "HSDPA Mass Storage",
+ US_SC_DEVICE, US_PR_DEVICE, usb_stor_hsdpa_init,
+ 0),
+
+/*ZTExx HSDPA with storage device support --fmay*/
+UNUSUAL_DEV( 0x19d2, 0x2000, 0x0000, 0x0000,
+ "HSDPA Support",
+ "HSDPA Mass Storage",
+ US_SC_DEVICE, US_PR_DEVICE,
+ usb_stor_hsdpa_init, 0),
+
diff -uNr --exclude='.*' --exclude='*.o' --exclude='*~'
a/include/linux/tty_flip.h include/linux/tty_flip.h
--- a/include/linux/tty_flip.h 2008-05-21 08:18:31.000000000 -0400
+++ b/include/linux/tty_flip.h 2008-05-20 14:34:48.000000000 -0400
@@ -7,6 +7,9 @@
#define _INLINE_ static __inline__
#endif
+extern int tty_buffer_request_room(struct tty_struct *tty, size_t size);
+extern int tty_insert_flip_string(struct tty_struct *tty, const unsigned
char *chars, size_t size);
+
_INLINE_ void tty_insert_flip_char(struct tty_struct *tty,
unsigned char ch, char flag)
{
diff -uNr --exclude='.*' --exclude='*.o' --exclude='*~' a/include/linux/usb.h
include/linux/usb.h
--- a/include/linux/usb.h 2008-05-19 11:19:54.000000000 -0400
+++ b/include/linux/usb.h 2008-05-21 16:03:19.000000000 -0400
@@ -225,6 +225,8 @@
#define USB_MAXALTSETTING 128 /* Hard limit */
#define USB_MAXINTERFACES 32
#define USB_MAXENDPOINTS 32
+#define USB_CTRL_SET_TIMEOUT 5000
+
/* All standard descriptors have these 2 fields in common */
struct usb_descriptor_header {
@@ -361,6 +363,15 @@
#define USB_INTERFACE_INFO(cl,sc,pr) \
match_flags: USB_DEVICE_ID_MATCH_INT_INFO, bInterfaceClass: (cl),
bInterfaceSubClass: (sc), bInterfaceProtocol: (pr)
+#define USB_DEVICE_AND_INTERFACE_INFO(vend, prod, cl, sc, pr) \
+ match_flags: USB_DEVICE_ID_MATCH_INT_INFO \
+ | USB_DEVICE_ID_MATCH_DEVICE, \
+ idVendor: (vend), \
+ idProduct: (prod), \
+ bInterfaceClass: (cl), \
+ bInterfaceSubClass: (sc), \
+ bInterfaceProtocol: (pr)
+
struct usb_device_id {
/* This bitmask is used to determine which of the following fields
* are to be used for matching.
_______________________________________________
openwrt-devel mailing list
[email protected]
http://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel