Subject: [PATCH 001/001] USB: serial: sierra driver memory reduction From: Elina Pasheva <epasheva@xxxxxxxxxxxxxxxxxx> This patch deals with reducing the memory footprint for sierra driver. This optimization is aimed for embedded software customers. The interfaces with low memory requirements are assigned less memory (in number of urbs) thus reducing the overall memory footprint of the driver. Applying this patch to the current sierra.c driver in the kernel will make the source identical with the version available from the sierra wireless knowledgebase website. That is why the version number is being changed to 1.7.12 as part of this patch. Signed-off-by: Elina Pasheva <epasheva@xxxxxxxxxxxxxxxxxx> --- drivers/usb/serial/sierra.c | 96 +++++++++++++++++++++++++++++----- 1 file changed, 82 insertions(+), 14 deletions(-) --- a/drivers/usb/serial/sierra.c 2009-10-08 14:37:01.000000000 -0700 +++ b/drivers/usb/serial/sierra.c 2009-10-08 14:52:39.000000000 -0700 @@ -16,8 +16,9 @@ Portions based on the option driver by Matthias Urlichs <smurf@xxxxxxxxxxxxxx> Whom based his on the Keyspan driver by Hugh Blemings <hugh@xxxxxxxxxxxx> */ - -#define DRIVER_VERSION "v.1.3.7" +/* Uncomment to log function calls */ +/* #define DEBUG */ +#define DRIVER_VERSION "v.1.7.12" #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_seria 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) */ @@ -456,7 +492,7 @@ static int sierra_write(struct tty_struc 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; @@ -661,7 +697,9 @@ static int sierra_write_room(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) { + dev_dbg(&port->dev, "%s - outstanding_urbs: %d\n", __func__, + portdata->outstanding_urbs); + 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; @@ -676,7 +714,7 @@ static void sierra_stop_rx_urbs(struct u 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); @@ -691,7 +729,7 @@ static int sierra_submit_rx_urbs(struct 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; @@ -784,7 +822,7 @@ static void sierra_close(struct usb_seri /* 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; } @@ -813,7 +851,7 @@ static int sierra_open(struct tty_struct 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); @@ -825,6 +863,8 @@ static int sierra_open(struct tty_struct err = sierra_submit_rx_urbs(port, GFP_KERNEL); if (err) { + /* restore balance for autopm */ + usb_autopm_put_interface(serial->interface); /* get rid of everything as in close */ sierra_close(port); return err; @@ -862,7 +902,9 @@ static int sierra_startup(struct usb_ser { 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,6 +928,33 @@ static int sierra_startup(struct usb_ser 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); } @@ -915,7 +984,7 @@ static void sierra_release(struct usb_se #ifdef CONFIG_PM static void stop_read_write_urbs(struct usb_serial *serial) { - int i, j; + int i; struct usb_serial_port *port; struct sierra_port_private *portdata; @@ -923,8 +992,7 @@ static void stop_read_write_urbs(struct for (i = 0; i < serial->num_ports; ++i) { port = serial->port[i]; portdata = usb_get_serial_port_data(port); - for (j = 0; j < N_IN_URB; j++) - usb_kill_urb(portdata->in_urbs[j]); + sierra_stop_rx_urbs(port); usb_kill_anchored_urbs(&portdata->active); } } -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html