]> rtime.felk.cvut.cz Git - linux-imx.git/blobdiff - drivers/usb/serial/mos7840.c
Merge tag 'fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty...
[linux-imx.git] / drivers / usb / serial / mos7840.c
index 603fb70dde8005ddfcf9743e2f3f8d0acd616bb5..d953d674f22295a0aac144a7f9958c8dbb4df6a6 100644 (file)
 #define LED_ON_MS      500
 #define LED_OFF_MS     500
 
-static int device_type;
+enum mos7840_flag {
+       MOS7840_FLAG_CTRL_BUSY,
+       MOS7840_FLAG_LED_BUSY,
+};
 
 static const struct usb_device_id id_table[] = {
        {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7840)},
@@ -238,9 +241,12 @@ struct moschip_port {
 
        /* For device(s) with LED indicator */
        bool has_led;
-       bool led_flag;
        struct timer_list led_timer1;   /* Timer for LED on */
        struct timer_list led_timer2;   /* Timer for LED off */
+       struct urb *led_urb;
+       struct usb_ctrlrequest *led_dr;
+
+       unsigned long flags;
 };
 
 /*
@@ -460,10 +466,10 @@ static void mos7840_control_callback(struct urb *urb)
        case -ESHUTDOWN:
                /* this urb is terminated, clean up */
                dev_dbg(dev, "%s - urb shutting down with status: %d\n", __func__, status);
-               return;
+               goto out;
        default:
                dev_dbg(dev, "%s - nonzero urb status received: %d\n", __func__, status);
-               return;
+               goto out;
        }
 
        dev_dbg(dev, "%s urb buffer size is %d\n", __func__, urb->actual_length);
@@ -476,6 +482,8 @@ static void mos7840_control_callback(struct urb *urb)
                mos7840_handle_new_msr(mos7840_port, regval);
        else if (mos7840_port->MsrLsr == 1)
                mos7840_handle_new_lsr(mos7840_port, regval);
