upgrade to fedora-2.6.12-1.1398.FC4 + vserver 2.0.rc7
[linux-2.6.git] / drivers / usb / class / cdc-acm.c
index cf7fcf8..6d1f9b6 100644 (file)
@@ -60,6 +60,7 @@
 #include <linux/smp_lock.h>
 #include <asm/uaccess.h>
 #include <linux/usb.h>
+#include <linux/usb_cdc.h>
 #include <asm/byteorder.h>
 #include <asm/unaligned.h>
 
@@ -89,7 +90,7 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int
        int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
                request, USB_RT_ACM, value,
                acm->control->altsetting[0].desc.bInterfaceNumber,
-               buf, len, HZ * 5);
+               buf, len, 5000);
        dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", request, value, len, retval);
        return retval < 0 ? retval : 0;
 }
@@ -97,9 +98,12 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int
 /* devices aren't required to support these requests.
  * the cdc acm descriptor tells whether they do...
  */
-#define acm_set_control(acm, control)  acm_ctrl_msg(acm, ACM_REQ_SET_CONTROL, control, NULL, 0)
-#define acm_set_line(acm, line)                acm_ctrl_msg(acm, ACM_REQ_SET_LINE, 0, line, sizeof(struct acm_line))
-#define acm_send_break(acm, ms)                acm_ctrl_msg(acm, ACM_REQ_SEND_BREAK, ms, NULL, 0)
+#define acm_set_control(acm, control) \
+       acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0)
+#define acm_set_line(acm, line) \
+       acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line))
+#define acm_send_break(acm, ms) \
+       acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
 
 /*
  * Interrupt handlers for various ACM device responses
@@ -109,7 +113,7 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int
 static void acm_ctrl_irq(struct urb *urb, struct pt_regs *regs)
 {
        struct acm *acm = urb->context;
-       struct usb_ctrlrequest *dr = urb->transfer_buffer;
+       struct usb_cdc_notification *dr = urb->transfer_buffer;
        unsigned char *data;
        int newctrl;
        int status;
@@ -133,14 +137,14 @@ static void acm_ctrl_irq(struct urb *urb, struct pt_regs *regs)
                goto exit;
 
        data = (unsigned char *)(dr + 1);
-       switch (dr->bRequest) {
+       switch (dr->bNotificationType) {
 
-               case ACM_IRQ_NETWORK:
+               case USB_CDC_NOTIFY_NETWORK_CONNECTION:
 
                        dbg("%s network", dr->wValue ? "connected to" : "disconnected from");
                        break;
 
-               case ACM_IRQ_LINE_STATE:
+               case USB_CDC_NOTIFY_SERIAL_STATE:
 
                        newctrl = le16_to_cpu(get_unaligned((__le16 *) data));
 
@@ -160,8 +164,9 @@ static void acm_ctrl_irq(struct urb *urb, struct pt_regs *regs)
                        break;
 
                default:
-                       dbg("unknown control event received: request %d index %d len %d data0 %d data1 %d",
-                               dr->bRequest, dr->wIndex, dr->wLength, data[0], data[1]);
+                       dbg("unknown notification %d received: index %d len %d data0 %d data1 %d",
+                               dr->bNotificationType, dr->wIndex,
+                               dr->wLength, data[0], data[1]);
                        break;
        }
 exit:
@@ -261,18 +266,24 @@ static void acm_softint(void *private)
 
 static int acm_tty_open(struct tty_struct *tty, struct file *filp)
 {
-       struct acm *acm = acm_table[tty->index];
+       struct acm *acm;
+       int rv = -EINVAL;
        dbg("Entering acm_tty_open.\n");
+       
+       down(&open_sem);
 
+       acm = acm_table[tty->index];
        if (!acm || !acm->dev)
-               return -EINVAL;
+               goto err_out;
+       else
+               rv = 0;
 
        tty->driver_data = acm;
        acm->tty = tty;
 
-        down(&open_sem);
 
-       if (acm->used) {
+
+       if (acm->used++) {
                goto done;
         }
 
@@ -296,15 +307,16 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
        tty->low_latency = 1;
 
 done:
-       acm->used++;
+err_out:
        up(&open_sem);
-       return 0;
+       return rv;
 
 full_bailout:
        usb_kill_urb(acm->readurb);
 bail_out_and_unlink:
        usb_kill_urb(acm->ctrlurb);
 bail_out:
+       acm->used--;
        up(&open_sem);
        return -EIO;
 }
@@ -478,32 +490,34 @@ static void acm_tty_set_termios(struct tty_struct *tty, struct termios *termios_
 {
        struct acm *acm = tty->driver_data;
        struct termios *termios = tty->termios;
-       struct acm_line newline;
+       struct usb_cdc_line_coding newline;
        int newctrl = acm->ctrlout;
 
        if (!ACM_READY(acm))
                return;
 
-       newline.speed = cpu_to_le32p(acm_tty_speed +
+       newline.dwDTERate = cpu_to_le32p(acm_tty_speed +
                (termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0));
-       newline.stopbits = termios->c_cflag & CSTOPB ? 2 : 0;
-       newline.parity = termios->c_cflag & PARENB ?
+       newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
+       newline.bParityType = termios->c_cflag & PARENB ?
                (termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
-       newline.databits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
+       newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
 
        acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
 
-       if (!newline.speed) {
-               newline.speed = acm->line.speed;
+       if (!newline.dwDTERate) {
+               newline.dwDTERate = acm->line.dwDTERate;
                newctrl &= ~ACM_CTRL_DTR;
        } else  newctrl |=  ACM_CTRL_DTR;
 
        if (newctrl != acm->ctrlout)
                acm_set_control(acm, acm->ctrlout = newctrl);
 
-       if (memcmp(&acm->line, &newline, sizeof(struct acm_line))) {
-               memcpy(&acm->line, &newline, sizeof(struct acm_line));
-               dbg("set line: %d %d %d %d", newline.speed, newline.stopbits, newline.parity, newline.databits);
+       if (memcmp(&acm->line, &newline, sizeof newline)) {
+               memcpy(&acm->line, &newline, sizeof newline);
+               dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate),
+                       newline.bCharFormat, newline.bParityType,
+                       newline.bDataBits);
                acm_set_line(acm, &acm->line);
        }
 }
@@ -515,7 +529,7 @@ static void acm_tty_set_termios(struct tty_struct *tty, struct termios *termios_
 static int acm_probe (struct usb_interface *intf,
                      const struct usb_device_id *id)
 {
-       struct union_desc *union_header = NULL;
+       struct usb_cdc_union_desc *union_header = NULL;
        char *buffer = intf->altsetting->extra;
        int buflen = intf->altsetting->extralen;
        struct usb_interface *control_interface;
@@ -532,45 +546,56 @@ static int acm_probe (struct usb_interface *intf,
        u8 call_management_function = 0;
        int call_interface_num = -1;
        int data_interface_num;
-
+       unsigned long quirks;
+
+       /* handle quirks deadly to normal probing*/
+       quirks = (unsigned long)id->driver_info;
+       if (quirks == NO_UNION_NORMAL) {
+               data_interface = usb_ifnum_to_if(usb_dev, 1);
+               control_interface = usb_ifnum_to_if(usb_dev, 0);
+               goto skip_normal_probe;
+       }
+       
+       /* normal probing*/
        if (!buffer) {
-               err("Wierd descriptor references");
+               err("Wierd descriptor references\n");
                return -EINVAL;
        }
 
        if (!buflen) {
                if (intf->cur_altsetting->endpoint->extralen && intf->cur_altsetting->endpoint->extra) {
-                       dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint");
+                       dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint\n");
                        buflen = intf->cur_altsetting->endpoint->extralen;
                        buffer = intf->cur_altsetting->endpoint->extra;
                } else {
-                       err("Zero length descriptor references");
+                       err("Zero length descriptor references\n");
                        return -EINVAL;
                }
        }
 
        while (buflen > 0) {
                if (buffer [1] != USB_DT_CS_INTERFACE) {
-                       err("skipping garbage");
+                       err("skipping garbage\n");
                        goto next_desc;
                }
 
                switch (buffer [2]) {
-                       case CDC_UNION_TYPE: /* we've found it */
+                       case USB_CDC_UNION_TYPE: /* we've found it */
                                if (union_header) {
                                        err("More than one union descriptor, skipping ...");
                                        goto next_desc;
                                }
-                               union_header = (struct union_desc *)buffer;
+                               union_header = (struct usb_cdc_union_desc *)
+                                                       buffer;
                                break;
-                       case CDC_COUNTRY_TYPE: /* maybe somehow export */
+                       case USB_CDC_COUNTRY_TYPE: /* maybe somehow export */
                                break; /* for now we ignore it */
-                       case CDC_HEADER_TYPE: /* maybe check version */ 
+                       case USB_CDC_HEADER_TYPE: /* maybe check version */ 
                                break; /* for now we ignore it */ 
-                       case CDC_AC_MANAGEMENT_TYPE:
+                       case USB_CDC_ACM_TYPE:
                                ac_management_function = buffer[3];
                                break;
-                       case CDC_CALL_MANAGEMENT_TYPE:
+                       case USB_CDC_CALL_MANAGEMENT_TYPE:
                                call_management_function = buffer[3];
                                call_interface_num = buffer[4];
                                if ((call_management_function & 3) != 3)
@@ -604,13 +629,10 @@ next_desc:
                }
        }
        
