netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 2.6.33/2 01/15] wimax/i2400m: USB driver uses a configurable endpoint map
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
@ 2009-11-04 21:39 ` Inaky Perez-Gonzalez
  2009-11-04 21:39 ` [PATCH 2.6.33/2 02/15] wimax/i2400m/sdio: clear the INTR status bit after reading size Inaky Perez-Gonzalez
                   ` (26 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:39 UTC (permalink / raw)
  To: netdev, wimax; +Cc: Dirk Brandewie

From: Dirk Brandewie <dirk.j.brandewie@intel.com>

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.

Signed-off-by: Dirk Brandewie <dirk.j.brandewie@intel.com>
Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 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(-)

diff --git a/drivers/net/wimax/i2400m/i2400m-usb.h b/drivers/net/wimax/i2400m/i2400m-usb.h
index 79c3753..f73a067 100644
--- a/drivers/net/wimax/i2400m/i2400m-usb.h
+++ b/drivers/net/wimax/i2400m/i2400m-usb.h
@@ -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;
diff --git a/drivers/net/wimax/i2400m/usb-fw.c b/drivers/net/wimax/i2400m/usb-fw.c
index 5ad287c..a2250e4 100644
--- a/drivers/net/wimax/i2400m/usb-fw.c
+++ b/drivers/net/wimax/i2400m/usb-fw.c
@@ -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,
diff --git a/drivers/net/wimax/i2400m/usb-notif.c b/drivers/net/wimax/i2400m/usb-notif.c
index 6add27c..3e11e35 100644
--- a/drivers/net/wimax/i2400m/usb-notif.c
+++ b/drivers/net/wimax/i2400m/usb-notif.c
@@ -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,
diff --git a/drivers/net/wimax/i2400m/usb-rx.c b/drivers/net/wimax/i2400m/usb-rx.c
index a314799..e494e37 100644
--- a/drivers/net/wimax/i2400m/usb-rx.c
+++ b/drivers/net/wimax/i2400m/usb-rx.c
@@ -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;
diff --git a/drivers/net/wimax/i2400m/usb-tx.c b/drivers/net/wimax/i2400m/usb-tx.c
index dfd8933..90dfff1 100644
--- a/drivers/net/wimax/i2400m/usb-tx.c
+++ b/drivers/net/wimax/i2400m/usb-tx.c
@@ -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,
diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c
index 8f7b16a..a5879e2 100644
--- a/drivers/net/wimax/i2400m/usb.c
+++ b/drivers/net/wimax/i2400m/usb.c
@@ -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);
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/2 02/15] wimax/i2400m/sdio: clear the INTR status bit after reading size
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
  2009-11-04 21:39 ` [PATCH 2.6.33/2 01/15] wimax/i2400m: USB driver uses a configurable endpoint map Inaky Perez-Gonzalez
@ 2009-11-04 21:39 ` Inaky Perez-Gonzalez
  2009-11-04 21:39 ` [PATCH 2.6.33/2 03/15] wimax/i2400m: be smarter about copying command buffer to bm_cmd_buf Inaky Perez-Gonzalez
                   ` (25 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:39 UTC (permalink / raw)
  To: netdev, wimax; +Cc: Cindy H Kao

From: Cindy H Kao <cindy.h.kao@intel.com>

In order to avoid issues during high-load traffic, the interrupt
status register has to be cleared ONLY after the RX size is read.

Signed-off-by: Cindy H Kao <cindy.h.kao@intel.com>
Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 drivers/net/wimax/i2400m/sdio-rx.c |    6 +++++-
 1 files changed, 5 insertions(+), 1 deletions(-)

diff --git a/drivers/net/wimax/i2400m/sdio-rx.c b/drivers/net/wimax/i2400m/sdio-rx.c
index f6ca51a..1c90469 100644
--- a/drivers/net/wimax/i2400m/sdio-rx.c
+++ b/drivers/net/wimax/i2400m/sdio-rx.c
@@ -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);
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/2 03/15] wimax/i2400m: be smarter about copying command buffer to bm_cmd_buf
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
  2009-11-04 21:39 ` [PATCH 2.6.33/2 01/15] wimax/i2400m: USB driver uses a configurable endpoint map Inaky Perez-Gonzalez
  2009-11-04 21:39 ` [PATCH 2.6.33/2 02/15] wimax/i2400m/sdio: clear the INTR status bit after reading size Inaky Perez-Gonzalez
@ 2009-11-04 21:39 ` Inaky Perez-Gonzalez
  2009-11-04 21:39 ` [PATCH 2.6.33/2 04/15] wimax/i2400m: don't write to memory allocated by request_firmware() Inaky Perez-Gonzalez
                   ` (24 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:39 UTC (permalink / raw)
  To: netdev, wimax

Because 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

Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 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(-)

diff --git a/drivers/net/wimax/i2400m/fw.c b/drivers/net/wimax/i2400m/fw.c
index 55bd69e..0018cdb 100644
--- a/drivers/net/wimax/i2400m/fw.c
+++ b/drivers/net/wimax/i2400m/fw.c
@@ -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;
diff --git a/drivers/net/wimax/i2400m/sdio-fw.c b/drivers/net/wimax/i2400m/sdio-fw.c
index c8dc538..8e02541 100644
--- a/drivers/net/wimax/i2400m/sdio-fw.c
+++ b/drivers/net/wimax/i2400m/sdio-fw.c
@@ -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);
diff --git a/drivers/net/wimax/i2400m/usb-fw.c b/drivers/net/wimax/i2400m/usb-fw.c
index a2250e4..f162c81 100644
--- a/drivers/net/wimax/i2400m/usb-fw.c
+++ b/drivers/net/wimax/i2400m/usb-fw.c
@@ -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);
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/2 04/15] wimax/i2400m: don't write to memory allocated by request_firmware()
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
                   ` (2 preceding siblings ...)
  2009-11-04 21:39 ` [PATCH 2.6.33/2 03/15] wimax/i2400m: be smarter about copying command buffer to bm_cmd_buf Inaky Perez-Gonzalez
@ 2009-11-04 21:39 ` Inaky Perez-Gonzalez
  2009-11-04 21:39 ` [PATCH 2.6.33/2 05/15] wimax/i2400m: during probe, call sdio_disable at most once Inaky Perez-Gonzalez
                   ` (23 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:39 UTC (permalink / raw)
  To: netdev, wimax; +Cc: Cindy H Kao

From: Cindy H Kao <cindy.h.kao@intel.com>

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.

Signed-off-by: Cindy H Kao <cindy.h.kao@intel.com>
Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 drivers/net/wimax/i2400m/fw.c |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wimax/i2400m/fw.c b/drivers/net/wimax/i2400m/fw.c
index 0018cdb..92d4d60 100644
--- a/drivers/net/wimax/i2400m/fw.c
+++ b/drivers/net/wimax/i2400m/fw.c
@@ -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),
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/2 05/15] wimax/i2400m: during probe, call sdio_disable at most once
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
                   ` (3 preceding siblings ...)
  2009-11-04 21:39 ` [PATCH 2.6.33/2 04/15] wimax/i2400m: don't write to memory allocated by request_firmware() Inaky Perez-Gonzalez
@ 2009-11-04 21:39 ` Inaky Perez-Gonzalez
  2009-11-04 21:39 ` [PATCH 2.6.33/2 06/15] wimax/i2400m: add missing debug submodule definition Inaky Perez-Gonzalez
                   ` (22 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:39 UTC (permalink / raw)
  To: netdev, wimax

In 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.

Reported-by: Cindy H Kao <cindy.h.kao@intel.com>
Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 drivers/net/wimax/i2400m/sdio.c |   16 +++++++++++++---
 1 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wimax/i2400m/sdio.c b/drivers/net/wimax/i2400m/sdio.c
index 9d6046f..7c1b843 100644
--- a/drivers/net/wimax/i2400m/sdio.c
+++ b/drivers/net/wimax/i2400m/sdio.c
@@ -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;
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/2 06/15] wimax/i2400m: add missing debug submodule definition
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
                   ` (4 preceding siblings ...)
  2009-11-04 21:39 ` [PATCH 2.6.33/2 05/15] wimax/i2400m: during probe, call sdio_disable at most once Inaky Perez-Gonzalez
@ 2009-11-04 21:39 ` Inaky Perez-Gonzalez
  2009-11-04 21:39 ` [PATCH 2.6.33/2 07/15] wimax: allow specifying debug levels as command line option Inaky Perez-Gonzalez
                   ` (21 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:39 UTC (permalink / raw)
  To: netdev, wimax

The i2400m driver was missing the definition for the sysfs debug
level, which is declared in debug-levels.h.

Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 drivers/net/wimax/i2400m/driver.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c
index 20d574c..7ba00de 100644
--- a/drivers/net/wimax/i2400m/driver.c
+++ b/drivers/net/wimax/i2400m/driver.c
@@ -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);
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/2 07/15] wimax: allow specifying debug levels as command line option
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
                   ` (5 preceding siblings ...)
  2009-11-04 21:39 ` [PATCH 2.6.33/2 06/15] wimax/i2400m: add missing debug submodule definition Inaky Perez-Gonzalez
@ 2009-11-04 21:39 ` Inaky Perez-Gonzalez
  2009-11-04 21:39 ` [PATCH 2.6.33/2 08/15] wimax/i2400m: workaround not-so-working %zd printf format Inaky Perez-Gonzalez
                   ` (20 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:39 UTC (permalink / raw)
  To: netdev, wimax

Add "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.

Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 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(-)

diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c
index 7ba00de..e3b2c24 100644
--- a/drivers/net/wimax/i2400m/driver.c
+++ b/drivers/net/wimax/i2400m/driver.c
@@ -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);
diff --git a/drivers/net/wimax/i2400m/sdio.c b/drivers/net/wimax/i2400m/sdio.c
index 7c1b843..2d2cc5a 100644
--- a/drivers/net/wimax/i2400m/sdio.c
+++ b/drivers/net/wimax/i2400m/sdio.c
@@ -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);
diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c
index a5879e2..0634222 100644
--- a/drivers/net/wimax/i2400m/usb.c
+++ b/drivers/net/wimax/i2400m/usb.c
@@ -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);
diff --git a/include/linux/wimax/debug.h b/include/linux/wimax/debug.h
index c703e03..db8096e 100644
--- a/include/linux/wimax/debug.h
+++ b/include/linux/wimax/debug.h
@@ -450,4 +450,76 @@ do {							\
 })
 
 
