summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/orinoco/orinoco_usb.c
diff options
context:
space:
mode:
authorDavid Kilroy <kilroyd@googlemail.com>2010-05-01 14:05:43 +0100
committerJohn W. Linville <linville@tuxdriver.com>2010-05-03 14:53:09 -0400
commit07cefe7ac983374ee4c369f1d4aee3093bf3b44f (patch)
treec4db80ac5a3f28bda1a5c93fb9b88b7db728898e /drivers/net/wireless/orinoco/orinoco_usb.c
parentfc97431a50962e66c052ec6909d4b2582efd3554 (diff)
orinoco_usb: implement fw download
This involves some refactorring of the common fw download code to substitute ezusb versions of various functions. Note that WPA-enabled firmwares (9.xx series) will not work fully with orinoco_usb yet. Signed-off-by: David Kilroy <kilroyd@googlemail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/orinoco/orinoco_usb.c')
-rw-r--r--drivers/net/wireless/orinoco/orinoco_usb.c120
1 files changed, 120 insertions, 0 deletions
diff --git a/drivers/net/wireless/orinoco/orinoco_usb.c b/drivers/net/wireless/orinoco/orinoco_usb.c
index 8e1b31cbd375..e22093359f3e 100644
--- a/drivers/net/wireless/orinoco/orinoco_usb.c
+++ b/drivers/net/wireless/orinoco/orinoco_usb.c
@@ -184,6 +184,11 @@ MODULE_FIRMWARE("orinoco_ezusb_fw");
#define EZUSB_RID_RX 0x0701
#define EZUSB_RID_INIT1 0x0702
#define EZUSB_RID_ACK 0x0710
+#define EZUSB_RID_READ_PDA 0x0800
+#define EZUSB_RID_PROG_INIT 0x0852
+#define EZUSB_RID_PROG_SET_ADDR 0x0853
+#define EZUSB_RID_PROG_BYTES 0x0854
+#define EZUSB_RID_PROG_END 0x0855
#define EZUSB_RID_DOCMD 0x0860
/* Recognize info frames */
@@ -198,6 +203,8 @@ MODULE_FIRMWARE("orinoco_ezusb_fw");
#define BULK_BUF_SIZE 2048
+#define MAX_DL_SIZE (BULK_BUF_SIZE - sizeof(struct ezusb_packet))
+
#define FW_BUF_SIZE 64
#define FW_VAR_OFFSET_PTR 0x359
#define FW_VAR_VALUE 0
@@ -1077,6 +1084,115 @@ static int ezusb_bap_pread(struct hermes *hw, int bap,
return 0;
}
+static int ezusb_read_pda(struct hermes *hw, __le16 *pda,
+ u32 pda_addr, u16 pda_len)
+{
+ struct ezusb_priv *upriv = hw->priv;
+ struct request_context *ctx;
+ __le16 data[] = {
+ cpu_to_le16(pda_addr & 0xffff),
+ cpu_to_le16(pda_len - 4)
+ };
+ ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_READ_PDA, EZUSB_RID_READ_PDA);
+ if (!ctx)
+ return -ENOMEM;
+
+ /* wl_lkm does not include PDA size in the PDA area.
+ * We will pad the information into pda, so other routines
+ * don't have to be modified */
+ pda[0] = cpu_to_le16(pda_len - 2);
+ /* Includes CFG_PROD_DATA but not itself */
+ pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */
+
+ return ezusb_access_ltv(upriv, ctx, sizeof(data), &data,
+ EZUSB_FRAME_CONTROL, &pda[2], pda_len - 4,
+ NULL);
+}
+
+static int ezusb_program_init(struct hermes *hw, u32 entry_point)
+{
+ struct ezusb_priv *upriv = hw->priv;
+ struct request_context *ctx;
+ __le32 data = cpu_to_le32(entry_point);
+
+ ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_INIT, EZUSB_RID_ACK);
+ if (!ctx)
+ return -ENOMEM;
+
+ return ezusb_access_ltv(upriv, ctx, sizeof(data), &data,
+ EZUSB_FRAME_CONTROL, NULL, 0, NULL);
+}
+
+static int ezusb_program_end(struct hermes *hw)
+{
+ struct ezusb_priv *upriv = hw->priv;
+ struct request_context *ctx;
+
+ ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_END, EZUSB_RID_ACK);
+ if (!ctx)
+ return -ENOMEM;
+
+ return ezusb_access_ltv(upriv, ctx, 0, NULL,
+ EZUSB_FRAME_CONTROL, NULL, 0, NULL);
+}
+
+static int ezusb_program_bytes(struct hermes *hw, const char *buf,
+ u32 addr, u32 len)
+{
+ struct ezusb_priv *upriv = hw->priv;
+ struct request_context *ctx;
+ __le32 data = cpu_to_le32(addr);
+ int err;
+
+ ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_SET_ADDR, EZUSB_RID_ACK);
+ if (!ctx)
+ return -ENOMEM;
+
+ err = ezusb_access_ltv(upriv, ctx, sizeof(data), &data,
+ EZUSB_FRAME_CONTROL, NULL, 0, NULL);
+ if (err)
+ return err;
+
+ ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_BYTES, EZUSB_RID_ACK);
+ if (!ctx)
+ return -ENOMEM;
+
+ return ezusb_access_ltv(upriv, ctx, len, buf,
+ EZUSB_FRAME_CONTROL, NULL, 0, NULL);
+}
+
+static int ezusb_program(struct hermes *hw, const char *buf,
+ u32 addr, u32 len)
+{
+ u32 ch_addr;
+ u32 ch_len;
+ int err = 0;
+
+ /* We can only send 2048 bytes out of the bulk xmit at a time,
+ * so we have to split any programming into chunks of <2048
+ * bytes. */
+
+ ch_len = (len < MAX_DL_SIZE) ? len : MAX_DL_SIZE;
+ ch_addr = addr;
+
+ while (ch_addr < (addr + len)) {
+ pr_debug("Programming subblock of length %d "
+ "to address 0x%08x. Data @ %p\n",
+ ch_len, ch_addr, &buf[ch_addr - addr]);
+
+ err = ezusb_program_bytes(hw, &buf[ch_addr - addr],
+ ch_addr, ch_len);
+ if (err)
+ break;
+
+ ch_addr += ch_len;
+ ch_len = ((addr + len - ch_addr) < MAX_DL_SIZE) ?
+ (addr + len - ch_addr) : MAX_DL_SIZE;
+ }
+
+ return err;
+}
+
static netdev_tx_t ezusb_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct orinoco_private *priv = ndev_priv(dev);
@@ -1440,6 +1556,10 @@ static const struct hermes_ops ezusb_ops = {
.read_ltv = ezusb_read_ltv,
.write_ltv = ezusb_write_ltv,
.bap_pread = ezusb_bap_pread,
+ .read_pda = ezusb_read_pda,
+ .program_init = ezusb_program_init,
+ .program_end = ezusb_program_end,
+ .program = ezusb_program,
.lock_irqsave = ezusb_lock_irqsave,
.unlock_irqrestore = ezusb_unlock_irqrestore,
.lock_irq = ezusb_lock_irq,