-               if (data_interface_num != call_interface_num)
-                       dev_dbg(&intf->dev,"Seperate call control interface. That is not fully supported.");
+       if (data_interface_num != call_interface_num)
+               dev_dbg(&intf->dev,"Seperate call control interface. That is not fully supported.\n");
 
-       if (usb_interface_claimed(data_interface)) { /* valid in this context */
-               dev_dbg(&intf->dev,"The data interface isn't available\n");
-               return -EBUSY;
-       }
+skip_normal_probe:
 
        /*workaround for switched interfaces */
        if (data_interface->cur_altsetting->desc.bInterfaceClass != CDC_DATA_INTERFACE_TYPE) {
@@ -625,6 +647,13 @@ next_desc:
                        return -EINVAL;
                }
        }
+       
+       if (usb_interface_claimed(data_interface)) { /* valid in this context */
+               dev_dbg(&intf->dev,"The data interface isn't available\n");
+               return -EBUSY;
+       }
+
+
        if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
                return -EINVAL;
 
@@ -657,9 +686,9 @@ next_desc:
        }
        memset(acm, 0, sizeof(struct acm));
 
-       ctrlsize = epctrl->wMaxPacketSize;
-       readsize = epread->wMaxPacketSize;
-       acm->writesize = epwrite->wMaxPacketSize;
+       ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
+       readsize = le16_to_cpu(epread->wMaxPacketSize);
+       acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize);
        acm->control = control_interface;
        acm->data = data_interface;
        acm->minor = minor;