+static inline
+void d_submodule_set(struct d_level *d_level, size_t d_level_size,
+		     const char *submodule, u8 level, const char *tag)
+{
+	struct d_level *itr, *top;
+	int index = -1;
+
+	for (itr = d_level, top = itr + d_level_size; itr < top; itr++) {
+		index++;
+		if (itr->name == NULL) {
+			printk(KERN_ERR "%s: itr->name NULL?? (%p, #%d)\n",
+			       tag, itr, index);
+			continue;
+		}
+		if (!strcmp(itr->name, submodule)) {
+			itr->level = level;
+			return;
+		}
+	}
+	printk(KERN_ERR "%s: unknown submodule %s\n", tag, submodule);
+}
+
+
+/**
+ * d_parse_params - Parse a string with debug parameters from the
+ * command line
+ *
+ * @d_level: level structure (D_LEVEL)
+ * @d_level_size: number of items in the level structure
+ *     (D_LEVEL_SIZE).
+ * @_params: string with the parameters; this is a space (not tab!)
+ *     separated list of NAME:VALUE, where value is the debug level
+ *     and NAME is the name of the submodule.
+ * @tag: string for error messages (example: MODULE.ARGNAME).
+ */
+static inline
+void d_parse_params(struct d_level *d_level, size_t d_level_size,
+		    const char *_params, const char *tag)
+{
+	char submodule[130], *params, *params_orig, *token, *colon;
+	unsigned level, tokens;
+
+	if (_params == NULL)
+		return;
+	params_orig = kstrdup(_params, GFP_KERNEL);
+	params = params_orig;
+	while (1) {
+		token = strsep(&params, " ");
+		if (token == NULL)
+			break;
+		if (*token == '\0')	/* eat joint spaces */
+			continue;
+		/* kernel's sscanf %s eats until whitespace, so we
+		 * replace : by \n so it doesn't get eaten later by
+		 * strsep */
+		colon = strchr(token, ':');
+		if (colon != NULL)
+			*colon = '\n';
+		tokens = sscanf(token, "%s\n%u", submodule, &level);
+		if (colon != NULL)
+			*colon = ':';	/* set back, for error messages */
+		if (tokens == 2)
+			d_submodule_set(d_level, d_level_size,
+					submodule, level, tag);
+		else
+			printk(KERN_ERR "%s: can't parse '%s' as a "
+			       "SUBMODULE:LEVEL (%d tokens)\n",
+			       tag, token, tokens);
+	}
+	kfree(params_orig);
+}
+
 #endif /* #ifndef __debug__h__ */
diff --git a/net/wimax/stack.c b/net/wimax/stack.c
index 79fb7d7..c886641 100644
--- a/net/wimax/stack.c
+++ b/net/wimax/stack.c
@@ -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);
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/2 08/15] wimax/i2400m: workaround not-so-working %zd printf format
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
                   ` (6 preceding siblings ...)
  2009-11-04 21:39 ` [PATCH 2.6.33/2 07/15] wimax: allow specifying debug levels as command line option Inaky Perez-Gonzalez
@ 2009-11-04 21:39 ` Inaky Perez-Gonzalez
  2009-11-04 21:39 ` [PATCH 2.6.33/2 09/15] wimax/i2400m: decide properly if using signed vs non-signed firmware loading Inaky Perez-Gonzalez
                   ` (19 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:39 UTC (permalink / raw)
  To: netdev, wimax

The kernel's %zd modifier does not really work. Use %ld (has to cast
ssize_t to long).

Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 drivers/net/wimax/i2400m/usb-fw.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wimax/i2400m/usb-fw.c b/drivers/net/wimax/i2400m/usb-fw.c
index f162c81..b59aee0 100644
--- a/drivers/net/wimax/i2400m/usb-fw.c
+++ b/drivers/net/wimax/i2400m/usb-fw.c
@@ -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:
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/2 09/15] wimax/i2400m: decide properly if using signed vs non-signed firmware loading
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
                   ` (7 preceding siblings ...)
  2009-11-04 21:39 ` [PATCH 2.6.33/2 08/15] wimax/i2400m: workaround not-so-working %zd printf format Inaky Perez-Gonzalez
@ 2009-11-04 21:39 ` Inaky Perez-Gonzalez
  2009-11-04 21:39 ` [PATCH 2.6.33/2 10/15] wimax/i2400m: rework bootrom initialization to be more flexible Inaky Perez-Gonzalez
                   ` (18 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:39 UTC (permalink / raw)
  To: netdev, wimax

The 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.

Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 drivers/net/wimax/i2400m/fw.c |   35 +++++++++++++++++++++--------------
 include/linux/wimax/i2400m.h  |   10 ----------
 2 files changed, 21 insertions(+), 24 deletions(-)

diff --git a/drivers/net/wimax/i2400m/fw.c b/drivers/net/wimax/i2400m/fw.c
index 92d4d60..c962a8d 100644
--- a/drivers/net/wimax/i2400m/fw.c
+++ b/drivers/net/wimax/i2400m/fw.c
@@ -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);
 	}
diff --git a/include/linux/wimax/i2400m.h b/include/linux/wimax/i2400m.h
index 433693e..d6e2a35 100644
--- a/include/linux/wimax/i2400m.h
+++ b/include/linux/wimax/i2400m.h
@@ -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
  *
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3)
@ 2009-11-04 21:39 Inaky Perez-Gonzalez
  2009-11-04 21:39 ` [PATCH 2.6.33/2 01/15] wimax/i2400m: USB driver uses a configurable endpoint map Inaky Perez-Gonzalez
                   ` (27 more replies)
  0 siblings, 28 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:39 UTC (permalink / raw)
  To: netdev, wimax

  [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(-)


^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/2 10/15] wimax/i2400m: rework bootrom initialization to be more flexible
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
                   ` (8 preceding siblings ...)
  2009-11-04 21:39 ` [PATCH 2.6.33/2 09/15] wimax/i2400m: decide properly if using signed vs non-signed firmware loading Inaky Perez-Gonzalez
