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 openwrt-devel@lists.openwrt.org http://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel