Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6
[sfrench/cifs-2.6.git] / drivers / usb / serial / garmin_gps.c
index 04bd3b7a298516a4f4409033df182ddfaf54ea80..d74e43d69230b5ea1d7b75a4b599f9052f658004 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Garmin GPS driver
  *
- * Copyright (C) 2006 Hermann Kneissel herkne@users.sourceforge.net
+ * Copyright (C) 2006,2007 Hermann Kneissel herkne@users.sourceforge.net
  *
  * The latest version of the driver can be found at
  * http://sourceforge.net/projects/garmin-gps/
@@ -34,6 +34,7 @@
 #include <linux/module.h>
 #include <linux/spinlock.h>
 #include <asm/uaccess.h>
+#include <asm/atomic.h>
 #include <linux/usb.h>
 #include <linux/usb/serial.h>
 
@@ -52,7 +53,7 @@ static int debug = 0;
  */
 
 #define VERSION_MAJOR  0
-#define VERSION_MINOR  28
+#define VERSION_MINOR  31
 
 #define _STR(s) #s
 #define _DRIVER_VERSION(a,b) "v" _STR(a) "." _STR(b)
@@ -141,6 +142,8 @@ struct garmin_data {
        __u8   inbuffer [GPS_IN_BUFSIZ];  /* tty -> usb */
        __u8   outbuffer[GPS_OUT_BUFSIZ]; /* usb -> tty */
        __u8   privpkt[4*6];
+       atomic_t req_count;
+       atomic_t resp_count;
        spinlock_t lock;
        struct list_head pktlist;
 };
@@ -171,8 +174,6 @@ struct garmin_data {
 #define CLEAR_HALT_REQUIRED       0x0001
 
 #define FLAGS_QUEUING             0x0100
-#define FLAGS_APP_RESP_SEEN       0x0200
-#define FLAGS_APP_REQ_SEEN        0x0400
 #define FLAGS_DROP_DATA           0x0800
 
 #define FLAGS_GSP_SKIP            0x1000
@@ -186,7 +187,8 @@ struct garmin_data {
 /* function prototypes */
 static void gsp_next_packet(struct garmin_data * garmin_data_p);
 static int  garmin_write_bulk(struct usb_serial_port *port,
-                            const unsigned char *buf, int count);
+                            const unsigned char *buf, int count,
+                            int dismiss_ack);
 
 /* some special packets to be send or received */
 static unsigned char const GARMIN_START_SESSION_REQ[]
@@ -233,9 +235,7 @@ static struct usb_driver garmin_driver = {
 
 static inline int noResponseFromAppLayer(struct garmin_data * garmin_data_p)
 {
-       return ((garmin_data_p->flags
-                               & (FLAGS_APP_REQ_SEEN|FLAGS_APP_RESP_SEEN))
-               == FLAGS_APP_REQ_SEEN);
+       return atomic_read(&garmin_data_p->req_count) == atomic_read(&garmin_data_p->resp_count);
 }
 
 
@@ -463,7 +463,7 @@ static int gsp_rec_packet(struct garmin_data * garmin_data_p, int count)
        usbdata[2] = __cpu_to_le32(size);
 
        garmin_write_bulk (garmin_data_p->port, garmin_data_p->inbuffer,
-                          GARMIN_PKTHDR_LENGTH+size);
+                          GARMIN_PKTHDR_LENGTH+size, 0);
 
        /* if this was an abort-transfer command, flush all
           queued data. */
@@ -818,7 +818,7 @@ static int nat_receive(struct garmin_data * garmin_data_p,
                        if (garmin_data_p->insize >= len) {
                                garmin_write_bulk (garmin_data_p->port,
                                                   garmin_data_p->inbuffer,
-                                                  len);
+                                                  len, 0);
                                garmin_data_p->insize = 0;
 
                                /* if this was an abort-transfer command,
@@ -893,10 +893,11 @@ static int garmin_clear(struct garmin_data * garmin_data_p)
 
        struct usb_serial_port *port = garmin_data_p->port;
 
-       if (port != NULL && garmin_data_p->flags & FLAGS_APP_RESP_SEEN) {
+       if (port != NULL && atomic_read(&garmin_data_p->resp_count)) {
                /* send a terminate command */
                status = garmin_write_bulk(port, GARMIN_STOP_TRANSFER_REQ,
-                                          sizeof(GARMIN_STOP_TRANSFER_REQ));
+                                          sizeof(GARMIN_STOP_TRANSFER_REQ),
+                                          1);
        }
 
        /* flush all queued data */