@ 2009-11-04 21:39 ` Inaky Perez-Gonzalez
  2009-11-04 21:39 ` [PATCH 2.6.33/3 01/13] wimax/iwmc3200: add new sdio device ID to support iwmc3200 2.5GHz sku Inaky Perez-Gonzalez
                   ` (17 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:39 UTC (permalink / raw)
  To: netdev, wimax

This 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 <dirk.brandewie@intel.com>.

Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 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(-)

diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c
index e3b2c24..73f45ea 100644
--- a/drivers/net/wimax/i2400m/driver.c
+++ b/drivers/net/wimax/i2400m/driver.c
@@ -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);
diff --git a/drivers/net/wimax/i2400m/fw.c b/drivers/net/wimax/i2400m/fw.c
index c962a8d..798564e 100644
--- a/drivers/net/wimax/i2400m/fw.c
+++ b/drivers/net/wimax/i2400m/fw.c
@@ -40,11 +40,9 @@
  *
  * THE PROCEDURE
  *
- * (this is decribed for USB, but for SDIO is similar)
- *
- * The 2400m works in two modes: boot-mode or normal mode. In boot
- * mode we can execute only a handful of commands targeted at
- * uploading the firmware and launching it.
+ * The 2400m and derived devices work in two modes: boot-mode or
+ * normal mode. In boot mode we can execute only a handful of commands
+ * targeted at uploading the firmware and launching it.
  *
  * The 2400m enters boot mode when it is first connected to the
  * system, when it crashes and when you ask it to reboot. There are
@@ -52,18 +50,26 @@
  * firmwares signed with a certain private key, non-signed takes any
  * firmware. Normal hardware takes only signed firmware.
  *
- * Upon entrance to boot mode, the device sends a few zero length
- * packets (ZLPs) on the notification endpoint, then a reboot barker
- * (4 le32 words with value I2400M_{S,N}BOOT_BARKER). We ack it by
- * sending the same barker on the bulk out endpoint. The device acks
- * with a reboot ack barker (4 le32 words with value 0xfeedbabe) and
- * then the device is fully rebooted. At this point we can upload the
- * firmware.
+ * On boot mode, in USB, we write to the device using the bulk out
+ * endpoint and read from it in the notification endpoint. In SDIO we
+ * talk to it via the write address and read from the read address.
+ *
+ * Upon entrance to boot mode, the device sends (preceeded with a few
+ * zero length packets (ZLPs) on the notification endpoint in USB) a
+ * reboot barker (4 le32 words with the same value). We ack it by
+ * sending the same barker to the device. The device acks with a
+ * reboot ack barker (4 le32 words with value I2400M_ACK_BARKER) and
+ * then is fully booted. At this point we can upload the firmware.
+ *
+ * Note that different iterations of the device and EEPROM
+ * configurations will send different [re]boot barkers; these are
+ * collected in i2400m_barker_db along with the firmware
+ * characteristics they require.
  *
  * This process is accomplished by the i2400m_bootrom_init()
  * function. All the device interaction happens through the
  * i2400m_bm_cmd() [boot mode command]. Special return values will
- * indicate if the device resets.
+ * indicate if the device did reset during the process.
  *
  * After this, we read the MAC address and then (if needed)
  * reinitialize the device. We need to read it ahead of time because
@@ -101,6 +107,11 @@
  *
  * ROADMAP
  *
+ * i2400m_barker_db_init              Called by i2400m_driver_init()
+ *   i2400m_barker_db_add
+ *
+ * i2400m_barker_db_exit              Called by i2400m_driver_exit()
+ *
  * i2400m_dev_bootstrap               Called by __i2400m_dev_start()
  *   request_firmware
  *   i2400m_fw_check
@@ -125,6 +136,7 @@
  *   i2400m->bus_bm_cmd_send()
  *   i2400m->bus_bm_wait_for_ack
  *   __i2400m_bm_ack_verify
+ *     i2400m_is_boot_barker
  *
  * i2400m_bm_cmd_prepare              Used by bus-drivers to prep
  *                                    commands before sending
@@ -175,6 +187,237 @@ EXPORT_SYMBOL_GPL(i2400m_bm_cmd_prepare);
 
 
 /*
+ * Database of known barkers.
+ *
+ * A barker is what the device sends indicating he is ready to be
+ * bootloaded. Different versions of the device will send different
+ * barkers. Depending on the barker, it might mean the device wants
+ * some kind of firmware or the other.
+ */
+static struct i2400m_barker_db {
+	__le32 data[4];
+} *i2400m_barker_db;
+static size_t i2400m_barker_db_used, i2400m_barker_db_size;
+
+
+static
+int i2400m_zrealloc_2x(void **ptr, size_t *_count, size_t el_size,
+		       gfp_t gfp_flags)
+{
+	size_t old_count = *_count,
+		new_count = old_count ? 2 * old_count : 2,
+		old_size = el_size * old_count,
+		new_size = el_size * new_count;
+	void *nptr = krealloc(*ptr, new_size, gfp_flags);
+	if (nptr) {
+		/* zero the other half or the whole thing if old_count
+		 * was zero */
+		if (old_size == 0)
+			memset(nptr, 0, new_size);
+		else
+			memset(nptr + old_size, 0, old_size);
+		*_count = new_count;
+		*ptr = nptr;
+		return 0;
+	} else
+		return -ENOMEM;
+}
+
+
+/*
+ * Add a barker to the database
+ *
+ * This cannot used outside of this module and only at at module_init
+ * time. This is to avoid the need to do locking.
+ */
+static
+int i2400m_barker_db_add(u32 barker_id)
+{
+	int result;
+
+	struct i2400m_barker_db *barker;
+	if (i2400m_barker_db_used >= i2400m_barker_db_size) {
+		result = i2400m_zrealloc_2x(
+			(void **) &i2400m_barker_db, &i2400m_barker_db_size,
+			sizeof(i2400m_barker_db[0]), GFP_KERNEL);
+		if (result < 0)
+			return result;
+	}
+	barker = i2400m_barker_db + i2400m_barker_db_used++;
+	barker->data[0] = le32_to_cpu(barker_id);
+	barker->data[1] = le32_to_cpu(barker_id);
+	barker->data[2] = le32_to_cpu(barker_id);
+	barker->data[3] = le32_to_cpu(barker_id);
+	return 0;
+}
+
+
+void i2400m_barker_db_exit(void)
+{
+	kfree(i2400m_barker_db);
+	i2400m_barker_db = NULL;
+	i2400m_barker_db_size = 0;
+	i2400m_barker_db_used = 0;
+}
+
+
+/*
+ * Helper function to add all the known stable barkers to the barker
+ * database.
+ */
+static
+int i2400m_barker_db_known_barkers(void)
+{
+	int result;
+
+	result = i2400m_barker_db_add(I2400M_NBOOT_BARKER);
+	if (result < 0)
+		goto error_add;
+	result = i2400m_barker_db_add(I2400M_SBOOT_BARKER);
+	if (result < 0)
+		goto error_add;
+error_add:
+       return result;
+}
+
+
+/*
+ * Initialize the barker database
+ *
+ * This can only be used from the module_init function for this
+ * module; this is to avoid the need to do locking.
+ *
+ * @options: command line argument with extra barkers to
+ *     recognize. This is a comma-separated list of 32-bit hex
+ *     numbers. They are appended to the existing list. Setting 0
+ *     cleans the existing list and starts a new one.
+ */
+int i2400m_barker_db_init(const char *_options)
+{
+	int result;
+	char *options = NULL, *options_orig, *token;
+
+	i2400m_barker_db = NULL;
+	i2400m_barker_db_size = 0;
+	i2400m_barker_db_used = 0;
+
+	result = i2400m_barker_db_known_barkers();
+	if (result < 0)
+		goto error_add;
+	/* parse command line options from i2400m.barkers */
+	if (_options != NULL) {
+		unsigned barker;
+
+		options_orig = kstrdup(_options, GFP_KERNEL);
+		if (options_orig == NULL)
+			goto error_parse;
+		options = options_orig;
+
+		while ((token = strsep(&options, ",")) != NULL) {
+			if (*token == '\0')	/* eat joint commas */
+				continue;
+			if (sscanf(token, "%x", &barker) != 1
+			    || barker > 0xffffffff) {
+				printk(KERN_ERR "%s: can't recognize "
+				       "i2400m.barkers value '%s' as "
+				       "a 32-bit number\n",
+				       __func__, token);
+				result = -EINVAL;
+				goto error_parse;
+			}
+			if (barker == 0) {
+				/* clean list and start new */
+				i2400m_barker_db_exit();
+				continue;
+			}
+			result = i2400m_barker_db_add(barker);
+			if (result < 0)
+				goto error_add;
+		}
+		kfree(options_orig);
+	}
+	return 0;
+
+error_parse:
+error_add:
+	kfree(i2400m_barker_db);
+	return result;
+}
+
+
+/*
+ * Recognize a boot barker
+ *
+ * @buf: buffer where the boot barker.
+ * @buf_size: size of the buffer (has to be 16 bytes). It is passed
+ *     here so the function can check it for the caller.
+ *
+ * Note that as a side effect, upon identifying the obtained boot
+ * barker, this function will set i2400m->barker to point to the right
+ * barker database entry. Subsequent calls to the function will result
+ * in verifying that the same type of boot barker is returned when the
+ * device [re]boots (as long as the same device instance is used).
+ *
+ * Return: 0 if @buf matches a known boot barker. -ENOENT if the
+ *     buffer in @buf doesn't match any boot barker in the database or
+ *     -EILSEQ if the buffer doesn't have the right size.
+ */
+int i2400m_is_boot_barker(struct i2400m *i2400m,
+			  const void *buf, size_t buf_size)
+{
+	int result;
+	struct device *dev = i2400m_dev(i2400m);
+	struct i2400m_barker_db *barker;
+	int i;
+
+	result = -ENOENT;
+	if (buf_size != sizeof(i2400m_barker_db[i].data))
+		return result;
+
+	/* Short circuit if we have already discovered the barker
+	 * associated with the device. */
+	if (i2400m->barker
+	    && !memcmp(buf, i2400m->barker, sizeof(i2400m->barker->data))) {
+		unsigned index = (i2400m->barker - i2400m_barker_db)
+			/ sizeof(*i2400m->barker);
+		d_printf(2, dev, "boot barker cache-confirmed #%u/%08x\n",
+			 index, le32_to_cpu(i2400m->barker->data[0]));
+		return 0;
+	}
+
+	for (i = 0; i < i2400m_barker_db_used; i++) {
+		barker = &i2400m_barker_db[i];
+		BUILD_BUG_ON(sizeof(barker->data) != 16);
+		if (memcmp(buf, barker->data, sizeof(barker->data)))
+			continue;
+
+		if (i2400m->barker == NULL) {
+			i2400m->barker = barker;
+			d_printf(1, dev, "boot barker set to #%u/%08x\n",
+				 i, le32_to_cpu(barker->data[0]));
+			if (barker->data[0] == le32_to_cpu(I2400M_NBOOT_BARKER))
+				i2400m->sboot = 0;
+			else
+				i2400m->sboot = 1;
+		} else if (i2400m->barker != barker) {
+			dev_err(dev, "HW inconsistency: device "
+				"reports a different boot barker "
+				"than set (from %08x to %08x)\n",
+				le32_to_cpu(i2400m->barker->data[0]),
+				le32_to_cpu(barker->data[0]));
+			result = -EIO;
+		} else
+			d_printf(2, dev, "boot barker confirmed #%u/%08x\n",
+				 i, le32_to_cpu(barker->data[0]));
+		result = 0;
+		break;
+	}
+	return result;
+}
+EXPORT_SYMBOL_GPL(i2400m_is_boot_barker);
+
+
+/*
  * Verify the ack data received
  *
  * Given a reply to a boot mode command, chew it and verify everything
@@ -204,20 +447,10 @@ ssize_t __i2400m_bm_ack_verify(struct i2400m *i2400m, int opcode,
 			opcode, ack_size, sizeof(*ack));
 		goto error_ack_short;
 	}
-	if (ack_size == sizeof(i2400m_NBOOT_BARKER)
-		 && memcmp(ack, i2400m_NBOOT_BARKER, sizeof(*ack)) == 0) {
-		result = -ERESTARTSYS;
-		i2400m->sboot = 0;
-		d_printf(6, dev, "boot-mode cmd %d: "
-			 "HW non-signed boot barker\n", opcode);
-		goto error_reboot;
-	}
-	if (ack_size == sizeof(i2400m_SBOOT_BARKER)
-		 && memcmp(ack, i2400m_SBOOT_BARKER, sizeof(*ack)) == 0) {
+	result = i2400m_is_boot_barker(i2400m, ack, ack_size);
+	if (result >= 0) {
 		result = -ERESTARTSYS;
-		i2400m->sboot = 1;
-		d_printf(6, dev, "boot-mode cmd %d: HW signed reboot barker\n",
-			 opcode);
+		d_printf(6, dev, "boot-mode cmd %d: HW boot barker\n", opcode);
 		goto error_reboot;
 	}
 	if (ack_size == sizeof(i2400m_ACK_BARKER)
@@ -590,9 +823,6 @@ int i2400m_dnload_finalize(struct i2400m *i2400m,
  *
  *     < 0 errno code on error, 0 if ok.
  *
- *     i2400m->sboot set to 0 for unsecure boot process, 1 for secure
- *     boot process.
- *
  * Description:
  *
  * Tries hard enough to put the device in boot-mode. There are two
@@ -619,7 +849,7 @@ int i2400m_bootrom_init(struct i2400m *i2400m, enum i2400m_bri flags)
 	int count = i2400m->bus_bm_retries;
 	int ack_timeout_cnt = 1;
 
-	BUILD_BUG_ON(sizeof(*cmd) != sizeof(i2400m_NBOOT_BARKER));
+	BUILD_BUG_ON(sizeof(*cmd) != sizeof(i2400m_barker_db[0].data));
 	BUILD_BUG_ON(sizeof(ack) != sizeof(i2400m_ACK_BARKER));
 
 	d_fnstart(4, dev, "(i2400m %p flags 0x%08x)\n", i2400m, flags);
@@ -647,8 +877,14 @@ do_reboot:
 	case -ETIMEDOUT:	/* device has timed out, we might be in boot
 				 * mode already and expecting an ack, let's try
 				 * that */
-		dev_info(dev, "warm reset timed out, trying an ack\n");
-		goto do_reboot_ack;
+		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");
+			goto do_reboot_ack;
+		}
 	case -EPROTO:
 	case -ESHUTDOWN:	/* dev is gone */
 	case -EINTR:		/* user cancelled */
@@ -664,12 +900,7 @@ do_reboot:
 	 * notification and report it as -EISCONN. */
 do_reboot_ack:
 	d_printf(4, dev, "device reboot ack: sending ack [%d # left]\n", count);
-	if (i2400m->sboot == 0)
-		memcpy(cmd, i2400m_NBOOT_BARKER,
-		       sizeof(i2400m_NBOOT_BARKER));
-	else
-		memcpy(cmd, i2400m_SBOOT_BARKER,
-		       sizeof(i2400m_SBOOT_BARKER));
+	memcpy(cmd, i2400m->barker->data, sizeof(i2400m->barker->data));
 	result = i2400m_bm_cmd(i2400m, cmd, sizeof(*cmd),
 			       &ack, sizeof(ack), I2400M_BM_CMD_RAW);
 	switch (result) {
@@ -682,10 +913,8 @@ do_reboot_ack:
 		d_printf(4, dev, "reboot ack: got ack barker - good\n");
 		break;
 	case -ETIMEDOUT:	/* no response, maybe it is the other type? */
-		if (ack_timeout_cnt-- >= 0) {
-			d_printf(4, dev, "reboot ack timedout: "
-				 "trying the other type?\n");
-			i2400m->sboot = !i2400m->sboot;
+		if (ack_timeout_cnt-- < 0) {
+			d_printf(4, dev, "reboot ack timedout: retrying\n");
 			goto do_reboot_ack;
 		} else {
 			dev_err(dev, "reboot ack timedout too long: "
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h
index 73b4e6a..bcb1882 100644
--- a/drivers/net/wimax/i2400m/i2400m.h
+++ b/drivers/net/wimax/i2400m/i2400m.h
@@ -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;
diff --git a/drivers/net/wimax/i2400m/rx.c b/drivers/net/wimax/i2400m/rx.c
index 07c32e6..bcd411f 100644
--- a/drivers/net/wimax/i2400m/rx.c
+++ b/drivers/net/wimax/i2400m/rx.c
@@ -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
  *
diff --git a/drivers/net/wimax/i2400m/sdio-rx.c b/drivers/net/wimax/i2400m/sdio-rx.c
index 1c90469..87263be 100644
--- a/drivers/net/wimax/i2400m/sdio-rx.c
+++ b/drivers/net/wimax/i2400m/sdio-rx.c
@@ -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;
 }
diff --git a/drivers/net/wimax/i2400m/usb-notif.c b/drivers/net/wimax/i2400m/usb-notif.c
index 3e11e35..a0751a3 100644
--- a/drivers/net/wimax/i2400m/usb-notif.c
+++ b/drivers/net/wimax/i2400m/usb-notif.c
@@ -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;
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/2 11/15] wimax/i2400m: retry loading firmware files in sequence
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
                   ` (10 preceding siblings ...)
  2009-11-04 21:39 ` [PATCH 2.6.33/3 01/13] wimax/iwmc3200: add new sdio device ID to support iwmc3200 2.5GHz sku Inaky Perez-Gonzalez
@ 2009-11-04 21:39 ` Inaky Perez-Gonzalez
  2009-11-04 21:39 ` [PATCH 2.6.33/2 12/15] wimax/i2400m: fix reboot echo/ack barker deadlock Inaky Perez-Gonzalez
                   ` (15 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:39 UTC (permalink / raw)
  To: netdev, wimax

The 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.

Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 drivers/net/wimax/i2400m/fw.c |   35 +++++++++++++++++------------------
 1 files changed, 17 insertions(+), 18 deletions(-)

diff --git a/drivers/net/wimax/i2400m/fw.c b/drivers/net/wimax/i2400m/fw.c
index 798564e..55fe011 100644
--- a/drivers/net/wimax/i2400m/fw.c
+++ b/drivers/net/wimax/i2400m/fw.c
@@ -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;
 }
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/3 01/13] wimax/iwmc3200: add new sdio device ID to support iwmc3200 2.5GHz sku
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
                   ` (9 preceding siblings ...)
  2009-11-04 21:39 ` [PATCH 2.6.33/2 10/15] wimax/i2400m: rework bootrom initialization to be more flexible Inaky Perez-Gonzalez
