|
@@ -16,8 +16,9 @@
|
|
|
Portions based on the option driver by Matthias Urlichs <smurf@smurf.noris.de>
|
|
|
Whom based his on the Keyspan driver by Hugh Blemings <hugh@blemings.org>
|
|
|
*/
|
|
|
-
|
|
|
-#define DRIVER_VERSION "v.1.3.8"
|
|
|
+/* Uncomment to log function calls */
|
|
|
+/* #define DEBUG */
|
|
|
+#define DRIVER_VERSION "v.1.7.16"
|
|
|
#define DRIVER_AUTHOR "Kevin Lloyd, Elina Pasheva, Matthew Safar, Rory Filer"
|
|
|
#define DRIVER_DESC "USB Driver for Sierra Wireless USB modems"
|
|
|
|
|
@@ -33,8 +34,10 @@
|
|
|
#define SWIMS_USB_REQUEST_SetPower 0x00
|
|
|
#define SWIMS_USB_REQUEST_SetNmea 0x07
|
|
|
|
|
|
-#define N_IN_URB 8
|
|
|
-#define N_OUT_URB 64
|
|
|
+#define N_IN_URB_HM 8
|
|
|
+#define N_OUT_URB_HM 64
|
|
|
+#define N_IN_URB 4
|
|
|
+#define N_OUT_URB 4
|
|
|
#define IN_BUFLEN 4096
|
|
|
|
|
|
#define MAX_TRANSFER (PAGE_SIZE - 512)
|
|
@@ -124,6 +127,23 @@ static int is_blacklisted(const u8 ifnum,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int is_himemory(const u8 ifnum,
|
|
|
+ const struct sierra_iface_info *himemorylist)
|
|
|
+{
|
|
|
+ const u8 *info;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (himemorylist) {
|
|
|
+ info = himemorylist->ifaceinfo;
|
|
|
+
|
|
|
+ for (i=0; i < himemorylist->infolen; i++) {
|
|
|
+ if (info[i] == ifnum)
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int sierra_calc_interface(struct usb_serial *serial)
|
|
|
{
|
|
|
int interface;
|
|
@@ -186,6 +206,20 @@ static int sierra_probe(struct usb_serial *serial,
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+/* interfaces with higher memory requirements */
|
|
|
+static const u8 hi_memory_typeA_ifaces[] = { 0, 2 };
|
|
|
+static const struct sierra_iface_info typeA_interface_list = {
|
|
|
+ .infolen = ARRAY_SIZE(hi_memory_typeA_ifaces),
|
|
|
+ .ifaceinfo = hi_memory_typeA_ifaces,
|
|
|
+};
|
|
|
+
|
|
|
+static const u8 hi_memory_typeB_ifaces[] = { 3, 4, 5, 6 };
|
|
|
+static const struct sierra_iface_info typeB_interface_list = {
|
|
|
+ .infolen = ARRAY_SIZE(hi_memory_typeB_ifaces),
|
|
|
+ .ifaceinfo = hi_memory_typeB_ifaces,
|
|
|
+};
|
|
|
+
|
|
|
+/* 'blacklist' of interfaces not served by this driver */
|
|
|
static const u8 direct_ip_non_serial_ifaces[] = { 7, 8, 9, 10, 11 };
|
|
|
static const struct sierra_iface_info direct_ip_interface_blacklist = {
|
|
|
.infolen = ARRAY_SIZE(direct_ip_non_serial_ifaces),
|
|
@@ -286,8 +320,10 @@ struct sierra_port_private {
|
|
|
struct usb_anchor active;
|
|
|
struct usb_anchor delayed;
|
|
|
|
|
|
+ int num_out_urbs;
|
|
|
+ int num_in_urbs;
|
|
|
/* Input endpoints and buffers for this port */
|
|
|
- struct urb *in_urbs[N_IN_URB];
|
|
|
+ struct urb *in_urbs[N_IN_URB_HM];
|
|
|
|
|
|
/* Settings for the port */
|
|
|
int rts_state; /* Handshaking pins (outputs) */
|
|
@@ -460,7 +496,7 @@ static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port,
|
|
|
spin_lock_irqsave(&portdata->lock, flags);
|
|
|
dev_dbg(&port->dev, "%s - outstanding_urbs: %d\n", __func__,
|
|
|
portdata->outstanding_urbs);
|
|
|
- if (portdata->outstanding_urbs > N_OUT_URB) {
|
|
|
+ if (portdata->outstanding_urbs > portdata->num_out_urbs) {
|
|
|
spin_unlock_irqrestore(&portdata->lock, flags);
|
|
|
dev_dbg(&port->dev, "%s - write limit hit\n", __func__);
|
|
|
return 0;
|
|
@@ -665,7 +701,7 @@ static int sierra_write_room(struct tty_struct *tty)
|
|
|
/* try to give a good number back based on if we have any free urbs at
|
|
|
* this point in time */
|
|
|
spin_lock_irqsave(&portdata->lock, flags);
|
|
|
- if (portdata->outstanding_urbs > N_OUT_URB * 2 / 3) {
|
|
|
+ if (portdata->outstanding_urbs > (portdata->num_out_urbs * 2) / 3) {
|
|
|
spin_unlock_irqrestore(&portdata->lock, flags);
|
|
|
dev_dbg(&port->dev, "%s - write limit hit\n", __func__);
|
|
|
return 0;
|
|
@@ -680,7 +716,7 @@ static void sierra_stop_rx_urbs(struct usb_serial_port *port)
|
|
|
int i;
|
|
|
struct sierra_port_private *portdata = usb_get_serial_port_data(port);
|
|
|
|
|
|
- for (i = 0; i < ARRAY_SIZE(portdata->in_urbs); i++)
|
|
|
+ for (i = 0; i < portdata->num_in_urbs; i++)
|
|
|
usb_kill_urb(portdata->in_urbs[i]);
|
|
|
|
|
|
usb_kill_urb(port->interrupt_in_urb);
|
|
@@ -695,7 +731,7 @@ static int sierra_submit_rx_urbs(struct usb_serial_port *port, gfp_t mem_flags)
|
|
|
struct sierra_port_private *portdata = usb_get_serial_port_data(port);
|
|
|
|
|
|
ok_cnt = 0;
|
|
|
- for (i = 0; i < ARRAY_SIZE(portdata->in_urbs); i++) {
|
|
|
+ for (i = 0; i < portdata->num_in_urbs; i++) {
|
|
|
urb = portdata->in_urbs[i];
|
|
|
if (!urb)
|
|
|
continue;
|
|
@@ -791,7 +827,7 @@ static void sierra_close(struct usb_serial_port *port)
|
|
|
/* Stop reading urbs */
|
|
|
sierra_stop_rx_urbs(port);
|
|
|
/* .. and release them */
|
|
|
- for (i = 0; i < N_IN_URB; i++) {
|
|
|
+ for (i = 0; i < portdata->num_in_urbs; i++) {
|
|
|
sierra_release_urb(portdata->in_urbs[i]);
|
|
|
portdata->in_urbs[i] = NULL;
|
|
|
}
|
|
@@ -818,7 +854,7 @@ static int sierra_open(struct tty_struct *tty, struct usb_serial_port *port)
|
|
|
|
|
|
|
|
|
endpoint = port->bulk_in_endpointAddress;
|
|
|
- for (i = 0; i < ARRAY_SIZE(portdata->in_urbs); i++) {
|
|
|
+ for (i = 0; i < portdata->num_in_urbs; i++) {
|
|
|
urb = sierra_setup_urb(serial, endpoint, USB_DIR_IN, port,
|
|
|
IN_BUFLEN, GFP_KERNEL,
|
|
|
sierra_indat_callback);
|
|
@@ -869,7 +905,9 @@ static int sierra_startup(struct usb_serial *serial)
|
|
|
{
|
|
|
struct usb_serial_port *port;
|
|
|
struct sierra_port_private *portdata;
|
|
|
+ struct sierra_iface_info *himemoryp = NULL;
|
|
|
int i;
|
|
|
+ u8 ifnum;
|
|
|
|
|
|
dev_dbg(&serial->dev->dev, "%s\n", __func__);
|
|
|
|
|
@@ -886,13 +924,40 @@ static int sierra_startup(struct usb_serial *serial)
|
|
|
portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
|
|
|
if (!portdata) {
|
|
|
dev_dbg(&port->dev, "%s: kmalloc for "
|
|
|
- "sierra_port_private (%d) failed!.\n",
|
|
|
+ "sierra_port_private (%d) failed!\n",
|
|
|
__func__, i);
|
|
|
return -ENOMEM;
|
|
|
}
|
|
|
spin_lock_init(&portdata->lock);
|
|
|
init_usb_anchor(&portdata->active);
|
|
|
init_usb_anchor(&portdata->delayed);
|
|
|
+ ifnum = i;
|
|
|
+ /* Assume low memory requirements */
|
|
|
+ portdata->num_out_urbs = N_OUT_URB;
|
|
|
+ portdata->num_in_urbs = N_IN_URB;
|
|
|
+
|
|
|
+ /* Determine actual memory requirements */
|
|
|
+ if (serial->num_ports == 1) {
|
|
|
+ /* Get interface number for composite device */
|
|
|
+ ifnum = sierra_calc_interface(serial);
|
|
|
+ himemoryp =
|
|
|
+ (struct sierra_iface_info *)&typeB_interface_list;
|
|
|
+ if (is_himemory(ifnum, himemoryp)) {
|
|
|
+ portdata->num_out_urbs = N_OUT_URB_HM;
|
|
|
+ portdata->num_in_urbs = N_IN_URB_HM;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ himemoryp =
|
|
|
+ (struct sierra_iface_info *)&typeA_interface_list;
|
|
|
+ if (is_himemory(i, himemoryp)) {
|
|
|
+ portdata->num_out_urbs = N_OUT_URB_HM;
|
|
|
+ portdata->num_in_urbs = N_IN_URB_HM;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ dev_dbg(&serial->dev->dev,
|
|
|
+ "Memory usage (urbs) interface #%d, in=%d, out=%d\n",
|
|
|
+ ifnum,portdata->num_in_urbs, portdata->num_out_urbs );
|
|
|
/* Set the port private data pointer */
|
|
|
usb_set_serial_port_data(port, portdata);
|
|
|
}
|