+out:
+       clear_bit_unlock(MOS7840_FLAG_CTRL_BUSY, &mos7840_port->flags);
 }
 
 static int mos7840_get_reg(struct moschip_port *mcs, __u16 Wval, __u16 reg,
@@ -486,6 +494,9 @@ static int mos7840_get_reg(struct moschip_port *mcs, __u16 Wval, __u16 reg,
        unsigned char *buffer = mcs->ctrl_buf;
        int ret;
 
+       if (test_and_set_bit_lock(MOS7840_FLAG_CTRL_BUSY, &mcs->flags))
+               return -EBUSY;
+
        dr->bRequestType = MCS_RD_RTYPE;
        dr->bRequest = MCS_RDREQ;
        dr->wValue = cpu_to_le16(Wval); /* 0 */
@@ -497,6 +508,9 @@ static int mos7840_get_reg(struct moschip_port *mcs, __u16 Wval, __u16 reg,
                             mos7840_control_callback, mcs);
        mcs->control_urb->transfer_buffer_length = 2;
        ret = usb_submit_urb(mcs->control_urb, GFP_ATOMIC);
+       if (ret)
+               clear_bit_unlock(MOS7840_FLAG_CTRL_BUSY, &mcs->flags);
+
        return ret;
 }
 
@@ -523,7 +537,7 @@ static void mos7840_set_led_async(struct moschip_port *mcs, __u16 wval,
                                __u16 reg)
 {
        struct usb_device *dev = mcs->port->serial->dev;
-       struct usb_ctrlrequest *dr = mcs->dr;
+       struct usb_ctrlrequest *dr = mcs->led_dr;
 
        dr->bRequestType = MCS_WR_RTYPE;
        dr->bRequest = MCS_WRREQ;
@@ -531,10 +545,10 @@ static void mos7840_set_led_async(struct moschip_port *mcs, __u16 wval,
        dr->wIndex = cpu_to_le16(reg);
        dr->wLength = cpu_to_le16(0);
 
-       usb_fill_control_urb(mcs->control_urb, dev, usb_sndctrlpipe(dev, 0),
+       usb_fill_control_urb(mcs->led_urb, dev, usb_sndctrlpipe(dev, 0),
                (unsigned char *)dr, NULL, 0, mos7840_set_led_callback, NULL);
 
-       usb_submit_urb(mcs->control_urb, GFP_ATOMIC);
+       usb_submit_urb(mcs->led_urb, GFP_ATOMIC);
 }
 
 static void mos7840_set_led_sync(struct usb_serial_port *port, __u16 reg,
@@ -560,7 +574,19 @@ static void mos7840_led_flag_off(unsigned long arg)
 {
        struct moschip_port *mcs = (struct moschip_port *) arg;
 
-       mcs->led_flag = false;
+       clear_bit_unlock(MOS7840_FLAG_LED_BUSY, &mcs->flags);
+}
+
+static void mos7840_led_activity(struct usb_serial_port *port)
+{
+       struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
+
+       if (test_and_set_bit_lock(MOS7840_FLAG_LED_BUSY, &mos7840_port->flags))
+               return;
+
+       mos7840_set_led_async(mos7840_port, 0x0301, MODEM_CONTROL_REGISTER);
+       mod_timer(&mos7840_port->led_timer1,
+                               jiffies + msecs_to_jiffies(LED_ON_MS));
 }
 
 /*****************************************************************************
@@ -758,14 +784,8 @@ static void mos7840_bulk_in_callback(struct urb *urb)
                return;
        }
 
-       /* Turn on LED */
-       if (mos7840_port->has_led && !mos7840_port->led_flag) {
-               mos7840_port->led_flag = true;
-               mos7840_set_led_async(mos7840_port, 0x0301,
-                                       MODEM_CONTROL_REGISTER);
-               mod_timer(&mos7840_port->led_timer1,
-                               jiffies + msecs_to_jiffies(LED_ON_MS));
-       }
+       if (mos7840_port->has_led)
+               mos7840_led_activity(port);
 
        mos7840_port->read_urb_busy = true;
        retval = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
@@ -816,18 +836,6 @@ static void mos7840_bulk_out_data_callback(struct urb *urb)
 /************************************************************************/
 /*       D R I V E R  T T Y  I N T E R F A C E  F U N C T I O N S       */
 /************************************************************************/
-#ifdef MCSSerialProbe
-static int mos7840_serial_probe(struct usb_serial *serial,
-                               const struct usb_device_id *id)
-{
-
-       /*need to implement the mode_reg reading and updating\
-          structures usb_serial_ device_type\
-          (i.e num_ports, num_bulkin,bulkout etc) */
-       /* Also we can update the changes  attach */
-       return 1;
-}
-#endif
 
 /*****************************************************************************
  * mos7840_open
@@ -1454,13 +1462,8 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port,
        data1 = urb->transfer_buffer;
        dev_dbg(&port->dev, "bulkout endpoint is %d\n", port->bulk_out_endpointAddress);
 
-       /* Turn on LED */
-       if (mos7840_port->has_led && !mos7840_port->led_flag) {
-               mos7840_port->led_flag = true;
-               mos7840_set_led_sync(port, MODEM_CONTROL_REGISTER, 0x0301);
-               mod_timer(&mos7840_port->led_timer1,
-                               jiffies + msecs_to_jiffies(LED_ON_MS));
-       }
+       if (mos7840_port->has_led)
+               mos7840_led_activity(port);
 
        /* send it down the pipe */
        status = usb_submit_urb(urb, GFP_ATOMIC);
@@ -2187,38 +2190,48 @@ static int mos7810_check(struct usb_serial *serial)
        return 0;
 }
 
-static int mos7840_calc_num_ports(struct usb_serial *serial)
+static int mos7840_probe(struct usb_serial *serial,
+                               const struct usb_device_id *id)
 {
-       __u16 data = 0x00;
+       u16 product = serial->dev->descriptor.idProduct;
        u8 *buf;
-       int mos7840_num_ports;
+       int device_type;
+
+       if (product == MOSCHIP_DEVICE_ID_7810 ||
+               product == MOSCHIP_DEVICE_ID_7820) {
+               device_type = product;
+               goto out;
+       }
 
        buf = kzalloc(VENDOR_READ_LENGTH, GFP_KERNEL);
-       if (buf) {
-               usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+       if (!buf)
+               return -ENOMEM;
+
+       usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
                        MCS_RDREQ, MCS_RD_RTYPE, 0, GPIO_REGISTER, buf,
                        VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT);
-               data = *buf;
-               kfree(buf);
-       }
 
-       if (serial->dev->descriptor.idProduct == MOSCHIP_DEVICE_ID_7810 ||
-               serial->dev->descriptor.idProduct == MOSCHIP_DEVICE_ID_7820) {
-               device_type = serial->dev->descriptor.idProduct;
-       } else {
-               /* For a MCS7840 device GPIO0 must be set to 1 */
-               if ((data & 0x01) == 1)
-                       device_type = MOSCHIP_DEVICE_ID_7840;
-               else if (mos7810_check(serial))
-                       device_type = MOSCHIP_DEVICE_ID_7810;
-               else
-                       device_type = MOSCHIP_DEVICE_ID_7820;
-       }
+       /* For a MCS7840 device GPIO0 must be set to 1 */
+       if (buf[0] & 0x01)
+               device_type = MOSCHIP_DEVICE_ID_7840;
+       else if (mos7810_check(serial))
+               device_type = MOSCHIP_DEVICE_ID_7810;
+       else
+               device_type = MOSCHIP_DEVICE_ID_7820;
+
+       kfree(buf);
+out:
+       usb_set_serial_data(serial, (void *)(unsigned long)device_type);
+
+       return 0;
+}
+
+static int mos7840_calc_num_ports(struct usb_serial *serial)
+{
+       int device_type = (unsigned long)usb_get_serial_data(serial);
+       int mos7840_num_ports;
 
        mos7840_num_ports = (device_type >> 4) & 0x000F;
-       serial->num_bulk_in = mos7840_num_ports;
-       serial->num_bulk_out = mos7840_num_ports;
-       serial->num_ports = mos7840_num_ports;
 
        return mos7840_num_ports;
 }
@@ -2226,6 +2239,7 @@ static int mos7840_calc_num_ports(struct usb_serial *serial)
 static int mos7840_port_probe(struct usb_serial_port *port)
 {
        struct usb_serial *serial = port->serial;
+       int device_type = (unsigned long)usb_get_serial_data(serial);
        struct moschip_port *mos7840_port;
        int status;
        int pnum;
@@ -2401,6 +2415,14 @@ static int mos7840_port_probe(struct usb_serial_port *port)
        if (device_type == MOSCHIP_DEVICE_ID_7810) {
                mos7840_port->has_led = true;
 
+               mos7840_port->led_urb = usb_alloc_urb(0, GFP_KERNEL);
+               mos7840_port->led_dr = kmalloc(sizeof(*mos7840_port->led_dr),
+                                                               GFP_KERNEL);
+               if (!mos7840_port->led_urb || !mos7840_port->led_dr) {
+                       status = -ENOMEM;
+                       goto error;
+               }
+
                init_timer(&mos7840_port->led_timer1);
                mos7840_port->led_timer1.function = mos7840_led_off;
                mos7840_port->led_timer1.expires =
@@ -2413,8 +2435,6 @@ static int mos7840_port_probe(struct usb_serial_port *port)
                        jiffies + msecs_to_jiffies(LED_OFF_MS);
                mos7840_port->led_timer2.data = (unsigned long)mos7840_port;
 
-               mos7840_port->led_flag = false;
-
                /* Turn off LED */
                mos7840_set_led_sync(port, MODEM_CONTROL_REGISTER, 0x0300);
        }
@@ -2436,6 +2456,8 @@ out:
        }
        return 0;
 error:
+       kfree(mos7840_port->led_dr);
+       usb_free_urb(mos7840_port->led_urb);
        kfree(mos7840_port->dr);
        kfree(mos7840_port->ctrl_buf);
        usb_free_urb(mos7840_port->control_urb);
@@ -2456,6 +2478,10 @@ static int mos7840_port_remove(struct usb_serial_port *port)
 
                del_timer_sync(&mos7840_port->led_timer1);
                del_timer_sync(&mos7840_port->led_timer2);
+
+               usb_kill_urb(mos7840_port->led_urb);
+               usb_free_urb(mos7840_port->led_urb);
+               kfree(mos7840_port->led_dr);
        }
        usb_kill_urb(mos7840_port->control_urb);
        usb_free_urb(mos7840_port->control_urb);
@@ -2482,9 +2508,7 @@ static struct usb_serial_driver moschip7840_4port_device = {
        .throttle = mos7840_throttle,
        .unthrottle = mos7840_unthrottle,
        .calc_num_ports = mos7840_calc_num_ports,
-#ifdef MCSSerialProbe
-       .probe = mos7840_serial_probe,
-#endif
+       .probe = mos7840_probe,
        .ioctl = mos7840_ioctl,
        .set_termios = mos7840_set_termios,
        .break_ctl = mos7840_break,