@ 2009-11-04 21:39 ` Inaky Perez-Gonzalez
  2009-11-04 21:39 ` [PATCH 2.6.33/2 11/15] wimax/i2400m: retry loading firmware files in sequence Inaky Perez-Gonzalez
                   ` (16 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:39 UTC (permalink / raw)
  To: netdev, wimax; +Cc: Cindy H Kao

From: Cindy H Kao <cindy.h.kao@intel.com>

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.

Signed-off-by: Cindy H Kao <cindy.h.kao@intel.com>
Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 drivers/net/wimax/i2400m/sdio.c |    2 ++
 include/linux/mmc/sdio_ids.h    |    1 +
 2 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wimax/i2400m/sdio.c b/drivers/net/wimax/i2400m/sdio.c
index 2d2cc5a..de158ee 100644
--- a/drivers/net/wimax/i2400m/sdio.c
+++ b/drivers/net/wimax/i2400m/sdio.c
@@ -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);
diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h
index 2dbfb5a..33b2ea0 100644
--- a/include/linux/mmc/sdio_ids.h
+++ b/include/linux/mmc/sdio_ids.h
@@ -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
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/3 02/13] wimax/i6x50: add Intel WiFi/WiMAX Link 6050 Series support
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
                   ` (12 preceding siblings ...)
  2009-11-04 21:39 ` [PATCH 2.6.33/2 12/15] wimax/i2400m: fix reboot echo/ack barker deadlock Inaky Perez-Gonzalez
@ 2009-11-04 21:39 ` Inaky Perez-Gonzalez
  2009-11-04 21:39 ` [PATCH 2.6.33/2 13/15] wimax/i2400m: verify firmware format version is known Inaky Perez-Gonzalez
                   ` (13 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:39 UTC (permalink / raw)
  To: netdev, wimax; +Cc: Dirk Brandewie

From: Dirk Brandewie <dirk.j.brandewie@intel.com>

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

Signed-off-by: Dirk Brandewie <dirk.j.brandewie@intel.com>
Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 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(-)

diff --git a/drivers/net/wimax/i2400m/fw.c b/drivers/net/wimax/i2400m/fw.c
index 84a39c3..5719f4a 100644
--- a/drivers/net/wimax/i2400m/fw.c
+++ b/drivers/net/wimax/i2400m/fw.c
@@ -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;
 }
diff --git a/drivers/net/wimax/i2400m/i2400m-usb.h b/drivers/net/wimax/i2400m/i2400m-usb.h
index f73a067..5cc0f27 100644
--- a/drivers/net/wimax/i2400m/i2400m-usb.h
+++ b/drivers/net/wimax/i2400m/i2400m-usb.h
@@ -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,
 };
 
 
diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c
index 0634222..77d08d9 100644
--- a/drivers/net/wimax/i2400m/usb.c
+++ b/drivers/net/wimax/i2400m/usb.c
@@ -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 <linux-wimax@intel.com>");
-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);
diff --git a/include/linux/wimax/i2400m.h b/include/linux/wimax/i2400m.h
index fd5af05..62d3561 100644
--- a/include/linux/wimax/i2400m.h
+++ b/include/linux/wimax/i2400m.h
@@ -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,
 };
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/2 12/15] wimax/i2400m: fix reboot echo/ack barker deadlock
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
                   ` (11 preceding siblings ...)
  2009-11-04 21:39 ` [PATCH 2.6.33/2 11/15] wimax/i2400m: retry loading firmware files in sequence Inaky Perez-Gonzalez
@ 2009-11-04 21:39 ` Inaky Perez-Gonzalez
  2009-11-04 21:39 ` [PATCH 2.6.33/3 02/13] wimax/i6x50: add Intel WiFi/WiMAX Link 6050 Series support Inaky Perez-Gonzalez
                   ` (14 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:39 UTC (permalink / raw)
  To: netdev, wimax

The 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.

Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 drivers/net/wimax/i2400m/fw.c |   63 +++++++++++++++++++++++++++++++++--------
 include/linux/wimax/i2400m.h  |    2 +-
 2 files changed, 52 insertions(+), 13 deletions(-)

diff --git a/drivers/net/wimax/i2400m/fw.c b/drivers/net/wimax/i2400m/fw.c
index 55fe011..eef236d 100644
--- a/drivers/net/wimax/i2400m/fw.c
+++ b/drivers/net/wimax/i2400m/fw.c
@@ -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
diff --git a/include/linux/wimax/i2400m.h b/include/linux/wimax/i2400m.h
index d6e2a35..fd5af05 100644
--- a/include/linux/wimax/i2400m.h
+++ b/include/linux/wimax/i2400m.h
@@ -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 */
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/3 03/13] wimax/i2400m: clean up & add a payload argument to i2400m_schedule_work()
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
                   ` (14 preceding siblings ...)
  2009-11-04 21:39 ` [PATCH 2.6.33/2 13/15] wimax/i2400m: verify firmware format version is known Inaky Perez-Gonzalez
@ 2009-11-04 21:39 ` Inaky Perez-Gonzalez
  2009-11-04 21:39 ` [PATCH 2.6.33/3 04/13] wimax/i2400m: add reason argument to i2400m_dev_reset_handle() Inaky Perez-Gonzalez
                   ` (11 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:39 UTC (permalink / raw)
  To: netdev, wimax

Forthcoming 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().

Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 drivers/net/wimax/i2400m/driver.c |   52 +++++++++++++++++++++++-------------
 drivers/net/wimax/i2400m/i2400m.h |    6 +++-
 2 files changed, 37 insertions(+), 21 deletions(-)

diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c
index 73f45ea..5803a2b 100644
--- a/drivers/net/wimax/i2400m/driver.c
+++ b/drivers/net/wimax/i2400m/driver.c
@@ -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);
 
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h
index 5ac2cce..700f87b 100644
--- a/drivers/net/wimax/i2400m/i2400m.h
+++ b/drivers/net/wimax/i2400m/i2400m.h
@@ -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 *,
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/2 13/15] wimax/i2400m: verify firmware format version is known
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
                   ` (13 preceding siblings ...)
  2009-11-04 21:39 ` [PATCH 2.6.33/3 02/13] wimax/i6x50: add Intel WiFi/WiMAX Link 6050 Series support Inaky Perez-Gonzalez
