WiMAX patches for 2.6.33 (batch #3)
Subsystems (3)
cover WiMAX patches for 2.6.33 (batch #3)
[this series of patches has been split in four batches as it was too long; the grouping is just sequential as they are not really related, except for dependencies].
Add support for two new SKUs of the USB and SDIO devices. USB: fix resume timing out when requesting firmware (caching it); add reset_resume support. Initialization sequence cleanup, introducing bus_setup/bus_release() function pointers to allow simpler .pre and .post reset implementations that move complexity away from the bus specific subdrivers.
Please pull from:
git://git.kernel.org/pub/scm/linux/kernel/git/inaky/wimax.git
Patches follow for ease of review.
Cindy H Kao (1):
wimax/iwmc3200: add new sdio device ID to support iwmc3200 2.5GHz sku
Dirk Brandewie (1):
wimax/i6x50: add Intel WiFi/WiMAX Link 6050 Series support
Inaky Perez-Gonzalez (11):
wimax/i2400m: clean up & add a payload argument to
i2400m_schedule_work()
wimax/i2400m: add reason argument to i2400m_dev_reset_handle() wimax/i2400m: cache firmware on system suspend wimax/i2400m: implement .reset_resume in USB subdriver wimax/i2400m: don't overwrite error codes when failing to load
firmware
wimax/i2400m: on device stop, clean up pending wake & TX work wimax/i2400m: cleanup initialization/destruction flow wimax/i2400m: clarify and fix i2400m->{ready,updown} wimax/i2400m: introduce i2400m->bus_setup/release wimax/i2400m: do bootmode buffer management in i2400m_setup/release() wimax/i2400m: Implement pre/post reset support in the USB driver
drivers/net/wimax/i2400m/driver.c | 332 ++++++++++++++++++++++++++------- drivers/net/wimax/i2400m/fw.c | 163 +++++++++++++++-- drivers/net/wimax/i2400m/i2400m-usb.h | 3 + drivers/net/wimax/i2400m/i2400m.h | 109 +++++++++-- drivers/net/wimax/i2400m/netdev.c | 63 ++++--- drivers/net/wimax/i2400m/rx.c | 16 +- drivers/net/wimax/i2400m/sdio-rx.c | 2 +- drivers/net/wimax/i2400m/sdio.c | 116 +++++++----- drivers/net/wimax/i2400m/usb-notif.c | 2 +- drivers/net/wimax/i2400m/usb.c | 92 ++++++++-- include/linux/mmc/sdio_ids.h | 1 + include/linux/wimax/i2400m.h | 1 + include/net/wimax.h | 6 + 13 files changed, 705 insertions(+), 201 deletions(-)
PATCH 1/15 wimax/i2400m: USB driver uses a configurable endpoint map
From: Dirk Brandewie <[email protected]>
Newer generations of the i2400m USB WiMAX device use a different endpoint map; in order to make it easy to support it, we make the endpoint-to-function mapeable instead of static.
--- drivers/net/wimax/i2400m/i2400m-usb.h | 14 ++++++++------ drivers/net/wimax/i2400m/usb-fw.c | 5 +++-- drivers/net/wimax/i2400m/usb-notif.c | 3 ++- drivers/net/wimax/i2400m/usb-rx.c | 2 +- drivers/net/wimax/i2400m/usb-tx.c | 2 +- drivers/net/wimax/i2400m/usb.c | 20 ++++++++++++++------ 6 files changed, 29 insertions(+), 17 deletions(-)
drivers/net/wimax/i2400m/i2400m-usb.h +8 -6
@@ -88,6 +88,13 @@ struct edc {u16 errorcount; }; +struct i2400m_endpoint_cfg { +unsigned char bulk_out; +unsigned char notification; +unsigned char reset_cold; +unsigned char bulk_in; +}; +static inline void edc_init (struct edc *edc) { edc->timestart = jiffies;
@@ -141,12 +148,6 @@ enum {I2400MU_MAX_NOTIFICATION_LEN =256 ,I2400MU_BLK_SIZE =16 , I2400MU_PL_SIZE_MAX = 0x3EFF, - -/* Endpoints */ -I2400MU_EP_BULK_OUT =0 , -I2400MU_EP_NOTIFICATION , -I2400MU_EP_RESET_COLD , - I2400MU_EP_BULK_IN, };
@@ -216,6 +217,7 @@ struct i2400mu {struct usb_device *usb_dev;struct usb_interface *usb_iface;struct edc urb_edc;/* Error density counter */ +struct i2400m_endpoint_cfg endpoint_cfg;struct urb *notif_urb;struct task_struct *tx_kthread;
drivers/net/wimax/i2400m/usb-fw.c +3 -2
@@ -99,7 +99,7 @@ ssize_t i2400mu_tx_bulk_out(struct i2400mu *i2400mu, void *buf, size_t buf_size)dev_err (dev,"BM-CMD: can't get autopm: %d\n" , result); do_autopm =0 ; } - epd =usb_get_epd (i2400mu->usb_iface,I2400MU_EP_BULK_OUT ); + epd =usb_get_epd (i2400mu->usb_iface, i2400mu->endpoint_cfg.bulk_out); pipe =usb_sndbulkpipe (i2400mu->usb_dev, epd->bEndpointAddress); retry: result =usb_bulk_msg (i2400mu->usb_dev, pipe, buf, buf_size, &len,HZ );
@@ -226,7 +226,8 @@ int i2400mu_notif_submit(struct i2400mu *i2400mu, struct urb *urb,struct usb_endpoint_descriptor *epd;int pipe; - epd =usb_get_epd (i2400mu->usb_iface,I2400MU_EP_NOTIFICATION ); + epd =usb_get_epd (i2400mu->usb_iface, + i2400mu->endpoint_cfg.notification); pipe =usb_rcvintpipe (i2400mu->usb_dev, epd->bEndpointAddress); usb_fill_int_urb(urb, i2400mu->usb_dev, pipe, i2400m->bm_ack_buf, I2400M_BM_ACK_BUF_SIZE,
drivers/net/wimax/i2400m/usb-notif.c +2 -1
@@ -220,7 +220,8 @@ int i2400mu_notification_setup(struct i2400mu *i2400mu)dev_err (dev,"notification: cannot allocate URB\n" ); goto error_alloc_urb; } - epd =usb_get_epd (i2400mu->usb_iface,I2400MU_EP_NOTIFICATION ); + epd =usb_get_epd (i2400mu->usb_iface, + i2400mu->endpoint_cfg.notification); usb_pipe =usb_rcvintpipe (i2400mu->usb_dev, epd->bEndpointAddress); usb_fill_int_urb(i2400mu->notif_urb, i2400mu->usb_dev, usb_pipe, buf, I2400MU_MAX_NOTIFICATION_LEN,
drivers/net/wimax/i2400m/usb-rx.c +1 -1
@@ -204,7 +204,7 @@ struct sk_buff *i2400mu_rx(struct i2400mu *i2400mu, struct sk_buff *rx_skb)dev_err (dev,"RX: can't get autopm: %d\n" , result); do_autopm =0 ; } - epd =usb_get_epd (i2400mu->usb_iface,I2400MU_EP_BULK_IN ); + epd =usb_get_epd (i2400mu->usb_iface, i2400mu->endpoint_cfg.bulk_in); usb_pipe =usb_rcvbulkpipe (i2400mu->usb_dev, epd->bEndpointAddress); retry: rx_size =skb_end_pointer (rx_skb) - rx_skb->data - rx_skb->len;
drivers/net/wimax/i2400m/usb-tx.c +1 -1
@@ -101,7 +101,7 @@ int i2400mu_tx(struct i2400mu *i2400mu, struct i2400m_msg_hdr *tx_msg,dev_err (dev,"TX: can't get autopm: %d\n" , result); do_autopm =0 ; } - epd =usb_get_epd (i2400mu->usb_iface,I2400MU_EP_BULK_OUT ); + epd =usb_get_epd (i2400mu->usb_iface, i2400mu->endpoint_cfg.bulk_out); usb_pipe =usb_sndbulkpipe (i2400mu->usb_dev, epd->bEndpointAddress); retry: result = usb_bulk_msg(i2400mu->usb_dev, usb_pipe,
drivers/net/wimax/i2400m/usb.c +14 -6
@@ -232,13 +232,15 @@ int i2400mu_bus_reset(struct i2400m *i2400m, enum i2400m_reset_type rt)d_fnstart (3 , dev,"(i2400m %p rt %u)\n" , i2400m, rt);if (rt ==I2400M_RT_WARM ) - result =__i2400mu_send_barker (i2400mu, i2400m_WARM_BOOT_BARKER, -sizeof (i2400m_WARM_BOOT_BARKER), -I2400MU_EP_BULK_OUT ); + result =__i2400mu_send_barker ( + i2400mu, i2400m_WARM_BOOT_BARKER, +sizeof (i2400m_WARM_BOOT_BARKER), + i2400mu->endpoint_cfg.bulk_out);else if (rt ==I2400M_RT_COLD ) - result =__i2400mu_send_barker (i2400mu, i2400m_COLD_BOOT_BARKER, -sizeof (i2400m_COLD_BOOT_BARKER), -I2400MU_EP_RESET_COLD ); + result =__i2400mu_send_barker ( + i2400mu, i2400m_COLD_BOOT_BARKER, +sizeof (i2400m_COLD_BOOT_BARKER), + i2400mu->endpoint_cfg.reset_cold);else if (rt ==I2400M_RT_BUS ) { do_bus_reset: result =usb_reset_device (i2400mu->usb_dev);
@@ -412,6 +414,12 @@ int i2400mu_probe(struct usb_interface *iface, i2400m->bus_fw_names = i2400mu_bus_fw_names; i2400m->bus_bm_mac_addr_impaired =0 ; + { + i2400mu->endpoint_cfg.bulk_out =0 ; + i2400mu->endpoint_cfg.notification =1 ; + i2400mu->endpoint_cfg.reset_cold =2 ; + i2400mu->endpoint_cfg.bulk_in =3 ; + }#ifdef CONFIG_PM iface->needs_remote_wakeup =1 ;/* autosuspend (15s delay) */ device_init_wakeup (dev,1 );
Signature · 1 line
1.6.2.5
PATCH 2/15 wimax/i2400m/sdio: clear the INTR status bit after reading size
In reply to Inaky Perez-GonzalezFrom: Cindy H Kao <[email protected]>
In order to avoid issues during high-load traffic, the interrupt status register has to be cleared ONLY after the RX size is read.
--- drivers/net/wimax/i2400m/sdio-rx.c | 6 +++++- 1 files changed, 5 insertions(+), 1 deletions(-)
drivers/net/wimax/i2400m/sdio-rx.c +5 -1
@@ -138,6 +138,11 @@ void i2400ms_rx(struct i2400ms *i2400ms) ret = rx_size; goto error_get_size; } +/* +* Hardware quirk: make sure to clear the INTR status register +* AFTER getting the data transfer size. +*/ +sdio_writeb (func,1 ,I2400MS_INTR_CLEAR_ADDR , &ret); ret = -ENOMEM ; skb =alloc_skb (rx_size,GFP_ATOMIC );
@@ -209,7 +214,6 @@ void i2400ms_irq(struct sdio_func *func)dev_err (dev,"RX: BUG? got IRQ but no interrupt ready?\n" ); goto error_no_irq; } -sdio_writeb (func,1 ,I2400MS_INTR_CLEAR_ADDR , &ret);i2400ms_rx (i2400ms); error_no_irq:d_fnend (6 , dev,"(i2400ms %p) = void\n" , i2400ms);
Signature · 1 line
1.6.2.5
PATCH 3/15 wimax/i2400m: be smarter about copying command buffer to bm_cmd_buf
In reply to Inaky Perez-GonzalezBecause some underlying bus APIs (like USB) don't like data buffers in the stack or vmalloced areas, the i2400m driver provides a scratch buffer (i2400m->bm_cmd_buf) for said low-level drivers to copy command data to before passing it to said API. This is only used during boot mode.
However, at some the code was copying the buffer even when the command was already specified in said buffer. This is ok, but it needs to be more careful. As thus, change so that:
(a) the copy happens only if command buffer is not the scratch buffer
(b) use memmove() in case there is overlapping
--- drivers/net/wimax/i2400m/fw.c | 1 - drivers/net/wimax/i2400m/sdio-fw.c | 3 ++- drivers/net/wimax/i2400m/usb-fw.c | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-)
drivers/net/wimax/i2400m/fw.c +0 -1
@@ -343,7 +343,6 @@ ssize_t i2400m_bm_cmd(struct i2400m *i2400m,BUG_ON (i2400m->boot_mode ==0 );if (cmd !=NULL ) {/* send the command */ -memcpy (i2400m->bm_cmd_buf, cmd, cmd_size); result = i2400m->bus_bm_cmd_send (i2400m, cmd, cmd_size, flags);if (result <0 ) goto error_cmd_send;
drivers/net/wimax/i2400m/sdio-fw.c +2 -1
@@ -118,7 +118,8 @@ ssize_t i2400ms_bus_bm_cmd_send(struct i2400m *i2400m,if (cmd_size >I2400M_BM_CMD_BUF_SIZE ) goto error_too_big; -memcpy (i2400m->bm_cmd_buf, _cmd, cmd_size);/* Prep command */ +if (_cmd != i2400m->bm_cmd_buf) +memmove (i2400m->bm_cmd_buf, _cmd, cmd_size); cmd = i2400m->bm_cmd_buf;if (cmd_size_a > cmd_size)/* Zero pad space */ memset (i2400m->bm_cmd_buf + cmd_size,0 , cmd_size_a - cmd_size);
drivers/net/wimax/i2400m/usb-fw.c +2 -1
@@ -172,7 +172,8 @@ ssize_t i2400mu_bus_bm_cmd_send(struct i2400m *i2400m, result = -E2BIG ;if (cmd_size >I2400M_BM_CMD_BUF_SIZE ) goto error_too_big; -memcpy (i2400m->bm_cmd_buf, _cmd, cmd_size); +if (_cmd != i2400m->bm_cmd_buf) +memmove (i2400m->bm_cmd_buf, _cmd, cmd_size); cmd = i2400m->bm_cmd_buf;if (cmd_size_a > cmd_size)/* Zero pad space */ memset (i2400m->bm_cmd_buf + cmd_size,0 , cmd_size_a - cmd_size);
Signature · 1 line
1.6.2.5
PATCH 4/15 wimax/i2400m: don't write to memory allocated by request_firmware()
In reply to Inaky Perez-GonzalezFrom: Cindy H Kao <[email protected]>
In kernel 2.6.31, the firmware requested to ram could be marked with read only attribute, and we can't write any thing directly to the memory when setting up the last JUMP brh cmd.
Changed so that the scratch buffer is used.
--- drivers/net/wimax/i2400m/fw.c | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-)
drivers/net/wimax/i2400m/fw.c +4 -0
@@ -533,6 +533,10 @@ int i2400m_dnload_finalize(struct i2400m *i2400m,struct i2400m_bootrom_header jump_ack;d_printf (1 , dev,"unsecure boot, jumping to 0x%08x\n" ,le32_to_cpu (cmd->target_addr)); + cmd_buf = i2400m->bm_cmd_buf; +memcpy (&cmd_buf->cmd, cmd,sizeof (*cmd)); + cmd = &cmd_buf->cmd; +/* now cmd points to the actual bootrom_header in cmd_buf */ i2400m_brh_set_opcode (cmd,I2400M_BRH_JUMP ); cmd->data_size =0 ; ret = i2400m_bm_cmd(i2400m, cmd, sizeof(*cmd),
Signature · 1 line
1.6.2.5
PATCH 5/15 wimax/i2400m: during probe, call sdio_disable at most once
In reply to Inaky Perez-GonzalezIn the Intel Wireless Multicomm 3200, the initialization is orchestrated by a component called Top. This component also monitors how many times a function is reset (via sdio_disable) to detect possible issues and will reset the whole multifunction device if any function triggers a maximum reset level.
During WiMAX's probe, the driver needs to wait for Top to come up before it can enable the WiMAX function. If it cannot, it will return -ENODEV and the Top driver will rescan the SDIO bus once done loading.
Currently, the WiMAX SDIO probe routine was trying a few times before returning -ENODEV, and this was triggering Top's too-many-resets detector. This is, in any case, unnecessary because the Top driver will force the bus rescan when the functions can be probed successfully.
Added then a maxtries argument to i2400ms_enable_func() and set it to 1 when calling from probe. We want to reuse this function instead of flat calling out sdio_enable_func() to take advantage of hardware quirk workarounds.
--- drivers/net/wimax/i2400m/sdio.c | 16 +++++++++++++--- 1 files changed, 13 insertions(+), 3 deletions(-)
drivers/net/wimax/i2400m/sdio.c +13 -3
@@ -95,17 +95,23 @@ static const struct i2400m_poke_table i2400ms_pokes[] = {* when we ask it to explicitly doing). Tries until a timeout is * reached. * +* The @maxtries argument indicates how many times (at most) it should +* be tried to enable the function. 0 means forever. This acts along +* with the timeout (ie: it'll stop trying as soon as the maximum +* number of tries is reached _or_ as soon as the timeout is reached). +* * The reverse of this is...sdio_disable_function() * * Returns: 0 if the SDIO function was enabled, < 0 errno code on * error (-ENODEV when it was unable to enable the function). */ static -int i2400ms_enable_function (struct sdio_func *func) +int i2400ms_enable_function (struct sdio_func *func,unsigned maxtries) {u64 timeout;int err;struct device *dev = &func->dev; +unsigned tries =0 ;d_fnstart (3 , dev,"(func %p)\n" , func);/* Setup timeout (FIXME: This needs to read the CIS table to
@@ -131,6 +137,10 @@ int i2400ms_enable_function(struct sdio_func *func) }d_printf (2 , dev,"SDIO function failed to enable: %d\n" , err);sdio_release_host (func); +if (maxtries >0 && ++tries >= maxtries) { + err = -ETIME ; +break ; + }msleep (I2400MS_INIT_SLEEP_INTERVAL ); }/* If timed out, device is not there yet -- get -ENODEV so
@@ -305,7 +315,7 @@ do_bus_reset:/* Wait for the device to settle */ msleep (40 ); - result =i2400ms_enable_function (i2400ms->func); + result =i2400ms_enable_function (i2400ms->func,0 );if (result >=0 )i2400ms_rx_setup (i2400ms); }else
@@ -452,7 +462,7 @@ int i2400ms_probe(struct sdio_func *func, goto error_set_blk_size; } - result =i2400ms_enable_function (i2400ms->func); + result =i2400ms_enable_function (i2400ms->func,1 );if (result <0 ) {dev_err (dev,"Cannot enable SDIO function: %d\n" , result); goto error_func_enable;
Signature · 1 line
1.6.2.5
PATCH 6/15 wimax/i2400m: add missing debug submodule definition
In reply to Inaky Perez-GonzalezThe i2400m driver was missing the definition for the sysfs debug level, which is declared in debug-levels.h.
--- drivers/net/wimax/i2400m/driver.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
drivers/net/wimax/i2400m/driver.c +1 -0
@@ -785,6 +785,7 @@ struct d_level D_LEVEL[] = {D_SUBMODULE_DEFINE (netdev),D_SUBMODULE_DEFINE (rfkill),D_SUBMODULE_DEFINE (rx), +D_SUBMODULE_DEFINE (sysfs), D_SUBMODULE_DEFINE(tx), };size_t D_LEVEL_SIZE =ARRAY_SIZE (D_LEVEL );
Signature · 1 line
1.6.2.5
PATCH 7/15 wimax: allow specifying debug levels as command line option
In reply to Inaky Perez-GonzalezAdd "debug" module options to all the wimax modules (including drivers) so that the debug levels can be set upon kernel boot or module load time.
This is needed as currently there was a limitation where the debug levels could only be set when a device was succesfully enumerated. This made it difficult to debug issues that made a device not probe properly.
--- drivers/net/wimax/i2400m/driver.c | 10 +++++ drivers/net/wimax/i2400m/sdio.c | 10 +++++ drivers/net/wimax/i2400m/usb.c | 9 +++++ include/linux/wimax/debug.h | 72 +++++++++++++++++++++++++++++++++++++ net/wimax/stack.c | 11 ++++++ 5 files changed, 112 insertions(+), 0 deletions(-)
drivers/net/wimax/i2400m/driver.c +10 -0
@@ -90,6 +90,14 @@ MODULE_PARM_DESC(power_save_disabled,"False by default (so the device is told to do power " "saving)."); +static char i2400m_debug_params[128 ]; +module_param_string (debug, i2400m_debug_params,sizeof (i2400m_debug_params), +0644 ); +MODULE_PARM_DESC (debug, +"String of space-separated NAME:VALUE pairs, where NAMEs " +"are the different debug submodules and VALUE are the " +"initial debug value to set." ); +/** * i2400m_queue_work - schedule work on a i2400m's queue *
@@ -794,6 +802,8 @@ size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL);static int __init i2400m_driver_init (void ) { +d_parse_params (D_LEVEL ,D_LEVEL_SIZE , i2400m_debug_params, +"i2400m.debug" );return 0 ; }module_init (i2400m_driver_init);
drivers/net/wimax/i2400m/sdio.c +10 -0
@@ -71,6 +71,14 @@static int ioe_timeout =2 ;module_param (ioe_timeout, int,0 ); +static char i2400ms_debug_params[128 ]; +module_param_string (debug, i2400ms_debug_params,sizeof (i2400ms_debug_params), +0644 ); +MODULE_PARM_DESC (debug, +"String of space-separated NAME:VALUE pairs, where NAMEs " +"are the different debug submodules and VALUE are the " +"initial debug value to set." ); +/* Our firmware file name list */ static const char *i2400ms_bus_fw_names[] = { #define I2400MS_FW_FILE_NAME "i2400m-fw-sdio-1.3.sbcf"
@@ -559,6 +567,8 @@ struct sdio_driver i2400m_sdio_driver = {static int __init i2400ms_driver_init (void ) { +d_parse_params (D_LEVEL ,D_LEVEL_SIZE , i2400ms_debug_params, +"i2400m_sdio.debug" );return sdio_register_driver (&i2400m_sdio_driver); }module_init (i2400ms_driver_init);
drivers/net/wimax/i2400m/usb.c +9 -0
@@ -71,6 +71,13 @@#define D_SUBMODULE usb#include "usb-debug-levels.h" +static char i2400mu_debug_params[128 ]; +module_param_string (debug, i2400mu_debug_params,sizeof (i2400mu_debug_params), +0644 ); +MODULE_PARM_DESC (debug, +"String of space-separated NAME:VALUE pairs, where NAMEs " +"are the different debug submodules and VALUE are the " +"initial debug value to set." );/* Our firmware file name */ static const char *i2400mu_bus_fw_names[] = {
@@ -633,6 +640,8 @@ struct usb_driver i2400mu_driver = {static int __init i2400mu_driver_init (void ) { +d_parse_params (D_LEVEL ,D_LEVEL_SIZE , i2400mu_debug_params, +"i2400m_usb.debug" );return usb_register (&i2400mu_driver); }module_init (i2400mu_driver_init);
include/linux/wimax/debug.h +72 -0
76 lines not on this page · raw message
net/wimax/stack.c +11 -0
@@ -60,6 +60,14 @@#define D_SUBMODULE stack#include "debug-levels.h" +static char wimax_debug_params[128 ]; +module_param_string (debug, wimax_debug_params,sizeof (wimax_debug_params), +0644 ); +MODULE_PARM_DESC (debug, +"String of space-separated NAME:VALUE pairs, where NAMEs " +"are the different debug submodules and VALUE are the " +"initial debug value to set." ); +/* * Authoritative source for the RE_STATE_CHANGE attribute policy *
@@ -562,6 +570,9 @@ int __init wimax_subsys_init(void)int result, cnt;d_fnstart (4 ,NULL ,"()\n" ); +d_parse_params (D_LEVEL ,D_LEVEL_SIZE , wimax_debug_params, +"wimax.debug" ); +snprintf (wimax_gnl_family.name,sizeof (wimax_gnl_family.name),"WiMAX" ); result =genl_register_family (&wimax_gnl_family);
Signature · 1 line
1.6.2.5
PATCH 8/15 wimax/i2400m: workaround not-so-working %zd printf format
In reply to Inaky Perez-GonzalezThe kernel's %zd modifier does not really work. Use %ld (has to cast ssize_t to long).
--- drivers/net/wimax/i2400m/usb-fw.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-)
drivers/net/wimax/i2400m/usb-fw.c +2 -2
@@ -330,8 +330,8 @@ error_dev_gone: out:if (do_autopm)usb_autopm_put_interface (i2400mu->usb_iface); -d_fnend (8 , dev,"(i2400m %p ack %p size %zu) = %zd\n" , - i2400m, ack, ack_size, result); +d_fnend (8 , dev,"(i2400m %p ack %p size %zu) = %ld\n" , + i2400m, ack, ack_size, (long ) result);return result; error_exceeded:
Signature · 1 line
1.6.2.5
PATCH 9/15 wimax/i2400m: decide properly if using signed vs non-signed firmware loading
In reply to Inaky Perez-GonzalezThe i2400m based devices can boot two main types of firmware images:
signed and non-signed. Signed images have signature data included that must match that of a certificate stored in the device.
Currently the code is making the decission on what type of firmware load (signed vs non-signed) is going to be loaded based on a hardcoded decission in __i2400m_ack_verify(), based on the barker the device sent upon boot.
This is not flexible enough as future hardware will emit more barkers; thus the bit has to be set in a place where there is better knowledge of what is going on. This will be done in follow-up commits -- however this patch paves the way for it.
So the querying of the mode is packed into i2400m_boot_is_signed(); the main changes are just using i2400m_boot_is_signed() to determine the method to follow and setting i2400m->sboot in i2400m_is_boot_barker(). The modifications in i2400m_dnload_init() and i2400m_dnload_finalize() are just reorganizing the order of the if blocks and thus look larger than they really are.
--- drivers/net/wimax/i2400m/fw.c | 35 +++++++++++++++++++++-------------- include/linux/wimax/i2400m.h | 10 ---------- 2 files changed, 21 insertions(+), 24 deletions(-)
drivers/net/wimax/i2400m/fw.c +21 -14
@@ -509,6 +509,17 @@ error_send:/* +* Indicate if the device emitted a reboot barker that indicates +* "signed boot" +*/ +static +unsigned i2400m_boot_is_signed (struct i2400m *i2400m) +{ +return likely (i2400m->sboot); +} + + +/* * Do the final steps of uploading firmware * * Depending on the boot mode (signed vs non-signed), different
@@ -529,7 +540,7 @@ int i2400m_dnload_finalize(struct i2400m *i2400m,d_fnstart (3 , dev,"offset %zu\n" , offset); cmd = (void *) bcf + offset; -if (i2400m->sboot ==0 ) { +if (i2400m_boot_is_signed (i2400m) ==0 ) {struct i2400m_bootrom_header jump_ack;d_printf (1 , dev,"unsecure boot, jumping to 0x%08x\n" ,le32_to_cpu (cmd->target_addr));
@@ -846,28 +857,24 @@ int i2400m_dnload_init(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf) {int result;struct device *dev =i2400m_dev (i2400m); -u32 module_id =le32_to_cpu (bcf->module_id); -if (i2400m->sboot ==0 - && (module_id &I2400M_BCF_MOD_ID_POKES ) ==0 ) { -/* non-signed boot process without pokes */ - result =i2400m_dnload_init_nonsigned (i2400m); +if (i2400m_boot_is_signed (i2400m)) { +d_printf (1 , dev,"signed boot\n" ); + result =i2400m_dnload_init_signed (i2400m, bcf);if (result == -ERESTARTSYS )return result;if (result <0 ) -dev_err (dev,"fw %s: non-signed download " +dev_err (dev,"firmware %s: signed boot download " "initialization failed: %d\n" , i2400m->fw_name, result); - }else if (i2400m->sboot ==0 - && (module_id &I2400M_BCF_MOD_ID_POKES )) { -/* non-signed boot process with pokes, nothing to do */ - result =0 ; - }else {/* signed boot process */ - result =i2400m_dnload_init_signed (i2400m, bcf); + }else { +/* non-signed boot process without pokes */ +d_printf (1 , dev,"non-signed boot\n" ); + result =i2400m_dnload_init_nonsigned (i2400m);if (result == -ERESTARTSYS )return result;if (result <0 ) -dev_err (dev,"fw %s: signed boot download " +dev_err (dev,"firmware %s: non-signed download " "initialization failed: %d\n" , i2400m->fw_name, result); }
include/linux/wimax/i2400m.h +0 -10
@@ -168,16 +168,6 @@ enum i2400m_brh { }; -/* Constants for bcf->module_id */ -enum i2400m_bcf_mod_id { -/* Firmware file carries its own pokes -- pokes are a set of -* magical values that have to be written in certain memory -* addresses to get the device up and ready for firmware -* download when it is in non-signed boot mode. */ -I2400M_BCF_MOD_ID_POKES =0x000000001 , -}; - -/** * i2400m_bootrom_header - Header for a boot-mode command *
Signature · 1 line
1.6.2.5
PATCH 10/15 wimax/i2400m: rework bootrom initialization to be more flexible
In reply to Inaky Perez-GonzalezThis modifies the bootrom initialization code of the i2400m driver so it can more easily support upcoming hardware.
Currently, the code detects two types of barkers (magic numbers) sent by the device to indicate the types of firmware it would take (signed vs non-signed).
This schema is extended so that multiple reboot barkers are recognized; upcoming hw will expose more types barkers which will have to match a header in the firmware image before we can load it.
For that, a barker database is introduced; the first time the device sends a barker, it is matched in the database. That gives the driver the information needed to decide how to upload the firmware and which types of firmware to use. The database can be populated from module parameters.
The execution flow is not altered; a new function (i2400m_is_boot_barker) is introduced to determine in the RX path if the device has sent a boot barker. This function is becoming heavier, so it is put away from the hot reception path [this is why there is some reorganization in sdio-rx.c:i2400ms_rx and usb-notifc.c:i2400mu_notification_grok()].
The documentation on the process has also been updated.
All these modifications are heavily based on previous work by Dirk Brandewie <[email protected]>.
--- drivers/net/wimax/i2400m/driver.c | 12 ++- drivers/net/wimax/i2400m/fw.c | 313 +++++++++++++++++++++++++++++----- drivers/net/wimax/i2400m/i2400m.h | 36 +++-- drivers/net/wimax/i2400m/rx.c | 22 +++ drivers/net/wimax/i2400m/sdio-rx.c | 25 ++- drivers/net/wimax/i2400m/usb-notif.c | 32 ++--- 6 files changed, 355 insertions(+), 85 deletions(-)
drivers/net/wimax/i2400m/driver.c +11 -1
@@ -98,6 +98,15 @@ MODULE_PARM_DESC(debug,"are the different debug submodules and VALUE are the " "initial debug value to set."); +static char i2400m_barkers_params[128 ]; +module_param_string (barkers, i2400m_barkers_params, +sizeof (i2400m_barkers_params),0644 ); +MODULE_PARM_DESC (barkers, +"String of comma-separated 32-bit values; each is " +"recognized as the value the device sends as a reboot " +"signal; values are appended to a list--setting one value " +"as zero cleans the existing list and starts a new one." ); +/** * i2400m_queue_work - schedule work on a i2400m's queue *
@@ -804,7 +813,7 @@ int __init i2400m_driver_init(void) {d_parse_params (D_LEVEL ,D_LEVEL_SIZE , i2400m_debug_params,"i2400m.debug" ); -return 0 ; +return i2400m_barker_db_init (i2400m_barkers_params); }module_init (i2400m_driver_init);
@@ -813,6 +822,7 @@ void __exit i2400m_driver_exit(void) {/* for scheds i2400m_dev_reset_handle() */ flush_scheduled_work (); +i2400m_barker_db_exit ();return ; }module_exit (i2400m_driver_exit);
drivers/net/wimax/i2400m/fw.c +271 -42
384 lines not on this page · raw message
drivers/net/wimax/i2400m/i2400m.h +22 -14
@@ -194,6 +194,7 @@ enum i2400m_reset_type {struct i2400m_reset_ctx ;struct i2400m_roq ; +struct i2400m_barker_db ;/** * struct i2400m - descriptor for an Intel 2400m
@@ -419,6 +420,12 @@ struct i2400m_roq;* * @fw_version: version of the firmware interface, Major.minor, * encoded in the high word and low word (major << 16 | minor). +* +* @barker: barker type that the device uses; this is initialized by +* i2400m_is_boot_barker() the first time it is called. Then it +* won't change during the life cycle of the device and everytime +* a boot barker is received, it is just verified for it being the +* same. */ struct i2400m {struct wimax_dev wimax_dev;/* FIRST! See doc */
@@ -484,6 +491,7 @@ struct i2400m {struct dentry *debugfs_dentry;const char *fw_name;/* name of the current firmware image */ unsigned long fw_version;/* version of the firmware interface */ +struct i2400m_barker_db *barker; };
@@ -574,6 +582,14 @@ extern void i2400m_bm_cmd_prepare(struct i2400m_bootrom_header *);extern int i2400m_dev_bootstrap (struct i2400m *,enum i2400m_bri );extern int i2400m_read_mac_addr (struct i2400m *);extern int i2400m_bootrom_init (struct i2400m *,enum i2400m_bri ); +extern int i2400m_is_boot_barker (struct i2400m *,const void *,size_t ); +static inline +int i2400m_is_d2h_barker (const void *buf) +{ +const __le32 *barker = buf; +return le32_to_cpu (*barker) ==I2400M_D2H_MSG_BARKER ; +} +extern void i2400m_unknown_barker (struct i2400m *,const void *,size_t );/* Make/grok boot-rom header commands */
@@ -736,20 +752,6 @@ extern int i2400m_rx(struct i2400m *, struct sk_buff *);extern struct i2400m_msg_hdr *i2400m_tx_msg_get (struct i2400m *,size_t *);extern void i2400m_tx_msg_sent (struct i2400m *); -static const __le32 i2400m_NBOOT_BARKER[4 ] = { -cpu_to_le32 (I2400M_NBOOT_BARKER ), -cpu_to_le32 (I2400M_NBOOT_BARKER ), -cpu_to_le32 (I2400M_NBOOT_BARKER ), -cpu_to_le32 (I2400M_NBOOT_BARKER ) -}; - -static const __le32 i2400m_SBOOT_BARKER[4 ] = { -cpu_to_le32 (I2400M_SBOOT_BARKER ), -cpu_to_le32 (I2400M_SBOOT_BARKER ), -cpu_to_le32 (I2400M_SBOOT_BARKER ), -cpu_to_le32 (I2400M_SBOOT_BARKER ) -}; -extern int i2400m_power_save_disabled;/*
@@ -848,6 +850,12 @@ void __i2400m_msleep(unsigned ms)#endif } + +/* module initialization helpers */ +extern int i2400m_barker_db_init (const char *); +extern void i2400m_barker_db_exit (void ); + +/* Module parameters */ extern int i2400m_idle_mode_disabled;
drivers/net/wimax/i2400m/rx.c +22 -0
@@ -1194,6 +1194,28 @@ error_msg_hdr_check:EXPORT_SYMBOL_GPL (i2400m_rx); +void i2400m_unknown_barker (struct i2400m *i2400m, +const void *buf,size_t size) +{ +struct device *dev =i2400m_dev (i2400m); +char prefix[64 ]; +const __le32 *barker = buf; +dev_err (dev,"RX: HW BUG? unknown barker %08x, " +"dropping %zu bytes\n" ,le32_to_cpu (*barker), size); +snprintf (prefix,sizeof (prefix),"%s %s: " , +dev_driver_string (dev),dev_name (dev)); +if (size >64 ) { +print_hex_dump (KERN_ERR , prefix,DUMP_PREFIX_OFFSET , +8 ,4 , buf,64 ,0 ); +printk (KERN_ERR "%s... (only first 64 bytes " +"dumped)\n" , prefix); + }else +print_hex_dump (KERN_ERR , prefix,DUMP_PREFIX_OFFSET , +8 ,4 , buf, size,0 ); +} +EXPORT_SYMBOL (i2400m_unknown_barker); + +/* * Initialize the RX queue and infrastructure *
drivers/net/wimax/i2400m/sdio-rx.c +18 -7
@@ -53,6 +53,7 @@* i2400ms_irq() * i2400ms_rx() * __i2400ms_rx_get_size() +* i2400m_is_boot_barker() * i2400m_rx() * * i2400ms_rx_setup()
@@ -158,7 +159,7 @@ void i2400ms_rx(struct i2400ms *i2400ms) }rmb ();/* make sure we get boot_mode from dev_reset_handle */ -if (i2400m->boot_mode ==1 ) { +if (unlikely (i2400m->boot_mode ==1 )) {spin_lock (&i2400m->rx_lock); i2400ms->bm_ack_size = rx_size;spin_unlock (&i2400m->rx_lock);
@@ -166,17 +167,26 @@ void i2400ms_rx(struct i2400ms *i2400ms)wake_up (&i2400ms->bm_wfa_wq);dev_err (dev,"RX: SDIO boot mode message\n" );kfree_skb (skb); - }else if (unlikely (!memcmp (skb->data, i2400m_NBOOT_BARKER, -sizeof (i2400m_NBOOT_BARKER)) - || !memcmp (skb->data, i2400m_SBOOT_BARKER, -sizeof (i2400m_SBOOT_BARKER)))) { + goto out; + } + ret = -EIO ; +if (unlikely (rx_size <sizeof (__le32))) { +dev_err (dev,"HW BUG? only %zu bytes received\n" , rx_size); + goto error_bad_size; + } +if (likely (i2400m_is_d2h_barker (skb->data))) { +skb_put (skb, rx_size); +i2400m_rx (i2400m, skb); + }else if (unlikely (i2400m_is_boot_barker (i2400m, + skb->data, rx_size))) { ret =i2400m_dev_reset_handle (i2400m);dev_err (dev,"RX: SDIO reboot barker\n" );kfree_skb (skb); }else { -skb_put (skb, rx_size); -i2400m_rx (i2400m, skb); +i2400m_unknown_barker (i2400m, skb->data, rx_size); +kfree_skb (skb); } +out:d_fnend (7 , dev,"(i2400ms %p) = void\n" , i2400ms);return ;
@@ -184,6 +194,7 @@ error_memcpy_fromio:kfree_skb (skb); error_alloc_skb: error_get_size: +error_bad_size:d_fnend (7 , dev,"(i2400ms %p) = %d\n" , i2400ms, ret);return ; }
drivers/net/wimax/i2400m/usb-notif.c +11 -21
@@ -51,6 +51,7 @@* * i2400mu_usb_notification_cb() Called when a URB is ready * i2400mu_notif_grok() +* i2400m_is_boot_barker() * i2400m_dev_reset_handle() * i2400mu_rx_kick() */
@@ -87,32 +88,21 @@ int i2400mu_notification_grok(struct i2400mu *i2400mu, const void *buf,d_fnstart (4 , dev,"(i2400m %p buf %p buf_len %zu)\n" , i2400mu, buf, buf_len); ret = -EIO ; -if (buf_len <sizeof (i2400m_NBOOT_BARKER)) +if (buf_len <sizeof (i2400m_ZERO_BARKER))/* Not a bug, just ignore */ goto error_bad_size; -if (!memcmp (i2400m_NBOOT_BARKER, buf,sizeof (i2400m_NBOOT_BARKER)) - || !memcmp (i2400m_SBOOT_BARKER, buf,sizeof (i2400m_SBOOT_BARKER))) - ret =i2400m_dev_reset_handle (i2400m); -else if (!memcmp (i2400m_ZERO_BARKER, buf,sizeof (i2400m_ZERO_BARKER))) { + ret =0 ; +if (!memcmp (i2400m_ZERO_BARKER, buf,sizeof (i2400m_ZERO_BARKER))) {i2400mu_rx_kick (i2400mu); - ret =0 ; - }else {/* Unknown or unexpected data in the notif message */ -char prefix[64 ]; - ret = -EIO ; -dev_err (dev,"HW BUG? Unknown/unexpected data in notification " -"message (%zu bytes)\n" , buf_len); -snprintf (prefix,sizeof (prefix),"%s %s: " , -dev_driver_string (dev),dev_name (dev)); -if (buf_len >64 ) { -print_hex_dump (KERN_ERR , prefix,DUMP_PREFIX_OFFSET , -8 ,4 , buf,64 ,0 ); -printk (KERN_ERR "%s... (only first 64 bytes " -"dumped)\n" , prefix); - }else -print_hex_dump (KERN_ERR , prefix,DUMP_PREFIX_OFFSET , -8 ,4 , buf, buf_len,0 ); + goto out; } + ret =i2400m_is_boot_barker (i2400m, buf, buf_len); +if (unlikely (ret >=0 )) + ret =i2400m_dev_reset_handle (i2400m); +else /* Unknown or unexpected data in the notif message */ +i2400m_unknown_barker (i2400m, buf, buf_len); error_bad_size: +out:d_fnend (4 , dev,"(i2400m %p buf %p buf_len %zu) = %d\n" , i2400mu, buf, buf_len, ret);return ret;
Signature · 1 line
1.6.2.5
PATCH 11/15 wimax/i2400m: retry loading firmware files in sequence
In reply to Inaky Perez-GonzalezThe i2400m firmware loader is given a list of firmware files to try to load by the probe() function (which can be different based on the device's model / generation).
Current code didn't attempt to load, check and try to boot with each file, but just to try to load if off disk. This is limiting in some cases, where we might want to try to load a firmware and if it fails to load onto the device, just fall back to another one.
This changes the behaviour so all files are tried for being loaded from disk, checked and uploaded to the device until one suceeds in bringing the device up.
--- drivers/net/wimax/i2400m/fw.c | 35 +++++++++++++++++------------------ 1 files changed, 17 insertions(+), 18 deletions(-)
drivers/net/wimax/i2400m/fw.c +17 -18
@@ -1288,7 +1288,7 @@ error_dev_rebooted:*/ int i2400m_dev_bootstrap (struct i2400m *i2400m,enum i2400m_bri flags) { -int ret =0 , itr =0 ; +int ret, itr;struct device *dev =i2400m_dev (i2400m);const struct firmware *fw;const struct i2400m_bcf_hdr *bcf;/* Firmware data */
@@ -1297,32 +1297,31 @@ int i2400m_dev_bootstrap(struct i2400m *i2400m, enum i2400m_bri flags)d_fnstart (5 , dev,"(i2400m %p)\n" , i2400m);/* Load firmware files to memory. */ - itr =0 ; -while (1 ) { +for (itr =0 , bcf =NULL , ret = -ENOENT ; ; itr++) { fw_name = i2400m->bus_fw_names[itr];if (fw_name ==NULL ) {dev_err (dev,"Could not find a usable firmware image\n" ); ret = -ENOENT ; - goto error_no_fw; +break ; } +d_printf (1 , dev,"trying firmware %s (%d)\n" , fw_name, itr); ret =request_firmware (&fw, fw_name, dev); -if (ret ==0 ) -break ;/* got it */ -if (ret <0 ) +if (ret <0 ) {dev_err (dev,"fw %s: cannot load file: %d\n" , fw_name, ret); - itr++; +continue ; + } + bcf = (void *) fw->data; + i2400m->fw_name = fw_name; + ret =i2400m_fw_check (i2400m, bcf, fw->size); +if (ret >=0 ) { + ret =i2400m_fw_dnload (i2400m, bcf, fw->size, flags); +if (ret >=0 ) +break ; + }else +dev_err (dev,"%s: cannot use, skipping\n" , fw_name); +release_firmware (fw); } - - bcf = (void *) fw->data; - i2400m->fw_name = fw_name; - ret =i2400m_fw_check (i2400m, bcf, fw->size); -if (ret <0 ) - goto error_fw_bad; - ret =i2400m_fw_dnload (i2400m, bcf, fw->size, flags); -error_fw_bad: -release_firmware (fw); -error_no_fw:d_fnend (5 , dev,"(i2400m %p) = %d\n" , i2400m, ret);return ret; }
Signature · 1 line
1.6.2.5
PATCH 12/15 wimax/i2400m: fix reboot echo/ack barker deadlock
In reply to Inaky Perez-GonzalezThe i2400m based devices can get in a sort of a deadlock some times; when they boot, they send a reboot "barker" (a magic number) and then the driver has to echo that same barker to ack reception (echo/ack). Then the device does a final ack by sending an ACK barker.
The first time this happens, we don't know ahead of time with barker the device is going to send, as different device models and SKUs will send different barker depending on the EEPROM programming.
If the device has sent the barker before the driver has been able to read it, the driver looses, as it doesn't know which barker it has to echo/ack back. With older devices, we tried a couple of combinations and that always worked; but now, with adding support for more, in which we have an unlimited number of new barkers, that is not an option.
So we rework said case so that when the device gets stuck, we just cycle through all the known types until one forces the device to send an ack. Otherwise, the driver gives up and aborts.
--- drivers/net/wimax/i2400m/fw.c | 63 +++++++++++++++++++++++++++++++++-------- include/linux/wimax/i2400m.h | 2 +- 2 files changed, 52 insertions(+), 13 deletions(-)
drivers/net/wimax/i2400m/fw.c +51 -12
@@ -812,7 +812,7 @@ int i2400m_dnload_finalize(struct i2400m *i2400m,* * @i2400m: device descriptor * @flags: -* I2400M_BRI_SOFT: a reboot notification has been seen +* I2400M_BRI_SOFT: a reboot barker has been seen * already, so don't wait for it. * * I2400M_BRI_NO_REBOOT: Don't send a reboot command, but wait
@@ -829,8 +829,9 @@ int i2400m_dnload_finalize(struct i2400m *i2400m,* main phases to this: * * a. (1) send a reboot command and (2) get a reboot barker -* b. (1) ack the reboot sending a reboot barker and (2) getting an -* ack barker in return +* +* b. (1) echo/ack the reboot sending the reboot barker back and (2) +* getting an ack barker in return * * We want to skip (a) in some cases [soft]. The state machine is * horrible, but it is basically: on each phase, send what has to be
@@ -838,6 +839,16 @@ int i2400m_dnload_finalize(struct i2400m *i2400m,* have to backtrack and retry, so we keep a max tries counter for * that. * +* It sucks because we don't know ahead of time which is going to be +* the reboot barker (the device might send different ones depending +* on its EEPROM config) and once the device reboots and waits for the +* echo/ack reboot barker being sent back, it doesn't understand +* anything else. So we can be left at the point where we don't know +* what to send to it -- cold reset and bus reset seem to have little +* effect. So the function iterates (in this case) through all the +* known barkers and tries them all until an ACK is +* received. Otherwise, it gives up. +* * If we get a timeout after sending a warm reset, we do it again. */ int i2400m_bootrom_init (struct i2400m *i2400m,enum i2400m_bri flags)
@@ -848,6 +859,7 @@ int i2400m_bootrom_init(struct i2400m *i2400m, enum i2400m_bri flags)struct i2400m_bootrom_header ack;int count = i2400m->bus_bm_retries;int ack_timeout_cnt =1 ; +unsigned i;BUILD_BUG_ON (sizeof (*cmd) !=sizeof (i2400m_barker_db[0 ].data));BUILD_BUG_ON (sizeof (ack) !=sizeof (i2400m_ACK_BARKER));
@@ -858,6 +870,7 @@ int i2400m_bootrom_init(struct i2400m *i2400m, enum i2400m_bri flags)if (flags &I2400M_BRI_SOFT ) goto do_reboot_ack; do_reboot: + ack_timeout_cnt =1 ;if (--count <0 ) goto error_timeout; d_printf(4, dev, "device reboot: reboot command [%d # left]\n",
@@ -869,22 +882,47 @@ do_reboot: flags &= ~I2400M_BRI_NO_REBOOT ;switch (result) {case -ERESTARTSYS : +/* +* at this point, i2400m_bm_cmd(), through +* __i2400m_bm_ack_process(), has updated +* i2400m->barker and we are good to go. +*/ d_printf (4 , dev,"device reboot: got reboot barker\n" );break ;case -EISCONN :/* we don't know how it got here...but we follow it */ d_printf (4 , dev,"device reboot: got ack barker - whatever\n" ); goto do_reboot; -case -ETIMEDOUT :/* device has timed out, we might be in boot -* mode already and expecting an ack, let's try -* that */ -if (i2400m->barker ==NULL ) { -dev_info (dev,"warm reset timed out, unknown barker " -"type, rebooting\n" ); - goto do_reboot; - }else { -dev_info (dev,"warm reset timed out, trying an ack\n" ); +case -ETIMEDOUT : +/* +* Device has timed out, we might be in boot mode +* already and expecting an ack; if we don't know what +* the barker is, we just send them all. Cold reset +* and bus reset don't work. Beats me. +*/ +if (i2400m->barker !=NULL ) { +dev_err (dev,"device boot: reboot barker timed out, " +"trying (set) %08x echo/ack\n" , +le32_to_cpu (i2400m->barker->data[0 ])); goto do_reboot_ack; } +for (i =0 ; i < i2400m_barker_db_used; i++) { +struct i2400m_barker_db *barker = &i2400m_barker_db[i]; +memcpy (cmd, barker->data,sizeof (barker->data)); + result =i2400m_bm_cmd (i2400m, cmd,sizeof (*cmd), + &ack,sizeof (ack), +I2400M_BM_CMD_RAW ); +if (result == -EISCONN ) { +dev_warn (dev,"device boot: got ack barker " +"after sending echo/ack barker " +"#%d/%08x; rebooting j.i.c.\n" , + i,le32_to_cpu (barker->data[0 ])); + flags &= ~I2400M_BRI_NO_REBOOT ; + goto do_reboot; + } + } +dev_err (dev,"device boot: tried all the echo/acks, could " +"not get device to respond; giving up" ); + result = -ESHUTDOWN ;case -EPROTO :case -ESHUTDOWN :/* dev is gone */ case -EINTR :/* user cancelled */
@@ -892,6 +930,7 @@ do_reboot:default :dev_err (dev,"device reboot: error %d while waiting " "for reboot barker - rebooting\n" , result); +d_dump (1 , dev, &ack, result); goto do_reboot; }/* At this point we ack back with 4 REBOOT barkers and expect
include/linux/wimax/i2400m.h +1 -1
@@ -138,7 +138,7 @@ struct i2400m_bcf_hdr {__le32 module_id;__le32 module_vendor;__le32 date;/* BCD YYYMMDD */ -__le32 size; +__le32 size;/* in dwords */ __le32 key_size;/* in dwords */ __le32 modulus_size;/* in dwords */ __le32 exponent_size;/* in dwords */
Signature · 1 line
1.6.2.5
PATCH 13/15 wimax/i2400m: verify firmware format version is known
In reply to Inaky Perez-GonzalezMake sure the bootloading code checks that the format of the file is understood (major version match). This also fixes a dumb typo in extracting the major version field.
--- drivers/net/wimax/i2400m/fw.c | 8 +++++++- 1 files changed, 7 insertions(+), 1 deletions(-)
drivers/net/wimax/i2400m/fw.c +7 -1
@@ -1179,7 +1179,7 @@ int i2400m_fw_check(struct i2400m *i2400m, module_type = bcf->module_type; header_len =sizeof (u32) *le32_to_cpu (bcf->header_len); - major_version =le32_to_cpu (bcf->header_version) &0xffff0000 >>16 ; + major_version = (le32_to_cpu (bcf->header_version) &0xffff0000 ) >>16 ; minor_version =le32_to_cpu (bcf->header_version) &0x0000ffff ; module_id =le32_to_cpu (bcf->module_id); module_vendor =le32_to_cpu (bcf->module_vendor);
@@ -1205,6 +1205,12 @@ int i2400m_fw_check(struct i2400m *i2400m, goto error; } +if (major_version !=1 ) { +dev_err (dev,"%s: major header version v%u.%u not supported\n" , + i2400m->fw_name, major_version, minor_version); + goto error; + } +/* Check soft-er errors */ result =0 ;if (module_vendor !=0x8086 )
Signature · 1 line
1.6.2.5
PATCH 14/15 wimax/i2400m: support extended firmware format
In reply to Inaky Perez-GonzalezThe SBCF firmware format has been extended to support extra headers after the main payload. These extra headers are used to sign the firmware code with more than one certificate. This eases up distributing single code images that work in more than one SKU of the device.
The changes to support this feature will be spread in a series of commits. This one just adds the support to parse the extra headers and store them in i2400m->fw_hdrs. Coming changes to the loader code will use that to determine which header to upload to the device.
The i2400m_fw_check() function now iterates over all the headers and for each, calls i2400m_fw_hdr_check(), which does some basic checks on each header. It then stores the headers for the bootloader code to use.
The i2400m_dev_bootstrap() function has been modified to cleanup i2400m->fw_hdrs when done.
--- drivers/net/wimax/i2400m/fw.c | 189 +++++++++++++++++++++++++------------ drivers/net/wimax/i2400m/i2400m.h | 4 + 2 files changed, 134 insertions(+), 59 deletions(-)
drivers/net/wimax/i2400m/fw.c +130 -59
@@ -78,11 +78,11 @@* * We can then upload the firmware file. The file is composed of a BCF * header (basic data, keys and signatures) and a list of write -* commands and payloads. We first upload the header -* [i2400m_dnload_init()] and then pass the commands and payloads -* verbatim to the i2400m_bm_cmd() function -* [i2400m_dnload_bcf()]. Then we tell the device to jump to the new -* firmware [i2400m_dnload_finalize()]. +* commands and payloads. Optionally more BCF headers might follow the +* main payload. We first upload the header [i2400m_dnload_init()] and +* then pass the commands and payloads verbatim to the i2400m_bm_cmd() +* function [i2400m_dnload_bcf()]. Then we tell the device to jump to +* the new firmware [i2400m_dnload_finalize()]. * * Once firmware is uploaded, we are good to go :) *
@@ -115,6 +115,7 @@* i2400m_dev_bootstrap Called by __i2400m_dev_start() * request_firmware * i2400m_fw_check +* i2400m_fw_hdr_check * i2400m_fw_dnload * release_firmware *
@@ -1151,75 +1152,142 @@ int i2400m_dnload_init(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf)/* -* Run quick consistency tests on the firmware file +* Run consistency tests on the firmware file and load up headers * * Check for the firmware being made for the i2400m device, * etc...These checks are mostly informative, as the device will make * them too; but the driver's response is more informative on what * went wrong. +* +* This will also look at all the headers present on the firmware +* file, and update i2400m->fw_bcf_hdr to point to them. */ static -int i2400m_fw_check (struct i2400m *i2400m, -const struct i2400m_bcf_hdr *bcf, -size_t bcf_size) +int i2400m_fw_hdr_check (struct i2400m *i2400m, +const struct i2400m_bcf_hdr *bcf_hdr, +size_t index,size_t offset) { -int result;struct device *dev =i2400m_dev (i2400m); +unsigned module_type, header_len, major_version, minor_version, module_id, module_vendor, date, size; -/* Check hard errors */ - result = -EINVAL ; -if (bcf_size <sizeof (*bcf)) {/* big enough header? */ -dev_err (dev,"firmware %s too short: " -"%zu B vs %zu (at least) expected\n" , - i2400m->fw_name, bcf_size,sizeof (*bcf)); - goto error; - } - - module_type = bcf->module_type; - header_len =sizeof (u32) *le32_to_cpu (bcf->header_len); - major_version = (le32_to_cpu (bcf->header_version) &0xffff0000 ) >>16 ; - minor_version =le32_to_cpu (bcf->header_version) &0x0000ffff ; - module_id =le32_to_cpu (bcf->module_id); - module_vendor =le32_to_cpu (bcf->module_vendor); - date =le32_to_cpu (bcf->date); - size =sizeof (u32) *le32_to_cpu (bcf->size); - -if (bcf_size != size) {/* annoyingly paranoid */ -dev_err (dev,"firmware %s: bad size, got " -"%zu B vs %u expected\n" , - i2400m->fw_name, bcf_size, size); - goto error; + module_type = bcf_hdr->module_type; + header_len =sizeof (u32) *le32_to_cpu (bcf_hdr->header_len); + major_version = (le32_to_cpu (bcf_hdr->header_version) &0xffff0000 ) + >>16 ; + minor_version =le32_to_cpu (bcf_hdr->header_version) &0x0000ffff ; + module_id =le32_to_cpu (bcf_hdr->module_id); + module_vendor =le32_to_cpu (bcf_hdr->module_vendor); + date =le32_to_cpu (bcf_hdr->date); + size =sizeof (u32) *le32_to_cpu (bcf_hdr->size); + +d_printf (1 , dev,"firmware %s #%d@%08x: BCF header " +"type:vendor:id 0x%x:%x:%x v%u.%u (%zu/%zu B) built %08x\n" , + i2400m->fw_name, index, offset, + module_type, module_vendor, module_id, + major_version, minor_version, header_len, size, date); + +/* Hard errors */ +if (major_version !=1 ) { +dev_err (dev,"firmware %s #%d@%08x: major header version " +"v%u.%u not supported\n" , + i2400m->fw_name, index, offset, + major_version, minor_version); +return -EBADF ; } -d_printf (2 , dev,"type 0x%x id 0x%x vendor 0x%x; header v%u.%u (%zu B) " -"date %08x (%zu B)\n" , - module_type, module_id, module_vendor, - major_version, minor_version, (size_t ) header_len, - date, (size_t ) size); -if (module_type !=6 ) {/* built for the right hardware? */ -dev_err (dev,"bad fw %s: unexpected module type 0x%x; " -"aborting\n" , i2400m->fw_name, module_type); - goto error; +dev_err (dev,"firmware %s #%d@%08x: unexpected module " +"type 0x%x; aborting\n" , + i2400m->fw_name, index, offset, + module_type); +return -EBADF ; } -if (major_version !=1 ) { -dev_err (dev,"%s: major header version v%u.%u not supported\n" , - i2400m->fw_name, major_version, minor_version); - goto error; +if (module_vendor !=0x8086 ) { +dev_err (dev,"firmware %s #%d@%08x: unexpected module " +"vendor 0x%x; aborting\n" , + i2400m->fw_name, index, offset, module_vendor); +return -EBADF ; } -/* Check soft-er errors */ - result =0 ; -if (module_vendor !=0x8086 ) -dev_err (dev,"bad fw %s? unexpected vendor 0x%04x\n" , - i2400m->fw_name, module_vendor);if (date <0x20080300 ) -dev_err (dev,"bad fw %s? build date too old %08x\n" , - i2400m->fw_name, date); -error: +dev_warn (dev,"firmware %s #%d@%08x: build date %08x " +"too old; unsupported\n" , + i2400m->fw_name, index, offset, date); +return 0 ; +} + + +/* +* Run consistency tests on the firmware file and load up headers +* +* Check for the firmware being made for the i2400m device, +* etc...These checks are mostly informative, as the device will make +* them too; but the driver's response is more informative on what +* went wrong. +* +* This will also look at all the headers present on the firmware +* file, and update i2400m->fw_hdrs to point to them. +*/ +static +int i2400m_fw_check (struct i2400m *i2400m,const void *bcf,size_t bcf_size) +{ +int result; +struct device *dev =i2400m_dev (i2400m); +size_t headers =0 ; +const struct i2400m_bcf_hdr *bcf_hdr; +const void *itr, *next, *top; +unsigned slots =0 , used_slots =0 ; + +for (itr = bcf, top = itr + bcf_size; + itr < top; + headers++, itr = next) { +size_t leftover, offset, header_len, size; + + leftover = top - itr; + offset = itr - (const void *) bcf; +if (leftover <=sizeof (*bcf_hdr)) { +dev_err (dev,"firmware %s: %zu B left at @%x, " +"not enough for BCF header\n" , + i2400m->fw_name, leftover, offset); +break ; + } + bcf_hdr = itr; +/* Only the first header is supposed to be followed by +* payload */ + header_len =sizeof (u32) *le32_to_cpu (bcf_hdr->header_len); + size =sizeof (u32) *le32_to_cpu (bcf_hdr->size); +if (headers ==0 ) + next = itr + size; +else + next = itr + header_len; + + result =i2400m_fw_hdr_check (i2400m, bcf_hdr, headers, offset); +if (result <0 ) +continue ; +if (used_slots +1 >= slots) { +/* +1 -> we need to account for the one we'll +* occupy and at least an extra one for +* always being NULL */ + result =i2400m_zrealloc_2x ( + (void **) &i2400m->fw_hdrs, &slots, +sizeof (i2400m->fw_hdrs[0 ]), +GFP_KERNEL ); +if (result <0 ) + goto error_zrealloc; + } + i2400m->fw_hdrs[used_slots] = bcf_hdr; + used_slots++; + } +if (headers ==0 ) { +dev_err (dev,"firmware %s: no usable headers found\n" , + i2400m->fw_name); + result = -EBADF ; + }else + result =0 ; +error_zrealloc:return result; }
@@ -1359,13 +1427,16 @@ int i2400m_dev_bootstrap(struct i2400m *i2400m, enum i2400m_bri flags) bcf = (void *) fw->data; i2400m->fw_name = fw_name; ret =i2400m_fw_check (i2400m, bcf, fw->size); -if (ret >=0 ) { +if (ret >=0 ) ret =i2400m_fw_dnload (i2400m, bcf, fw->size, flags); -if (ret >=0 ) -break ; - }else -dev_err (dev,"%s: cannot use, skipping\n" , fw_name); +if (ret <0 ) +dev_err (dev,"%s: cannot use: %d, skipping\n" , + fw_name, ret); +kfree (i2400m->fw_hdrs); + i2400m->fw_hdrs =NULL ;release_firmware (fw); +if (ret >=0 )/* firmware loaded succesfully */ +break ; }d_fnend (5 , dev,"(i2400m %p) = %d\n" , i2400m, ret);return ret;
drivers/net/wimax/i2400m/i2400m.h +4 -0
@@ -421,6 +421,9 @@ struct i2400m_barker_db;* @fw_version: version of the firmware interface, Major.minor, * encoded in the high word and low word (major << 16 | minor). * +* @fw_hdrs: NULL terminated array of pointers to the firmware +* headers. This is only available during firmware load time. +* * @barker: barker type that the device uses; this is initialized by * i2400m_is_boot_barker() the first time it is called. Then it * won't change during the life cycle of the device and everytime
@@ -491,6 +494,7 @@ struct i2400m {struct dentry *debugfs_dentry;const char *fw_name;/* name of the current firmware image */ unsigned long fw_version;/* version of the firmware interface */ +const struct i2400m_bcf_hdr **fw_hdrs;struct i2400m_barker_db *barker; };
Signature · 1 line
1.6.2.5
PATCH 15/15 wimax/i2400m: on firmware upload, select BCF header that matches device's request
In reply to Inaky Perez-GonzalezDevices based on the i2400m emit a "barker" (32 bit unsigned) when they boot. This barker is used to select, in the firmware file image, which header should be used to process the rest of the file.
This commit implements said support, completing the series started by previous commits.
We modify the i2400m_fw_dnload() firmware loading path by adding a call to i2400m_bcf_hdr_find() [new function], in which the right BCF header [as listed in i2400m->fw_hdrs by i2400m_fw_check()] is located. Then this header is fed to i2400m_dnload_init() and i2400m_dnload_finalize().
The changes to i2400m_dnload_finalize() are smaller than they look; they add the bcf_hdr argument and use that instead of bcf. Likewise in i2400m_dnload_init().
--- drivers/net/wimax/i2400m/fw.c | 111 ++++++++++++++++++++++++++++++++++------- 1 files changed, 92 insertions(+), 19 deletions(-)
drivers/net/wimax/i2400m/fw.c +92 -19
@@ -665,8 +665,8 @@ static int i2400m_download_chunk(struct i2400m *i2400m, const void *chunk,* Download a BCF file's sections to the device * * @i2400m: device descriptor -* @bcf: pointer to firmware data (followed by the payloads). Assumed -* verified and consistent. +* @bcf: pointer to firmware data (first header followed by the +* payloads). Assumed verified and consistent. * @bcf_len: length (in bytes) of the @bcf buffer. * * Returns: < 0 errno code on error or the offset to the jump instruction.
@@ -756,11 +756,17 @@ unsigned i2400m_boot_is_signed(struct i2400m *i2400m)/* * Do the final steps of uploading firmware * +* @bcf_hdr: BCF header we are actually using +* @bcf: pointer to the firmware image (which matches the first header +* that is followed by the actual payloads). +* @offset: [byte] offset into @bcf for the command we need to send. +* * Depending on the boot mode (signed vs non-signed), different * actions need to be taken. */ static int i2400m_dnload_finalize (struct i2400m *i2400m, +const struct i2400m_bcf_hdr *bcf_hdr,const struct i2400m_bcf_hdr *bcf,size_t offset) {int ret =0 ;
@@ -792,12 +798,13 @@ int i2400m_dnload_finalize(struct i2400m *i2400m, cmd_buf = i2400m->bm_cmd_buf;memcpy (&cmd_buf->cmd, cmd,sizeof (*cmd)); signature_block_offset = -sizeof (*bcf) - +le32_to_cpu (bcf->key_size) *sizeof (u32) - +le32_to_cpu (bcf->exponent_size) *sizeof (u32); +sizeof (*bcf_hdr) + +le32_to_cpu (bcf_hdr->key_size) *sizeof (u32) + +le32_to_cpu (bcf_hdr->exponent_size) *sizeof (u32); signature_block_size = -le32_to_cpu (bcf->modulus_size) *sizeof (u32); -memcpy (cmd_buf->cmd_pl, (void *) bcf + signature_block_offset, +le32_to_cpu (bcf_hdr->modulus_size) *sizeof (u32); +memcpy (cmd_buf->cmd_pl, + (void *) bcf_hdr + signature_block_offset, signature_block_size); ret = i2400m_bm_cmd(i2400m, &cmd_buf->cmd, sizeof(cmd_buf->cmd) + signature_block_size,
@@ -1122,14 +1129,15 @@ int i2400m_dnload_init_signed(struct i2400m *i2400m,* (signed or non-signed). */ static -int i2400m_dnload_init (struct i2400m *i2400m,const struct i2400m_bcf_hdr *bcf) +int i2400m_dnload_init (struct i2400m *i2400m, +const struct i2400m_bcf_hdr *bcf_hdr) {int result;struct device *dev =i2400m_dev (i2400m);if (i2400m_boot_is_signed (i2400m)) {d_printf (1 , dev,"signed boot\n" ); - result =i2400m_dnload_init_signed (i2400m, bcf); + result =i2400m_dnload_init_signed (i2400m, bcf_hdr);if (result == -ERESTARTSYS )return result;if (result <0 )
@@ -1182,15 +1190,15 @@ int i2400m_fw_hdr_check(struct i2400m *i2400m, date =le32_to_cpu (bcf_hdr->date); size =sizeof (u32) *le32_to_cpu (bcf_hdr->size); -d_printf (1 , dev,"firmware %s #%d@%08x: BCF header " -"type:vendor:id 0x%x:%x:%x v%u.%u (%zu/%zu B) built %08x\n" , +d_printf (1 , dev,"firmware %s #%zd@%08zx: BCF header " +"type:vendor:id 0x%x:%x:%x v%u.%u (%u/%u B) built %08x\n" , i2400m->fw_name, index, offset, module_type, module_vendor, module_id, major_version, minor_version, header_len, size, date);/* Hard errors */ if (major_version !=1 ) { -dev_err (dev,"firmware %s #%d@%08x: major header version " +dev_err (dev,"firmware %s #%zd@%08zx: major header version " "v%u.%u not supported\n" , i2400m->fw_name, index, offset, major_version, minor_version);
@@ -1198,7 +1206,7 @@ int i2400m_fw_hdr_check(struct i2400m *i2400m, }if (module_type !=6 ) {/* built for the right hardware? */ -dev_err (dev,"firmware %s #%d@%08x: unexpected module " +dev_err (dev,"firmware %s #%zd@%08zx: unexpected module " "type 0x%x; aborting\n" , i2400m->fw_name, index, offset, module_type);
@@ -1206,14 +1214,14 @@ int i2400m_fw_hdr_check(struct i2400m *i2400m, }if (module_vendor !=0x8086 ) { -dev_err (dev,"firmware %s #%d@%08x: unexpected module " +dev_err (dev,"firmware %s #%zd@%08zx: unexpected module " "vendor 0x%x; aborting\n" , i2400m->fw_name, index, offset, module_vendor);return -EBADF ; }if (date <0x20080300 ) -dev_warn (dev,"firmware %s #%d@%08x: build date %08x " +dev_warn (dev,"firmware %s #%zd@%08zx: build date %08x " "too old; unsupported\n" , i2400m->fw_name, index, offset, date);return 0 ;
@@ -1239,7 +1247,7 @@ int i2400m_fw_check(struct i2400m *i2400m, const void *bcf, size_t bcf_size)size_t headers =0 ;const struct i2400m_bcf_hdr *bcf_hdr;const void *itr, *next, *top; -unsigned slots =0 , used_slots =0 ; +size_t slots =0 , used_slots =0 ; for (itr = bcf, top = itr + bcf_size; itr < top;
@@ -1249,7 +1257,7 @@ int i2400m_fw_check(struct i2400m *i2400m, const void *bcf, size_t bcf_size) leftover = top - itr; offset = itr - (const void *) bcf;if (leftover <=sizeof (*bcf_hdr)) { -dev_err (dev,"firmware %s: %zu B left at @%x, " +dev_err (dev,"firmware %s: %zu B left at @%zx, " "not enough for BCF header\n" , i2400m->fw_name, leftover, offset);break ;
@@ -1293,6 +1301,60 @@ error_zrealloc:/* +* Match a barker to a BCF header module ID +* +* The device sends a barker which tells the firmware loader which +* header in the BCF file has to be used. This does the matching. +*/ +static +unsigned i2400m_bcf_hdr_match (struct i2400m *i2400m, +const struct i2400m_bcf_hdr *bcf_hdr) +{ +u32 barker =le32_to_cpu (i2400m->barker->data[0 ]) + &0x7fffffff ; +u32 module_id =le32_to_cpu (bcf_hdr->module_id) + &0x7fffffff ;/* high bit used for something else */ + +/* special case for 5x50 */ +if (barker ==I2400M_SBOOT_BARKER && module_id ==0 ) +return 1 ; +if (module_id == barker) +return 1 ; +return 0 ; +} + +static +const struct i2400m_bcf_hdr *i2400m_bcf_hdr_find (struct i2400m *i2400m) +{ +struct device *dev =i2400m_dev (i2400m); +const struct i2400m_bcf_hdr **bcf_itr, *bcf_hdr; +unsigned i =0 ; +u32 barker =le32_to_cpu (i2400m->barker->data[0 ]); + +d_printf (2 , dev,"finding BCF header for barker %08x\n" , barker); +if (barker ==I2400M_NBOOT_BARKER ) { + bcf_hdr = i2400m->fw_hdrs[0 ]; +d_printf (1 , dev,"using BCF header #%u/%08x for non-signed " +"barker\n" ,0 ,le32_to_cpu (bcf_hdr->module_id)); +return bcf_hdr; + } +for (bcf_itr = i2400m->fw_hdrs; *bcf_itr !=NULL ; bcf_itr++, i++) { + bcf_hdr = *bcf_itr; +if (i2400m_bcf_hdr_match (i2400m, bcf_hdr)) { +d_printf (1 , dev,"hit on BCF hdr #%u/%08x\n" , + i,le32_to_cpu (bcf_hdr->module_id)); +return bcf_hdr; + }else +d_printf (1 , dev,"miss on BCF hdr #%u/%08x\n" , + i,le32_to_cpu (bcf_hdr->module_id)); + } +dev_err (dev,"cannot find a matching BCF header for barker %08x\n" , + barker); +return NULL ; +} + + +/* * Download the firmware to the device * * @i2400m: device descriptor
@@ -1313,6 +1375,7 @@ int i2400m_fw_dnload(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf,int ret =0 ;struct device *dev =i2400m_dev (i2400m);int count = i2400m->bus_bm_retries; +const struct i2400m_bcf_hdr *bcf_hdr;d_fnstart (5 , dev,"(i2400m %p bcf %p size %zu)\n" , i2400m, bcf, bcf_size);
@@ -1337,8 +1400,17 @@ hw_reboot:* Initialize the download, push the bytes to the device and * then jump to the new firmware. Note @ret is passed with the * offset of the jump instruction to _dnload_finalize() +* +* Note we need to use the BCF header in the firmware image +* that matches the barker that the device sent when it +* rebooted, so it has to be passed along. */ - ret =i2400m_dnload_init (i2400m, bcf);/* Init device's dnload */ + ret = -EBADF ; + bcf_hdr =i2400m_bcf_hdr_find (i2400m); +if (bcf_hdr ==NULL ) + goto error_bcf_hdr_find; + + ret =i2400m_dnload_init (i2400m, bcf_hdr);if (ret == -ERESTARTSYS ) goto error_dev_rebooted;if (ret <0 )
@@ -1353,7 +1425,7 @@ hw_reboot: goto error_dnload_bcf; } - ret =i2400m_dnload_finalize (i2400m, bcf, ret); + ret =i2400m_dnload_finalize (i2400m, bcf_hdr, bcf, ret);if (ret == -ERESTARTSYS ) goto error_dev_rebooted;if (ret <0 ) {
@@ -1370,6 +1442,7 @@ hw_reboot: error_dnload_finalize: error_dnload_bcf: error_dnload_init: +error_bcf_hdr_find: error_bootrom_init: error_too_many_reboots: d_fnend(5, dev, "(i2400m %p bcf %p size %zu) = %d\n",
Signature · 1 line
1.6.2.5
PATCH 1/13 wimax/iwmc3200: add new sdio device ID to support iwmc3200 2.5GHz sku
In reply to Inaky Perez-GonzalezFrom: Cindy H Kao <[email protected]>
Different sdio device IDs are designated to support different intel wimax silicon sku. The new macro SDIO_DEVICE_ID_IWMC3200_WIMAX_2G5(0x1407) is added to support iwmc3200 2.5GHz sku. The existing SDIO_DEVICE_ID_IWMC3200_WIMAX(0x1402) is for iwmc3200 general sku.
--- drivers/net/wimax/i2400m/sdio.c | 2 ++ include/linux/mmc/sdio_ids.h | 1 + 2 files changed, 3 insertions(+), 0 deletions(-)
drivers/net/wimax/i2400m/sdio.c +2 -0
@@ -550,6 +550,8 @@ const struct sdio_device_id i2400ms_sdio_ids[] = {/* Intel: i2400m WiMAX (iwmc3200) over SDIO */ {SDIO_DEVICE (SDIO_VENDOR_ID_INTEL , SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, + SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX_2G5) }, { /* end: all zeroes */ }, };MODULE_DEVICE_TABLE (sdio, i2400ms_sdio_ids);
include/linux/mmc/sdio_ids.h +1 -0
@@ -28,6 +28,7 @@#define SDIO_DEVICE_ID_INTEL_IWMC3200TOP 0x1404#define SDIO_DEVICE_ID_INTEL_IWMC3200GPS 0x1405#define SDIO_DEVICE_ID_INTEL_IWMC3200BT 0x1406 +#define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX_2G5 0x1407#define SDIO_VENDOR_ID_MARVELL 0x02df#define SDIO_DEVICE_ID_MARVELL_LIBERTAS 0x9103
Signature · 1 line
1.6.2.5
PATCH 2/13 wimax/i6x50: add Intel WiFi/WiMAX Link 6050 Series support
In reply to Inaky Perez-GonzalezFrom: Dirk Brandewie <[email protected]>
Add support for the WiMAX device in the Intel WiFi/WiMAX Link 6050 Series; this involves:
- adding the device ID to bind to and an endpoint mapping for the driver to use.
- at probe() time, some things are set depending on the device id:
+ the list of firmware names to try
+ mapping of endpoints
--- drivers/net/wimax/i2400m/fw.c | 3 +++ drivers/net/wimax/i2400m/i2400m-usb.h | 3 +++ drivers/net/wimax/i2400m/usb.c | 26 +++++++++++++++++++------- include/linux/wimax/i2400m.h | 1 + 4 files changed, 26 insertions(+), 7 deletions(-)
drivers/net/wimax/i2400m/fw.c +3 -0
@@ -277,6 +277,9 @@ int i2400m_barker_db_known_barkers(void) result =i2400m_barker_db_add (I2400M_SBOOT_BARKER );if (result <0 ) goto error_add; + result =i2400m_barker_db_add (I2400M_SBOOT_BARKER_6050 ); +if (result <0 ) + goto error_add; error_add:return result; }
drivers/net/wimax/i2400m/i2400m-usb.h +3 -0
@@ -148,6 +148,9 @@ enum {I2400MU_MAX_NOTIFICATION_LEN =256 ,I2400MU_BLK_SIZE =16 ,I2400MU_PL_SIZE_MAX =0x3EFF , + +/* Device IDs */ + USB_DEVICE_ID_I6050 = 0x0186, };
drivers/net/wimax/i2400m/usb.c +19 -7
@@ -80,11 +80,16 @@ MODULE_PARM_DESC(debug, "initial debug value to set.");/* Our firmware file name */ -static const char *i2400mu_bus_fw_names[] = { +static const char *i2400mu_bus_fw_names_5x50[] = { #define I2400MU_FW_FILE_NAME_v1_4 "i2400m-fw-usb-1.4.sbcf" I2400MU_FW_FILE_NAME_v1_4, -#define I2400MU_FW_FILE_NAME_v1_3 "i2400m-fw-usb-1.3.sbcf" - I2400MU_FW_FILE_NAME_v1_3, +NULL , +}; + + +static const char *i2400mu_bus_fw_names_6050[] = { +#define I6050U_FW_FILE_NAME_v1_5 "i6050-fw-usb-1.5.sbcf" + I6050U_FW_FILE_NAME_v1_5,NULL , };
@@ -418,10 +423,16 @@ int i2400mu_probe(struct usb_interface *iface, i2400m->bus_bm_retries =I2400M_USB_BOOT_RETRIES ; i2400m->bus_bm_cmd_send = i2400mu_bus_bm_cmd_send; i2400m->bus_bm_wait_for_ack = i2400mu_bus_bm_wait_for_ack; - i2400m->bus_fw_names = i2400mu_bus_fw_names; i2400m->bus_bm_mac_addr_impaired =0 ; - { +if (id->idProduct ==USB_DEVICE_ID_I6050 ) { + i2400m->bus_fw_names = i2400mu_bus_fw_names_6050; + i2400mu->endpoint_cfg.bulk_out =0 ; + i2400mu->endpoint_cfg.notification =3 ; + i2400mu->endpoint_cfg.reset_cold =2 ; + i2400mu->endpoint_cfg.bulk_in =1 ; + }else { + i2400m->bus_fw_names = i2400mu_bus_fw_names_5x50; i2400mu->endpoint_cfg.bulk_out =0 ; i2400mu->endpoint_cfg.notification =1 ; i2400mu->endpoint_cfg.reset_cold =2 ;
@@ -614,6 +625,7 @@ out:static struct usb_device_id i2400mu_id_table[] = { + {USB_DEVICE (0x8086 ,USB_DEVICE_ID_I6050 ) }, {USB_DEVICE (0x8086 ,0x0181 ) }, {USB_DEVICE (0x8086 ,0x1403 ) }, {USB_DEVICE (0x8086 ,0x1405 ) },
@@ -656,7 +668,7 @@ void __exit i2400mu_driver_exit(void)module_exit (i2400mu_driver_exit);MODULE_AUTHOR ("Intel Corporation <[email protected]>" ); -MODULE_DESCRIPTION ("Intel 2400M WiMAX networking for USB" ); +MODULE_DESCRIPTION ("Driver for USB based Intel Wireless WiMAX Connection 2400M " +"(5x50 & 6050)" );MODULE_LICENSE ("GPL" );MODULE_FIRMWARE (I2400MU_FW_FILE_NAME_v1_4); -MODULE_FIRMWARE (I2400MU_FW_FILE_NAME_v1_3);
include/linux/wimax/i2400m.h +1 -0
@@ -266,6 +266,7 @@ enum {I2400M_WARM_RESET_BARKER =0x50f750f7 ,I2400M_NBOOT_BARKER =0xdeadbeef ,I2400M_SBOOT_BARKER =0x0ff1c1a1 , +I2400M_SBOOT_BARKER_6050 =0x80000001 ,I2400M_ACK_BARKER =0xfeedbabe , I2400M_D2H_MSG_BARKER = 0xbeefbabe, };
Signature · 1 line
1.6.2.5
PATCH 3/13 wimax/i2400m: clean up & add a payload argument to i2400m_schedule_work()
In reply to Inaky Perez-GonzalezForthcoming commits use having a payload argument added to i2400m_schedule_work(), which then becomes nearly identical to i2400m_queue_work().
This patch thus cleans up both's implementation, making it share common helpers and adding the payload argument to i2400m_schedule_work().
--- drivers/net/wimax/i2400m/driver.c | 52 +++++++++++++++++++++++------------- drivers/net/wimax/i2400m/i2400m.h | 6 +++- 2 files changed, 37 insertions(+), 21 deletions(-)
drivers/net/wimax/i2400m/driver.c +33 -19
@@ -107,6 +107,24 @@ MODULE_PARM_DESC(barkers,"signal; values are appended to a list--setting one value " "as zero cleans the existing list and starts a new one."); +static +struct i2400m_work *__i2400m_work_setup ( +struct i2400m *i2400m,void (*fn)(struct work_struct *), +gfp_t gfp_flags,const void *pl,size_t pl_size) +{ +struct i2400m_work *iw; + + iw =kzalloc (sizeof (*iw) + pl_size, gfp_flags); +if (iw ==NULL ) +return NULL ; + iw->i2400m =i2400m_get (i2400m); + iw->pl_size = pl_size; +memcpy (iw->pl, pl, pl_size); +INIT_WORK (&iw->ws, fn); +return iw; +} + +/** * i2400m_queue_work - schedule work on a i2400m's queue *
@@ -166,14 +184,12 @@ int i2400m_queue_work(struct i2400m *i2400m,BUG_ON (i2400m->work_queue ==NULL ); result = -ENOMEM ; - iw =kzalloc (sizeof (*iw) + pl_size, gfp_flags); -if (iw ==NULL ) - goto error_kzalloc; - iw->i2400m =i2400m_get (i2400m); -memcpy (iw->pl, pl, pl_size); -INIT_WORK (&iw->ws, fn); - result =queue_work (i2400m->work_queue, &iw->ws); -error_kzalloc: + iw =__i2400m_work_setup (i2400m, fn, gfp_flags, pl, pl_size); +if (iw !=NULL ) { + result =queue_work (i2400m->work_queue, &iw->ws); +if (WARN_ON (result ==0 )) + result = -ENXIO ; + }return result; }EXPORT_SYMBOL_GPL (i2400m_queue_work);
@@ -192,21 +208,19 @@ EXPORT_SYMBOL_GPL(i2400m_queue_work);* it should not happen. */ int i2400m_schedule_work (struct i2400m *i2400m, -void (*fn)(struct work_struct *),gfp_t gfp_flags) +void (*fn)(struct work_struct *),gfp_t gfp_flags, +const void *pl,size_t pl_size) {int result;struct i2400m_work *iw; result = -ENOMEM ; - iw =kzalloc (sizeof (*iw), gfp_flags); -if (iw ==NULL ) - goto error_kzalloc; - iw->i2400m =i2400m_get (i2400m); -INIT_WORK (&iw->ws, fn); - result =schedule_work (&iw->ws); -if (result ==0 ) - result = -ENXIO ; -error_kzalloc: + iw =__i2400m_work_setup (i2400m, fn, gfp_flags, pl, pl_size); +if (iw !=NULL ) { + result =schedule_work (&iw->ws); +if (WARN_ON (result ==0 )) + result = -ENXIO ; + }return result; }
@@ -630,7 +644,7 @@ int i2400m_dev_reset_handle(struct i2400m *i2400m) i2400m->boot_mode =1 ;wmb ();/* Make sure i2400m_msg_to_dev() sees boot_mode */ return i2400m_schedule_work (i2400m, __i2400m_dev_reset_handle, -GFP_ATOMIC ); +GFP_ATOMIC ,NULL ,0 ); }EXPORT_SYMBOL_GPL (i2400m_dev_reset_handle);
drivers/net/wimax/i2400m/i2400m.h +4 -2
@@ -695,8 +695,6 @@ extern void i2400m_dev_shutdown(struct i2400m *);extern struct attribute_group i2400m_dev_attr_group; -extern int i2400m_schedule_work (struct i2400m *, -void (*)(struct work_struct *),gfp_t );/* HDI message's payload description handling */
@@ -778,10 +776,14 @@ struct device *i2400m_dev(struct i2400m *i2400m)struct i2400m_work {struct work_struct ws;struct i2400m *i2400m; +size_t pl_size;u8 pl[0 ]; };extern int i2400m_queue_work (struct i2400m *,void (*)(struct work_struct *),gfp_t , +const void *,size_t ); +extern int i2400m_schedule_work (struct i2400m *, +void (*)(struct work_struct *),gfp_t ,const void *,size_t ); extern int i2400m_msg_check_status(const struct i2400m_l3l4_hdr *,
Signature · 1 line
1.6.2.5
PATCH 4/13 wimax/i2400m: add reason argument to i2400m_dev_reset_handle()
In reply to Inaky Perez-GonzalezIn preparation for reset_resume support, in which the same code path is going to be used, add a diagnostic message to dev_reset_handle() that can be used to distinguish how the device got there.
This uses the new payload argument added to i2400m_schedule_work() by the previous commit.
--- drivers/net/wimax/i2400m/driver.c | 27 +++++++++++++++++++-------- drivers/net/wimax/i2400m/i2400m.h | 2 +- drivers/net/wimax/i2400m/sdio-rx.c | 2 +- drivers/net/wimax/i2400m/usb-notif.c | 2 +- 4 files changed, 22 insertions(+), 11 deletions(-)
drivers/net/wimax/i2400m/driver.c +19 -8
@@ -573,18 +573,28 @@ void i2400m_dev_stop(struct i2400m *i2400m)* _stop()], don't do anything, let it fail and handle it. * * This function is ran always in a thread context +* +* This function gets passed, as payload to i2400m_work() a 'const +* char *' ptr with a "reason" why the reset happened (for messages). */ static void __i2400m_dev_reset_handle (struct work_struct *ws) {int result; struct i2400m_work *iw = container_of(ws, struct i2400m_work, ws); +const char *reason;struct i2400m *i2400m = iw->i2400m;struct device *dev =i2400m_dev (i2400m);enum wimax_st wimax_state;struct i2400m_reset_ctx *ctx = i2400m->reset_ctx; -d_fnstart (3 , dev,"(ws %p i2400m %p)\n" , ws, i2400m); +if (WARN_ON (iw->pl_size !=sizeof (reason))) + reason ="SW BUG: reason n/a" ; +else +memcpy (&reason, iw->pl,sizeof (reason)); + +d_fnstart (3 , dev,"(ws %p i2400m %p reason %s)\n" , ws, i2400m, reason); + result =0 ;if (mutex_trylock (&i2400m->init_mutex) ==0 ) {/* We are still in i2400m_dev_start() [let it fail] or
@@ -597,17 +607,17 @@ void __i2400m_dev_reset_handle(struct work_struct *ws) } wimax_state =wimax_state_get (&i2400m->wimax_dev);if (wimax_state <WIMAX_ST_UNINITIALIZED ) { -dev_info (dev,"device rebooted: it is down, ignoring\n" ); +dev_info (dev,"%s: it is down, ignoring\n" , reason); goto out_unlock;/* ifconfig up/down wasn't called */ } -dev_err (dev,"device rebooted: reinitializing driver\n" ); +dev_err (dev,"%s: reinitializing driver\n" , reason);__i2400m_dev_stop (i2400m); i2400m->updown =0 ; result =__i2400m_dev_start (i2400m,I2400M_BRI_SOFT |I2400M_BRI_MAC_REINIT );if (result <0 ) { -dev_err (dev,"device reboot: cannot start the device: %d\n" , - result); +dev_err (dev,"%s: cannot start the device: %d\n" , + reason, result); result = i2400m->bus_reset (i2400m,I2400M_RT_BUS );if (result >=0 ) result = -ENODEV ;
@@ -622,7 +632,8 @@ out_unlock: out:i2400m_put (i2400m);kfree (iw); -d_fnend (3 , dev,"(ws %p i2400m %p) = void\n" , ws, i2400m); +d_fnend (3 , dev,"(ws %p i2400m %p reason %s) = void\n" , + ws, i2400m, reason);return ; }
@@ -639,12 +650,12 @@ out:* reinitializing the driver to handle the reset, calling into the * bus-specific functions ops as needed. */ -int i2400m_dev_reset_handle (struct i2400m *i2400m) +int i2400m_dev_reset_handle (struct i2400m *i2400m,const char *reason) { i2400m->boot_mode =1 ;wmb ();/* Make sure i2400m_msg_to_dev() sees boot_mode */ return i2400m_schedule_work (i2400m, __i2400m_dev_reset_handle, -GFP_ATOMIC ,NULL ,0 ); +GFP_ATOMIC , &reason,sizeof (reason)); }EXPORT_SYMBOL_GPL (i2400m_dev_reset_handle);
drivers/net/wimax/i2400m/i2400m.h +1 -1
@@ -739,7 +739,7 @@ void i2400m_put(struct i2400m *i2400m)dev_put (i2400m->wimax_dev.net_dev); } -extern int i2400m_dev_reset_handle (struct i2400m *); +extern int i2400m_dev_reset_handle (struct i2400m *,const char *);extern int i2400m_bm_buf_alloc (struct i2400m *i2400m);extern void i2400m_bm_buf_free (struct i2400m *i2400m);
drivers/net/wimax/i2400m/sdio-rx.c +1 -1
@@ -179,7 +179,7 @@ void i2400ms_rx(struct i2400ms *i2400ms)i2400m_rx (i2400m, skb); }else if (unlikely (i2400m_is_boot_barker (i2400m, skb->data, rx_size))) { - ret =i2400m_dev_reset_handle (i2400m); + ret =i2400m_dev_reset_handle (i2400m,"device rebooted" );dev_err (dev,"RX: SDIO reboot barker\n" );kfree_skb (skb); }else {
drivers/net/wimax/i2400m/usb-notif.c +1 -1
@@ -98,7 +98,7 @@ int i2400mu_notification_grok(struct i2400mu *i2400mu, const void *buf, } ret =i2400m_is_boot_barker (i2400m, buf, buf_len);if (unlikely (ret >=0 )) - ret =i2400m_dev_reset_handle (i2400m); + ret =i2400m_dev_reset_handle (i2400m,"device rebooted" );else /* Unknown or unexpected data in the notif message */ i2400m_unknown_barker (i2400m, buf, buf_len); error_bad_size:
Signature · 1 line
1.6.2.5
PATCH 5/13 wimax/i2400m: cache firmware on system suspend
In reply to Inaky Perez-GonzalezIn preparation for a reset_resume implementation, have the firmware image be cached in memory when the system goes to suspend and released when out.
This is needed in case the device resets during suspend; the driver can't load firmware until resume is completed or bad deadlocks happen.
The modus operandi for this was copied from the Orinoco USB driver.
The caching is done with a kobject to avoid race conditions when releasing it. The fw loader path is altered only to first check for a cached image before trying to load from disk. A Power Management event notifier is register to call i2400m_fw_cache() or i2400m_fw_uncache() which take care of the actual cache management.
--- drivers/net/wimax/i2400m/driver.c | 51 ++++++++++++ drivers/net/wimax/i2400m/fw.c | 159 ++++++++++++++++++++++++++++++++++--- drivers/net/wimax/i2400m/i2400m.h | 16 ++++ 3 files changed, 213 insertions(+), 13 deletions(-)
drivers/net/wimax/i2400m/driver.c +51 -0
@@ -66,6 +66,7 @@#include <linux/wimax/i2400m.h> #include <linux/module.h> #include <linux/moduleparam.h> +#include <linux/suspend.h> #define D_SUBMODULE driver#include "debug-levels.h"
@@ -555,6 +556,51 @@ void i2400m_dev_stop(struct i2400m *i2400m)/* +* Listen to PM events to cache the firmware before suspend/hibernation +* +* When the device comes out of suspend, it might go into reset and +* firmware has to be uploaded again. At resume, most of the times, we +* can't load firmware images from disk, so we need to cache it. +* +* i2400m_fw_cache() will allocate a kobject and attach the firmware +* to it; that way we don't have to worry too much about the fw loader +* hitting a race condition. +* +* Note: modus operandi stolen from the Orinoco driver; thx. +*/ +static +int i2400m_pm_notifier (struct notifier_block *notifier, +unsigned long pm_event, +void *unused) +{ +struct i2400m *i2400m = + container_of(notifier, struct i2400m, pm_notifier); +struct device *dev =i2400m_dev (i2400m); + +d_fnstart (3 , dev,"(i2400m %p pm_event %lx)\n" , i2400m, pm_event); +switch (pm_event) { +case PM_HIBERNATION_PREPARE : +case PM_SUSPEND_PREPARE : +i2400m_fw_cache (i2400m); +break ; +case PM_POST_RESTORE : +/* Restore from hibernation failed. We need to clean +* up in exactly the same way, so fall through. */ +case PM_POST_HIBERNATION : +case PM_POST_SUSPEND : +i2400m_fw_uncache (i2400m); +break ; + +case PM_RESTORE_PREPARE : +default : +break ; + } +d_fnend (3 , dev,"(i2400m %p pm_event %lx) = void\n" , i2400m, pm_event); +return NOTIFY_DONE ; +} + + +/* * The device has rebooted; fix up the device and the driver * * Tear down the driver communication with the device, reload the
@@ -738,6 +784,9 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags) goto error_read_mac_addr;random_ether_addr (i2400m->src_mac_addr); + i2400m->pm_notifier.notifier_call = i2400m_pm_notifier; +register_pm_notifier (&i2400m->pm_notifier); + result =register_netdev (net_dev);/* Okey dokey, bring it up */ if (result <0 ) { dev_err(dev, "cannot register i2400m network device: %d\n",
@@ -783,6 +832,7 @@ error_wimax_dev_add: error_dev_start:unregister_netdev (net_dev); error_register_netdev: +unregister_pm_notifier (&i2400m->pm_notifier); error_read_mac_addr: error_bootrom_init:d_fnend (3 , dev,"(i2400m %p) = %d\n" , i2400m, result);
@@ -809,6 +859,7 @@ void i2400m_release(struct i2400m *i2400m)wimax_dev_rm (&i2400m->wimax_dev);i2400m_dev_stop (i2400m);unregister_netdev (i2400m->wimax_dev.net_dev); +unregister_pm_notifier (&i2400m->pm_notifier);kfree (i2400m->bm_ack_buf);kfree (i2400m->bm_cmd_buf);d_fnend (3 , dev,"(i2400m %p) = void\n" , i2400m);
drivers/net/wimax/i2400m/fw.c +146 -13
206 lines not on this page · raw message
drivers/net/wimax/i2400m/i2400m.h +16 -0
@@ -424,11 +424,21 @@ struct i2400m_barker_db;* @fw_hdrs: NULL terminated array of pointers to the firmware * headers. This is only available during firmware load time. * +* @fw_cached: Used to cache firmware when the system goes to +* suspend/standby/hibernation (as on resume we can't read it). If +* NULL, no firmware was cached, read it. If ~0, you can't read +* any firmware files (the system still didn't come out of suspend +* and failed to cache one), so abort; otherwise, a valid cached +* firmware to be used. Access to this variable is protected by +* the spinlock i2400m->rx_lock. +* * @barker: barker type that the device uses; this is initialized by * i2400m_is_boot_barker() the first time it is called. Then it * won't change during the life cycle of the device and everytime * a boot barker is received, it is just verified for it being the * same. +* +* @pm_notifier: used to register for PM events */ struct i2400m {struct wimax_dev wimax_dev;/* FIRST! See doc */
@@ -495,7 +505,10 @@ struct i2400m {const char *fw_name;/* name of the current firmware image */ unsigned long fw_version;/* version of the firmware interface */ const struct i2400m_bcf_hdr **fw_hdrs; +struct i2400m_fw *fw_cached;/* protected by rx_lock */ struct i2400m_barker_db *barker; + +struct notifier_block pm_notifier; };
@@ -671,6 +684,9 @@ extern void i2400m_tx_release(struct i2400m *);extern int i2400m_rx_setup (struct i2400m *);extern void i2400m_rx_release (struct i2400m *); +extern void i2400m_fw_cache (struct i2400m *); +extern void i2400m_fw_uncache (struct i2400m *); +extern void i2400m_net_rx (struct i2400m *,struct sk_buff *,unsigned ,const void *,int ); extern void i2400m_net_erx(struct i2400m *, struct sk_buff *,
Signature · 1 line
1.6.2.5
PATCH 6/13 wimax/i2400m: implement .reset_resume in USB subdriver
In reply to Inaky Perez-GonzalezCurrent driver didn't implement the .reset_resume method. The i2400m normally always reset on a comeback from system standby/hibernation.
This requires previously applied commits to cache the firmware image file.
--- drivers/net/wimax/i2400m/usb.c | 16 ++++++++++++++++ 1 files changed, 16 insertions(+), 0 deletions(-)
drivers/net/wimax/i2400m/usb.c +16 -0
@@ -624,6 +624,21 @@ out:static +int i2400mu_reset_resume (struct usb_interface *iface) +{ +int result; +struct device *dev = &iface->dev; +struct i2400mu *i2400mu =usb_get_intfdata (iface); +struct i2400m *i2400m = &i2400mu->i2400m; + +d_fnstart (3 , dev,"(iface %p)\n" , iface); + result =i2400m_dev_reset_handle (i2400m,"device reset on resume" ); +d_fnend (3 , dev,"(iface %p) = %d\n" , iface, result); +return result <0 ? result :0 ; +} + + +static struct usb_device_id i2400mu_id_table[] = { {USB_DEVICE (0x8086 ,USB_DEVICE_ID_I6050 ) }, {USB_DEVICE (0x8086 ,0x0181 ) },
@@ -643,6 +658,7 @@ struct usb_driver i2400mu_driver = { .name = KBUILD_MODNAME, .suspend = i2400mu_suspend, .resume = i2400mu_resume, + .reset_resume = i2400mu_reset_resume, .probe = i2400mu_probe, .disconnect = i2400mu_disconnect, .id_table = i2400mu_id_table,
Signature · 1 line
1.6.2.5
PATCH 7/13 wimax/i2400m: don't overwrite error codes when failing to load firmware
In reply to Inaky Perez-GonzalezMake sure that i2400m_dev_bootstrap() doesn't overwrite the last known error code with -ENOENT; when a firmware fails to load, we want to know the cause and not a generic error code.
--- drivers/net/wimax/i2400m/fw.c | 1 - 1 files changed, 0 insertions(+), 1 deletions(-)
drivers/net/wimax/i2400m/fw.c +0 -1
@@ -1573,7 +1573,6 @@ int i2400m_dev_bootstrap(struct i2400m *i2400m, enum i2400m_bri flags) fw_name = i2400m->bus_fw_names[itr];if (fw_name ==NULL ) {dev_err (dev,"Could not find a usable firmware image\n" ); - ret = -ENOENT ;break ; }d_printf (1 , dev,"trying firmware %s (%d)\n" , fw_name, itr);
Signature · 1 line
1.6.2.5
PATCH 8/13 wimax/i2400m: on device stop, clean up pending wake & TX work
In reply to Inaky Perez-GonzalezWhen the i2400m device needs to wake up an idle WiMAX connection, it schedules a workqueue job to do it.
Currently, only when the network stack called the _stop() method this work struct was being cancelled. This has to be done every time the device is stopped.
So add a call in i2400m_dev_stop() to take care of such cleanup, which is now wrapped in i2400m_net_wake_stop().
--- drivers/net/wimax/i2400m/driver.c | 1 + drivers/net/wimax/i2400m/i2400m.h | 1 + drivers/net/wimax/i2400m/netdev.c | 53 +++++++++++++++++++++++-------------- 3 files changed, 35 insertions(+), 20 deletions(-)
drivers/net/wimax/i2400m/driver.c +1 -0
@@ -527,6 +527,7 @@ void __i2400m_dev_stop(struct i2400m *i2400m)d_fnstart (3 , dev,"(i2400m %p)\n" , i2400m);wimax_state_change (wimax_dev, __WIMAX_ST_QUIESCING); +i2400m_net_wake_stop (i2400m);i2400m_dev_shutdown (i2400m); i2400m->ready =0 ; i2400m->bus_dev_stop (i2400m);
drivers/net/wimax/i2400m/i2400m.h +1 -0
@@ -691,6 +691,7 @@ extern void i2400m_net_rx(struct i2400m *, struct sk_buff *, unsigned, const void *, int);extern void i2400m_net_erx (struct i2400m *,struct sk_buff *,enum i2400m_cs ); +extern void i2400m_net_wake_stop (struct i2400m *);enum i2400m_pt ;extern int i2400m_tx (struct i2400m *,const void *,size_t ,enum i2400m_pt );
drivers/net/wimax/i2400m/netdev.c +33 -20
71 lines not on this page · raw message
Signature · 1 line
1.6.2.5
PATCH 9/13 wimax/i2400m: cleanup initialization/destruction flow
In reply to Inaky Perez-GonzalezCurrently the i2400m driver was starting in a weird way: registering a network device, setting the device up and then registering a WiMAX device.
This is an historic artifact, and was causing issues, a some early reports the device sends were getting lost by issue of the wimax_dev not being registered.
Fix said situation by doing the wimax device registration in i2400m_setup() after network device registration and before starting thed device.
As well, removed spurious setting of the state to UNINITIALIZED; i2400m.dev_start() does that already.
--- drivers/net/wimax/i2400m/driver.c | 32 ++++++++++++++------------------ drivers/net/wimax/i2400m/i2400m.h | 30 +++++++++++++++++++++--------- drivers/net/wimax/i2400m/netdev.c | 10 ++++++---- 3 files changed, 41 insertions(+), 31 deletions(-)
drivers/net/wimax/i2400m/driver.c +14 -18
@@ -754,13 +754,9 @@ EXPORT_SYMBOL_GPL(i2400m_bm_buf_free* * Returns: 0 if ok, < 0 errno code on error. * -* Initializes the bus-generic parts of the i2400m driver; the -* bus-specific parts have been initialized, function pointers filled -* out by the bus-specific probe function. -* -* As well, this registers the WiMAX and net device nodes. Once this -* function returns, the device is operative and has to be ready to -* receive and send network traffic and WiMAX control operations. +* Sets up basic device comunication infrastructure, boots the ROM to +* read the MAC address, registers with the WiMAX and network stacks +* and then brings up the device. */ int i2400m_setup (struct i2400m *i2400m,enum i2400m_bri bm_flags) {
@@ -796,18 +792,13 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags) }netif_carrier_off (net_dev); - result =i2400m_dev_start (i2400m, bm_flags); -if (result <0 ) - goto error_dev_start; - i2400m->wimax_dev.op_msg_from_user = i2400m_op_msg_from_user; i2400m->wimax_dev.op_rfkill_sw_toggle = i2400m_op_rfkill_sw_toggle; i2400m->wimax_dev.op_reset = i2400m_op_reset; + result =wimax_dev_add (&i2400m->wimax_dev, net_dev);if (result <0 ) goto error_wimax_dev_add; -/* User space needs to do some init stuff */ -wimax_state_change (wimax_dev,WIMAX_ST_UNINITIALIZED );/* Now setup all that requires a registered net and wimax device. */ result =sysfs_create_group (&net_dev->dev.kobj, &i2400m_dev_attr_group);
@@ -815,22 +806,27 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags)dev_err (dev,"cannot setup i2400m's sysfs: %d\n" , result); goto error_sysfs_setup; } + result =i2400m_debugfs_add (i2400m);if (result <0 ) {dev_err (dev,"cannot setup i2400m's debugfs: %d\n" , result); goto error_debugfs_setup; } + + result =i2400m_dev_start (i2400m, bm_flags); +if (result <0 ) + goto error_dev_start;d_fnend (3 , dev,"(i2400m %p) = %d\n" , i2400m, result);return result; +error_dev_start: +i2400m_debugfs_rm (i2400m); error_debugfs_setup:sysfs_remove_group (&i2400m->wimax_dev.net_dev->dev.kobj, &i2400m_dev_attr_group); error_sysfs_setup:wimax_dev_rm (&i2400m->wimax_dev); error_wimax_dev_add: -i2400m_dev_stop (i2400m); -error_dev_start:unregister_netdev (net_dev); error_register_netdev:unregister_pm_notifier (&i2400m->pm_notifier);
@@ -854,15 +850,15 @@ void i2400m_release(struct i2400m *i2400m)d_fnstart (3 , dev,"(i2400m %p)\n" , i2400m);netif_stop_queue (i2400m->wimax_dev.net_dev); +i2400m_dev_stop (i2400m); +i2400m_debugfs_rm (i2400m);sysfs_remove_group (&i2400m->wimax_dev.net_dev->dev.kobj, &i2400m_dev_attr_group);wimax_dev_rm (&i2400m->wimax_dev); -i2400m_dev_stop (i2400m);unregister_netdev (i2400m->wimax_dev.net_dev);unregister_pm_notifier (&i2400m->pm_notifier); -kfree (i2400m->bm_ack_buf); -kfree (i2400m->bm_cmd_buf); +i2400m_bm_buf_free (i2400m);d_fnend (3 , dev,"(i2400m %p) = void\n" , i2400m); }EXPORT_SYMBOL_GPL (i2400m_release);
drivers/net/wimax/i2400m/i2400m.h +21 -9
@@ -117,16 +117,28 @@* well as i2400m->wimax_dev.net_dev and call i2400m_setup(). The * i2400m driver will only register with the WiMAX and network stacks; * the only access done to the device is to read the MAC address so we -* can register a network device. This calls i2400m_dev_start() to -* load firmware, setup communication with the device and configure it -* for operation. +* can register a network device. * -* At this point, control and data communications are possible. +* The high-level call flow is: +* +* bus_probe() +* i2400m_setup() +* boot rom initialization / read mac addr +* network / WiMAX stacks registration +* i2400m_dev_start() +* i2400m->bus_dev_start() +* i2400m_dev_initialize() +* +* The reverse applies for a disconnect() call: * -* On disconnect/driver unload, the bus-specific disconnect function -* calls i2400m_release() to undo i2400m_setup(). i2400m_dev_stop() -* shuts the firmware down and releases resources uses to communicate -* with the device. +* bus_disconnect() +* i2400m_release() +* i2400m_dev_stop() +* i2400m_dev_shutdown() +* i2400m->bus_dev_stop() +* network / WiMAX stack unregistration +* +* At this point, control and data communications are possible. * * While the device is up, it might reset. The bus-specific driver has * to catch that situation and call i2400m_dev_reset_handle() to deal
@@ -706,7 +718,7 @@ static inline int i2400m_debugfs_add(struct i2400m *i2400m)static inline void i2400m_debugfs_rm (struct i2400m *i2400m) {}#endif -/* Called by _dev_start()/_dev_stop() to initialize the device itself */ +/* Initialize/shutdown the device */ extern int i2400m_dev_initialize (struct i2400m *);extern void i2400m_dev_shutdown (struct i2400m *);
drivers/net/wimax/i2400m/netdev.c +6 -4
@@ -102,11 +102,13 @@ int i2400m_open(struct net_device *net_dev)struct device *dev =i2400m_dev (i2400m);d_fnstart (3 , dev,"(net_dev %p [i2400m %p])\n" , net_dev, i2400m); -if (i2400m->ready ==0 ) { -dev_err (dev,"Device is still initializing\n" ); - result = -EBUSY ; - }else +/* Make sure we wait until init is complete... */ +mutex_lock (&i2400m->init_mutex); +if (i2400m->updown) result =0 ; +else + result = -EBUSY ; +mutex_unlock (&i2400m->init_mutex);d_fnend (3 , dev,"(net_dev %p [i2400m %p]) = %d\n" , net_dev, i2400m, result);return result;
Signature · 1 line
1.6.2.5
PATCH 10/13 wimax/i2400m: clarify and fix i2400m->{ready,updown}
In reply to Inaky Perez-GonzalezThe i2400m driver uses two different bits to distinguish how much the driver is up. i2400m->ready is used to denote that the infrastructure to communicate with the device is up and running. i2400m->updown is used to indicate if 'ready' and the device is up and running, ready to take control and data traffic.
However, all this was pretty dirty and not clear, with many open spots where race conditions were present.
This commit cleans up the situation by:
- documenting the usage of both bits
- setting them only in specific, well controlled places (i2400m_dev_start, i2400m_dev_stop)
- ensuring the i2400m workqueue can't get in the middle of the setting by flushing it when i2400m->ready is set to zero. This allows the report hook not having to check again for the bit to be set [rx.c:i2400m_report_hook_work()].
- using i2400m->updown to determine if the device is up and running instead of the wimax state in i2400m_dev_reset_handle().
- not loosing missed messages sent by the hardware before i2400m->ready is set. In rx.c, whatever the device sends can be sent to user space over the message pipes as soon as the wimax device is registered, so don't wait for i2400m->ready to be set.
--- drivers/net/wimax/i2400m/driver.c | 44 ++++++++++++++++++++++++------------ drivers/net/wimax/i2400m/i2400m.h | 23 ++++++++++++++++++- drivers/net/wimax/i2400m/rx.c | 16 ++++++------- drivers/net/wimax/i2400m/usb.c | 7 +++++- include/net/wimax.h | 6 +++++ 5 files changed, 70 insertions(+), 26 deletions(-)
drivers/net/wimax/i2400m/driver.c +29 -15
@@ -455,6 +455,8 @@ retry: result = i2400m->bus_dev_start (i2400m);if (result <0 ) goto error_bus_dev_start; + i2400m->ready =1 ; +wmb ();/* see i2400m->ready's documentation */ result =i2400m_firmware_check (i2400m);/* fw versions ok? */ if (result <0 ) goto error_fw_check;
@@ -462,7 +464,6 @@ retry: result =i2400m_check_mac_addr (i2400m);if (result <0 ) goto error_check_mac_addr; - i2400m->ready =1 ;wimax_state_change (wimax_dev,WIMAX_ST_UNINITIALIZED ); result =i2400m_dev_initialize (i2400m);if (result <0 )
@@ -475,6 +476,9 @@ retry: error_dev_initialize: error_check_mac_addr: + i2400m->ready =0 ; +wmb ();/* see i2400m->ready's documentation */ +flush_workqueue (i2400m->work_queue); error_fw_check: i2400m->bus_dev_stop (i2400m); error_bus_dev_start:
@@ -498,11 +502,15 @@ error_bootstrap:static int i2400m_dev_start (struct i2400m *i2400m,enum i2400m_bri bm_flags) { -int result; +int result =0 ;mutex_lock (&i2400m->init_mutex);/* Well, start the device */ - result =__i2400m_dev_start (i2400m, bm_flags); -if (result >=0 ) - i2400m->updown =1 ; +if (i2400m->updown ==0 ) { + result =__i2400m_dev_start (i2400m, bm_flags); +if (result >=0 ) { + i2400m->updown =1 ; +wmb ();/* see i2400m->updown's documentation */ + } + }mutex_unlock (&i2400m->init_mutex);return result; }
@@ -529,7 +537,14 @@ void __i2400m_dev_stop(struct i2400m *i2400m)wimax_state_change (wimax_dev, __WIMAX_ST_QUIESCING);i2400m_net_wake_stop (i2400m);i2400m_dev_shutdown (i2400m); - i2400m->ready =0 ; +/* +* Make sure no report hooks are running *before* we stop the +* communication infrastructure with the device. +*/ + i2400m->ready =0 ;/* nobody can queue work anymore */ +wmb ();/* see i2400m->ready's documentation */ +flush_workqueue (i2400m->work_queue); + i2400m->bus_dev_stop (i2400m);destroy_workqueue (i2400m->work_queue);i2400m_rx_release (i2400m);
@@ -551,6 +566,7 @@ void i2400m_dev_stop(struct i2400m *i2400m)if (i2400m->updown) {__i2400m_dev_stop (i2400m); i2400m->updown =0 ; +wmb ();/* see i2400m->updown's documentation */ }mutex_unlock (&i2400m->init_mutex); }
@@ -632,7 +648,6 @@ void __i2400m_dev_reset_handle(struct work_struct *ws)const char *reason;struct i2400m *i2400m = iw->i2400m;struct device *dev =i2400m_dev (i2400m); -enum wimax_st wimax_state;struct i2400m_reset_ctx *ctx = i2400m->reset_ctx;if (WARN_ON (iw->pl_size !=sizeof (reason)))
@@ -647,29 +662,28 @@ void __i2400m_dev_reset_handle(struct work_struct *ws)/* We are still in i2400m_dev_start() [let it fail] or * i2400m_dev_stop() [we are shutting down anyway, so * ignore it] or we are resetting somewhere else. */ -dev_err (dev,"device rebooted\n" ); +dev_err (dev,"device rebooted somewhere else?\n" );i2400m_msg_to_dev_cancel_wait (i2400m, -EL3RST );complete (&i2400m->msg_completion); goto out; } - wimax_state =wimax_state_get (&i2400m->wimax_dev); -if (wimax_state <WIMAX_ST_UNINITIALIZED ) { -dev_info (dev,"%s: it is down, ignoring\n" , reason); - goto out_unlock;/* ifconfig up/down wasn't called */ +if (i2400m->updown ==0 ) { +dev_info (dev,"%s: device is down, doing nothing\n" , reason); + goto out_unlock; }dev_err (dev,"%s: reinitializing driver\n" , reason);__i2400m_dev_stop (i2400m); - i2400m->updown =0 ; result =__i2400m_dev_start (i2400m,I2400M_BRI_SOFT |I2400M_BRI_MAC_REINIT );if (result <0 ) { + i2400m->updown =0 ; +wmb ();/* see i2400m->updown's documentation */ dev_err (dev,"%s: cannot start the device: %d\n" , reason, result); result = i2400m->bus_reset (i2400m,I2400M_RT_BUS );if (result >=0 ) result = -ENODEV ; - }else - i2400m->updown =1 ; + } out_unlock:if (i2400m->reset_ctx) { ctx->result = result;
drivers/net/wimax/i2400m/i2400m.h +22 -1
@@ -307,6 +307,27 @@ struct i2400m_barker_db;* force this to be the first field so that we can get from * netdev_priv() the right pointer. * +* @updown: the device is up and ready for transmitting control and +* data packets. This implies @ready (communication infrastructure +* with the device is ready) and the device's firmware has been +* loaded and the device initialized. +* +* Write to it only inside a i2400m->init_mutex protected area +* followed with a wmb(); rmb() before accesing (unless locked +* inside i2400m->init_mutex). Read access can be loose like that +* [just using rmb()] because the paths that use this also do +* other error checks later on. +* +* @ready: Communication infrastructure with the device is ready, data +* frames can start to be passed around (this is lighter than +* using the WiMAX state for certain hot paths). +* +* Write to it only inside a i2400m->init_mutex protected area +* followed with a wmb(); rmb() before accesing (unless locked +* inside i2400m->init_mutex). Read access can be loose like that +* [just using rmb()] because the paths that use this also do +* other error checks later on. +* * @rx_reorder: 1 if RX reordering is enabled; this can only be * set at probe time. *
@@ -458,7 +479,7 @@ struct i2400m { unsigned updown:1; /* Network device is up or down */ unsigned boot_mode:1; /* is the device in boot mode? */ unsigned sboot:1; /* signed or unsigned fw boot */ - unsigned ready:1; /* all probing steps done */ + unsigned ready:1; /* Device comm infrastructure ready */ unsigned rx_reorder:1; /* RX reorder is enabled */u8 trace_msg_from_user;/* echo rx msgs to 'trace' pipe */ /* typed u8 so /sys/kernel/debug/u8 can tweak */
drivers/net/wimax/i2400m/rx.c +7 -9
@@ -177,8 +177,7 @@ void i2400m_report_hook_work(struct work_struct *ws)struct i2400m_work *iw = container_of(ws, struct i2400m_work, ws);struct i2400m_report_hook_args *args = (void *) iw->pl; -if (iw->i2400m->ready) -i2400m_report_hook (iw->i2400m, args->l3l4_hdr, args->size); +i2400m_report_hook (iw->i2400m, args->l3l4_hdr, args->size);kfree_skb (args->skb_rx);i2400m_put (iw->i2400m);kfree (iw);
@@ -305,11 +304,12 @@ void i2400m_rx_ctl(struct i2400m *i2400m, struct sk_buff *skb_rx, .l3l4_hdr = l3l4_hdr, .size = size }; -if (unlikely (i2400m->ready ==0 ))/* only send if up */ -return ; -skb_get (skb_rx); -i2400m_queue_work (i2400m, i2400m_report_hook_work, -GFP_KERNEL , &args,sizeof (args)); +rmb ();/* see i2400m->ready's documentation */ +if (likely (i2400m->ready)) {/* only send if up */ +skb_get (skb_rx); +i2400m_queue_work (i2400m, i2400m_report_hook_work, +GFP_KERNEL , &args,sizeof (args)); + }if (unlikely (i2400m->trace_msg_from_user))wimax_msg (&i2400m->wimax_dev,"echo" , l3l4_hdr, size,GFP_KERNEL );
@@ -363,8 +363,6 @@ void i2400m_rx_trace(struct i2400m *i2400m, msg_type &I2400M_MT_REPORT_MASK ?"REPORT" :"CMD/SET/GET" , msg_type, size);d_dump (2 , dev, l3l4_hdr, size); -if (unlikely (i2400m->ready ==0 ))/* only send if up */ -return ; result =wimax_msg (wimax_dev,"trace" , l3l4_hdr, size,GFP_KERNEL );if (result <0 ) dev_err(dev, "error sending trace to userspace: %d\n",
drivers/net/wimax/i2400m/usb.c +6 -1
@@ -516,7 +516,10 @@ void i2400mu_disconnect(struct usb_interface *iface)* So at the end, the three cases require common handling. * * If at the time of this call the device's firmware is not loaded, -* nothing has to be done. +* nothing has to be done. Note we can be "loose" about not reading +* i2400m->updown under i2400m->init_mutex. If it happens to change +* inmediately, other parts of the call flow will fail and effectively +* catch it. * * If the firmware is loaded, we need to: *
@@ -555,6 +558,7 @@ int i2400mu_suspend(struct usb_interface *iface, pm_message_t pm_msg)#endif d_fnstart (3 , dev,"(iface %p pm_msg %u)\n" , iface, pm_msg.event); +rmb ();/* see i2400m->updown's documentation */ if (i2400m->updown ==0 ) goto no_firmware;if (i2400m->state ==I2400M_SS_DATA_PATH_CONNECTED && is_autosuspend) {
@@ -608,6 +612,7 @@ int i2400mu_resume(struct usb_interface *iface)struct i2400m *i2400m = &i2400mu->i2400m;d_fnstart (3 , dev,"(iface %p)\n" , iface); +rmb ();/* see i2400m->updown's documentation */ if (i2400m->updown ==0 ) {d_printf (1 , dev,"fw was down, no resume neeed\n" ); goto out;
include/net/wimax.h +6 -0
@@ -195,6 +195,12 @@* defining the `struct nla_policy` for each message, it has to have * an array size of WIMAX_GNL_ATTR_MAX+1. * +* The op_*() function pointers will not be called if the wimax_dev is +* in a state <= %WIMAX_ST_UNINITIALIZED. The exception is: +* +* - op_reset: can be called at any time after wimax_dev_add() has +* been called. +* * THE PIPE INTERFACE: * * This interface is kept intentionally simple. The driver can send
Signature · 1 line
1.6.2.5
PATCH 11/13 wimax/i2400m: introduce i2400m->bus_setup/release
In reply to Inaky Perez-GonzalezThe SDIO subdriver of the i2400m requires certain steps to be done before we do any acces to the device, even for doing firmware upload.
This lead to a few ugly hacks, which basically involve doing those steps in probe() before calling i2400m_setup() and undoing them in disconnect() after claling i2400m_release(); but then, much of those steps have to be repeated when resetting the device, suspending, etc (in upcoming pre/post reset support).
Thus, a new pair of optional, bus-specific calls i2400m->bus_{setup/release} are introduced. These are used to setup basic infrastructure needed to load firmware onto the device.
This commit also updates the SDIO subdriver to use said calls.
--- drivers/net/wimax/i2400m/driver.c | 20 +++++++- drivers/net/wimax/i2400m/i2400m.h | 27 ++++++++++ drivers/net/wimax/i2400m/sdio.c | 100 +++++++++++++++++++++++------------- drivers/net/wimax/i2400m/usb.c | 2 + 4 files changed, 111 insertions(+), 38 deletions(-)
drivers/net/wimax/i2400m/driver.c +18 -2
@@ -41,8 +41,10 @@* __i2400m_dev_start() * * i2400m_setup() +* i2400m->bus_setup() * i2400m_bootrom_init() * register_netdev() +* wimax_dev_add() * i2400m_dev_start() * __i2400m_dev_start() * i2400m_dev_bootstrap()
@@ -50,15 +52,15 @@* i2400m->bus_dev_start() * i2400m_firmware_check() * i2400m_check_mac_addr() -* wimax_dev_add() * * i2400m_release() -* wimax_dev_rm() * i2400m_dev_stop() * __i2400m_dev_stop() * i2400m_dev_shutdown() * i2400m->bus_dev_stop() * i2400m_tx_release() +* i2400m->bus_release() +* wimax_dev_rm() * unregister_netdev() */ #include "i2400m.h"
@@ -784,6 +786,15 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags)snprintf (wimax_dev->name,sizeof (wimax_dev->name),"i2400m-%s:%s" , dev->bus->name,dev_name (dev)); +if (i2400m->bus_setup) { + result = i2400m->bus_setup (i2400m); +if (result <0 ) { +dev_err (dev,"bus-specific setup failed: %d\n" , + result); + goto error_bus_setup; + } + } + result =i2400m_bootrom_init (i2400m, bm_flags);if (result <0 ) { dev_err(dev, "read mac addr: bootrom init "
@@ -846,6 +857,9 @@ error_register_netdev:unregister_pm_notifier (&i2400m->pm_notifier); error_read_mac_addr: error_bootrom_init: +if (i2400m->bus_release) + i2400m->bus_release (i2400m); +error_bus_setup:d_fnend (3 , dev,"(i2400m %p) = %d\n" , i2400m, result);return result; }
@@ -872,6 +886,8 @@ void i2400m_release(struct i2400m *i2400m)wimax_dev_rm (&i2400m->wimax_dev);unregister_netdev (i2400m->wimax_dev.net_dev);unregister_pm_notifier (&i2400m->pm_notifier); +if (i2400m->bus_release) + i2400m->bus_release (i2400m);i2400m_bm_buf_free (i2400m);d_fnend (3 , dev,"(i2400m %p) = void\n" , i2400m); }
drivers/net/wimax/i2400m/i2400m.h +27 -0
@@ -123,6 +123,7 @@* * bus_probe() * i2400m_setup() +* i2400m->bus_setup() * boot rom initialization / read mac addr * network / WiMAX stacks registration * i2400m_dev_start()
@@ -137,6 +138,7 @@* i2400m_dev_shutdown() * i2400m->bus_dev_stop() * network / WiMAX stack unregistration +* i2400m->bus_release() * * At this point, control and data communications are possible. *
@@ -214,12 +216,35 @@ struct i2400m_barker_db;* Members marked with [fill] must be filled out/initialized before * calling i2400m_setup(). * +* Note the @bus_setup/@bus_release, @bus_dev_start/@bus_dev_release +* call pairs are very much doing almost the same, and depending on +* the underlying bus, some stuff has to be put in one or the +* other. The idea of setup/release is that they setup the minimal +* amount needed for loading firmware, where us dev_start/stop setup +* the rest needed to do full data/control traffic. +* * @bus_tx_block_size: [fill] SDIO imposes a 256 block size, USB 16, * so we have a tx_blk_size variable that the bus layer sets to * tell the engine how much of that we need. * * @bus_pl_size_max: [fill] Maximum payload size. * +* @bus_setup: [optional fill] Function called by the bus-generic code +* [i2400m_setup()] to setup the basic bus-specific communications +* to the the device needed to load firmware. See LIFE CYCLE above. +* +* NOTE: Doesn't need to upload the firmware, as that is taken +* care of by the bus-generic code. +* +* @bus_release: [optional fill] Function called by the bus-generic +* code [i2400m_release()] to shutdown the basic bus-specific +* communications to the the device needed to load firmware. See +* LIFE CYCLE above. +* +* This function does not need to reset the device, just tear down +* all the host resources created to handle communication with +* the device. +* * @bus_dev_start: [fill] Function called by the bus-generic code * [i2400m_dev_start()] to setup the bus-specific communications * to the the device. See LIFE CYCLE above.
@@ -490,8 +515,10 @@ struct i2400m {size_t bus_pl_size_max;unsigned bus_bm_retries; +int (*bus_setup)(struct i2400m *);int (*bus_dev_start)(struct i2400m *);void (*bus_dev_stop)(struct i2400m *); +void (*bus_release)(struct i2400m *);void (*bus_tx_kick)(struct i2400m *);int (*bus_reset)(struct i2400m *,enum i2400m_reset_type ); ssize_t (*bus_bm_cmd_send)(struct i2400m *,
drivers/net/wimax/i2400m/sdio.c +64 -36
152 lines not on this page · raw message
drivers/net/wimax/i2400m/usb.c +2 -0
@@ -416,8 +416,10 @@ int i2400mu_probe(struct usb_interface *iface, i2400m->bus_tx_block_size =I2400MU_BLK_SIZE ; i2400m->bus_pl_size_max =I2400MU_PL_SIZE_MAX ; + i2400m->bus_setup =NULL ; i2400m->bus_dev_start = i2400mu_bus_dev_start; i2400m->bus_dev_stop = i2400mu_bus_dev_stop; + i2400m->bus_release =NULL ; i2400m->bus_tx_kick = i2400mu_bus_tx_kick; i2400m->bus_reset = i2400mu_bus_reset; i2400m->bus_bm_retries =I2400M_USB_BOOT_RETRIES ;
Signature · 1 line
1.6.2.5
PATCH 12/13 wimax/i2400m: do bootmode buffer management in i2400m_setup/release()
In reply to Inaky Perez-GonzalezAfter the introduction of i2400m->bus_setup/release, there is no more race condition where the bootmode buffers are needed before i2400m_setup() is called.
Before, the SDIO driver would setup RX before calling i2400m_setup() and thus need those buffers; now RX setup is done in i2400m->bus_setup(), which is called by i2400m_setup().
Thus, all the bootmode buffer management can now be done completely inside i2400m_setup()/i2400m_release(), removing complexity from the bus-specific drivers.
--- drivers/net/wimax/i2400m/driver.c | 28 +++++++++++++++++----------- drivers/net/wimax/i2400m/i2400m.h | 2 -- drivers/net/wimax/i2400m/sdio.c | 14 -------------- drivers/net/wimax/i2400m/usb.c | 8 -------- 4 files changed, 17 insertions(+), 35 deletions(-)
drivers/net/wimax/i2400m/driver.c +17 -11
@@ -723,12 +723,13 @@ int i2400m_dev_reset_handle(struct i2400m *i2400m, const char *reason)EXPORT_SYMBOL_GPL (i2400m_dev_reset_handle); -/** -* i2400m_bm_buf_alloc - Alloc the command and ack buffers for boot mode +/* +* Alloc the command and ack buffers for boot mode * * Get the buffers needed to deal with boot mode messages. These * buffers need to be allocated before the sdio recieve irq is setup. */ +static int i2400m_bm_buf_alloc (struct i2400m *i2400m) {int result;
@@ -747,22 +748,19 @@ error_bm_ack_buf_kzalloc: error_bm_cmd_kzalloc:return result; } -EXPORT_SYMBOL_GPL (i2400m_bm_buf_alloc); -/** -* i2400m_bm_buf_free - Free boot mode command and ack buffers. -* -* Free the command and ack buffers -* + +/* +* Free boot mode command and ack buffers. */ +static void i2400m_bm_buf_free (struct i2400m *i2400m) {kfree (i2400m->bm_ack_buf);kfree (i2400m->bm_cmd_buf); -return ; } -EXPORT_SYMBOL_GPL (i2400m_bm_buf_free -); + +/** * i2400m_setup - bus-generic setup function for the i2400m device *
@@ -786,6 +784,12 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags)snprintf (wimax_dev->name,sizeof (wimax_dev->name),"i2400m-%s:%s" , dev->bus->name,dev_name (dev)); + result =i2400m_bm_buf_alloc (i2400m); +if (result <0 ) { +dev_err (dev,"cannot allocate bootmode scratch buffers\n" ); + goto error_bm_buf_alloc; + } +if (i2400m->bus_setup) { result = i2400m->bus_setup (i2400m);if (result <0 ) {
@@ -860,6 +864,8 @@ error_bootrom_init:if (i2400m->bus_release) i2400m->bus_release (i2400m); error_bus_setup: +i2400m_bm_buf_free (i2400m); +error_bm_buf_alloc:d_fnend (3 , dev,"(i2400m %p) = %d\n" , i2400m, result);return result; }
drivers/net/wimax/i2400m/i2400m.h +0 -2
@@ -817,8 +817,6 @@ void i2400m_put(struct i2400m *i2400m) }extern int i2400m_dev_reset_handle (struct i2400m *,const char *); -extern int i2400m_bm_buf_alloc (struct i2400m *i2400m); -extern void i2400m_bm_buf_free (struct i2400m *i2400m);/* * _setup()/_release() are called by the probe/disconnect functions of
drivers/net/wimax/i2400m/sdio.c +0 -14
@@ -519,18 +519,6 @@ int i2400ms_probe(struct sdio_func *func, i2400m->bus_bm_mac_addr_impaired =1 ; i2400m->bus_bm_pokes_table = &i2400ms_pokes[0 ]; -/* -* Before we are enabling the device interrupt register, make -* sure the buffer used during bootmode operation is setup so -* when the first D2H data interrupt comes, the memory is -* available for copying the D2H data. -*/ - result =i2400m_bm_buf_alloc (i2400m); -if (result <0 ) { -dev_err (dev,"cannot allocate SDIO bootmode buffer\n" ); - goto error_bootmode_buf_setup; - } - result =i2400m_setup (i2400m,I2400M_BRI_NO_REBOOT );if (result <0 ) {dev_err (dev,"cannot setup device: %d\n" , result);
@@ -548,8 +536,6 @@ int i2400ms_probe(struct sdio_func *func, error_debugfs_add:i2400m_release (i2400m); error_setup: -i2400m_bm_buf_free (i2400m); -error_bootmode_buf_setup:sdio_set_drvdata (func,NULL );free_netdev (net_dev); error_alloc_netdev:
drivers/net/wimax/i2400m/usb.c +0 -8
@@ -447,12 +447,6 @@ int i2400mu_probe(struct usb_interface *iface, usb_dev->autosuspend_disabled =0 ;#endif - result =i2400m_bm_buf_alloc (i2400m); -if (result <0 ) { -dev_err (dev,"cannot allocate USB bootmode buffer\n" ); - goto error_bm_buf_alloc; - } - result =i2400m_setup (i2400m,I2400M_BRI_MAC_REINIT );if (result <0 ) {dev_err (dev,"cannot setup device: %d\n" , result);
@@ -468,8 +462,6 @@ int i2400mu_probe(struct usb_interface *iface, error_debugfs_add:i2400m_release (i2400m); error_setup: -i2400m_bm_buf_free (i2400m); -error_bm_buf_alloc:usb_set_intfdata (iface,NULL );usb_put_dev (i2400mu->usb_dev);free_netdev (net_dev);
Signature · 1 line
1.6.2.5
PATCH 13/13 wimax/i2400m: Implement pre/post reset support in the USB driver
In reply to Inaky Perez-GonzalezThe USB stack can callback a driver is about to be reset by an external entity and right after it, so the driver can save state and then restore it.
This commit implements said support; it is implemented actually in the core, bus-generic driver [i2400m_{pre,post}_reset()] and used by the bus-specific drivers. This way the SDIO driver can also use it once said support is brought to the SDIO stack.
--- drivers/net/wimax/i2400m/driver.c | 81 +++++++++++++++++++++++++++++++++++++ drivers/net/wimax/i2400m/i2400m.h | 2 + drivers/net/wimax/i2400m/usb.c | 33 +++++++++++++++ 3 files changed, 116 insertions(+), 0 deletions(-)
drivers/net/wimax/i2400m/driver.c +81 -0
@@ -620,6 +620,87 @@ int i2400m_pm_notifier(struct notifier_block *notifier,/* +* pre-reset is called before a device is going on reset +* +* This has to be followed by a call to i2400m_post_reset(), otherwise +* bad things might happen. +*/ +int i2400m_pre_reset (struct i2400m *i2400m) +{ +int result; +struct device *dev =i2400m_dev (i2400m); + +d_fnstart (3 , dev,"(i2400m %p)\n" , i2400m); +d_printf (1 , dev,"pre-reset shut down\n" ); + + result =0 ; +mutex_lock (&i2400m->init_mutex); +if (i2400m->updown) { +netif_tx_disable (i2400m->wimax_dev.net_dev); +__i2400m_dev_stop (i2400m); + result =0 ; +/* down't set updown to zero -- this way +* post_reset can restore properly */ + } +mutex_unlock (&i2400m->init_mutex); +if (i2400m->bus_release) + i2400m->bus_release (i2400m); +d_fnend (3 , dev,"(i2400m %p) = %d\n" , i2400m, result); +return result; +} +EXPORT_SYMBOL_GPL (i2400m_pre_reset); + + +/* +* Restore device state after a reset +* +* Do the work needed after a device reset to bring it up to the same +* state as it was before the reset. +* +* NOTE: this requires i2400m->init_mutex taken +*/ +int i2400m_post_reset (struct i2400m *i2400m) +{ +int result =0 ; +struct device *dev =i2400m_dev (i2400m); + +d_fnstart (3 , dev,"(i2400m %p)\n" , i2400m); +d_printf (1 , dev,"post-reset start\n" ); +if (i2400m->bus_setup) { + result = i2400m->bus_setup (i2400m); +if (result <0 ) { +dev_err (dev,"bus-specific setup failed: %d\n" , + result); + goto error_bus_setup; + } + } +mutex_lock (&i2400m->init_mutex); +if (i2400m->updown) { + result =__i2400m_dev_start ( + i2400m,I2400M_BRI_SOFT |I2400M_BRI_MAC_REINIT ); +if (result <0 ) + goto error_dev_start; + } +mutex_unlock (&i2400m->init_mutex); +d_fnend (3 , dev,"(i2400m %p) = %d\n" , i2400m, result); +return result; + +error_dev_start: +if (i2400m->bus_release) + i2400m->bus_release (i2400m); +error_bus_setup: +/* even if the device was up, it could not be recovered, so we +* mark it as down. */ + i2400m->updown =0 ; +wmb ();/* see i2400m->updown's documentation */ +mutex_unlock (&i2400m->init_mutex); +d_fnend (3 , dev,"(i2400m %p) = %d\n" , i2400m, result); +return result; +} +EXPORT_SYMBOL_GPL (i2400m_post_reset); + + +/* * The device has rebooted; fix up the device and the driver * * Tear down the driver communication with the device, reload the
drivers/net/wimax/i2400m/i2400m.h +2 -0
@@ -817,6 +817,8 @@ void i2400m_put(struct i2400m *i2400m) }extern int i2400m_dev_reset_handle (struct i2400m *,const char *); +extern int i2400m_pre_reset (struct i2400m *); +extern int i2400m_post_reset (struct i2400m *);/* * _setup()/_release() are called by the probe/disconnect functions of
drivers/net/wimax/i2400m/usb.c +33 -0
@@ -637,6 +637,37 @@ int i2400mu_reset_resume(struct usb_interface *iface) } +/* +* Another driver or user space is triggering a reset on the device +* which contains the interface passed as an argument. Cease IO and +* save any device state you need to restore. +* +* If you need to allocate memory here, use GFP_NOIO or GFP_ATOMIC, if +* you are in atomic context. +*/ +static +int i2400mu_pre_reset (struct usb_interface *iface) +{ +struct i2400mu *i2400mu =usb_get_intfdata (iface); +return i2400m_pre_reset (&i2400mu->i2400m); +} + + +/* +* The reset has completed. Restore any saved device state and begin +* using the device again. +* +* If you need to allocate memory here, use GFP_NOIO or GFP_ATOMIC, if +* you are in atomic context. +*/ +static +int i2400mu_post_reset (struct usb_interface *iface) +{ +struct i2400mu *i2400mu =usb_get_intfdata (iface); +return i2400m_post_reset (&i2400mu->i2400m); +} + +static struct usb_device_id i2400mu_id_table[] = { {USB_DEVICE (0x8086 ,USB_DEVICE_ID_I6050 ) },
@@ -660,6 +691,8 @@ struct usb_driver i2400mu_driver = { .reset_resume = i2400mu_reset_resume, .probe = i2400mu_probe, .disconnect = i2400mu_disconnect, + .pre_reset = i2400mu_pre_reset, + .post_reset = i2400mu_post_reset, .id_table = i2400mu_id_table, .supports_autosuspend = 1, };
Signature · 1 line
1.6.2.5