@@ -939,7 +940,8 @@ static int garmin_init_session(struct usb_serial_port *port)
                dbg("%s - starting session ...", __FUNCTION__);
                garmin_data_p->state = STATE_ACTIVE;
                status = garmin_write_bulk(port, GARMIN_START_SESSION_REQ,
-                                          sizeof(GARMIN_START_SESSION_REQ));
+                                          sizeof(GARMIN_START_SESSION_REQ),
+                                          0);
 
                if (status >= 0) {
 
@@ -950,7 +952,8 @@ static int garmin_init_session(struct usb_serial_port *port)
                        /* not needed, but the win32 driver does it too ... */
                        status = garmin_write_bulk(port,
                                                   GARMIN_START_SESSION_REQ2,
-                                                  sizeof(GARMIN_START_SESSION_REQ2));
+                                                  sizeof(GARMIN_START_SESSION_REQ2),
+                                                  0);
                        if (status >= 0) {
                                status = 0;
                                spin_lock_irqsave(&garmin_data_p->lock, flags);
@@ -987,6 +990,8 @@ static int garmin_open (struct usb_serial_port *port, struct file *filp)
        garmin_data_p->mode  = initial_mode;
        garmin_data_p->count = 0;
        garmin_data_p->flags = 0;
+       atomic_set(&garmin_data_p->req_count, 0);
+       atomic_set(&garmin_data_p->resp_count, 0);
        spin_unlock_irqrestore(&garmin_data_p->lock, flags);
 
        /* shutdown any bulk reads that might be going on */
@@ -1015,19 +1020,26 @@ static void garmin_close (struct usb_serial_port *port, struct file * filp)
        if (!serial)
                return;
 
-       garmin_clear(garmin_data_p);
+       mutex_lock(&port->serial->disc_mutex);
+       if (!port->serial->disconnected)
+               garmin_clear(garmin_data_p);
 
        /* shutdown our urbs */
        usb_kill_urb (port->read_urb);
        usb_kill_urb (port->write_urb);
 
-       if (noResponseFromAppLayer(garmin_data_p) ||
-           ((garmin_data_p->flags & CLEAR_HALT_REQUIRED) != 0)) {
-               process_resetdev_request(port);
-               garmin_data_p->state = STATE_RESET;
+       if (!port->serial->disconnected) {
+               if (noResponseFromAppLayer(garmin_data_p) ||
+                   ((garmin_data_p->flags & CLEAR_HALT_REQUIRED) != 0)) {
+                       process_resetdev_request(port);
+                       garmin_data_p->state = STATE_RESET;
+               } else {
+                       garmin_data_p->state = STATE_DISCONNECTED;
+               }
        } else {
                garmin_data_p->state = STATE_DISCONNECTED;
        }
+       mutex_unlock(&port->serial->disc_mutex);
 }
 
 
@@ -1035,28 +1047,39 @@ static void garmin_write_bulk_callback (struct urb *urb)
 {
        unsigned long flags;
        struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
-       struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
        int status = urb->status;
 
-       /* free up the transfer buffer, as usb_free_urb() does not do this */
-       kfree (urb->transfer_buffer);
+       if (port) {
+               struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
 
-       dbg("%s - port %d", __FUNCTION__, port->number);
+               dbg("%s - port %d", __FUNCTION__, port->number);
 
-       if (status) {
-               dbg("%s - nonzero write bulk status received: %d",
-                       __FUNCTION__, status);
-               spin_lock_irqsave(&garmin_data_p->lock, flags);
-               garmin_data_p->flags |= CLEAR_HALT_REQUIRED;
-               spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+               if (GARMIN_LAYERID_APPL == getLayerId(urb->transfer_buffer)
+                   && (garmin_data_p->mode == MODE_GARMIN_SERIAL))  {
+                       gsp_send_ack(garmin_data_p, ((__u8 *)urb->transfer_buffer)[4]);
+               }
+
+               if (status) {
+                       dbg("%s - nonzero write bulk status received: %d",
+                           __FUNCTION__, urb->status);
+                       spin_lock_irqsave(&garmin_data_p->lock, flags);
+                       garmin_data_p->flags |= CLEAR_HALT_REQUIRED;
+                       spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+               }
+
+               usb_serial_port_softint(port);
        }
 
-       usb_serial_port_softint(port);
+       /* Ignore errors that resulted from garmin_write_bulk with dismiss_ack=1 */
+
+       /* free up the transfer buffer, as usb_free_urb() does not do this */
+       kfree (urb->transfer_buffer);
 }
 
 
 static int garmin_write_bulk (struct usb_serial_port *port,
-                             const unsigned char *buf, int count)
+                             const unsigned char *buf, int count,
+                             int dismiss_ack)
 {
        unsigned long flags;
        struct usb_serial *serial = port->serial;
@@ -1093,13 +1116,12 @@ static int garmin_write_bulk (struct usb_serial_port *port,
                                usb_sndbulkpipe (serial->dev,
                                port->bulk_out_endpointAddress),
                                buffer, count,
-                               garmin_write_bulk_callback, port);
+                               garmin_write_bulk_callback,
+                               dismiss_ack ? NULL : port);
        urb->transfer_flags |= URB_ZERO_PACKET;
 
        if (GARMIN_LAYERID_APPL == getLayerId(buffer)) {
-               spin_lock_irqsave(&garmin_data_p->lock, flags);
-               garmin_data_p->flags |= FLAGS_APP_REQ_SEEN;
-               spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+               atomic_inc(&garmin_data_p->req_count);
                if (garmin_data_p->mode == MODE_GARMIN_SERIAL)  {
                        pkt_clear(garmin_data_p);
                        garmin_data_p->state = STATE_GSP_WAIT_DATA;
@@ -1114,13 +1136,6 @@ static int garmin_write_bulk (struct usb_serial_port *port,
                        "failed with status = %d\n",
                                __FUNCTION__, status);
                count = status;
-       } else {
-
-               if (GARMIN_LAYERID_APPL == getLayerId(buffer)
-                   && (garmin_data_p->mode == MODE_GARMIN_SERIAL))  {
-
-                       gsp_send_ack(garmin_data_p, buffer[4]);
-               }
        }
 
        /* we are done with this urb, so let the host driver
@@ -1135,7 +1150,6 @@ static int garmin_write_bulk (struct usb_serial_port *port,
 static int garmin_write (struct usb_serial_port *port,
                         const unsigned char *buf, int count)
 {
-       unsigned long flags;
        int pktid, pktsiz, len;
        struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
        __le32 *privpkt = (__le32 *)garmin_data_p->privpkt;
@@ -1186,9 +1200,7 @@ static int garmin_write (struct usb_serial_port *port,
                                break;
 
                        case PRIV_PKTID_RESET_REQ:
-                               spin_lock_irqsave(&garmin_data_p->lock, flags);
-                               garmin_data_p->flags |= FLAGS_APP_REQ_SEEN;
-                               spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+                               atomic_inc(&garmin_data_p->req_count);
                                break;
 
                        case PRIV_PKTID_SET_DEF_MODE:
@@ -1241,8 +1253,6 @@ static int garmin_chars_in_buffer (struct usb_serial_port *port)
 static void garmin_read_process(struct garmin_data * garmin_data_p,
                                 unsigned char *data, unsigned data_length)
 {
-       unsigned long flags;
-
        if (garmin_data_p->flags & FLAGS_DROP_DATA) {
                /* abort-transfer cmd is actice */
                dbg("%s - pkt dropped", __FUNCTION__);
@@ -1254,9 +1264,7 @@ static void garmin_read_process(struct garmin_data * garmin_data_p,
                   the device */
                if (0 == memcmp(data, GARMIN_APP_LAYER_REPLY,
                                sizeof(GARMIN_APP_LAYER_REPLY))) {
-                       spin_lock_irqsave(&garmin_data_p->lock, flags);
-                       garmin_data_p->flags |= FLAGS_APP_RESP_SEEN;
-                       spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+                       atomic_inc(&garmin_data_p->resp_count);
                }
 
                /* if throttling is active or postprecessing is required