@ 2009-11-04 21:39 ` Inaky Perez-Gonzalez
  2009-11-04 21:39 ` [PATCH 2.6.33/3 03/13] wimax/i2400m: clean up & add a payload argument to i2400m_schedule_work() Inaky Perez-Gonzalez
                   ` (12 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:39 UTC (permalink / raw)
  To: netdev, wimax

Make 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.

Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 drivers/net/wimax/i2400m/fw.c |    8 +++++++-
 1 files changed, 7 insertions(+), 1 deletions(-)

diff --git a/drivers/net/wimax/i2400m/fw.c b/drivers/net/wimax/i2400m/fw.c
index eef236d..1fd2fee 100644
--- a/drivers/net/wimax/i2400m/fw.c
+++ b/drivers/net/wimax/i2400m/fw.c
@@ -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)
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/3 04/13] wimax/i2400m: add reason argument to i2400m_dev_reset_handle()
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
                   ` (15 preceding siblings ...)
  2009-11-04 21:39 ` [PATCH 2.6.33/3 03/13] wimax/i2400m: clean up & add a payload argument to i2400m_schedule_work() Inaky Perez-Gonzalez
@ 2009-11-04 21:39 ` Inaky Perez-Gonzalez
  2009-11-04 21:39 ` [PATCH 2.6.33/2 14/15] wimax/i2400m: support extended firmware format Inaky Perez-Gonzalez
                   ` (10 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:39 UTC (permalink / raw)
  To: netdev, wimax

In 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.

Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 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(-)

diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c
index 5803a2b..f07d852 100644
--- a/drivers/net/wimax/i2400m/driver.c
+++ b/drivers/net/wimax/i2400m/driver.c
@@ -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);
 
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h
index 700f87b..0c165de 100644
--- a/drivers/net/wimax/i2400m/i2400m.h
+++ b/drivers/net/wimax/i2400m/i2400m.h
@@ -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);
 
diff --git a/drivers/net/wimax/i2400m/sdio-rx.c b/drivers/net/wimax/i2400m/sdio-rx.c
index 87263be..98ee7fd 100644
--- a/drivers/net/wimax/i2400m/sdio-rx.c
+++ b/drivers/net/wimax/i2400m/sdio-rx.c
@@ -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 {
diff --git a/drivers/net/wimax/i2400m/usb-notif.c b/drivers/net/wimax/i2400m/usb-notif.c
index a0751a3..f88d1c6 100644
--- a/drivers/net/wimax/i2400m/usb-notif.c
+++ b/drivers/net/wimax/i2400m/usb-notif.c
@@ -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:
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/2 14/15] wimax/i2400m: support extended firmware format
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
                   ` (16 preceding siblings ...)
  2009-11-04 21:39 ` [PATCH 2.6.33/3 04/13] wimax/i2400m: add reason argument to i2400m_dev_reset_handle() Inaky Perez-Gonzalez
@ 2009-11-04 21:39 ` Inaky Perez-Gonzalez
  2009-11-04 21:39 ` [PATCH 2.6.33/2 15/15] wimax/i2400m: on firmware upload, select BCF header that matches device's request Inaky Perez-Gonzalez
                   ` (9 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:39 UTC (permalink / raw)
  To: netdev, wimax

The 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.

Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 drivers/net/wimax/i2400m/fw.c     |  189 +++++++++++++++++++++++++------------
 drivers/net/wimax/i2400m/i2400m.h |    4 +
 2 files changed, 134 insertions(+), 59 deletions(-)

diff --git a/drivers/net/wimax/i2400m/fw.c b/drivers/net/wimax/i2400m/fw.c
index 1fd2fee..897e0be 100644
--- a/drivers/net/wimax/i2400m/fw.c
+++ b/drivers/net/wimax/i2400m/fw.c
@@ -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;
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h
index bcb1882..5ac2cce 100644
--- a/drivers/net/wimax/i2400m/i2400m.h
+++ b/drivers/net/wimax/i2400m/i2400m.h
@@ -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;
 };
 
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/3 05/13] wimax/i2400m: cache firmware on system suspend
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
                   ` (18 preceding siblings ...)
  2009-11-04 21:39 ` [PATCH 2.6.33/2 15/15] wimax/i2400m: on firmware upload, select BCF header that matches device's request Inaky Perez-Gonzalez
@ 2009-11-04 21:39 ` Inaky Perez-Gonzalez
  2009-11-04 21:39 ` [PATCH 2.6.33/3 06/13] wimax/i2400m: implement .reset_resume in USB subdriver Inaky Perez-Gonzalez
                   ` (7 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:39 UTC (permalink / raw)
  To: netdev, wimax

In 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.

Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 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(-)

diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c
index f07d852..07d12be 100644
--- a/drivers/net/wimax/i2400m/driver.c
+++ b/drivers/net/wimax/i2400m/driver.c
@@ -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);
diff --git a/drivers/net/wimax/i2400m/fw.c b/drivers/net/wimax/i2400m/fw.c
index 5719f4a..69f9e45 100644
--- a/drivers/net/wimax/i2400m/fw.c
+++ b/drivers/net/wimax/i2400m/fw.c
@@ -105,6 +105,13 @@
  * read an acknolwedgement from it (or an asynchronous notification)
  * from it.
  *
+ * FIRMWARE LOADING
+ *
+ * Note that in some cases, we can't just load a firmware file (for
+ * example, when resuming). For that, we might cache the firmware
+ * file. Thus, when doing the bootstrap, if there is a cache firmware
+ * file, it is used; if not, loading from disk is attempted.
+ *
  * ROADMAP
  *
  * i2400m_barker_db_init              Called by i2400m_driver_init()
@@ -114,9 +121,10 @@
  *
  * i2400m_dev_bootstrap               Called by __i2400m_dev_start()
  *   request_firmware
- *   i2400m_fw_check
- *     i2400m_fw_hdr_check
- *   i2400m_fw_dnload
+ *   i2400m_fw_bootstrap
+ *     i2400m_fw_check
+ *       i2400m_fw_hdr_check
+ *     i2400m_fw_dnload
  *   release_firmware
  *
  * i2400m_fw_dnload
@@ -141,6 +149,10 @@
  *
  * i2400m_bm_cmd_prepare              Used by bus-drivers to prep
  *                                    commands before sending
+ *
+ * i2400m_pm_notifier                 Called on Power Management events
+ *   i2400m_fw_cache
+ *   i2400m_fw_uncache
  */
 #include <linux/firmware.h>
 #include <linux/sched.h>
@@ -1459,6 +1471,61 @@ error_dev_rebooted:
 	goto hw_reboot;
 }
 
+static
+int i2400m_fw_bootstrap(struct i2400m *i2400m, const struct firmware *fw,
+			enum i2400m_bri flags)
+{
+	int ret;
+	struct device *dev = i2400m_dev(i2400m);
+	const struct i2400m_bcf_hdr *bcf;	/* Firmware data */
+
+	d_fnstart(5, dev, "(i2400m %p)\n", i2400m);
+	bcf = (void *) fw->data;
+	ret = i2400m_fw_check(i2400m, bcf, fw->size);
+	if (ret >= 0)
+		ret = i2400m_fw_dnload(i2400m, bcf, fw->size, flags);
+	if (ret < 0)
+		dev_err(dev, "%s: cannot use: %d, skipping\n",
+			i2400m->fw_name, ret);
+	kfree(i2400m->fw_hdrs);
+	i2400m->fw_hdrs = NULL;
+	d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, ret);
+	return ret;
+}
+
+
+/* Refcounted container for firmware data */
+struct i2400m_fw {
+	struct kref kref;
+	const struct firmware *fw;
+};
+
+
+static
+void i2400m_fw_destroy(struct kref *kref)
+{
+	struct i2400m_fw *i2400m_fw =
+		container_of(kref, struct i2400m_fw, kref);
+	release_firmware(i2400m_fw->fw);
+	kfree(i2400m_fw);
+}
+
+
+static
+struct i2400m_fw *i2400m_fw_get(struct i2400m_fw *i2400m_fw)
+{
+	if (i2400m_fw != NULL && i2400m_fw != (void *) ~0)
+		kref_get(&i2400m_fw->kref);
+	return i2400m_fw;
+}
+
+
+static
+void i2400m_fw_put(struct i2400m_fw *i2400m_fw)
+{
+	kref_put(&i2400m_fw->kref, i2400m_fw_destroy);
+}
+
 
 /**
  * i2400m_dev_bootstrap - Bring the device to a known state and upload firmware
@@ -1479,12 +1546,28 @@ int i2400m_dev_bootstrap(struct i2400m *i2400m, enum i2400m_bri flags)
 {
 	int ret, itr;
 	struct device *dev = i2400m_dev(i2400m);
-	const struct firmware *fw;
+	struct i2400m_fw *i2400m_fw;
 	const struct i2400m_bcf_hdr *bcf;	/* Firmware data */
+	const struct firmware *fw;
 	const char *fw_name;
 
 	d_fnstart(5, dev, "(i2400m %p)\n", i2400m);
 