@@ -729,8 +758,8 @@ next_desc:
 
        acm_set_control(acm, acm->ctrlout);
 
-       acm->line.speed = cpu_to_le32(9600);
-       acm->line.databits = 8;
+       acm->line.dwDTERate = cpu_to_le32(9600);
+       acm->line.bDataBits = 8;
        acm_set_line(acm, &acm->line);
 
        usb_driver_claim_interface(&acm_driver, data_interface, acm);
@@ -805,15 +834,25 @@ static void acm_disconnect(struct usb_interface *intf)
  */
 
 static struct usb_device_id acm_ids[] = {
+       /* quirky and broken devices */
+       { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
+       .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
+       },
        /* control interfaces with various AT-command sets */
-       { USB_INTERFACE_INFO(USB_CLASS_COMM, 2, 1) },
-       { USB_INTERFACE_INFO(USB_CLASS_COMM, 2, 2) },
-       { USB_INTERFACE_INFO(USB_CLASS_COMM, 2, 3) },
-       { USB_INTERFACE_INFO(USB_CLASS_COMM, 2, 4) },
-       { USB_INTERFACE_INFO(USB_CLASS_COMM, 2, 5) },
-       { USB_INTERFACE_INFO(USB_CLASS_COMM, 2, 6) },
-
-       /* NOTE:  COMM/2/0xff is likely MSFT RNDIS ... NOT a modem!! */
+       { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
+               USB_CDC_ACM_PROTO_AT_V25TER) },
+       { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
+               USB_CDC_ACM_PROTO_AT_PCCA101) },
+       { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
+               USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
+       { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
+               USB_CDC_ACM_PROTO_AT_GSM) },
+       { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
+               USB_CDC_ACM_PROTO_AT_3G ) },
+       { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
+               USB_CDC_ACM_PROTO_AT_CDMA) },
+
+       /* NOTE:  COMM/ACM/0xff is likely MSFT RNDIS ... NOT a modem!! */
        { }
 };