summaryrefslogtreecommitdiffstats
path: root/drivers/usb/serial
diff options
context:
space:
mode:
authorLinus Torvalds2014-06-08 20:31:16 +0200
committerLinus Torvalds2014-06-08 20:31:16 +0200
commit3f17ea6dea8ba5668873afa54628a91aaa3fb1c0 (patch)
treeafbeb2accd4c2199ddd705ae943995b143a0af02 /drivers/usb/serial
parentLinux 3.15 (diff)
parentMerge tag 'clk-for-linus-3.16' of git://git.linaro.org/people/mike.turquette/... (diff)
downloadkernel-qcow2-linux-3f17ea6dea8ba5668873afa54628a91aaa3fb1c0.tar.gz
kernel-qcow2-linux-3f17ea6dea8ba5668873afa54628a91aaa3fb1c0.tar.xz
kernel-qcow2-linux-3f17ea6dea8ba5668873afa54628a91aaa3fb1c0.zip
Merge branch 'next' (accumulated 3.16 merge window patches) into master
Now that 3.15 is released, this merges the 'next' branch into 'master', bringing us to the normal situation where my 'master' branch is the merge window. * accumulated work in next: (6809 commits) ufs: sb mutex merge + mutex_destroy powerpc: update comments for generic idle conversion cris: update comments for generic idle conversion idle: remove cpu_idle() forward declarations nbd: zero from and len fields in NBD_CMD_DISCONNECT. mm: convert some level-less printks to pr_* MAINTAINERS: adi-buildroot-devel is moderated MAINTAINERS: add linux-api for review of API/ABI changes mm/kmemleak-test.c: use pr_fmt for logging fs/dlm/debug_fs.c: replace seq_printf by seq_puts fs/dlm/lockspace.c: convert simple_str to kstr fs/dlm/config.c: convert simple_str to kstr mm: mark remap_file_pages() syscall as deprecated mm: memcontrol: remove unnecessary memcg argument from soft limit functions mm: memcontrol: clean up memcg zoneinfo lookup mm/memblock.c: call kmemleak directly from memblock_(alloc|free) mm/mempool.c: update the kmemleak stack trace for mempool allocations lib/radix-tree.c: update the kmemleak stack trace for radix tree allocations mm: introduce kmemleak_update_trace() mm/kmemleak.c: use %u to print ->checksum ...
Diffstat (limited to 'drivers/usb/serial')
-rw-r--r--drivers/usb/serial/bus.c14
-rw-r--r--drivers/usb/serial/keyspan.c4
-rw-r--r--drivers/usb/serial/kobil_sct.c22
-rw-r--r--drivers/usb/serial/option.c13
-rw-r--r--drivers/usb/serial/qcserial.c143
-rw-r--r--drivers/usb/serial/sierra.c198
-rw-r--r--drivers/usb/serial/usb-serial.c38
-rw-r--r--drivers/usb/serial/usb-wwan.h6
-rw-r--r--drivers/usb/serial/usb_wwan.c236
9 files changed, 368 insertions, 306 deletions
diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c
index 35a2373cde67..9374bd2aba20 100644
--- a/drivers/usb/serial/bus.c
+++ b/drivers/usb/serial/bus.c
@@ -97,13 +97,19 @@ static int usb_serial_device_remove(struct device *dev)
struct usb_serial_port *port;
int retval = 0;
int minor;
+ int autopm_err;
port = to_usb_serial_port(dev);
if (!port)
return -ENODEV;
- /* make sure suspend/resume doesn't race against port_remove */
- usb_autopm_get_interface(port->serial->interface);
+ /*
+ * Make sure suspend/resume doesn't race against port_remove.
+ *
+ * Note that no further runtime PM callbacks will be made if
+ * autopm_get fails.
+ */
+ autopm_err = usb_autopm_get_interface(port->serial->interface);
minor = port->minor;
tty_unregister_device(usb_serial_tty_driver, minor);
@@ -117,7 +123,9 @@ static int usb_serial_device_remove(struct device *dev)
dev_info(dev, "%s converter now disconnected from ttyUSB%d\n",
driver->description, minor);
- usb_autopm_put_interface(port->serial->interface);
+ if (!autopm_err)
+ usb_autopm_put_interface(port->serial->interface);
+
return retval;
}
diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c
index d3acaead5a81..93cb7cebda62 100644
--- a/drivers/usb/serial/keyspan.c
+++ b/drivers/usb/serial/keyspan.c
@@ -1535,14 +1535,14 @@ static int keyspan_usa26_send_setup(struct usb_serial *serial,
this_urb = p_priv->outcont_urb;
- dev_dbg(&port->dev, "%s - endpoint %d\n", __func__, usb_pipeendpoint(this_urb->pipe));
-
/* Make sure we have an urb then send the message */
if (this_urb == NULL) {
dev_dbg(&port->dev, "%s - oops no urb.\n", __func__);
return -1;
}
+ dev_dbg(&port->dev, "%s - endpoint %d\n", __func__, usb_pipeendpoint(this_urb->pipe));
+
/* Save reset port val for resend.
Don't overwrite resend for open/close condition. */
if ((reset_port + 1) > p_priv->resend_cont)
diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c
index fee242387f55..078f9ed419c8 100644
--- a/drivers/usb/serial/kobil_sct.c
+++ b/drivers/usb/serial/kobil_sct.c
@@ -215,13 +215,13 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port)
priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) {
/* Setting Baudrate, Parity and Stopbits */
result = usb_control_msg(port->serial->dev,
- usb_rcvctrlpipe(port->serial->dev, 0),
+ usb_sndctrlpipe(port->serial->dev, 0),
SUSBCRequest_SetBaudRateParityAndStopBits,
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
SUSBCR_SBR_9600 | SUSBCR_SPASB_EvenParity |
SUSBCR_SPASB_1StopBit,
0,
- transfer_buffer,
+ NULL,
0,
KOBIL_TIMEOUT
);
@@ -229,12 +229,12 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port)
/* reset all queues */
result = usb_control_msg(port->serial->dev,
- usb_rcvctrlpipe(port->serial->dev, 0),
+ usb_sndctrlpipe(port->serial->dev, 0),
SUSBCRequest_Misc,
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
SUSBCR_MSC_ResetAllQueues,
0,
- transfer_buffer,
+ NULL,
0,
KOBIL_TIMEOUT
);
@@ -445,12 +445,12 @@ static int kobil_tiocmset(struct tty_struct *tty,
else
dev_dbg(dev, "%s - Clearing DTR\n", __func__);
result = usb_control_msg(port->serial->dev,
- usb_rcvctrlpipe(port->serial->dev, 0),
+ usb_sndctrlpipe(port->serial->dev, 0),
SUSBCRequest_SetStatusLinesOrQueues,
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
((dtr != 0) ? SUSBCR_SSL_SETDTR : SUSBCR_SSL_CLRDTR),
0,
- transfer_buffer,
+ NULL,
0,
KOBIL_TIMEOUT);
} else {
@@ -459,12 +459,12 @@ static int kobil_tiocmset(struct tty_struct *tty,
else
dev_dbg(dev, "%s - Clearing RTS\n", __func__);
result = usb_control_msg(port->serial->dev,
- usb_rcvctrlpipe(port->serial->dev, 0),
+ usb_sndctrlpipe(port->serial->dev, 0),
SUSBCRequest_SetStatusLinesOrQueues,
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
((rts != 0) ? SUSBCR_SSL_SETRTS : SUSBCR_SSL_CLRRTS),
0,
- transfer_buffer,
+ NULL,
0,
KOBIL_TIMEOUT);
}
@@ -514,7 +514,7 @@ static void kobil_set_termios(struct tty_struct *tty,
tty_encode_baud_rate(tty, speed, speed);
result = usb_control_msg(port->serial->dev,
- usb_rcvctrlpipe(port->serial->dev, 0),
+ usb_sndctrlpipe(port->serial->dev, 0),
SUSBCRequest_SetBaudRateParityAndStopBits,
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
urb_val,
@@ -546,12 +546,12 @@ static int kobil_ioctl(struct tty_struct *tty,
return -ENOBUFS;
result = usb_control_msg(port->serial->dev,
- usb_rcvctrlpipe(port->serial->dev, 0),
+ usb_sndctrlpipe(port->serial->dev, 0),
SUSBCRequest_Misc,
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
SUSBCR_MSC_ResetAllQueues,
0,
- NULL, /* transfer_buffer, */
+ NULL,
0,
KOBIL_TIMEOUT
);
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 948a19f0cdf7..59c3108cc136 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -1731,7 +1731,6 @@ static struct usb_serial_driver option_1port_device = {
.write = usb_wwan_write,
.write_room = usb_wwan_write_room,
.chars_in_buffer = usb_wwan_chars_in_buffer,
- .set_termios = usb_wwan_set_termios,
.tiocmget = usb_wwan_tiocmget,
.tiocmset = usb_wwan_tiocmset,
.ioctl = usb_wwan_ioctl,
@@ -1906,6 +1905,7 @@ static void option_instat_callback(struct urb *urb)
/* Resubmit urb so we continue receiving IRQ data */
if (status != -ESHUTDOWN && status != -ENOENT) {
+ usb_mark_last_busy(port->serial->dev);
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err)
dev_dbg(dev, "%s: resubmit intr urb failed. (%d)\n",
@@ -1925,6 +1925,7 @@ static int option_send_setup(struct usb_serial_port *port)
struct option_private *priv = intfdata->private;
struct usb_wwan_port_private *portdata;
int val = 0;
+ int res;
portdata = usb_get_serial_port_data(port);
@@ -1933,9 +1934,17 @@ static int option_send_setup(struct usb_serial_port *port)
if (portdata->rts_state)
val |= 0x02;
- return usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+ res = usb_autopm_get_interface(serial->interface);
+ if (res)
+ return res;
+
+ res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
0x22, 0x21, val, priv->bInterfaceNumber, NULL,
0, USB_CTRL_SET_TIMEOUT);
+
+ usb_autopm_put_interface(serial->interface);
+
+ return res;
}
MODULE_AUTHOR(DRIVER_AUTHOR);
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index 6c0a542e8ec1..b2aa003bf411 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -22,8 +22,17 @@
#define DRIVER_AUTHOR "Qualcomm Inc"
#define DRIVER_DESC "Qualcomm USB Serial driver"
+/* standard device layouts supported by this driver */
+enum qcserial_layouts {
+ QCSERIAL_G2K = 0, /* Gobi 2000 */
+ QCSERIAL_G1K = 1, /* Gobi 1000 */
+ QCSERIAL_SWI = 2, /* Sierra Wireless */
+};
+
#define DEVICE_G1K(v, p) \
- USB_DEVICE(v, p), .driver_info = 1
+ USB_DEVICE(v, p), .driver_info = QCSERIAL_G1K
+#define DEVICE_SWI(v, p) \
+ USB_DEVICE(v, p), .driver_info = QCSERIAL_SWI
static const struct usb_device_id id_table[] = {
/* Gobi 1000 devices */
@@ -126,46 +135,27 @@ static const struct usb_device_id id_table[] = {
{USB_DEVICE(0x12D1, 0x14F1)}, /* Sony Gobi 3000 Composite */
{USB_DEVICE(0x0AF0, 0x8120)}, /* Option GTM681W */
- /* non Gobi Qualcomm serial devices */
- {USB_DEVICE_INTERFACE_NUMBER(0x0f3d, 0x68a2, 0)}, /* Sierra Wireless MC7700 Device Management */
- {USB_DEVICE_INTERFACE_NUMBER(0x0f3d, 0x68a2, 2)}, /* Sierra Wireless MC7700 NMEA */
- {USB_DEVICE_INTERFACE_NUMBER(0x0f3d, 0x68a2, 3)}, /* Sierra Wireless MC7700 Modem */
- {USB_DEVICE_INTERFACE_NUMBER(0x114f, 0x68a2, 0)}, /* Sierra Wireless MC7750 Device Management */
- {USB_DEVICE_INTERFACE_NUMBER(0x114f, 0x68a2, 2)}, /* Sierra Wireless MC7750 NMEA */
- {USB_DEVICE_INTERFACE_NUMBER(0x114f, 0x68a2, 3)}, /* Sierra Wireless MC7750 Modem */
- {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x68a2, 0)}, /* Sierra Wireless MC7710 Device Management */
- {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x68a2, 2)}, /* Sierra Wireless MC7710 NMEA */
- {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x68a2, 3)}, /* Sierra Wireless MC7710 Modem */
- {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x68c0, 0)}, /* Sierra Wireless MC73xx Device Management */
- {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x68c0, 2)}, /* Sierra Wireless MC73xx NMEA */
- {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x68c0, 3)}, /* Sierra Wireless MC73xx Modem */
- {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x901c, 0)}, /* Sierra Wireless EM7700 Device Management */
- {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x901c, 2)}, /* Sierra Wireless EM7700 NMEA */
- {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x901c, 3)}, /* Sierra Wireless EM7700 Modem */
- {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x901f, 0)}, /* Sierra Wireless EM7355 Device Management */
- {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x901f, 2)}, /* Sierra Wireless EM7355 NMEA */
- {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x901f, 3)}, /* Sierra Wireless EM7355 Modem */
- {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x9041, 0)}, /* Sierra Wireless MC7305/MC7355 Device Management */
- {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x9041, 2)}, /* Sierra Wireless MC7305/MC7355 NMEA */
- {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x9041, 3)}, /* Sierra Wireless MC7305/MC7355 Modem */
- {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x9051, 0)}, /* Netgear AirCard 340U Device Management */
- {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x9051, 2)}, /* Netgear AirCard 340U NMEA */
- {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x9051, 3)}, /* Netgear AirCard 340U Modem */
- {USB_DEVICE_INTERFACE_NUMBER(0x413c, 0x81a2, 0)}, /* Dell Wireless 5806 Gobi(TM) 4G LTE Mobile Broadband Card Device Management */
- {USB_DEVICE_INTERFACE_NUMBER(0x413c, 0x81a2, 2)}, /* Dell Wireless 5806 Gobi(TM) 4G LTE Mobile Broadband Card NMEA */
- {USB_DEVICE_INTERFACE_NUMBER(0x413c, 0x81a2, 3)}, /* Dell Wireless 5806 Gobi(TM) 4G LTE Mobile Broadband Card Modem */
- {USB_DEVICE_INTERFACE_NUMBER(0x413c, 0x81a3, 0)}, /* Dell Wireless 5570 HSPA+ (42Mbps) Mobile Broadband Card Device Management */
- {USB_DEVICE_INTERFACE_NUMBER(0x413c, 0x81a3, 2)}, /* Dell Wireless 5570 HSPA+ (42Mbps) Mobile Broadband Card NMEA */
- {USB_DEVICE_INTERFACE_NUMBER(0x413c, 0x81a3, 3)}, /* Dell Wireless 5570 HSPA+ (42Mbps) Mobile Broadband Card Modem */
- {USB_DEVICE_INTERFACE_NUMBER(0x413c, 0x81a4, 0)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card Device Management */
- {USB_DEVICE_INTERFACE_NUMBER(0x413c, 0x81a4, 2)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card NMEA */
- {USB_DEVICE_INTERFACE_NUMBER(0x413c, 0x81a4, 3)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card Modem */
- {USB_DEVICE_INTERFACE_NUMBER(0x413c, 0x81a8, 0)}, /* Dell Wireless 5808 Gobi(TM) 4G LTE Mobile Broadband Card Device Management */
- {USB_DEVICE_INTERFACE_NUMBER(0x413c, 0x81a8, 2)}, /* Dell Wireless 5808 Gobi(TM) 4G LTE Mobile Broadband Card NMEA */
- {USB_DEVICE_INTERFACE_NUMBER(0x413c, 0x81a8, 3)}, /* Dell Wireless 5808 Gobi(TM) 4G LTE Mobile Broadband Card Modem */
- {USB_DEVICE_INTERFACE_NUMBER(0x413c, 0x81a9, 0)}, /* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card Device Management */
- {USB_DEVICE_INTERFACE_NUMBER(0x413c, 0x81a9, 2)}, /* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card NMEA */
- {USB_DEVICE_INTERFACE_NUMBER(0x413c, 0x81a9, 3)}, /* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card Modem */
+ /* non-Gobi Sierra Wireless devices */
+ {DEVICE_SWI(0x0f3d, 0x68a2)}, /* Sierra Wireless MC7700 */
+ {DEVICE_SWI(0x114f, 0x68a2)}, /* Sierra Wireless MC7750 */
+ {DEVICE_SWI(0x1199, 0x68a2)}, /* Sierra Wireless MC7710 */
+ {DEVICE_SWI(0x1199, 0x68c0)}, /* Sierra Wireless MC73xx */
+ {DEVICE_SWI(0x1199, 0x901c)}, /* Sierra Wireless EM7700 */
+ {DEVICE_SWI(0x1199, 0x901f)}, /* Sierra Wireless EM7355 */
+ {DEVICE_SWI(0x1199, 0x9040)}, /* Sierra Wireless Modem */
+ {DEVICE_SWI(0x1199, 0x9041)}, /* Sierra Wireless MC7305/MC7355 */
+ {DEVICE_SWI(0x1199, 0x9051)}, /* Netgear AirCard 340U */
+ {DEVICE_SWI(0x1199, 0x9053)}, /* Sierra Wireless Modem */
+ {DEVICE_SWI(0x1199, 0x9054)}, /* Sierra Wireless Modem */
+ {DEVICE_SWI(0x1199, 0x9055)}, /* Netgear AirCard 341U */
+ {DEVICE_SWI(0x1199, 0x9056)}, /* Sierra Wireless Modem */
+ {DEVICE_SWI(0x1199, 0x9060)}, /* Sierra Wireless Modem */
+ {DEVICE_SWI(0x1199, 0x9061)}, /* Sierra Wireless Modem */
+ {DEVICE_SWI(0x413c, 0x81a2)}, /* Dell Wireless 5806 Gobi(TM) 4G LTE Mobile Broadband Card */
+ {DEVICE_SWI(0x413c, 0x81a3)}, /* Dell Wireless 5570 HSPA+ (42Mbps) Mobile Broadband Card */
+ {DEVICE_SWI(0x413c, 0x81a4)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */
+ {DEVICE_SWI(0x413c, 0x81a8)}, /* Dell Wireless 5808 Gobi(TM) 4G LTE Mobile Broadband Card */
+ {DEVICE_SWI(0x413c, 0x81a9)}, /* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card */
{ } /* Terminating entry */
};
@@ -178,11 +168,8 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
int retval = -ENODEV;
__u8 nintf;
__u8 ifnum;
- bool is_gobi1k = id->driver_info ? true : false;
int altsetting = -1;
- dev_dbg(dev, "Is Gobi 1000 = %d\n", is_gobi1k);
-
nintf = serial->dev->actconfig->desc.bNumInterfaces;
dev_dbg(dev, "Num Interfaces = %d\n", nintf);
ifnum = intf->desc.bInterfaceNumber;
@@ -210,32 +197,29 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
}
- /* allow any number of interfaces when doing direct interface match */
- if (id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER) {
- dev_dbg(dev, "Generic Qualcomm serial interface found\n");
- altsetting = 0;
- goto done;
- }
-
- if (nintf < 3 || nintf > 4) {
- dev_err(dev, "unknown number of interfaces: %d\n", nintf);
- goto done;
- }
-
/* default to enabling interface */
altsetting = 0;
- /* Composite mode; don't bind to the QMI/net interface as that
+ /*
+ * Composite mode; don't bind to the QMI/net interface as that
* gets handled by other drivers.
*/
- if (is_gobi1k) {
- /* Gobi 1K USB layout:
+ switch (id->driver_info) {
+ case QCSERIAL_G1K:
+ /*
+ * Gobi 1K USB layout:
* 0: DM/DIAG (use libqcdm from ModemManager for communication)
* 1: serial port (doesn't respond)
* 2: AT-capable modem port
* 3: QMI/net
*/
+ if (nintf < 3 || nintf > 4) {
+ dev_err(dev, "unknown number of interfaces: %d\n", nintf);
+ altsetting = -1;
+ goto done;
+ }
+
if (ifnum == 0) {
dev_dbg(dev, "Gobi 1K DM/DIAG interface found\n");
altsetting = 1;
@@ -243,13 +227,21 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
dev_dbg(dev, "Modem port found\n");
else
altsetting = -1;
- } else {
- /* Gobi 2K+ USB layout:
+ break;
+ case QCSERIAL_G2K:
+ /*
+ * Gobi 2K+ USB layout:
* 0: QMI/net
* 1: DM/DIAG (use libqcdm from ModemManager for communication)
* 2: AT-capable modem port
* 3: NMEA
*/
+ if (nintf < 3 || nintf > 4) {
+ dev_err(dev, "unknown number of interfaces: %d\n", nintf);
+ altsetting = -1;
+ goto done;
+ }
+
switch (ifnum) {
case 0:
/* Don't claim the QMI/net interface */
@@ -270,6 +262,35 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
dev_dbg(dev, "Gobi 2K+ NMEA GPS interface found\n");
break;
}
+ break;
+ case QCSERIAL_SWI:
+ /*
+ * Sierra Wireless layout:
+ * 0: DM/DIAG (use libqcdm from ModemManager for communication)
+ * 2: NMEA
+ * 3: AT-capable modem port
+ * 8: QMI/net
+ */
+ switch (ifnum) {
+ case 0:
+ dev_dbg(dev, "DM/DIAG interface found\n");
+ break;
+ case 2:
+ dev_dbg(dev, "NMEA GPS interface found\n");
+ break;
+ case 3:
+ dev_dbg(dev, "Modem port found\n");
+ break;
+ default:
+ /* don't claim any unsupported interface */
+ altsetting = -1;
+ break;
+ }
+ break;
+ default:
+ dev_err(dev, "unsupported device layout type: %lu\n",
+ id->driver_info);
+ break;
}
done:
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c
index 6b192e602ce0..6f7f01eb556a 100644
--- a/drivers/usb/serial/sierra.c
+++ b/drivers/usb/serial/sierra.c
@@ -58,6 +58,7 @@ struct sierra_intf_private {
spinlock_t susp_lock;
unsigned int suspended:1;
int in_flight;
+ unsigned int open_ports;
};
static int sierra_set_power_state(struct usb_device *udev, __u16 swiState)
@@ -315,7 +316,6 @@ struct sierra_port_private {
int dsr_state;
int dcd_state;
int ri_state;
- unsigned int opened:1;
};
static int sierra_send_setup(struct usb_serial_port *port)
@@ -364,20 +364,13 @@ static int sierra_send_setup(struct usb_serial_port *port)
if (retval < 0)
return retval;
- retval = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+ retval = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
0x22, 0x21, val, interface, NULL, 0, USB_CTRL_SET_TIMEOUT);
usb_autopm_put_interface(serial->interface);
return retval;
}
-static void sierra_set_termios(struct tty_struct *tty,
- struct usb_serial_port *port, struct ktermios *old_termios)
-{
- tty_termios_copy_hw(&tty->termios, old_termios);
- sierra_send_setup(port);
-}
-
static int sierra_tiocmget(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
@@ -418,9 +411,7 @@ static int sierra_tiocmset(struct tty_struct *tty,
static void sierra_release_urb(struct urb *urb)
{
- struct usb_serial_port *port;
if (urb) {
- port = urb->context;
kfree(urb->transfer_buffer);
usb_free_urb(urb);
}
@@ -433,7 +424,7 @@ static void sierra_outdat_callback(struct urb *urb)
struct sierra_intf_private *intfdata;
int status = urb->status;
- intfdata = port->serial->private;
+ intfdata = usb_get_serial_data(port->serial);
/* free up the transfer buffer, as usb_free_urb() does not do this */
kfree(urb->transfer_buffer);
@@ -470,7 +461,7 @@ static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port,
return 0;
portdata = usb_get_serial_port_data(port);
- intfdata = serial->private;
+ intfdata = usb_get_serial_data(serial);
dev_dbg(&port->dev, "%s: write (%zd bytes)\n", __func__, writesize);
spin_lock_irqsave(&portdata->lock, flags);
@@ -674,6 +665,23 @@ static int sierra_write_room(struct tty_struct *tty)
return 2048;
}
+static int sierra_chars_in_buffer(struct tty_struct *tty)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct sierra_port_private *portdata = usb_get_serial_port_data(port);
+ unsigned long flags;
+ int chars;
+
+ /* NOTE: This overcounts somewhat. */
+ spin_lock_irqsave(&portdata->lock, flags);
+ chars = portdata->outstanding_urbs * MAX_TRANSFER;
+ spin_unlock_irqrestore(&portdata->lock, flags);
+
+ dev_dbg(&port->dev, "%s - %d\n", __func__, chars);
+
+ return chars;
+}
+
static void sierra_stop_rx_urbs(struct usb_serial_port *port)
{
int i;
@@ -729,9 +737,6 @@ static struct urb *sierra_setup_urb(struct usb_serial *serial, int endpoint,
struct urb *urb;
u8 *buf;
- if (endpoint == -1)
- return NULL;
-
urb = usb_alloc_urb(0, mem_flags);
if (!urb)
return NULL;
@@ -758,40 +763,48 @@ static void sierra_close(struct usb_serial_port *port)
int i;
struct usb_serial *serial = port->serial;
struct sierra_port_private *portdata;
- struct sierra_intf_private *intfdata = port->serial->private;
+ struct sierra_intf_private *intfdata = usb_get_serial_data(serial);
+ struct urb *urb;
portdata = usb_get_serial_port_data(port);
- portdata->rts_state = 0;
- portdata->dtr_state = 0;
-
- mutex_lock(&serial->disc_mutex);
- if (!serial->disconnected) {
+ /*
+ * Need to take susp_lock to make sure port is not already being
+ * resumed, but no need to hold it due to ASYNC_INITIALIZED.
+ */
+ spin_lock_irq(&intfdata->susp_lock);
+ if (--intfdata->open_ports == 0)
serial->interface->needs_remote_wakeup = 0;
- /* odd error handling due to pm counters */
- if (!usb_autopm_get_interface(serial->interface))
- sierra_send_setup(port);
- else
- usb_autopm_get_interface_no_resume(serial->interface);
+ spin_unlock_irq(&intfdata->susp_lock);
+ for (;;) {
+ urb = usb_get_from_anchor(&portdata->delayed);
+ if (!urb)
+ break;
+ kfree(urb->transfer_buffer);
+ usb_free_urb(urb);
+ usb_autopm_put_interface_async(serial->interface);
+ spin_lock(&portdata->lock);
+ portdata->outstanding_urbs--;
+ spin_unlock(&portdata->lock);
}
- mutex_unlock(&serial->disc_mutex);
- spin_lock_irq(&intfdata->susp_lock);
- portdata->opened = 0;
- spin_unlock_irq(&intfdata->susp_lock);
sierra_stop_rx_urbs(port);
+ usb_kill_anchored_urbs(&portdata->active);
+
for (i = 0; i < portdata->num_in_urbs; i++) {
sierra_release_urb(portdata->in_urbs[i]);
portdata->in_urbs[i] = NULL;
}
+
+ usb_autopm_get_interface_no_resume(serial->interface);
}
static int sierra_open(struct tty_struct *tty, struct usb_serial_port *port)
{
struct sierra_port_private *portdata;
struct usb_serial *serial = port->serial;
- struct sierra_intf_private *intfdata = serial->private;
+ struct sierra_intf_private *intfdata = usb_get_serial_data(serial);
int i;
int err;
int endpoint;
@@ -799,11 +812,6 @@ static int sierra_open(struct tty_struct *tty, struct usb_serial_port *port)
portdata = usb_get_serial_port_data(port);
- /* Set some sane defaults */
- portdata->rts_state = 1;
- portdata->dtr_state = 1;
-
-
endpoint = port->bulk_in_endpointAddress;
for (i = 0; i < portdata->num_in_urbs; i++) {
urb = sierra_setup_urb(serial, endpoint, USB_DIR_IN, port,
@@ -816,23 +824,26 @@ static int sierra_open(struct tty_struct *tty, struct usb_serial_port *port)
usb_sndbulkpipe(serial->dev, endpoint) | USB_DIR_IN);
err = sierra_submit_rx_urbs(port, GFP_KERNEL);
- if (err) {
- /* get rid of everything as in close */
- sierra_close(port);
- /* restore balance for autopm */
- if (!serial->disconnected)
- usb_autopm_put_interface(serial->interface);
- return err;
- }
- sierra_send_setup(port);
+ if (err)
+ goto err_submit;
- serial->interface->needs_remote_wakeup = 1;
spin_lock_irq(&intfdata->susp_lock);
- portdata->opened = 1;
+ if (++intfdata->open_ports == 1)
+ serial->interface->needs_remote_wakeup = 1;
spin_unlock_irq(&intfdata->susp_lock);
usb_autopm_put_interface(serial->interface);
return 0;
+
+err_submit:
+ sierra_stop_rx_urbs(port);
+
+ for (i = 0; i < portdata->num_in_urbs; i++) {
+ sierra_release_urb(portdata->in_urbs[i]);
+ portdata->in_urbs[i] = NULL;
+ }
+
+ return err;
}
@@ -928,6 +939,7 @@ static int sierra_port_remove(struct usb_serial_port *port)
struct sierra_port_private *portdata;
portdata = usb_get_serial_port_data(port);
+ usb_set_serial_port_data(port, NULL);
kfree(portdata);
return 0;
@@ -944,6 +956,8 @@ static void stop_read_write_urbs(struct usb_serial *serial)
for (i = 0; i < serial->num_ports; ++i) {
port = serial->port[i];
portdata = usb_get_serial_port_data(port);
+ if (!portdata)
+ continue;
sierra_stop_rx_urbs(port);
usb_kill_anchored_urbs(&portdata->active);
}
@@ -951,58 +965,84 @@ static void stop_read_write_urbs(struct usb_serial *serial)
static int sierra_suspend(struct usb_serial *serial, pm_message_t message)
{
- struct sierra_intf_private *intfdata;
- int b;
+ struct sierra_intf_private *intfdata = usb_get_serial_data(serial);
+ spin_lock_irq(&intfdata->susp_lock);
if (PMSG_IS_AUTO(message)) {
- intfdata = serial->private;
- spin_lock_irq(&intfdata->susp_lock);
- b = intfdata->in_flight;
-
- if (b) {
+ if (intfdata->in_flight) {
spin_unlock_irq(&intfdata->susp_lock);
return -EBUSY;
- } else {
- intfdata->suspended = 1;
- spin_unlock_irq(&intfdata->susp_lock);
}
}
+ intfdata->suspended = 1;
+ spin_unlock_irq(&intfdata->susp_lock);
+
stop_read_write_urbs(serial);
return 0;
}
+/* Caller must hold susp_lock. */
+static int sierra_submit_delayed_urbs(struct usb_serial_port *port)
+{
+ struct sierra_port_private *portdata = usb_get_serial_port_data(port);
+ struct sierra_intf_private *intfdata;
+ struct urb *urb;
+ int ec = 0;
+ int err;
+
+ intfdata = usb_get_serial_data(port->serial);
+
+ for (;;) {
+ urb = usb_get_from_anchor(&portdata->delayed);
+ if (!urb)
+ break;
+
+ usb_anchor_urb(urb, &portdata->active);
+ intfdata->in_flight++;
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err) {
+ dev_err(&port->dev, "%s - submit urb failed: %d",
+ __func__, err);
+ ec++;
+ intfdata->in_flight--;
+ usb_unanchor_urb(urb);
+ kfree(urb->transfer_buffer);
+ usb_free_urb(urb);
+
+ spin_lock(&portdata->lock);
+ portdata->outstanding_urbs--;
+ spin_unlock(&portdata->lock);
+ }
+ }
+
+ if (ec)
+ return -EIO;
+
+ return 0;
+}
+
static int sierra_resume(struct usb_serial *serial)
{
struct usb_serial_port *port;
- struct sierra_intf_private *intfdata = serial->private;
- struct sierra_port_private *portdata;
- struct urb *urb;
+ struct sierra_intf_private *intfdata = usb_get_serial_data(serial);
int ec = 0;
int i, err;
spin_lock_irq(&intfdata->susp_lock);
for (i = 0; i < serial->num_ports; i++) {
port = serial->port[i];
- portdata = usb_get_serial_port_data(port);
- while ((urb = usb_get_from_anchor(&portdata->delayed))) {
- usb_anchor_urb(urb, &portdata->active);
- intfdata->in_flight++;
- err = usb_submit_urb(urb, GFP_ATOMIC);
- if (err < 0) {
- intfdata->in_flight--;
- usb_unanchor_urb(urb);
- usb_scuttle_anchored_urbs(&portdata->delayed);
- break;
- }
- }
+ if (!test_bit(ASYNCB_INITIALIZED, &port->port.flags))
+ continue;
- if (portdata->opened) {
- err = sierra_submit_rx_urbs(port, GFP_ATOMIC);
- if (err)
- ec++;
- }
+ err = sierra_submit_delayed_urbs(port);
+ if (err)
+ ec++;
+
+ err = sierra_submit_rx_urbs(port, GFP_ATOMIC);
+ if (err)
+ ec++;
}
intfdata->suspended = 0;
spin_unlock_irq(&intfdata->susp_lock);
@@ -1029,7 +1069,7 @@ static struct usb_serial_driver sierra_device = {
.dtr_rts = sierra_dtr_rts,
.write = sierra_write,
.write_room = sierra_write_room,
- .set_termios = sierra_set_termios,
+ .chars_in_buffer = sierra_chars_in_buffer,
.tiocmget = sierra_tiocmget,
.tiocmset = sierra_tiocmset,
.attach = sierra_startup,
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 6d40d56378d7..02de3110fe94 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -1060,6 +1060,7 @@ static void usb_serial_disconnect(struct usb_interface *interface)
struct usb_serial *serial = usb_get_intfdata(interface);
struct device *dev = &interface->dev;
struct usb_serial_port *port;
+ struct tty_struct *tty;
usb_serial_console_disconnect(serial);
@@ -1070,18 +1071,16 @@ static void usb_serial_disconnect(struct usb_interface *interface)
for (i = 0; i < serial->num_ports; ++i) {
port = serial->port[i];
- if (port) {
- struct tty_struct *tty = tty_port_tty_get(&port->port);
- if (tty) {
- tty_vhangup(tty);
- tty_kref_put(tty);
- }
- usb_serial_port_poison_urbs(port);
- wake_up_interruptible(&port->port.delta_msr_wait);
- cancel_work_sync(&port->work);
- if (device_is_registered(&port->dev))
- device_del(&port->dev);
+ tty = tty_port_tty_get(&port->port);
+ if (tty) {
+ tty_vhangup(tty);
+ tty_kref_put(tty);
}
+ usb_serial_port_poison_urbs(port);
+ wake_up_interruptible(&port->port.delta_msr_wait);
+ cancel_work_sync(&port->work);
+ if (device_is_registered(&port->dev))
+ device_del(&port->dev);
}
if (serial->type->disconnect)
serial->type->disconnect(serial);
@@ -1094,7 +1093,6 @@ static void usb_serial_disconnect(struct usb_interface *interface)
int usb_serial_suspend(struct usb_interface *intf, pm_message_t message)
{
struct usb_serial *serial = usb_get_intfdata(intf);
- struct usb_serial_port *port;
int i, r = 0;
serial->suspending = 1;
@@ -1112,12 +1110,8 @@ int usb_serial_suspend(struct usb_interface *intf, pm_message_t message)
}
}
- for (i = 0; i < serial->num_ports; ++i) {
- port = serial->port[i];
- if (port)
- usb_serial_port_poison_urbs(port);
- }
-
+ for (i = 0; i < serial->num_ports; ++i)
+ usb_serial_port_poison_urbs(serial->port[i]);
err_out:
return r;
}
@@ -1125,14 +1119,10 @@ EXPORT_SYMBOL(usb_serial_suspend);
static void usb_serial_unpoison_port_urbs(struct usb_serial *serial)
{
- struct usb_serial_port *port;
int i;
- for (i = 0; i < serial->num_ports; ++i) {
- port = serial->port[i];
- if (port)
- usb_serial_port_unpoison_urbs(port);
- }
+ for (i = 0; i < serial->num_ports; ++i)
+ usb_serial_port_unpoison_urbs(serial->port[i]);
}
int usb_serial_resume(struct usb_interface *intf)
diff --git a/drivers/usb/serial/usb-wwan.h b/drivers/usb/serial/usb-wwan.h
index 684739b8efd0..f22dff58b587 100644
--- a/drivers/usb/serial/usb-wwan.h
+++ b/drivers/usb/serial/usb-wwan.h
@@ -11,15 +11,11 @@ extern void usb_wwan_close(struct usb_serial_port *port);
extern int usb_wwan_port_probe(struct usb_serial_port *port);
extern int usb_wwan_port_remove(struct usb_serial_port *port);
extern int usb_wwan_write_room(struct tty_struct *tty);
-extern void usb_wwan_set_termios(struct tty_struct *tty,
- struct usb_serial_port *port,
- struct ktermios *old);
extern int usb_wwan_tiocmget(struct tty_struct *tty);
extern int usb_wwan_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear);
extern int usb_wwan_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg);
-extern int usb_wwan_send_setup(struct usb_serial_port *port);
extern int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count);
extern int usb_wwan_chars_in_buffer(struct tty_struct *tty);
@@ -39,6 +35,7 @@ struct usb_wwan_intf_private {
spinlock_t susp_lock;
unsigned int suspended:1;
int in_flight;
+ unsigned int open_ports;
int (*send_setup) (struct usb_serial_port *port);
void *private;
};
@@ -51,7 +48,6 @@ struct usb_wwan_port_private {
struct urb *out_urbs[N_OUT_URB];
u8 *out_buffer[N_OUT_URB];
unsigned long out_busy; /* Bit vector of URBs in use */
- int opened;
struct usb_anchor delayed;
/* Settings for the port */
diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c
index b078440e822f..2f805cb386a5 100644
--- a/drivers/usb/serial/usb_wwan.c
+++ b/drivers/usb/serial/usb_wwan.c
@@ -41,7 +41,7 @@ void usb_wwan_dtr_rts(struct usb_serial_port *port, int on)
struct usb_wwan_port_private *portdata;
struct usb_wwan_intf_private *intfdata;
- intfdata = port->serial->private;
+ intfdata = usb_get_serial_data(port->serial);
if (!intfdata->send_setup)
return;
@@ -55,20 +55,6 @@ void usb_wwan_dtr_rts(struct usb_serial_port *port, int on)
}
EXPORT_SYMBOL(usb_wwan_dtr_rts);
-void usb_wwan_set_termios(struct tty_struct *tty,
- struct usb_serial_port *port,
- struct ktermios *old_termios)
-{
- struct usb_wwan_intf_private *intfdata = port->serial->private;
-
- /* Doesn't support option setting */
- tty_termios_copy_hw(&tty->termios, old_termios);
-
- if (intfdata->send_setup)
- intfdata->send_setup(port);
-}
-EXPORT_SYMBOL(usb_wwan_set_termios);
-
int usb_wwan_tiocmget(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
@@ -96,7 +82,7 @@ int usb_wwan_tiocmset(struct tty_struct *tty,
struct usb_wwan_intf_private *intfdata;
portdata = usb_get_serial_port_data(port);
- intfdata = port->serial->private;
+ intfdata = usb_get_serial_data(port->serial);
if (!intfdata->send_setup)
return -EINVAL;
@@ -192,7 +178,6 @@ int usb_wwan_ioctl(struct tty_struct *tty,
}
EXPORT_SYMBOL(usb_wwan_ioctl);
-/* Write */
int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count)
{
@@ -205,7 +190,7 @@ int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
unsigned long flags;
portdata = usb_get_serial_port_data(port);
- intfdata = port->serial->private;
+ intfdata = usb_get_serial_data(port->serial);
dev_dbg(&port->dev, "%s: write (%d chars)\n", __func__, count);
@@ -228,8 +213,10 @@ int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
usb_pipeendpoint(this_urb->pipe), i);
err = usb_autopm_get_interface_async(port->serial->interface);
- if (err < 0)
+ if (err < 0) {
+ clear_bit(i, &portdata->out_busy);
break;
+ }
/* send the data */
memcpy(this_urb->transfer_buffer, buf, todo);
@@ -244,9 +231,9 @@ int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
spin_unlock_irqrestore(&intfdata->susp_lock, flags);
err = usb_submit_urb(this_urb, GFP_ATOMIC);
if (err) {
- dev_dbg(&port->dev,
- "usb_submit_urb %p (write bulk) failed (%d)\n",
- this_urb, err);
+ dev_err(&port->dev,
+ "%s: submit urb %d failed: %d\n",
+ __func__, i, err);
clear_bit(i, &portdata->out_busy);
spin_lock_irqsave(&intfdata->susp_lock, flags);
intfdata->in_flight--;
@@ -314,7 +301,7 @@ static void usb_wwan_outdat_callback(struct urb *urb)
int i;
port = urb->context;
- intfdata = port->serial->private;
+ intfdata = usb_get_serial_data(port->serial);
usb_serial_port_softint(port);
usb_autopm_put_interface_async(port->serial->interface);
@@ -325,7 +312,7 @@ static void usb_wwan_outdat_callback(struct urb *urb)
for (i = 0; i < N_OUT_URB; ++i) {
if (portdata->out_urbs[i] == urb) {
- smp_mb__before_clear_bit();
+ smp_mb__before_atomic();
clear_bit(i, &portdata->out_busy);
break;
}
@@ -384,7 +371,15 @@ int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port)
struct urb *urb;
portdata = usb_get_serial_port_data(port);
- intfdata = serial->private;
+ intfdata = usb_get_serial_data(serial);
+
+ if (port->interrupt_in_urb) {
+ err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+ if (err) {
+ dev_err(&port->dev, "%s: submit int urb failed: %d\n",
+ __func__, err);
+ }
+ }
/* Start reading from the IN endpoint */
for (i = 0; i < N_IN_URB; i++) {
@@ -393,17 +388,15 @@ int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port)
continue;
err = usb_submit_urb(urb, GFP_KERNEL);
if (err) {
- dev_dbg(&port->dev, "%s: submit urb %d failed (%d) %d\n",
- __func__, i, err, urb->transfer_buffer_length);
+ dev_err(&port->dev,
+ "%s: submit read urb %d failed: %d\n",
+ __func__, i, err);
}
}
- if (intfdata->send_setup)
- intfdata->send_setup(port);
-
- serial->interface->needs_remote_wakeup = 1;
spin_lock_irq(&intfdata->susp_lock);
- portdata->opened = 1;
+ if (++intfdata->open_ports == 1)
+ serial->interface->needs_remote_wakeup = 1;
spin_unlock_irq(&intfdata->susp_lock);
/* this balances a get in the generic USB serial code */
usb_autopm_put_interface(serial->interface);
@@ -412,32 +405,56 @@ int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port)
}
EXPORT_SYMBOL(usb_wwan_open);
+static void unbusy_queued_urb(struct urb *urb,
+ struct usb_wwan_port_private *portdata)
+{
+ int i;
+
+ for (i = 0; i < N_OUT_URB; i++) {
+ if (urb == portdata->out_urbs[i]) {
+ clear_bit(i, &portdata->out_busy);
+ break;
+ }
+ }
+}
+
void usb_wwan_close(struct usb_serial_port *port)
{
int i;
struct usb_serial *serial = port->serial;
struct usb_wwan_port_private *portdata;
- struct usb_wwan_intf_private *intfdata = port->serial->private;
+ struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
+ struct urb *urb;
portdata = usb_get_serial_port_data(port);
- /* Stop reading/writing urbs */
+ /*
+ * Need to take susp_lock to make sure port is not already being
+ * resumed, but no need to hold it due to ASYNC_INITIALIZED.
+ */
spin_lock_irq(&intfdata->susp_lock);
- portdata->opened = 0;
+ if (--intfdata->open_ports == 0)
+ serial->interface->needs_remote_wakeup = 0;
spin_unlock_irq(&intfdata->susp_lock);
+ for (;;) {
+ urb = usb_get_from_anchor(&portdata->delayed);
+ if (!urb)
+ break;
+ unbusy_queued_urb(urb, portdata);
+ usb_autopm_put_interface_async(serial->interface);
+ }
+
for (i = 0; i < N_IN_URB; i++)
usb_kill_urb(portdata->in_urbs[i]);
for (i = 0; i < N_OUT_URB; i++)
usb_kill_urb(portdata->out_urbs[i]);
+ usb_kill_urb(port->interrupt_in_urb);
- /* balancing - important as an error cannot be handled*/
usb_autopm_get_interface_no_resume(serial->interface);
- serial->interface->needs_remote_wakeup = 0;
}
EXPORT_SYMBOL(usb_wwan_close);
-/* Helper functions used by usb_wwan_setup_urbs */
static struct urb *usb_wwan_setup_urb(struct usb_serial_port *port,
int endpoint,
int dir, void *ctx, char *buf, int len,
@@ -450,7 +467,6 @@ static struct urb *usb_wwan_setup_urb(struct usb_serial_port *port,
if (!urb)
return NULL;
- /* Fill URB using supplied data. */
usb_fill_bulk_urb(urb, serial->dev,
usb_sndbulkpipe(serial->dev, endpoint) | dir,
buf, len, callback, ctx);
@@ -463,7 +479,6 @@ int usb_wwan_port_probe(struct usb_serial_port *port)
struct usb_wwan_port_private *portdata;
struct urb *urb;
u8 *buffer;
- int err;
int i;
if (!port->bulk_in_size || !port->bulk_out_size)
@@ -503,13 +518,6 @@ int usb_wwan_port_probe(struct usb_serial_port *port)
usb_set_serial_port_data(port, portdata);
- if (port->interrupt_in_urb) {
- err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
- if (err)
- dev_dbg(&port->dev, "%s: submit irq_in urb failed %d\n",
- __func__, err);
- }
-
return 0;
bail_out_error2:
@@ -536,32 +544,28 @@ int usb_wwan_port_remove(struct usb_serial_port *port)
portdata = usb_get_serial_port_data(port);
usb_set_serial_port_data(port, NULL);
- /* Stop reading/writing urbs and free them */
for (i = 0; i < N_IN_URB; i++) {
- usb_kill_urb(portdata->in_urbs[i]);
usb_free_urb(portdata->in_urbs[i]);
free_page((unsigned long)portdata->in_buffer[i]);
}
for (i = 0; i < N_OUT_URB; i++) {
- usb_kill_urb(portdata->out_urbs[i]);
usb_free_urb(portdata->out_urbs[i]);
kfree(portdata->out_buffer[i]);
}
- /* Now free port private data */
kfree(portdata);
+
return 0;
}
EXPORT_SYMBOL(usb_wwan_port_remove);
#ifdef CONFIG_PM
-static void stop_read_write_urbs(struct usb_serial *serial)
+static void stop_urbs(struct usb_serial *serial)
{
int i, j;
struct usb_serial_port *port;
struct usb_wwan_port_private *portdata;
- /* Stop reading/writing urbs */
for (i = 0; i < serial->num_ports; ++i) {
port = serial->port[i];
portdata = usb_get_serial_port_data(port);
@@ -571,123 +575,117 @@ static void stop_read_write_urbs(struct usb_serial *serial)
usb_kill_urb(portdata->in_urbs[j]);
for (j = 0; j < N_OUT_URB; j++)
usb_kill_urb(portdata->out_urbs[j]);
+ usb_kill_urb(port->interrupt_in_urb);
}
}
int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message)
{
- struct usb_wwan_intf_private *intfdata = serial->private;
- int b;
+ struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
+ spin_lock_irq(&intfdata->susp_lock);
if (PMSG_IS_AUTO(message)) {
- spin_lock_irq(&intfdata->susp_lock);
- b = intfdata->in_flight;
- spin_unlock_irq(&intfdata->susp_lock);
-
- if (b)
+ if (intfdata->in_flight) {
+ spin_unlock_irq(&intfdata->susp_lock);
return -EBUSY;
+ }
}
-
- spin_lock_irq(&intfdata->susp_lock);
intfdata->suspended = 1;
spin_unlock_irq(&intfdata->susp_lock);
- stop_read_write_urbs(serial);
+
+ stop_urbs(serial);
return 0;
}
EXPORT_SYMBOL(usb_wwan_suspend);
-static void unbusy_queued_urb(struct urb *urb, struct usb_wwan_port_private *portdata)
+/* Caller must hold susp_lock. */
+static int usb_wwan_submit_delayed_urbs(struct usb_serial_port *port)
{
- int i;
-
- for (i = 0; i < N_OUT_URB; i++) {
- if (urb == portdata->out_urbs[i]) {
- clear_bit(i, &portdata->out_busy);
- break;
- }
- }
-}
-
-static void play_delayed(struct usb_serial_port *port)
-{
- struct usb_wwan_intf_private *data;
+ struct usb_serial *serial = port->serial;
+ struct usb_wwan_intf_private *data = usb_get_serial_data(serial);
struct usb_wwan_port_private *portdata;
struct urb *urb;
+ int err_count = 0;
int err;
portdata = usb_get_serial_port_data(port);
- data = port->serial->private;
- while ((urb = usb_get_from_anchor(&portdata->delayed))) {
- err = usb_submit_urb(urb, GFP_ATOMIC);
- if (!err) {
- data->in_flight++;
- } else {
- /* we have to throw away the rest */
- do {
- unbusy_queued_urb(urb, portdata);
- usb_autopm_put_interface_no_suspend(port->serial->interface);
- } while ((urb = usb_get_from_anchor(&portdata->delayed)));
+
+ for (;;) {
+ urb = usb_get_from_anchor(&portdata->delayed);
+ if (!urb)
break;
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err) {
+ dev_err(&port->dev, "%s: submit urb failed: %d\n",
+ __func__, err);
+ err_count++;
+ unbusy_queued_urb(urb, portdata);
+ usb_autopm_put_interface_async(serial->interface);
+ continue;
}
+ data->in_flight++;
}
+
+ if (err_count)
+ return -EIO;
+
+ return 0;
}
int usb_wwan_resume(struct usb_serial *serial)
{
int i, j;
struct usb_serial_port *port;
- struct usb_wwan_intf_private *intfdata = serial->private;
+ struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
struct usb_wwan_port_private *portdata;
struct urb *urb;
- int err = 0;
+ int err;
+ int err_count = 0;
- /* get the interrupt URBs resubmitted unconditionally */
+ spin_lock_irq(&intfdata->susp_lock);
for (i = 0; i < serial->num_ports; i++) {
port = serial->port[i];
- if (!port->interrupt_in_urb) {
- dev_dbg(&port->dev, "%s: No interrupt URB for port\n", __func__);
+
+ if (!test_bit(ASYNCB_INITIALIZED, &port->port.flags))
continue;
- }
- err = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO);
- dev_dbg(&port->dev, "Submitted interrupt URB for port (result %d)\n", err);
- if (err < 0) {
- dev_err(&port->dev, "%s: Error %d for interrupt URB\n",
- __func__, err);
- goto err_out;
- }
- }
- for (i = 0; i < serial->num_ports; i++) {
- /* walk all ports */
- port = serial->port[i];
portdata = usb_get_serial_port_data(port);
- /* skip closed ports */
- spin_lock_irq(&intfdata->susp_lock);
- if (!portdata || !portdata->opened) {
- spin_unlock_irq(&intfdata->susp_lock);
- continue;
+ if (port->interrupt_in_urb) {
+ err = usb_submit_urb(port->interrupt_in_urb,
+ GFP_ATOMIC);
+ if (err) {
+ dev_err(&port->dev,
+ "%s: submit int urb failed: %d\n",
+ __func__, err);
+ err_count++;
+ }
}
+ err = usb_wwan_submit_delayed_urbs(port);
+ if (err)
+ err_count++;
+
for (j = 0; j < N_IN_URB; j++) {
urb = portdata->in_urbs[j];
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err < 0) {
- dev_err(&port->dev, "%s: Error %d for bulk URB %d\n",
- __func__, err, i);
- spin_unlock_irq(&intfdata->susp_lock);
- goto err_out;
+ dev_err(&port->dev,
+ "%s: submit read urb %d failed: %d\n",
+ __func__, i, err);
+ err_count++;
}
}
- play_delayed(port);
- spin_unlock_irq(&intfdata->susp_lock);
}
- spin_lock_irq(&intfdata->susp_lock);
intfdata->suspended = 0;
spin_unlock_irq(&intfdata->susp_lock);
-err_out:
- return err;
+
+ if (err_count)
+ return -EIO;
+
+ return 0;
}
EXPORT_SYMBOL(usb_wwan_resume);
#endif