+	ret = -ENODEV;
+	spin_lock(&i2400m->rx_lock);
+	i2400m_fw = i2400m_fw_get(i2400m->fw_cached);
+	spin_unlock(&i2400m->rx_lock);
+	if (i2400m_fw == (void *) ~0) {
+		dev_err(dev, "can't load firmware now!");
+		goto out;
+	} else if (i2400m_fw != NULL) {
+		dev_info(dev, "firmware %s: loading from cache\n",
+			 i2400m->fw_name);
+		ret = i2400m_fw_bootstrap(i2400m, i2400m_fw->fw, flags);
+		i2400m_fw_put(i2400m_fw);
+		goto out;
+	}
+
 	/* Load firmware files to memory. */
 	for (itr = 0, bcf = NULL, ret = -ENOENT; ; itr++) {
 		fw_name = i2400m->bus_fw_names[itr];
@@ -1500,21 +1583,71 @@ int i2400m_dev_bootstrap(struct i2400m *i2400m, enum i2400m_bri flags)
 				fw_name, ret);
 			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)
-			dev_err(dev, "%s: cannot use: %d, skipping\n",
-				fw_name, ret);
-		kfree(i2400m->fw_hdrs);
-		i2400m->fw_hdrs = NULL;
+		ret = i2400m_fw_bootstrap(i2400m, fw, flags);
 		release_firmware(fw);
 		if (ret >= 0)	/* firmware loaded succesfully */
 			break;
+		i2400m->fw_name = NULL;
 	}
+out:
 	d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, ret);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(i2400m_dev_bootstrap);
+
+
+void i2400m_fw_cache(struct i2400m *i2400m)
+{
+	int result;
+	struct i2400m_fw *i2400m_fw;
+	struct device *dev = i2400m_dev(i2400m);
+
+	/* if there is anything there, free it -- now, this'd be weird */
+	spin_lock(&i2400m->rx_lock);
+	i2400m_fw = i2400m->fw_cached;
+	spin_unlock(&i2400m->rx_lock);
+	if (i2400m_fw != NULL && i2400m_fw != (void *) ~0) {
+		i2400m_fw_put(i2400m_fw);
+		WARN(1, "%s:%u: still cached fw still present?\n",
+		     __func__, __LINE__);
+	}
+
+	if (i2400m->fw_name == NULL) {
+		dev_err(dev, "firmware n/a: can't cache\n");
+		i2400m_fw = (void *) ~0;
+		goto out;
+	}
+
+	i2400m_fw = kzalloc(sizeof(*i2400m_fw), GFP_ATOMIC);
+	if (i2400m_fw == NULL)
+		goto out;
+	kref_init(&i2400m_fw->kref);
+	result = request_firmware(&i2400m_fw->fw, i2400m->fw_name, dev);
+	if (result < 0) {
+		dev_err(dev, "firmware %s: failed to cache: %d\n",
+			i2400m->fw_name, result);
+		kfree(i2400m_fw);
+		i2400m_fw = (void *) ~0;
+	} else
+		dev_info(dev, "firmware %s: cached\n", i2400m->fw_name);
+out:
+	spin_lock(&i2400m->rx_lock);
+	i2400m->fw_cached = i2400m_fw;
+	spin_unlock(&i2400m->rx_lock);
+}
+
+
+void i2400m_fw_uncache(struct i2400m *i2400m)
+{
+	struct i2400m_fw *i2400m_fw;
+
+	spin_lock(&i2400m->rx_lock);
+	i2400m_fw = i2400m->fw_cached;
+	i2400m->fw_cached = NULL;
+	spin_unlock(&i2400m->rx_lock);
+
+	if (i2400m_fw != NULL && i2400m_fw != (void *) ~0)
+		i2400m_fw_put(i2400m_fw);
+}
+
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h
index 0c165de..916b1d3 100644
--- a/drivers/net/wimax/i2400m/i2400m.h
+++ b/drivers/net/wimax/i2400m/i2400m.h
@@ -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 *,
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/2 15/15] wimax/i2400m: on firmware upload, select BCF header that matches device's request
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
                   ` (17 preceding siblings ...)
  2009-11-04 21:39 ` [PATCH 2.6.33/2 14/15] wimax/i2400m: support extended firmware format Inaky Perez-Gonzalez
@ 2009-11-04 21:39 ` Inaky Perez-Gonzalez
  2009-11-04 21:39 ` [PATCH 2.6.33/3 05/13] wimax/i2400m: cache firmware on system suspend Inaky Perez-Gonzalez
                   ` (8 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:39 UTC (permalink / raw)
  To: netdev, wimax

Devices 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().

Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 drivers/net/wimax/i2400m/fw.c |  111 ++++++++++++++++++++++++++++++++++-------
 1 files changed, 92 insertions(+), 19 deletions(-)

diff --git a/drivers/net/wimax/i2400m/fw.c b/drivers/net/wimax/i2400m/fw.c
index 897e0be..84a39c3 100644
--- a/drivers/net/wimax/i2400m/fw.c
+++ b/drivers/net/wimax/i2400m/fw.c
@@ -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",
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/3 06/13] wimax/i2400m: implement .reset_resume in USB subdriver
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
                   ` (19 preceding siblings ...)
  2009-11-04 21:39 ` [PATCH 2.6.33/3 05/13] wimax/i2400m: cache firmware on system suspend Inaky Perez-Gonzalez
@ 2009-11-04 21:39 ` Inaky Perez-Gonzalez
  2009-11-04 21:39 ` [PATCH 2.6.33/3 07/13] wimax/i2400m: don't overwrite error codes when failing to load firmware Inaky Perez-Gonzalez
                   ` (6 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:39 UTC (permalink / raw)
  To: netdev, wimax

Current 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.

Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 drivers/net/wimax/i2400m/usb.c |   16 ++++++++++++++++
 1 files changed, 16 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c
index 77d08d9..07653de 100644
--- a/drivers/net/wimax/i2400m/usb.c
+++ b/drivers/net/wimax/i2400m/usb.c
@@ -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,
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/3 07/13] wimax/i2400m: don't overwrite error codes when failing to load firmware
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
                   ` (20 preceding siblings ...)
  2009-11-04 21:39 ` [PATCH 2.6.33/3 06/13] wimax/i2400m: implement .reset_resume in USB subdriver Inaky Perez-Gonzalez
@ 2009-11-04 21:39 ` Inaky Perez-Gonzalez
  2009-11-04 21:39 ` [PATCH 2.6.33/3 08/13] wimax/i2400m: on device stop, clean up pending wake & TX work Inaky Perez-Gonzalez
                   ` (5 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:39 UTC (permalink / raw)
  To: netdev, wimax

Make 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.

Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 drivers/net/wimax/i2400m/fw.c |    1 -
 1 files changed, 0 insertions(+), 1 deletions(-)

diff --git a/drivers/net/wimax/i2400m/fw.c b/drivers/net/wimax/i2400m/fw.c
index 69f9e45..3d67bcf 100644
--- a/drivers/net/wimax/i2400m/fw.c
+++ b/drivers/net/wimax/i2400m/fw.c
@@ -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);
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/3 08/13] wimax/i2400m: on device stop, clean up pending wake & TX work
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
                   ` (21 preceding siblings ...)
  2009-11-04 21:39 ` [PATCH 2.6.33/3 07/13] wimax/i2400m: don't overwrite error codes when failing to load firmware Inaky Perez-Gonzalez
@ 2009-11-04 21:39 ` Inaky Perez-Gonzalez
  2009-11-04 21:40 ` [PATCH 2.6.33/3 09/13] wimax/i2400m: cleanup initialization/destruction flow Inaky Perez-Gonzalez
                   ` (4 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:39 UTC (permalink / raw)
  To: netdev, wimax

When 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().

Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 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(-)

diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c
index 07d12be..a33df04 100644
--- a/drivers/net/wimax/i2400m/driver.c
+++ b/drivers/net/wimax/i2400m/driver.c
@@ -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);
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h
index 916b1d3..303eb78 100644
--- a/drivers/net/wimax/i2400m/i2400m.h
+++ b/drivers/net/wimax/i2400m/i2400m.h
@@ -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);
 
diff --git a/drivers/net/wimax/i2400m/netdev.c b/drivers/net/wimax/i2400m/netdev.c
index 960fb54..0e8f6a0 100644
--- a/drivers/net/wimax/i2400m/netdev.c
+++ b/drivers/net/wimax/i2400m/netdev.c
@@ -113,11 +113,6 @@ int i2400m_open(struct net_device *net_dev)
 }
 
 
-/*
- *
- * On kernel versions where cancel_work_sync() didn't return anything,
- * we rely on wake_tx_skb() being non-NULL.
- */
 static
 int i2400m_stop(struct net_device *net_dev)
 {
@@ -125,21 +120,7 @@ int i2400m_stop(struct net_device *net_dev)
 	struct device *dev = i2400m_dev(i2400m);
 
 	d_fnstart(3, dev, "(net_dev %p [i2400m %p])\n", net_dev, i2400m);
-	/* See i2400m_hard_start_xmit(), references are taken there
-	 * and here we release them if the work was still
-	 * pending. Note we can't differentiate work not pending vs
-	 * never scheduled, so the NULL check does that. */
-	if (cancel_work_sync(&i2400m->wake_tx_ws) == 0
-	    && i2400m->wake_tx_skb != NULL) {
-		unsigned long flags;
-		struct sk_buff *wake_tx_skb;
-		spin_lock_irqsave(&i2400m->tx_lock, flags);
-		wake_tx_skb = i2400m->wake_tx_skb;	/* compat help */
-		i2400m->wake_tx_skb = NULL;	/* compat help */
-		spin_unlock_irqrestore(&i2400m->tx_lock, flags);
-		i2400m_put(i2400m);
-		kfree_skb(wake_tx_skb);
-	}
+	i2400m_net_wake_stop(i2400m);
 	d_fnend(3, dev, "(net_dev %p [i2400m %p]) = 0\n", net_dev, i2400m);
 	return 0;
 }
@@ -230,6 +211,38 @@ void i2400m_tx_prep_header(struct sk_buff *skb)
 }
 
 
+
+/*
+ * Cleanup resources acquired during i2400m_net_wake_tx()
+ *
+ * This is called by __i2400m_dev_stop and means we have to make sure
+ * the workqueue is flushed from any pending work.
+ */
+void i2400m_net_wake_stop(struct i2400m *i2400m)
+{
+	struct device *dev = i2400m_dev(i2400m);
+
+	d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
+	/* See i2400m_hard_start_xmit(), references are taken there
+	 * and here we release them if the work was still
+	 * pending. Note we can't differentiate work not pending vs
+	 * never scheduled, so the NULL check does that. */
+	if (cancel_work_sync(&i2400m->wake_tx_ws) == 0
+	    && i2400m->wake_tx_skb != NULL) {
+		unsigned long flags;
+		struct sk_buff *wake_tx_skb;
+		spin_lock_irqsave(&i2400m->tx_lock, flags);
+		wake_tx_skb = i2400m->wake_tx_skb;	/* compat help */
+		i2400m->wake_tx_skb = NULL;	/* compat help */
+		spin_unlock_irqrestore(&i2400m->tx_lock, flags);
+		i2400m_put(i2400m);
+		kfree_skb(wake_tx_skb);
+	}
+	d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
+	return;
+}
+
+
 /*
  * TX an skb to an idle device
  *
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/3 09/13] wimax/i2400m: cleanup initialization/destruction flow
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
                   ` (22 preceding siblings ...)
  2009-11-04 21:39 ` [PATCH 2.6.33/3 08/13] wimax/i2400m: on device stop, clean up pending wake & TX work Inaky Perez-Gonzalez
@ 2009-11-04 21:40 ` Inaky Perez-Gonzalez
  2009-11-04 21:40 ` [PATCH 2.6.33/3 10/13] wimax/i2400m: clarify and fix i2400m->{ready,updown} Inaky Perez-Gonzalez
                   ` (3 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:40 UTC (permalink / raw)
  To: netdev, wimax

Currently 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.

Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 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(-)

diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c
index a33df04..a0ae199 100644
--- a/drivers/net/wimax/i2400m/driver.c
+++ b/drivers/net/wimax/i2400m/driver.c
@@ -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);
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h
index 303eb78..2b9c400 100644
--- a/drivers/net/wimax/i2400m/i2400m.h
+++ b/drivers/net/wimax/i2400m/i2400m.h
@@ -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 *);
 
diff --git a/drivers/net/wimax/i2400m/netdev.c b/drivers/net/wimax/i2400m/netdev.c
index 0e8f6a0..fefd794 100644
--- a/drivers/net/wimax/i2400m/netdev.c
+++ b/drivers/net/wimax/i2400m/netdev.c
@@ -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;
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/3 10/13] wimax/i2400m: clarify and fix i2400m->{ready,updown}
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
                   ` (23 preceding siblings ...)
  2009-11-04 21:40 ` [PATCH 2.6.33/3 09/13] wimax/i2400m: cleanup initialization/destruction flow Inaky Perez-Gonzalez
@ 2009-11-04 21:40 ` Inaky Perez-Gonzalez
  2009-11-04 21:40 ` [PATCH 2.6.33/3 11/13] wimax/i2400m: introduce i2400m->bus_setup/release Inaky Perez-Gonzalez
                   ` (2 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:40 UTC (permalink / raw)
  To: netdev, wimax

The 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.

Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 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(-)

diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c
index a0ae199..6280646 100644
--- a/drivers/net/wimax/i2400m/driver.c
+++ b/drivers/net/wimax/i2400m/driver.c
@@ -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;
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h
index 2b9c400..fbc156d 100644
--- a/drivers/net/wimax/i2400m/i2400m.h
+++ b/drivers/net/wimax/i2400m/i2400m.h
@@ -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 */
diff --git a/drivers/net/wimax/i2400m/rx.c b/drivers/net/wimax/i2400m/rx.c
index bcd411f..82c200a 100644
--- a/drivers/net/wimax/i2400m/rx.c
+++ b/drivers/net/wimax/i2400m/rx.c
@@ -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",
diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c
index 07653de..f4dfb60 100644
--- a/drivers/net/wimax/i2400m/usb.c
+++ b/drivers/net/wimax/i2400m/usb.c
@@ -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;
diff --git a/include/net/wimax.h b/include/net/wimax.h
index 2af7bf8..d69c4a7 100644
--- a/include/net/wimax.h
+++ b/include/net/wimax.h
@@ -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
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/3 11/13] wimax/i2400m: introduce i2400m->bus_setup/release
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
                   ` (24 preceding siblings ...)
  2009-11-04 21:40 ` [PATCH 2.6.33/3 10/13] wimax/i2400m: clarify and fix i2400m->{ready,updown} Inaky Perez-Gonzalez
@ 2009-11-04 21:40 ` Inaky Perez-Gonzalez
  2009-11-04 21:40 ` [PATCH 2.6.33/3 12/13] wimax/i2400m: do bootmode buffer management in i2400m_setup/release() Inaky Perez-Gonzalez
  2009-11-04 21:40 ` [PATCH 2.6.33/3 13/13] wimax/i2400m: Implement pre/post reset support in the USB driver Inaky Perez-Gonzalez
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:40 UTC (permalink / raw)
  To: netdev, wimax

The 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.

Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 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(-)

diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c
index 6280646..c57020f 100644
--- a/drivers/net/wimax/i2400m/driver.c
+++ b/drivers/net/wimax/i2400m/driver.c
@@ -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);
 }
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h
index fbc156d..407d097 100644
--- a/drivers/net/wimax/i2400m/i2400m.h
+++ b/drivers/net/wimax/i2400m/i2400m.h
@@ -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 *,
diff --git a/drivers/net/wimax/i2400m/sdio.c b/drivers/net/wimax/i2400m/sdio.c
index de158ee..6e39665 100644
--- a/drivers/net/wimax/i2400m/sdio.c
+++ b/drivers/net/wimax/i2400m/sdio.c
@@ -165,6 +165,66 @@ function_enabled:
 
 
 /*
+ * Setup minimal device communication infrastructure needed to at
+ * least be able to update the firmware.
+ */
+static
+int i2400ms_bus_setup(struct i2400m *i2400m)
+{
+	int result;
+	struct i2400ms *i2400ms =
+		container_of(i2400m, struct i2400ms, i2400m);
+	struct device *dev = i2400m_dev(i2400m);
+	struct sdio_func *func = i2400ms->func;
+
+	sdio_claim_host(func);
+	result = sdio_set_block_size(func, I2400MS_BLK_SIZE);
+	sdio_release_host(func);
+	if (result < 0) {
+		dev_err(dev, "Failed to set block size: %d\n", result);
+		goto error_set_blk_size;
+	}
+
+	result = i2400ms_enable_function(func, 1);
+	if (result < 0) {
+		dev_err(dev, "Cannot enable SDIO function: %d\n", result);
+		goto error_func_enable;
+	}
+
+	result = i2400ms_rx_setup(i2400ms);
+	if (result < 0)
+		goto error_rx_setup;
+	return 0;
+
+error_rx_setup:
+	sdio_claim_host(func);
+	sdio_disable_func(func);
+	sdio_release_host(func);
+error_func_enable:
+error_set_blk_size:
+	return result;
+}
+
+
+/*
+ * Tear down minimal device communication infrastructure needed to at
+ * least be able to update the firmware.
+ */
+static
+void i2400ms_bus_release(struct i2400m *i2400m)
+{
+	struct i2400ms *i2400ms =
+		container_of(i2400m, struct i2400ms, i2400m);
+	struct sdio_func *func = i2400ms->func;
+
+	i2400ms_rx_release(i2400ms);
+	sdio_claim_host(func);
+	sdio_disable_func(func);
+	sdio_release_host(func);
+}
+
+
+/*
  * Setup driver resources needed to communicate with the device
  *
  * The fw needs some time to settle, and it was just uploaded,
@@ -315,17 +375,12 @@ do_bus_reset:
 		if (i2400m->wimax_dev.net_dev->reg_state == NETREG_REGISTERED)
 			netif_tx_disable(i2400m->wimax_dev.net_dev);
 
-		i2400ms_rx_release(i2400ms);
-		sdio_claim_host(i2400ms->func);
-		sdio_disable_func(i2400ms->func);
-		sdio_release_host(i2400ms->func);
+		i2400ms_bus_release(i2400m);
 
 		/* Wait for the device to settle */
 		msleep(40);
 
-		result = i2400ms_enable_function(i2400ms->func, 0);
-		if (result >= 0)
-			i2400ms_rx_setup(i2400ms);
+		result =  i2400ms_bus_setup(i2400m);
 	} else
 		BUG();
 	if (result < 0 && rt != I2400M_RT_BUS) {
@@ -449,8 +504,10 @@ int i2400ms_probe(struct sdio_func *func,
 
 	i2400m->bus_tx_block_size = I2400MS_BLK_SIZE;
 	i2400m->bus_pl_size_max = I2400MS_PL_SIZE_MAX;
+	i2400m->bus_setup = i2400ms_bus_setup;
 	i2400m->bus_dev_start = i2400ms_bus_dev_start;
 	i2400m->bus_dev_stop = i2400ms_bus_dev_stop;
+	i2400m->bus_release = i2400ms_bus_release;
 	i2400m->bus_tx_kick = i2400ms_bus_tx_kick;
 	i2400m->bus_reset = i2400ms_bus_reset;
 	/* The iwmc3200-wimax sometimes requires the driver to try
@@ -462,20 +519,6 @@ int i2400ms_probe(struct sdio_func *func,
 	i2400m->bus_bm_mac_addr_impaired = 1;
 	i2400m->bus_bm_pokes_table = &i2400ms_pokes[0];
 
-	sdio_claim_host(func);
-	result = sdio_set_block_size(func, I2400MS_BLK_SIZE);
-	sdio_release_host(func);
-	if (result < 0) {
-		dev_err(dev, "Failed to set block size: %d\n", result);
-		goto error_set_blk_size;
-	}
-
-	result = i2400ms_enable_function(i2400ms->func, 1);
-	if (result < 0) {
-		dev_err(dev, "Cannot enable SDIO function: %d\n", result);
-		goto error_func_enable;
-	}
-
 	/*
 	 * Before we are enabling the device interrupt register, make
 	 * sure the buffer used during bootmode operation is setup so
@@ -488,10 +531,6 @@ int i2400ms_probe(struct sdio_func *func,
 		goto error_bootmode_buf_setup;
 	}
 
-	result = i2400ms_rx_setup(i2400ms);
-	if (result < 0)
-		goto error_rx_setup;
-
 	result = i2400m_setup(i2400m, I2400M_BRI_NO_REBOOT);
 	if (result < 0) {
 		dev_err(dev, "cannot setup device: %d\n", result);
@@ -509,15 +548,8 @@ int i2400ms_probe(struct sdio_func *func,
 error_debugfs_add:
 	i2400m_release(i2400m);
 error_setup:
-	i2400ms_rx_release(i2400ms);
-error_rx_setup:
 	i2400m_bm_buf_free(i2400m);
 error_bootmode_buf_setup:
-	sdio_claim_host(func);
-	sdio_disable_func(func);
-	sdio_release_host(func);
-error_func_enable:
-error_set_blk_size:
 	sdio_set_drvdata(func, NULL);
 	free_netdev(net_dev);
 error_alloc_netdev:
@@ -535,12 +567,8 @@ void i2400ms_remove(struct sdio_func *func)
 
 	d_fnstart(3, dev, "SDIO func %p\n", func);
 	debugfs_remove_recursive(i2400ms->debugfs_dentry);
-	i2400ms_rx_release(i2400ms);
 	i2400m_release(i2400m);
 	sdio_set_drvdata(func, NULL);
-	sdio_claim_host(func);
-	sdio_disable_func(func);
-	sdio_release_host(func);
 	free_netdev(net_dev);
 	d_fnend(3, dev, "SDIO func %p\n", func);
 }
diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c
index f4dfb60..3bf3f72 100644
--- a/drivers/net/wimax/i2400m/usb.c
+++ b/drivers/net/wimax/i2400m/usb.c
@@ -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;
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/3 12/13] wimax/i2400m: do bootmode buffer management in i2400m_setup/release()
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
                   ` (25 preceding siblings ...)
  2009-11-04 21:40 ` [PATCH 2.6.33/3 11/13] wimax/i2400m: introduce i2400m->bus_setup/release Inaky Perez-Gonzalez
@ 2009-11-04 21:40 ` Inaky Perez-Gonzalez
  2009-11-04 21:40 ` [PATCH 2.6.33/3 13/13] wimax/i2400m: Implement pre/post reset support in the USB driver Inaky Perez-Gonzalez
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:40 UTC (permalink / raw)
  To: netdev, wimax

After 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.

Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 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(-)

diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c
index c57020f..4fcdb18 100644
--- a/drivers/net/wimax/i2400m/driver.c
+++ b/drivers/net/wimax/i2400m/driver.c
@@ -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;
 }
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h
index 407d097..1724955 100644
--- a/drivers/net/wimax/i2400m/i2400m.h
+++ b/drivers/net/wimax/i2400m/i2400m.h
@@ -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
diff --git a/drivers/net/wimax/i2400m/sdio.c b/drivers/net/wimax/i2400m/sdio.c
index 6e39665..14e66f0 100644
--- a/drivers/net/wimax/i2400m/sdio.c
+++ b/drivers/net/wimax/i2400m/sdio.c
@@ -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:
diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c
index 3bf3f72..7756797 100644
--- a/drivers/net/wimax/i2400m/usb.c
+++ b/drivers/net/wimax/i2400m/usb.c
@@ -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);
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 2.6.33/3 13/13] wimax/i2400m: Implement pre/post reset support in the USB driver
  2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
                   ` (26 preceding siblings ...)
  2009-11-04 21:40 ` [PATCH 2.6.33/3 12/13] wimax/i2400m: do bootmode buffer management in i2400m_setup/release() Inaky Perez-Gonzalez
@ 2009-11-04 21:40 ` Inaky Perez-Gonzalez
  27 siblings, 0 replies; 29+ messages in thread
From: Inaky Perez-Gonzalez @ 2009-11-04 21:40 UTC (permalink / raw)
  To: netdev, wimax

The 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.

Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 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(-)

diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c
index 4fcdb18..1f6aa2a 100644
--- a/drivers/net/wimax/i2400m/driver.c
+++ b/drivers/net/wimax/i2400m/driver.c
@@ -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
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h
index 1724955..8fc8a0c 100644
--- a/drivers/net/wimax/i2400m/i2400m.h
+++ b/drivers/net/wimax/i2400m/i2400m.h
@@ -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
diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c
index 7756797..8b246cc 100644
--- a/drivers/net/wimax/i2400m/usb.c
+++ b/drivers/net/wimax/i2400m/usb.c
@@ -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,
 };
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 29+ messages in thread

end of thread, other threads:[~2009-11-04 21:42 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-11-04 21:39 [PATCH 2.6.33/3 00/13] WiMAX patches for 2.6.33 (batch #3) Inaky Perez-Gonzalez
2009-11-04 21:39 ` [PATCH 2.6.33/2 01/15] wimax/i2400m: USB driver uses a configurable endpoint map Inaky Perez-Gonzalez
2009-11-04 21:39 ` [PATCH 2.6.33/2 02/15] wimax/i2400m/sdio: clear the INTR status bit after reading size Inaky Perez-Gonzalez
2009-11-04 21:39 ` [PATCH 2.6.33/2 03/15] wimax/i2400m: be smarter about copying command buffer to bm_cmd_buf Inaky Perez-Gonzalez
2009-11-04 21:39 ` [PATCH 2.6.33/2 04/15] wimax/i2400m: don't write to memory allocated by request_firmware() Inaky Perez-Gonzalez
2009-11-04 21:39 ` [PATCH 2.6.33/2 05/15] wimax/i2400m: during probe, call sdio_disable at most once Inaky Perez-Gonzalez
2009-11-04 21:39 ` [PATCH 2.6.33/2 06/15] wimax/i2400m: add missing debug submodule definition Inaky Perez-Gonzalez
2009-11-04 21:39 ` [PATCH 2.6.33/2 07/15] wimax: allow specifying debug levels as command line option Inaky Perez-Gonzalez
2009-11-04 21:39 ` [PATCH 2.6.33/2 08/15] wimax/i2400m: workaround not-so-working %zd printf format Inaky Perez-Gonzalez
2009-11-04 21:39 ` [PATCH 2.6.33/2 09/15] wimax/i2400m: decide properly if using signed vs non-signed firmware loading Inaky Perez-Gonzalez
2009-11-04 21:39 ` [PATCH 2.6.33/2 10/15] wimax/i2400m: rework bootrom initialization to be more flexible Inaky Perez-Gonzalez
2009-11-04 21:39 ` [PATCH 2.6.33/3 01/13] wimax/iwmc3200: add new sdio device ID to support iwmc3200 2.5GHz sku Inaky Perez-Gonzalez
2009-11-04 21:39 ` [PATCH 2.6.33/2 11/15] wimax/i2400m: retry loading firmware files in sequence Inaky Perez-Gonzalez
2009-11-04 21:39 ` [PATCH 2.6.33/2 12/15] wimax/i2400m: fix reboot echo/ack barker deadlock Inaky Perez-Gonzalez
2009-11-04 21:39 ` [PATCH 2.6.33/3 02/13] wimax/i6x50: add Intel WiFi/WiMAX Link 6050 Series support Inaky Perez-Gonzalez
2009-11-04 21:39 ` [PATCH 2.6.33/2 13/15] wimax/i2400m: verify firmware format version is known Inaky Perez-Gonzalez
2009-11-04 21:39 ` [PATCH 2.6.33/3 03/13] wimax/i2400m: clean up & add a payload argument to i2400m_schedule_work() Inaky Perez-Gonzalez
2009-11-04 21:39 ` [PATCH 2.6.33/3 04/13] wimax/i2400m: add reason argument to i2400m_dev_reset_handle() Inaky Perez-Gonzalez
2009-11-04 21:39 ` [PATCH 2.6.33/2 14/15] wimax/i2400m: support extended firmware format Inaky Perez-Gonzalez
2009-11-04 21:39 ` [PATCH 2.6.33/2 15/15] wimax/i2400m: on firmware upload, select BCF header that matches device's request Inaky Perez-Gonzalez
2009-11-04 21:39 ` [PATCH 2.6.33/3 05/13] wimax/i2400m: cache firmware on system suspend Inaky Perez-Gonzalez
2009-11-04 21:39 ` [PATCH 2.6.33/3 06/13] wimax/i2400m: implement .reset_resume in USB subdriver Inaky Perez-Gonzalez
2009-11-04 21:39 ` [PATCH 2.6.33/3 07/13] wimax/i2400m: don't overwrite error codes when failing to load firmware Inaky Perez-Gonzalez
2009-11-04 21:39 ` [PATCH 2.6.33/3 08/13] wimax/i2400m: on device stop, clean up pending wake & TX work Inaky Perez-Gonzalez
2009-11-04 21:40 ` [PATCH 2.6.33/3 09/13] wimax/i2400m: cleanup initialization/destruction flow Inaky Perez-Gonzalez
2009-11-04 21:40 ` [PATCH 2.6.33/3 10/13] wimax/i2400m: clarify and fix i2400m->{ready,updown} Inaky Perez-Gonzalez
2009-11-04 21:40 ` [PATCH 2.6.33/3 11/13] wimax/i2400m: introduce i2400m->bus_setup/release Inaky Perez-Gonzalez
2009-11-04 21:40 ` [PATCH 2.6.33/3 12/13] wimax/i2400m: do bootmode buffer management in i2400m_setup/release() Inaky Perez-Gonzalez
2009-11-04 21:40 ` [PATCH 2.6.33/3 13/13] wimax/i2400m: Implement pre/post reset support in the USB driver Inaky Perez-Gonzalez

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).