]> nv-tegra.nvidia Code Review - linux-2.6.git/commitdiff
uwb: add the i1480 DFU driver
authorInaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
Wed, 17 Sep 2008 15:34:20 +0000 (16:34 +0100)
committerDavid Vrabel <dv02@dv02pc01.europe.root.pri>
Wed, 17 Sep 2008 15:54:28 +0000 (16:54 +0100)
Add the driver for downloading the firmware to an Intel i1480 device.

Signed-off-by: David Vrabel <david.vrabel@csr.com>
drivers/uwb/Kconfig
drivers/uwb/Makefile
drivers/uwb/i1480/Makefile [new file with mode: 0644]
drivers/uwb/i1480/dfu/Makefile [new file with mode: 0644]
drivers/uwb/i1480/dfu/dfu.c [new file with mode: 0644]
drivers/uwb/i1480/dfu/i1480-dfu.h [new file with mode: 0644]
drivers/uwb/i1480/dfu/mac.c [new file with mode: 0644]
drivers/uwb/i1480/dfu/phy.c [new file with mode: 0644]
drivers/uwb/i1480/dfu/usb.c [new file with mode: 0644]
drivers/uwb/i1480/i1480-est.c [new file with mode: 0644]

index 41f8984d198412065bd45d7abf279040e616f44c..59acb5b001d1953a089d45493a21570b07fee8d4 100644 (file)
@@ -64,4 +64,16 @@ config UWB_WLP
          This is a common library for drivers that implement
          networking over UWB.
 
          This is a common library for drivers that implement
          networking over UWB.
 
+config UWB_I1480U
+        tristate "Support for Intel Wireless UWB Link 1480 HWA"
+        depends on UWB_HWA
+        select FW_LOADER
+        help
+         This driver enables support for the i1480 when connected via
+         USB. It consists of a firmware uploader that will enable it
+         to behave as an HWA device.
+
+         To compile this driver select Y (built in) or M (module). It
+         is safe to select any even if you do not have the hardware.
+
 endif # UWB
 endif # UWB
index 79f48e3d4ec6e7554d5377345686ad95db5871b9..257e6908304cb304075ec42e5a718626d270ab92 100644 (file)
@@ -2,6 +2,7 @@ obj-$(CONFIG_UWB)               += uwb.o
 obj-$(CONFIG_UWB_WLP)          += wlp/
 obj-$(CONFIG_UWB_WHCI)         += umc.o whci.o whc-rc.o
 obj-$(CONFIG_UWB_HWA)          += hwa-rc.o
 obj-$(CONFIG_UWB_WLP)          += wlp/
 obj-$(CONFIG_UWB_WHCI)         += umc.o whci.o whc-rc.o
 obj-$(CONFIG_UWB_HWA)          += hwa-rc.o
+obj-$(CONFIG_UWB_I1480U)       += i1480/
 
 uwb-objs :=            \
        address.o       \
 
 uwb-objs :=            \
        address.o       \
diff --git a/drivers/uwb/i1480/Makefile b/drivers/uwb/i1480/Makefile
new file mode 100644 (file)
index 0000000..d69da16
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_UWB_I1480U)       += dfu/ i1480-est.o
diff --git a/drivers/uwb/i1480/dfu/Makefile b/drivers/uwb/i1480/dfu/Makefile
new file mode 100644 (file)
index 0000000..bd1b9f2
--- /dev/null
@@ -0,0 +1,9 @@
+obj-$(CONFIG_UWB_I1480U)       += i1480-dfu-usb.o
+
+i1480-dfu-usb-objs := \
+       dfu.o   \
+       mac.o   \
+       phy.o   \
+       usb.o
+
+
diff --git a/drivers/uwb/i1480/dfu/dfu.c b/drivers/uwb/i1480/dfu/dfu.c
new file mode 100644 (file)
index 0000000..ebffaf5
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * Intel Wireless UWB Link 1480
+ * Main driver
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * Common code for firmware upload used by the USB and PCI version;
+ * i1480_fw_upload() takes a device descriptor and uses the function
+ * pointers it provides to upload firmware and prepare the PHY.
+ *
+ * As well, provides common functions used by the rest of the code.
+ */
+#include "i1480-dfu.h"
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <linux/uwb.h>
+#include <linux/random.h>
+
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+/** @return 0 if If @evt is a valid reply event; otherwise complain */
+int i1480_rceb_check(const struct i1480 *i1480, const struct uwb_rceb *rceb,
+                    const char *cmd, u8 context,
+                    unsigned expected_type, unsigned expected_event)
+{
+       int result = 0;
+       struct device *dev = i1480->dev;
+       if (rceb->bEventContext != context) {
+               dev_err(dev, "%s: "
+                       "unexpected context id 0x%02x (expected 0x%02x)\n",
+                       cmd, rceb->bEventContext, context);
+               result = -EINVAL;
+       }
+       if (rceb->bEventType != expected_type) {
+               dev_err(dev, "%s: "
+                       "unexpected event type 0x%02x (expected 0x%02x)\n",
+                       cmd, rceb->bEventType, expected_type);
+               result = -EINVAL;
+       }
+       if (le16_to_cpu(rceb->wEvent) != expected_event) {
+               dev_err(dev, "%s: "
+                       "unexpected event 0x%04x (expected 0x%04x)\n",
+                       cmd, le16_to_cpu(rceb->wEvent), expected_event);
+               result = -EINVAL;
+       }
+       return result;
+}
+EXPORT_SYMBOL_GPL(i1480_rceb_check);
+
+
+/**
+ * Execute a Radio Control Command
+ *
+ * Command data has to be in i1480->cmd_buf.
+ *
+ * @returns size of the reply data filled in i1480->evt_buf or < 0 errno
+ *          code on error.
+ */
+ssize_t i1480_cmd(struct i1480 *i1480, const char *cmd_name, size_t cmd_size,
+                 size_t reply_size)
+{
+       ssize_t result;
+       struct uwb_rceb *reply = i1480->evt_buf;
+       struct uwb_rccb *cmd = i1480->cmd_buf;
+       u16 expected_event = reply->wEvent;
+       u8 expected_type = reply->bEventType;
+       u8 context;
+
+       d_fnstart(3, i1480->dev, "(%p, %s, %zu)\n", i1480, cmd_name, cmd_size);
+       init_completion(&i1480->evt_complete);
+       i1480->evt_result = -EINPROGRESS;
+       do {
+               get_random_bytes(&context, 1);
+       } while (context == 0x00 || context == 0xff);
+       cmd->bCommandContext = context;
+       result = i1480->cmd(i1480, cmd_name, cmd_size);
+       if (result < 0)
+               goto error;
+       /* wait for the callback to report a event was received */
+       result = wait_for_completion_interruptible_timeout(
+               &i1480->evt_complete, HZ);
+       if (result == 0) {
+               result = -ETIMEDOUT;
+               goto error;
+       }
+       if (result < 0)
+               goto error;
+       result = i1480->evt_result;
+       if (result < 0) {
+               dev_err(i1480->dev, "%s: command reply reception failed: %zd\n",
+                       cmd_name, result);
+               goto error;
+       }
+       if (result != reply_size) {
+               dev_err(i1480->dev, "%s returned only %zu bytes, %zu expected\n",
+                       cmd_name, result, reply_size);
+               result = -EINVAL;
+               goto error;
+       }
+       /* Verify we got the right event in response */
+       result = i1480_rceb_check(i1480, i1480->evt_buf, cmd_name, context,
+                                 expected_type, expected_event);
+error:
+       d_fnend(3, i1480->dev, "(%p, %s, %zu) = %zd\n",
+               i1480, cmd_name, cmd_size, result);
+       return result;
+}
+EXPORT_SYMBOL_GPL(i1480_cmd);
+
+
+/**
+ * Get information about the MAC and PHY
+ *
+ * @wa:      Wired adaptor
+ * @neh:     Notification/event handler
+ * @reply:   Pointer to the reply event buffer
+ * @returns: 0 if ok, < 0 errno code on error.
+ */
+static
+int i1480_cmd_get_mac_phy_info(struct i1480 *i1480)
+{
+       int result;
+       struct uwb_rccb *cmd = i1480->cmd_buf;
+       struct i1480_evt_confirm_GMPI *reply = i1480->evt_buf;
+
+       cmd->bCommandType = i1480_CET_VS1;
+       cmd->wCommand = cpu_to_le16(i1480_CMD_GET_MAC_PHY_INFO);
+       reply->rceb.bEventType = i1480_CET_VS1;
+       reply->rceb.wEvent = i1480_EVT_GET_MAC_PHY_INFO;
+       result = i1480_cmd(i1480, "GET_MAC_PHY_INFO", sizeof(*cmd),
+                          sizeof(*reply));
+       if (result < 0)
+               goto out;
+       if (le16_to_cpu(reply->status) != 0x00) {
+               dev_err(i1480->dev,
+                       "GET_MAC_PHY_INFO: command execution failed: %d\n",
+                       reply->status);
+               result = -EIO;
+       }
+out:
+       return result;
+}
+
+
+/**
+ * Get i1480's info and print it
+ *
+ * @wa:      Wire Adapter
+ * @neh:     Notification/event handler
+ * @returns: 0 if ok, < 0 errno code on error.
+ */
+static
+int i1480_check_info(struct i1480 *i1480)
+{
+       struct i1480_evt_confirm_GMPI *reply = i1480->evt_buf;
+       int result;
+       unsigned mac_fw_rev;
+#if i1480_FW <= 0x00000302
+       unsigned phy_fw_rev;
+#endif
+       if (i1480->quirk_no_check_info) {
+               dev_err(i1480->dev, "firmware info check disabled\n");
+               return 0;
+       }
+
+       result = i1480_cmd_get_mac_phy_info(i1480);
+       if (result < 0) {
+               dev_err(i1480->dev, "Cannot get MAC & PHY information: %d\n",
+                       result);
+               goto out;
+       }
+       mac_fw_rev = le16_to_cpu(reply->mac_fw_rev);
+#if i1480_FW > 0x00000302
+       dev_info(i1480->dev,
+                "HW v%02hx  "
+                "MAC FW v%02hx.%02hx caps %04hx  "
+                "PHY type %02hx v%02hx caps %02hx %02hx %02hx\n",
+                reply->hw_rev, mac_fw_rev >> 8, mac_fw_rev & 0xff,
+                le16_to_cpu(reply->mac_caps),
+                reply->phy_vendor, reply->phy_rev,
+                reply->phy_caps[0], reply->phy_caps[1], reply->phy_caps[2]);
+#else
+       phy_fw_rev = le16_to_cpu(reply->phy_fw_rev);
+       dev_info(i1480->dev, "MAC FW v%02hx.%02hx caps %04hx "
+                " PHY FW v%02hx.%02hx caps %04hx\n",
+                mac_fw_rev >> 8, mac_fw_rev & 0xff,
+                le16_to_cpu(reply->mac_caps),
+                phy_fw_rev >> 8, phy_fw_rev & 0xff,
+                le16_to_cpu(reply->phy_caps));
+#endif
+       dev_dbg(i1480->dev,
+               "key-stores:%hu mcast-addr-stores:%hu sec-modes:%hu\n",
+               (unsigned short) reply->key_stores,
+               le16_to_cpu(reply->mcast_addr_stores),
+               (unsigned short) reply->sec_mode_supported);
+       /* FIXME: complain if fw version too low -- pending for
+        * numbering to stabilize */
+out:
+       return result;
+}
+
+
+static
+int i1480_print_state(struct i1480 *i1480)
+{
+       int result;
+       u32 *buf = (u32 *) i1480->cmd_buf;
+
+       result = i1480->read(i1480, 0x80080000, 2 * sizeof(*buf));
+       if (result < 0) {
+               dev_err(i1480->dev, "cannot read U & L states: %d\n", result);
+               goto error;
+       }
+       dev_info(i1480->dev, "state U 0x%08x, L 0x%08x\n", buf[0], buf[1]);
+error:
+       return result;
+}
+
+
+/*
+ * PCI probe, firmware uploader
+ *
+ * _mac_fw_upload() will call rc_setup(), which needs an rc_release().
+ */
+int i1480_fw_upload(struct i1480 *i1480)
+{
+       int result;
+
+       result = i1480_pre_fw_upload(i1480);    /* PHY pre fw */
+       if (result < 0 && result != -ENOENT) {
+               i1480_print_state(i1480);
+               goto error;
+       }
+       result = i1480_mac_fw_upload(i1480);    /* MAC fw */
+       if (result < 0) {
+               if (result == -ENOENT)
+                       dev_err(i1480->dev, "Cannot locate MAC FW file '%s'\n",
+                               i1480->mac_fw_name);
+               else
+                       i1480_print_state(i1480);
+               goto error;
+       }
+       result = i1480_phy_fw_upload(i1480);    /* PHY fw */
+       if (result < 0 && result != -ENOENT) {
+               i1480_print_state(i1480);
+               goto error_rc_release;
+       }
+       result = i1480_check_info(i1480);
+       if (result < 0) {
+               dev_warn(i1480->dev, "Warning! Cannot check firmware info: %d\n",
+                        result);
+               result = 0;
+       }
+       dev_info(i1480->dev, "firmware uploaded successfully\n");
+error_rc_release:
+       if (i1480->rc_release)
+               i1480->rc_release(i1480);
+       result = 0;
+error:
+       return result;
+}
+EXPORT_SYMBOL_GPL(i1480_fw_upload);
diff --git a/drivers/uwb/i1480/dfu/i1480-dfu.h b/drivers/uwb/i1480/dfu/i1480-dfu.h
new file mode 100644 (file)
index 0000000..4103b28
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * i1480 Device Firmware Upload
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * This driver is the firmware uploader for the Intel Wireless UWB
+ * Link 1480 device (both in the USB and PCI incarnations).
+ *
+ * The process is quite simple: we stop the device, write the firmware
+ * to its memory and then restart it. Wait for the device to let us
+ * know it is done booting firmware. Ready.
+ *
+ * We might have to upload before or after a phy firmware (which might
+ * be done in two methods, using a normal firmware image or through
+ * the MPI port).
+ *
+ * Because USB and PCI use common methods, we just make ops out of the
+ * common operations (read, write, wait_init_done and cmd) and
+ * implement them in usb.c and pci.c.
+ *
+ * The flow is (some parts omitted):
+ *
+ * i1480_{usb,pci}_probe()       On enumerate/discovery
+ *   i1480_fw_upload()
+ *     i1480_pre_fw_upload()
+ *       __mac_fw_upload()
+ *         fw_hdrs_load()
+ *         mac_fw_hdrs_push()
+ *           i1480->write()       [i1480_{usb,pci}_write()]
+ *           i1480_fw_cmp()
+ *             i1480->read()      [i1480_{usb,pci}_read()]
+ *     i1480_mac_fw_upload()
+ *       __mac_fw_upload()
+ *       i1480->setup(()
+ *       i1480->wait_init_done()
+ *       i1480_cmd_reset()
+ *         i1480->cmd()           [i1480_{usb,pci}_cmd()]
+ *         ...
+ *     i1480_phy_fw_upload()
+ *       request_firmware()
+ *       i1480_mpi_write()
+ *         i1480->cmd()           [i1480_{usb,pci}_cmd()]
+ *       i1480_check_info()
+ *
+ * Once the probe function enumerates the device and uploads the
+ * firmware, we just exit with -ENODEV, as we don't really want to
+ * attach to the device.
+ */
+#ifndef __i1480_DFU_H__
+#define __i1480_DFU_H__
+
+#include <linux/uwb/spec.h>
+#include <linux/types.h>
+#include <linux/completion.h>
+
+#define i1480_FW_UPLOAD_MODE_MASK (cpu_to_le32(0x00000018))
+
+#if i1480_FW > 0x00000302
+#define i1480_RCEB_EXTENDED
+#endif
+
+struct uwb_rccb;
+struct uwb_rceb;
+
+/*
+ * Common firmware upload handlers
+ *
+ * Normally you embed this struct in another one specific to your hw.
+ *
+ * @write      Write to device's memory from buffer.
+ * @read       Read from device's memory to i1480->evt_buf.
+ * @setup      Setup device after basic firmware is uploaded
+ * @wait_init_done
+ *              Wait for the device to send a notification saying init
+ *              is done.
+ * @cmd         FOP for issuing the command to the hardware. The
+ *              command data is contained in i1480->cmd_buf and the size
+ *              is supplied as an argument. The command replied is put
+ *              in i1480->evt_buf and the size in i1480->evt_result (or if
+ *              an error, a < 0 errno code).
+ *
+ * @cmd_buf    Memory buffer used to send commands to the device.
+ *              Allocated by the upper layers i1480_fw_upload().
+ *              Size has to be @buf_size.
+ * @evt_buf    Memory buffer used to place the async notifications
+ *              received by the hw. Allocated by the upper layers
+ *              i1480_fw_upload().
+ *              Size has to be @buf_size.
+ * @cmd_complete
+ *              Low level driver uses this to notify code waiting afor
+ *              an event that the event has arrived and data is in
+ *              i1480->evt_buf (and size/result in i1480->evt_result).
+ * @hw_rev
+ *              Use this value to activate dfu code to support new revisions
+ *              of hardware.  i1480_init() sets this to a default value.
+ *              It should be updated by the USB and PCI code.
+ */
+struct i1480 {
+       struct device *dev;
+
+       int (*write)(struct i1480 *, u32 addr, const void *, size_t);
+       int (*read)(struct i1480 *, u32 addr, size_t);
+       int (*rc_setup)(struct i1480 *);
+       void (*rc_release)(struct i1480 *);
+       int (*wait_init_done)(struct i1480 *);
+       int (*cmd)(struct i1480 *, const char *cmd_name, size_t cmd_size);
+       const char *pre_fw_name;
+       const char *mac_fw_name;
+       const char *mac_fw_name_deprecate;      /* FIXME: Will go away */
+       const char *phy_fw_name;
+       u8 hw_rev;
+
+       size_t buf_size;        /* size of both evt_buf and cmd_buf */
+       void *evt_buf, *cmd_buf;
+       ssize_t evt_result;
+       struct completion evt_complete;
+
+       u8 quirk_no_check_info:1;
+};
+
+static inline
+void i1480_init(struct i1480 *i1480)
+{
+       i1480->hw_rev = 1;
+       init_completion(&i1480->evt_complete);
+}
+
+extern int i1480_fw_upload(struct i1480 *);
+extern int i1480_pre_fw_upload(struct i1480 *);
+extern int i1480_mac_fw_upload(struct i1480 *);
+extern int i1480_phy_fw_upload(struct i1480 *);
+extern ssize_t i1480_cmd(struct i1480 *, const char *, size_t, size_t);
+extern int i1480_rceb_check(const struct i1480 *,
+                           const struct uwb_rceb *, const char *, u8,
+                           unsigned, unsigned);
+
+enum {
+       /* Vendor specific command type */
+       i1480_CET_VS1 =                 0xfd,
+       /* i1480 commands */
+       i1480_CMD_SET_IP_MAS =          0x000e,
+       i1480_CMD_GET_MAC_PHY_INFO =    0x0003,
+       i1480_CMD_MPI_WRITE =           0x000f,
+       i1480_CMD_MPI_READ =            0x0010,
+       /* i1480 events */
+#if i1480_FW > 0x00000302
+       i1480_EVT_CONFIRM =             0x0002,
+       i1480_EVT_RM_INIT_DONE =        0x0101,
+       i1480_EVT_DEV_ADD =             0x0103,
+       i1480_EVT_DEV_RM =              0x0104,
+       i1480_EVT_DEV_ID_CHANGE =       0x0105,
+       i1480_EVT_GET_MAC_PHY_INFO =    i1480_CMD_GET_MAC_PHY_INFO,
+#else
+       i1480_EVT_CONFIRM =             0x0002,
+       i1480_EVT_RM_INIT_DONE =        0x0101,
+       i1480_EVT_DEV_ADD =             0x0103,
+       i1480_EVT_DEV_RM =              0x0104,
+       i1480_EVT_DEV_ID_CHANGE =       0x0105,
+       i1480_EVT_GET_MAC_PHY_INFO =    i1480_EVT_CONFIRM,
+#endif
+};
+
+
+struct i1480_evt_confirm {
+       struct uwb_rceb rceb;
+#ifdef i1480_RCEB_EXTENDED
+       __le16 wParamLength;
+#endif
+       u8 bResultCode;
+} __attribute__((packed));
+
+
+struct i1480_rceb {
+       struct uwb_rceb rceb;
+#ifdef i1480_RCEB_EXTENDED
+       __le16 wParamLength;
+#endif
+} __attribute__((packed));
+
+
+/**
+ * Get MAC & PHY Information confirm event structure
+ *
+ * Confirm event returned by the command.
+ */
+struct i1480_evt_confirm_GMPI {
+#if i1480_FW > 0x00000302
+       struct uwb_rceb rceb;
+       __le16 wParamLength;
+       __le16 status;
+       u8 mac_addr[6];         /* EUI-64 bit IEEE address [still 8 bytes?] */
+       u8 dev_addr[2];
+       __le16 mac_fw_rev;      /* major = v >> 8; minor = v & 0xff */
+       u8 hw_rev;
+       u8 phy_vendor;
+       u8 phy_rev;             /* major v = >> 8; minor = v & 0xff */
+       __le16 mac_caps;
+       u8 phy_caps[3];
+       u8 key_stores;
+       __le16 mcast_addr_stores;
+       u8 sec_mode_supported;
+#else
+       struct uwb_rceb rceb;
+       u8 status;
+       u8 mac_addr[8];         /* EUI-64 bit IEEE address [still 8 bytes?] */
+       u8 dev_addr[2];
+       __le16 mac_fw_rev;      /* major = v >> 8; minor = v & 0xff */
+       __le16 phy_fw_rev;      /* major v = >> 8; minor = v & 0xff */
+       __le16 mac_caps;
+       u8 phy_caps;
+       u8 key_stores;
+       __le16 mcast_addr_stores;
+       u8 sec_mode_supported;
+#endif
+} __attribute__((packed));
+
+
+struct i1480_cmd_mpi_write {
+       struct uwb_rccb rccb;
+       __le16 size;
+       u8 data[];
+};
+
+
+struct i1480_cmd_mpi_read {
+       struct uwb_rccb rccb;
+       __le16 size;
+       struct {
+               u8 page, offset;
+       } __attribute__((packed)) data[];
+} __attribute__((packed));
+
+
+struct i1480_evt_mpi_read {
+       struct uwb_rceb rceb;
+#ifdef i1480_RCEB_EXTENDED
+       __le16 wParamLength;
+#endif
+       u8 bResultCode;
+       __le16 size;
+       struct {
+               u8 page, offset, value;
+       } __attribute__((packed)) data[];
+} __attribute__((packed));
+
+
+#endif /* #ifndef __i1480_DFU_H__ */
diff --git a/drivers/uwb/i1480/dfu/mac.c b/drivers/uwb/i1480/dfu/mac.c
new file mode 100644 (file)
index 0000000..3d44554
--- /dev/null
@@ -0,0 +1,529 @@
+/*
+ * Intel Wireless UWB Link 1480
+ * MAC Firmware upload implementation
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * Implementation of the code for parsing the firmware file (extract
+ * the headers and binary code chunks) in the fw_*() functions. The
+ * code to upload pre and mac firmwares is the same, so it uses a
+ * common entry point in __mac_fw_upload(), which uses the i1480
+ * function pointers to push the firmware to the device.
+ */
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/uwb.h>
+#include "i1480-dfu.h"
+
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+/*
+ * Descriptor for a continuous segment of MAC fw data
+ */
+struct fw_hdr {
+       unsigned long address;
+       size_t length;
+       const u32 *bin;
+       struct fw_hdr *next;
+};
+
+
+/* Free a chain of firmware headers */
+static
+void fw_hdrs_free(struct fw_hdr *hdr)
+{
+       struct fw_hdr *next;
+
+       while (hdr) {
+               next = hdr->next;
+               kfree(hdr);
+               hdr = next;
+       }
+}
+
+
+/* Fill a firmware header descriptor from a memory buffer */
+static
+int fw_hdr_load(struct i1480 *i1480, struct fw_hdr *hdr, unsigned hdr_cnt,
+               const char *_data, const u32 *data_itr, const u32 *data_top)
+{
+       size_t hdr_offset =  (const char *) data_itr - _data;
+       size_t remaining_size = (void *) data_top - (void *) data_itr;
+       if (data_itr + 2 > data_top) {
+               dev_err(i1480->dev, "fw hdr #%u/%zu: EOF reached in header at "
+                      "offset %zu, limit %zu\n",
+                      hdr_cnt, hdr_offset,
+                      (const char *) data_itr + 2 - _data,
+                      (const char *) data_top - _data);
+               return -EINVAL;
+       }
+       hdr->next = NULL;
+       hdr->address = le32_to_cpu(*data_itr++);
+       hdr->length = le32_to_cpu(*data_itr++);
+       hdr->bin = data_itr;
+       if (hdr->length > remaining_size) {
+               dev_err(i1480->dev, "fw hdr #%u/%zu: EOF reached in data; "
+                      "chunk too long (%zu bytes), only %zu left\n",
+                      hdr_cnt, hdr_offset, hdr->length, remaining_size);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+
+/**
+ * Get a buffer where the firmware is supposed to be and create a
+ * chain of headers linking them together.
+ *
+ * @phdr: where to place the pointer to the first header (headers link
+ *        to the next via the @hdr->next ptr); need to free the whole
+ *        chain when done.
+ *
+ * @_data: Pointer to the data buffer.
+ *
+ * @_data_size: Size of the data buffer (bytes); data size has to be a
+ *              multiple of 4. Function will fail if not.
+ *
+ * Goes over the whole binary blob; reads the first chunk and creates
+ * a fw hdr from it (which points to where the data is in @_data and
+ * the length of the chunk); then goes on to the next chunk until
+ * done. Each header is linked to the next.
+ */
+static
+int fw_hdrs_load(struct i1480 *i1480, struct fw_hdr **phdr,
+                const char *_data, size_t data_size)
+{
+       int result;
+       unsigned hdr_cnt = 0;
+       u32 *data = (u32 *) _data, *data_itr, *data_top;
+       struct fw_hdr *hdr, **prev_hdr = phdr;
+
+       result = -EINVAL;
+       /* Check size is ok and pointer is aligned */
+       if (data_size % sizeof(u32) != 0)
+               goto error;
+       if ((unsigned long) _data % sizeof(u16) != 0)
+               goto error;
+       *phdr = NULL;
+       data_itr = data;
+       data_top = (u32 *) (_data + data_size);
+       while (data_itr < data_top) {
+               result = -ENOMEM;
+               hdr = kmalloc(sizeof(*hdr), GFP_KERNEL);
+               if (hdr == NULL) {
+                       dev_err(i1480->dev, "Cannot allocate fw header "
+                              "for chunk #%u\n", hdr_cnt);
+                       goto error_alloc;
+               }
+               result = fw_hdr_load(i1480, hdr, hdr_cnt,
+                                    _data, data_itr, data_top);
+               if (result < 0)
+                       goto error_load;
+               data_itr += 2 + hdr->length;
+               *prev_hdr = hdr;
+               prev_hdr = &hdr->next;
+               hdr_cnt++;
+       };
+       *prev_hdr = NULL;
+       return 0;
+
+error_load:
+       kfree(hdr);
+error_alloc:
+       fw_hdrs_free(*phdr);
+error:
+       return result;
+}
+
+
+/**
+ * Compares a chunk of fw with one in the devices's memory
+ *
+ * @i1480:     Device instance
+ * @hdr:     Pointer to the firmware chunk
+ * @returns: 0 if equal, < 0 errno on error. If > 0, it is the offset
+ *           where the difference was found (plus one).
+ *
+ * Kind of dirty and simplistic, but does the trick in both the PCI
+ * and USB version. We do a quick[er] memcmp(), and if it fails, we do
+ * a byte-by-byte to find the offset.
+ */
+static
+ssize_t i1480_fw_cmp(struct i1480 *i1480, struct fw_hdr *hdr)
+{
+       ssize_t result = 0;
+       u32 src_itr = 0, cnt;
+       size_t size = hdr->length*sizeof(hdr->bin[0]);
+       size_t chunk_size;
+       u8 *bin = (u8 *) hdr->bin;
+
+       while (size > 0) {
+               chunk_size = size < i1480->buf_size ? size : i1480->buf_size;
+               result = i1480->read(i1480, hdr->address + src_itr, chunk_size);
+               if (result < 0) {
+                       dev_err(i1480->dev, "error reading for verification: "
+                               "%zd\n", result);
+                       goto error;
+               }
+               if (memcmp(i1480->cmd_buf, bin + src_itr, result)) {
+                       u8 *buf = i1480->cmd_buf;
+                       d_printf(2, i1480->dev,
+                                "original data @ %p + %u, %zu bytes\n",
+                                bin, src_itr, result);
+                       d_dump(4, i1480->dev, bin + src_itr, result);
+                       for (cnt = 0; cnt < result; cnt++)
+                               if (bin[src_itr + cnt] != buf[cnt]) {
+                                       dev_err(i1480->dev, "byte failed at "
+                                               "src_itr %u cnt %u [0x%02x "
+                                               "vs 0x%02x]\n", src_itr, cnt,
+                                               bin[src_itr + cnt], buf[cnt]);
+                                       result = src_itr + cnt + 1;
+                                       goto cmp_failed;
+                               }
+               }
+               src_itr += result;
+               size -= result;
+       }
+       result = 0;
+error:
+cmp_failed:
+       return result;
+}
+
+
+/**
+ * Writes firmware headers to the device.
+ *
+ * @prd:     PRD instance
+ * @hdr:     Processed firmware
+ * @returns: 0 if ok, < 0 errno on error.
+ */
+static
+int mac_fw_hdrs_push(struct i1480 *i1480, struct fw_hdr *hdr,
+                    const char *fw_name, const char *fw_tag)
+{
+       struct device *dev = i1480->dev;
+       ssize_t result = 0;
+       struct fw_hdr *hdr_itr;
+       int verif_retry_count;
+
+       d_fnstart(3, dev, "(%p, %p)\n", i1480, hdr);
+       /* Now, header by header, push them to the hw */
+       for (hdr_itr = hdr; hdr_itr != NULL; hdr_itr = hdr_itr->next) {
+               verif_retry_count = 0;
+retry:
+               dev_dbg(dev, "fw chunk (%zu @ 0x%08lx)\n",
+                       hdr_itr->length * sizeof(hdr_itr->bin[0]),
+                       hdr_itr->address);
+               result = i1480->write(i1480, hdr_itr->address, hdr_itr->bin,
+                                   hdr_itr->length*sizeof(hdr_itr->bin[0]));
+               if (result < 0) {
+                       dev_err(dev, "%s fw '%s': write failed (%zuB @ 0x%lx):"
+                               " %zd\n", fw_tag, fw_name,
+                               hdr_itr->length * sizeof(hdr_itr->bin[0]),
+                               hdr_itr->address, result);
+                       break;
+               }
+               result = i1480_fw_cmp(i1480, hdr_itr);
+               if (result < 0) {
+                       dev_err(dev, "%s fw '%s': verification read "
+                               "failed (%zuB @ 0x%lx): %zd\n",
+                               fw_tag, fw_name,
+                               hdr_itr->length * sizeof(hdr_itr->bin[0]),
+                               hdr_itr->address, result);
+                       break;
+               }
+               if (result > 0) {       /* Offset where it failed + 1 */
+                       result--;
+                       dev_err(dev, "%s fw '%s': WARNING: verification "
+                               "failed at 0x%lx: retrying\n",
+                               fw_tag, fw_name, hdr_itr->address + result);
+                       if (++verif_retry_count < 3)
+                               goto retry;     /* write this block again! */
+                       dev_err(dev, "%s fw '%s': verification failed at 0x%lx: "
+                               "tried %d times\n", fw_tag, fw_name,
+                               hdr_itr->address + result, verif_retry_count);
+                       result = -EINVAL;
+                       break;
+               }
+       }
+       d_fnend(3, dev, "(%zd)\n", result);
+       return result;
+}
+
+
+/** Puts the device in firmware upload mode.*/
+static
+int mac_fw_upload_enable(struct i1480 *i1480)
+{
+       int result;
+       u32 reg = 0x800000c0;
+       u32 *buffer = (u32 *)i1480->cmd_buf;
+
+       if (i1480->hw_rev > 1)
+               reg = 0x8000d0d4;
+       result = i1480->read(i1480, reg, sizeof(u32));
+       if (result < 0)
+               goto error_cmd;
+       *buffer &= ~i1480_FW_UPLOAD_MODE_MASK;
+       result = i1480->write(i1480, reg, buffer, sizeof(u32));
+       if (result < 0)
+               goto error_cmd;
+       return 0;
+error_cmd:
+       dev_err(i1480->dev, "can't enable fw upload mode: %d\n", result);
+       return result;
+}
+
+
+/** Gets the device out of firmware upload mode. */
+static
+int mac_fw_upload_disable(struct i1480 *i1480)
+{
+       int result;
+       u32 reg = 0x800000c0;
+       u32 *buffer = (u32 *)i1480->cmd_buf;
+
+       if (i1480->hw_rev > 1)
+               reg = 0x8000d0d4;
+       result = i1480->read(i1480, reg, sizeof(u32));
+       if (result < 0)
+               goto error_cmd;
+       *buffer |= i1480_FW_UPLOAD_MODE_MASK;
+       result = i1480->write(i1480, reg, buffer, sizeof(u32));
+       if (result < 0)
+               goto error_cmd;
+       return 0;
+error_cmd:
+       dev_err(i1480->dev, "can't disable fw upload mode: %d\n", result);
+       return result;
+}
+
+
+
+/**
+ * Generic function for uploading a MAC firmware.
+ *
+ * @i1480:     Device instance
+ * @fw_name: Name of firmware file to upload.
+ * @fw_tag:  Name of the firmware type (for messages)
+ *           [eg: MAC, PRE]
+ * @do_wait: Wait for device to emit initialization done message (0
+ *           for PRE fws, 1 for MAC fws).
+ * @returns: 0 if ok, < 0 errno on error.
+ */
+static
+int __mac_fw_upload(struct i1480 *i1480, const char *fw_name,
+                   const char *fw_tag)
+{
+       int result;
+       const struct firmware *fw;
+       struct fw_hdr *fw_hdrs;
+
+       d_fnstart(3, i1480->dev, "(%p, %s, %s)\n", i1480, fw_name, fw_tag);
+       result = request_firmware(&fw, fw_name, i1480->dev);
+       if (result < 0) /* Up to caller to complain on -ENOENT */
+               goto out;
+       d_printf(3, i1480->dev, "%s fw '%s': uploading\n", fw_tag, fw_name);
+       result = fw_hdrs_load(i1480, &fw_hdrs, fw->data, fw->size);
+       if (result < 0) {
+               dev_err(i1480->dev, "%s fw '%s': failed to parse firmware "
+                       "file: %d\n", fw_tag, fw_name, result);
+               goto out_release;
+       }
+       result = mac_fw_upload_enable(i1480);
+       if (result < 0)
+               goto out_hdrs_release;
+       result = mac_fw_hdrs_push(i1480, fw_hdrs, fw_name, fw_tag);
+       mac_fw_upload_disable(i1480);
+out_hdrs_release:
+       if (result >= 0)
+               dev_info(i1480->dev, "%s fw '%s': uploaded\n", fw_tag, fw_name);
+       else
+               dev_err(i1480->dev, "%s fw '%s': failed to upload (%d), "
+                       "power cycle device\n", fw_tag, fw_name, result);
+       fw_hdrs_free(fw_hdrs);
+out_release:
+       release_firmware(fw);
+out:
+       d_fnend(3, i1480->dev, "(%p, %s, %s) = %d\n", i1480, fw_name, fw_tag,
+               result);
+       return result;
+}
+
+
+/**
+ * Upload a pre-PHY firmware
+ *
+ */
+int i1480_pre_fw_upload(struct i1480 *i1480)
+{
+       int result;
+       result = __mac_fw_upload(i1480, i1480->pre_fw_name, "PRE");
+       if (result == 0)
+               msleep(400);
+       return result;
+}
+
+
+/**
+ * Reset a the MAC and PHY
+ *
+ * @i1480:     Device's instance
+ * @returns: 0 if ok, < 0 errno code on error
+ *
+ * We put the command on kmalloc'ed memory as some arches cannot do
+ * USB from the stack. The reply event is copied from an stage buffer,
+ * so it can be in the stack. See WUSB1.0[8.6.2.4] for more details.
+ *
+ * We issue the reset to make sure the UWB controller reinits the PHY;
+ * this way we can now if the PHY init went ok.
+ */
+static
+int i1480_cmd_reset(struct i1480 *i1480)
+{
+       int result;
+       struct uwb_rccb *cmd = (void *) i1480->cmd_buf;
+       struct i1480_evt_reset {
+               struct uwb_rceb rceb;
+               u8 bResultCode;
+       } __attribute__((packed)) *reply = (void *) i1480->evt_buf;
+
+       result = -ENOMEM;
+       cmd->bCommandType = UWB_RC_CET_GENERAL;
+       cmd->wCommand = cpu_to_le16(UWB_RC_CMD_RESET);
+       reply->rceb.bEventType = UWB_RC_CET_GENERAL;
+       reply->rceb.wEvent = UWB_RC_CMD_RESET;
+       result = i1480_cmd(i1480, "RESET", sizeof(*cmd), sizeof(*reply));
+       if (result < 0)
+               goto out;
+       if (reply->bResultCode != UWB_RC_RES_SUCCESS) {
+               dev_err(i1480->dev, "RESET: command execution failed: %u\n",
+                       reply->bResultCode);
+               result = -EIO;
+       }
+out:
+       return result;
+
+}
+
+
+/** Wait for the MAC FW to start running */
+static
+int i1480_fw_is_running_q(struct i1480 *i1480)
+{
+       int cnt = 0;
+       int result;
+       u32 *val = (u32 *) i1480->cmd_buf;
+
+       d_fnstart(3, i1480->dev, "(i1480 %p)\n", i1480);
+       for (cnt = 0; cnt < 10; cnt++) {
+               msleep(100);
+               result = i1480->read(i1480, 0x80080000, 4);
+               if (result < 0) {
+                       dev_err(i1480->dev, "Can't read 0x8008000: %d\n", result);
+                       goto out;
+               }
+               if (*val == 0x55555555UL)       /* fw running? cool */
+                       goto out;
+               if (printk_ratelimit())
+                       d_printf(5, i1480->dev, "read #%d: 0x%08x\n", cnt, *val);
+       }
+       dev_err(i1480->dev, "Timed out waiting for fw to start\n");
+       result = -ETIMEDOUT;
+out:
+       d_fnend(3, i1480->dev, "(i1480 %p) = %d\n", i1480, result);
+       return result;
+
+}
+
+
+/**
+ * Upload MAC firmware, wait for it to start
+ *
+ * @i1480:     Device instance
+ * @fw_name: Name of the file that contains the firmware
+ *
+ * This has to be called after the pre fw has been uploaded (if
+ * there is any).
+ */
+int i1480_mac_fw_upload(struct i1480 *i1480)
+{
+       int result = 0, deprecated_name = 0;
+       struct i1480_rceb *rcebe = (void *) i1480->evt_buf;
+
+       d_fnstart(3, i1480->dev, "(%p)\n", i1480);
+       result = __mac_fw_upload(i1480, i1480->mac_fw_name, "MAC");
+       if (result == -ENOENT) {
+               result = __mac_fw_upload(i1480, i1480->mac_fw_name_deprecate,
+                                        "MAC");
+               deprecated_name = 1;
+       }
+       if (result < 0)
+               return result;
+       if (deprecated_name == 1)
+               dev_warn(i1480->dev,
+                        "WARNING: firmware file name %s is deprecated, "
+                        "please rename to %s\n",
+                        i1480->mac_fw_name_deprecate, i1480->mac_fw_name);
+       result = i1480_fw_is_running_q(i1480);
+       if (result < 0)
+               goto error_fw_not_running;
+       result = i1480->rc_setup ? i1480->rc_setup(i1480) : 0;
+       if (result < 0) {
+               dev_err(i1480->dev, "Cannot setup after MAC fw upload: %d\n",
+                       result);
+               goto error_setup;
+       }
+       result = i1480->wait_init_done(i1480);  /* wait init'on */
+       if (result < 0) {
+               dev_err(i1480->dev, "MAC fw '%s': Initialization timed out "
+                       "(%d)\n", i1480->mac_fw_name, result);
+               goto error_init_timeout;
+       }
+       /* verify we got the right initialization done event */
+       if (i1480->evt_result != sizeof(*rcebe)) {
+               dev_err(i1480->dev, "MAC fw '%s': initialization event returns "
+                       "wrong size (%zu bytes vs %zu needed)\n",
+                       i1480->mac_fw_name, i1480->evt_result, sizeof(*rcebe));
+               dump_bytes(i1480->dev, rcebe, min(i1480->evt_result, (ssize_t)32));
+               goto error_size;
+       }
+       result = -EIO;
+       if (rcebe->rceb.bEventType != i1480_CET_VS1
+           || le16_to_cpu(rcebe->rceb.wEvent) != i1480_EVT_RM_INIT_DONE) {
+               dev_err(i1480->dev, "wrong initialization event 0x%02x/%04x/%02x "
+                       "received; expected 0x%02x/%04x/00\n",
+                       rcebe->rceb.bEventType, le16_to_cpu(rcebe->rceb.wEvent),
+                       rcebe->rceb.bEventContext, i1480_CET_VS1,
+                       i1480_EVT_RM_INIT_DONE);
+               goto error_init_timeout;
+       }
+       result = i1480_cmd_reset(i1480);
+       if (result < 0)
+               dev_err(i1480->dev, "MAC fw '%s': MBOA reset failed (%d)\n",
+                       i1480->mac_fw_name, result);
+error_fw_not_running:
+error_init_timeout:
+error_size:
+error_setup:
+       d_fnend(3, i1480->dev, "(i1480 %p) = %d\n", i1480, result);
+       return result;
+}
diff --git a/drivers/uwb/i1480/dfu/phy.c b/drivers/uwb/i1480/dfu/phy.c
new file mode 100644 (file)
index 0000000..3b1a87d
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Intel Wireless UWB Link 1480
+ * PHY parameters upload
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * Code for uploading the PHY parameters to the PHY through the UWB
+ * Radio Control interface.
+ *
+ * We just send the data through the MPI interface using HWA-like
+ * commands and then reset the PHY to make sure it is ok.
+ */
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/usb/wusb.h>
+#include "i1480-dfu.h"
+
+
+/**
+ * Write a value array to an address of the MPI interface
+ *
+ * @i1480:     Device descriptor
+ * @data:      Data array to write
+ * @size:      Size of the data array
+ * @returns:   0 if ok, < 0 errno code on error.
+ *
+ * The data array is organized into pairs:
+ *
+ * ADDRESS VALUE
+ *
+ * ADDRESS is BE 16 bit unsigned, VALUE 8 bit unsigned. Size thus has
+ * to be a multiple of three.
+ */
+static
+int i1480_mpi_write(struct i1480 *i1480, const void *data, size_t size)
+{
+       int result;
+       struct i1480_cmd_mpi_write *cmd = i1480->cmd_buf;
+       struct i1480_evt_confirm *reply = i1480->evt_buf;
+
+       BUG_ON(size > 480);
+       result = -ENOMEM;
+       cmd->rccb.bCommandType = i1480_CET_VS1;
+       cmd->rccb.wCommand = cpu_to_le16(i1480_CMD_MPI_WRITE);
+       cmd->size = cpu_to_le16(size);
+       memcpy(cmd->data, data, size);
+       reply->rceb.bEventType = i1480_CET_VS1;
+       reply->rceb.wEvent = i1480_CMD_MPI_WRITE;
+       result = i1480_cmd(i1480, "MPI-WRITE", sizeof(*cmd) + size, sizeof(*reply));
+       if (result < 0)
+               goto out;
+       if (reply->bResultCode != UWB_RC_RES_SUCCESS) {
+               dev_err(i1480->dev, "MPI-WRITE: command execution failed: %d\n",
+                       reply->bResultCode);
+               result = -EIO;
+       }
+out:
+       return result;
+}
+
+
+/**
+ * Read a value array to from an address of the MPI interface
+ *
+ * @i1480:     Device descriptor
+ * @data:      where to place the read array
+ * @srcaddr:   Where to read from
+ * @size:      Size of the data read array
+ * @returns:   0 if ok, < 0 errno code on error.
+ *
+ * The command data array is organized into pairs ADDR0 ADDR1..., and
+ * the returned data in ADDR0 VALUE0 ADDR1 VALUE1...
+ *
+ * We generate the command array to be a sequential read and then
+ * rearrange the result.
+ *
+ * We use the i1480->cmd_buf for the command, i1480->evt_buf for the reply.
+ *
+ * As the reply has to fit in 512 bytes (i1480->evt_buffer), the max amount
+ * of values we can read is (512 - sizeof(*reply)) / 3
+ */
+static
+int i1480_mpi_read(struct i1480 *i1480, u8 *data, u16 srcaddr, size_t size)
+{
+       int result;
+       struct i1480_cmd_mpi_read *cmd = i1480->cmd_buf;
+       struct i1480_evt_mpi_read *reply = i1480->evt_buf;
+       unsigned cnt;
+
+       memset(i1480->cmd_buf, 0x69, 512);
+       memset(i1480->evt_buf, 0x69, 512);
+
+       BUG_ON(size > (i1480->buf_size - sizeof(*reply)) / 3);
+       result = -ENOMEM;
+       cmd->rccb.bCommandType = i1480_CET_VS1;
+       cmd->rccb.wCommand = cpu_to_le16(i1480_CMD_MPI_READ);
+       cmd->size = cpu_to_le16(3*size);
+       for (cnt = 0; cnt < size; cnt++) {
+               cmd->data[cnt].page = (srcaddr + cnt) >> 8;
+               cmd->data[cnt].offset = (srcaddr + cnt) & 0xff;
+       }
+       reply->rceb.bEventType = i1480_CET_VS1;
+       reply->rceb.wEvent = i1480_CMD_MPI_READ;
+       result = i1480_cmd(i1480, "MPI-READ", sizeof(*cmd) + 2*size,
+                       sizeof(*reply) + 3*size);
+       if (result < 0)
+               goto out;
+       if (reply->bResultCode != UWB_RC_RES_SUCCESS) {
+               dev_err(i1480->dev, "MPI-READ: command execution failed: %d\n",
+                       reply->bResultCode);
+               result = -EIO;
+       }
+       for (cnt = 0; cnt < size; cnt++) {
+               if (reply->data[cnt].page != (srcaddr + cnt) >> 8)
+                       dev_err(i1480->dev, "MPI-READ: page inconsistency at "
+                               "index %u: expected 0x%02x, got 0x%02x\n", cnt,
+                               (srcaddr + cnt) >> 8, reply->data[cnt].page);
+               if (reply->data[cnt].offset != ((srcaddr + cnt) & 0x00ff))
+                       dev_err(i1480->dev, "MPI-READ: offset inconsistency at "
+                               "index %u: expected 0x%02x, got 0x%02x\n", cnt,
+                               (srcaddr + cnt) & 0x00ff,
+                               reply->data[cnt].offset);
+               data[cnt] = reply->data[cnt].value;
+       }
+       result = 0;
+out:
+       return result;
+}
+
+
+/**
+ * Upload a PHY firmware, wait for it to start
+ *
+ * @i1480:     Device instance
+ * @fw_name: Name of the file that contains the firmware
+ *
+ * We assume the MAC fw is up and running. This means we can use the
+ * MPI interface to write the PHY firmware. Once done, we issue an
+ * MBOA Reset, which will force the MAC to reset and reinitialize the
+ * PHY. If that works, we are ready to go.
+ *
+ * Max packet size for the MPI write is 512, so the max buffer is 480
+ * (which gives us 160 byte triads of MSB, LSB and VAL for the data).
+ */
+int i1480_phy_fw_upload(struct i1480 *i1480)
+{
+       int result;
+       const struct firmware *fw;
+       const char *data_itr, *data_top;
+       const size_t MAX_BLK_SIZE = 480;        /* 160 triads */
+       size_t data_size;
+       u8 phy_stat;
+
+       result = request_firmware(&fw, i1480->phy_fw_name, i1480->dev);
+       if (result < 0)
+               goto out;
+       /* Loop writing data in chunks as big as possible until done. */
+       for (data_itr = fw->data, data_top = data_itr + fw->size;
+            data_itr < data_top; data_itr += MAX_BLK_SIZE) {
+               data_size = min(MAX_BLK_SIZE, (size_t) (data_top - data_itr));
+               result = i1480_mpi_write(i1480, data_itr, data_size);
+               if (result < 0)
+                       goto error_mpi_write;
+       }
+       /* Read MPI page 0, offset 6; if 0, PHY was initialized correctly. */
+       result = i1480_mpi_read(i1480, &phy_stat, 0x0006, 1);
+       if (result < 0) {
+               dev_err(i1480->dev, "PHY: can't get status: %d\n", result);
+               goto error_mpi_status;
+       }
+       if (phy_stat != 0) {
+               result = -ENODEV;
+               dev_info(i1480->dev, "error, PHY not ready: %u\n", phy_stat);
+               goto error_phy_status;
+       }
+       dev_info(i1480->dev, "PHY fw '%s': uploaded\n", i1480->phy_fw_name);
+error_phy_status:
+error_mpi_status:
+error_mpi_write:
+       release_firmware(fw);
+       if (result < 0)
+               dev_err(i1480->dev, "PHY fw '%s': failed to upload (%d), "
+                       "power cycle device\n", i1480->phy_fw_name, result);
+out:
+       return result;
+}
diff --git a/drivers/uwb/i1480/dfu/usb.c b/drivers/uwb/i1480/dfu/usb.c
new file mode 100644 (file)
index 0000000..98eeeff
--- /dev/null
@@ -0,0 +1,500 @@
+/*
+ * Intel Wireless UWB Link 1480
+ * USB SKU firmware upload implementation
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * This driver will prepare the i1480 device to behave as a real
+ * Wireless USB HWA adaptor by uploading the firmware.
+ *
+ * When the device is connected or driver is loaded, i1480_usb_probe()
+ * is called--this will allocate and initialize the device structure,
+ * fill in the pointers to the common functions (read, write,
+ * wait_init_done and cmd for HWA command execution) and once that is
+ * done, call the common firmware uploading routine. Then clean up and
+ * return -ENODEV, as we don't attach to the device.
+ *
+ * The rest are the basic ops we implement that the fw upload code
+ * uses to do its job. All the ops in the common code are i1480->NAME,
+ * the functions are i1480_usb_NAME().
+ */
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/usb.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/uwb.h>
+#include <linux/usb/wusb.h>
+#include <linux/usb/wusb-wa.h>
+#include "i1480-dfu.h"
+
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+
+struct i1480_usb {
+       struct i1480 i1480;
+       struct usb_device *usb_dev;
+       struct usb_interface *usb_iface;
+       struct urb *neep_urb;   /* URB for reading from EP1 */
+};
+
+
+static
+void i1480_usb_init(struct i1480_usb *i1480_usb)
+{
+       i1480_init(&i1480_usb->i1480);
+}
+
+
+static
+int i1480_usb_create(struct i1480_usb *i1480_usb, struct usb_interface *iface)
+{
+       struct usb_device *usb_dev = interface_to_usbdev(iface);
+       int result = -ENOMEM;
+
+       i1480_usb->usb_dev = usb_get_dev(usb_dev);      /* bind the USB device */
+       i1480_usb->usb_iface = usb_get_intf(iface);
+       usb_set_intfdata(iface, i1480_usb);             /* Bind the driver to iface0 */
+       i1480_usb->neep_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (i1480_usb->neep_urb == NULL)
+               goto error;
+       return 0;
+
+error:
+       usb_set_intfdata(iface, NULL);
+       usb_put_intf(iface);
+       usb_put_dev(usb_dev);
+       return result;
+}
+
+
+static
+void i1480_usb_destroy(struct i1480_usb *i1480_usb)
+{
+       usb_kill_urb(i1480_usb->neep_urb);
+       usb_free_urb(i1480_usb->neep_urb);
+       usb_set_intfdata(i1480_usb->usb_iface, NULL);
+       usb_put_intf(i1480_usb->usb_iface);
+       usb_put_dev(i1480_usb->usb_dev);
+}
+
+
+/**
+ * Write a buffer to a memory address in the i1480 device
+ *
+ * @i1480:  i1480 instance
+ * @memory_address:
+ *          Address where to write the data buffer to.
+ * @buffer: Buffer to the data
+ * @size:   Size of the buffer [has to be < 512].
+ * @returns: 0 if ok, < 0 errno code on error.
+ *
+ * Data buffers to USB cannot be on the stack or in vmalloc'ed areas,
+ * so we copy it to the local i1480 buffer before proceeding. In any
+ * case, we have a max size we can send, soooo.
+ */
+static
+int i1480_usb_write(struct i1480 *i1480, u32 memory_address,
+                   const void *buffer, size_t size)
+{
+       int result = 0;
+       struct i1480_usb *i1480_usb = container_of(i1480, struct i1480_usb, i1480);
+       size_t buffer_size, itr = 0;
+
+       d_fnstart(3, i1480->dev, "(%p, 0x%08x, %p, %zu)\n",
+                 i1480, memory_address, buffer, size);
+       BUG_ON(size & 0x3); /* Needs to be a multiple of 4 */
+       while (size > 0) {
+               buffer_size = size < i1480->buf_size ? size : i1480->buf_size;
+               memcpy(i1480->cmd_buf, buffer + itr, buffer_size);
+               result = usb_control_msg(
+                       i1480_usb->usb_dev, usb_sndctrlpipe(i1480_usb->usb_dev, 0),
+                       0xf0, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       cpu_to_le16(memory_address & 0xffff),
+                       cpu_to_le16((memory_address >> 16) & 0xffff),
+                       i1480->cmd_buf, buffer_size, 100 /* FIXME: arbitrary */);
+               if (result < 0)
+                       break;
+               d_printf(3, i1480->dev,
+                        "wrote @ 0x%08x %u bytes (of %zu bytes requested)\n",
+                        memory_address, result, buffer_size);
+               d_dump(4, i1480->dev, i1480->cmd_buf, result);
+               itr += result;
+               memory_address += result;
+               size -= result;
+       }
+       d_fnend(3, i1480->dev, "(%p, 0x%08x, %p, %zu) = %d\n",
+               i1480, memory_address, buffer, size, result);
+       return result;
+}
+
+
+/**
+ * Read a block [max size 512] of the device's memory to @i1480's buffer.
+ *
+ * @i1480: i1480 instance
+ * @memory_address:
+ *         Address where to read from.
+ * @size:  Size to read. Smaller than or equal to 512.
+ * @returns: >= 0 number of bytes written if ok, < 0 errno code on error.
+ *
+ * NOTE: if the memory address or block is incorrect, you might get a
+ *       stall or a different memory read. Caller has to verify the
+ *       memory address and size passed back in the @neh structure.
+ */
+static
+int i1480_usb_read(struct i1480 *i1480, u32 addr, size_t size)
+{
+       ssize_t result = 0, bytes = 0;
+       size_t itr, read_size = i1480->buf_size;
+       struct i1480_usb *i1480_usb = container_of(i1480, struct i1480_usb, i1480);
+
+       d_fnstart(3, i1480->dev, "(%p, 0x%08x, %zu)\n",
+                 i1480, addr, size);
+       BUG_ON(size > i1480->buf_size);
+       BUG_ON(size & 0x3); /* Needs to be a multiple of 4 */
+       BUG_ON(read_size > 512);
+
+       if (addr >= 0x8000d200 && addr < 0x8000d400)    /* Yeah, HW quirk */
+               read_size = 4;
+
+       for (itr = 0; itr < size; itr += read_size) {
+               size_t itr_addr = addr + itr;
+               size_t itr_size = min(read_size, size - itr);
+               result = usb_control_msg(
+                       i1480_usb->usb_dev, usb_rcvctrlpipe(i1480_usb->usb_dev, 0),
+                       0xf0, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       cpu_to_le16(itr_addr & 0xffff),
+                       cpu_to_le16((itr_addr >> 16) & 0xffff),
+                       i1480->cmd_buf + itr, itr_size,
+                       100 /* FIXME: arbitrary */);
+               if (result < 0) {
+                       dev_err(i1480->dev, "%s: USB read error: %zd\n",
+                               __func__, result);
+                       goto out;
+               }
+               if (result != itr_size) {
+                       result = -EIO;
+                       dev_err(i1480->dev,
+                               "%s: partial read got only %zu bytes vs %zu expected\n",
+                               __func__, result, itr_size);
+                       goto out;
+               }
+               bytes += result;
+       }
+       result = bytes;
+out:
+       d_fnend(3, i1480->dev, "(%p, 0x%08x, %zu) = %zd\n",
+               i1480, addr, size, result);
+       if (result > 0)
+               d_dump(4, i1480->dev, i1480->cmd_buf, result);
+       return result;
+}
+
+
+/**
+ * Callback for reads on the notification/event endpoint
+ *
+ * Just enables the completion read handler.
+ */
+static
+void i1480_usb_neep_cb(struct urb *urb)
+{
+       struct i1480 *i1480 = urb->context;
+       struct device *dev = i1480->dev;
+
+       switch (urb->status) {
+       case 0:
+               break;
+       case -ECONNRESET:       /* Not an error, but a controlled situation; */
+       case -ENOENT:           /* (we killed the URB)...so, no broadcast */
+               dev_dbg(dev, "NEEP: reset/noent %d\n", urb->status);
+               break;
+       case -ESHUTDOWN:        /* going away! */
+               dev_dbg(dev, "NEEP: down %d\n", urb->status);
+               break;
+       default:
+               dev_err(dev, "NEEP: unknown status %d\n", urb->status);
+               break;
+       }
+       i1480->evt_result = urb->actual_length;
+       complete(&i1480->evt_complete);
+       return;
+}
+
+
+/**
+ * Wait for the MAC FW to initialize
+ *
+ * MAC FW sends a 0xfd/0101/00 notification to EP1 when done
+ * initializing. Get that notification into i1480->evt_buf; upper layer
+ * will verify it.
+ *
+ * Set i1480->evt_result with the result of getting the event or its
+ * size (if succesful).
+ *
+ * Delivers the data directly to i1480->evt_buf
+ */
+static
+int i1480_usb_wait_init_done(struct i1480 *i1480)
+{
+       int result;
+       struct device *dev = i1480->dev;
+       struct i1480_usb *i1480_usb = container_of(i1480, struct i1480_usb, i1480);
+       struct usb_endpoint_descriptor *epd;
+
+       d_fnstart(3, dev, "(%p)\n", i1480);
+       init_completion(&i1480->evt_complete);
+       i1480->evt_result = -EINPROGRESS;
+       epd = &i1480_usb->usb_iface->cur_altsetting->endpoint[0].desc;
+       usb_fill_int_urb(i1480_usb->neep_urb, i1480_usb->usb_dev,
+                        usb_rcvintpipe(i1480_usb->usb_dev, epd->bEndpointAddress),
+                        i1480->evt_buf, i1480->buf_size,
+                        i1480_usb_neep_cb, i1480, epd->bInterval);
+       result = usb_submit_urb(i1480_usb->neep_urb, GFP_KERNEL);
+       if (result < 0) {
+               dev_err(dev, "init done: cannot submit NEEP read: %d\n",
+                       result);
+               goto error_submit;
+       }
+       /* Wait for the USB callback to get the data */
+       result = wait_for_completion_interruptible_timeout(
+               &i1480->evt_complete, HZ);
+       if (result <= 0) {
+               result = result == 0 ? -ETIMEDOUT : result;
+               goto error_wait;
+       }
+       usb_kill_urb(i1480_usb->neep_urb);
+       d_fnend(3, dev, "(%p) = 0\n", i1480);
+       return 0;
+
+error_wait:
+       usb_kill_urb(i1480_usb->neep_urb);
+error_submit:
+       i1480->evt_result = result;
+       d_fnend(3, dev, "(%p) = %d\n", i1480, result);
+       return result;
+}
+
+
+/**
+ * Generic function for issuing commands to the i1480
+ *
+ * @i1480:      i1480 instance
+ * @cmd_name:   Name of the command (for error messages)
+ * @cmd:        Pointer to command buffer
+ * @cmd_size:   Size of the command buffer
+ * @reply:      Buffer for the reply event
+ * @reply_size: Expected size back (including RCEB); the reply buffer
+ *              is assumed to be as big as this.
+ * @returns:    >= 0 size of the returned event data if ok,
+ *              < 0 errno code on error.
+ *
+ * Arms the NE handle, issues the command to the device and checks the
+ * basics of the reply event.
+ */
+static
+int i1480_usb_cmd(struct i1480 *i1480, const char *cmd_name, size_t cmd_size)
+{
+       int result;
+       struct device *dev = i1480->dev;
+       struct i1480_usb *i1480_usb = container_of(i1480, struct i1480_usb, i1480);
+       struct usb_endpoint_descriptor *epd;
+       struct uwb_rccb *cmd = i1480->cmd_buf;
+       u8 iface_no;
+
+       d_fnstart(3, dev, "(%p, %s, %zu)\n", i1480, cmd_name, cmd_size);
+       /* Post a read on the notification & event endpoint */
+       iface_no = i1480_usb->usb_iface->cur_altsetting->desc.bInterfaceNumber;
+       epd = &i1480_usb->usb_iface->cur_altsetting->endpoint[0].desc;
+       usb_fill_int_urb(
+               i1480_usb->neep_urb, i1480_usb->usb_dev,
+               usb_rcvintpipe(i1480_usb->usb_dev, epd->bEndpointAddress),
+               i1480->evt_buf, i1480->buf_size,
+               i1480_usb_neep_cb, i1480, epd->bInterval);
+       result = usb_submit_urb(i1480_usb->neep_urb, GFP_KERNEL);
+       if (result < 0) {
+               dev_err(dev, "%s: cannot submit NEEP read: %d\n",
+                       cmd_name, result);
+                       goto error_submit_ep1;
+       }
+       /* Now post the command on EP0 */
+       result = usb_control_msg(
+               i1480_usb->usb_dev, usb_sndctrlpipe(i1480_usb->usb_dev, 0),
+               WA_EXEC_RC_CMD,
+               USB_DIR_OUT | USB_RECIP_INTERFACE | USB_TYPE_CLASS,
+               0, iface_no,
+               cmd, cmd_size,
+               100 /* FIXME: this is totally arbitrary */);
+       if (result < 0) {
+               dev_err(dev, "%s: control request failed: %d\n",
+                       cmd_name, result);
+               goto error_submit_ep0;
+       }
+       d_fnend(3, dev, "(%p, %s, %zu) = %d\n",
+               i1480, cmd_name, cmd_size, result);
+       return result;
+
+error_submit_ep0:
+       usb_kill_urb(i1480_usb->neep_urb);
+error_submit_ep1:
+       d_fnend(3, dev, "(%p, %s, %zu) = %d\n",
+               i1480, cmd_name, cmd_size, result);
+       return result;
+}
+
+
+/*
+ * Probe a i1480 device for uploading firmware.
+ *
+ * We attach only to interface #0, which is the radio control interface.
+ */
+static
+int i1480_usb_probe(struct usb_interface *iface, const struct usb_device_id *id)
+{
+       struct i1480_usb *i1480_usb;
+       struct i1480 *i1480;
+       struct device *dev = &iface->dev;
+       int result;
+
+       result = -ENODEV;
+       if (iface->cur_altsetting->desc.bInterfaceNumber != 0) {
+               dev_dbg(dev, "not attaching to iface %d\n",
+                       iface->cur_altsetting->desc.bInterfaceNumber);
+               goto error;
+       }
+       if (iface->num_altsetting > 1
+           && interface_to_usbdev(iface)->descriptor.idProduct == 0xbabe) {
+               /* Need altsetting #1 [HW QUIRK] or EP1 won't work */
+               result = usb_set_interface(interface_to_usbdev(iface), 0, 1);
+               if (result < 0)
+                       dev_warn(dev,
+                                "can't set altsetting 1 on iface 0: %d\n",
+                                result);
+       }
+
+       result = -ENOMEM;
+       i1480_usb = kzalloc(sizeof(*i1480_usb), GFP_KERNEL);
+       if (i1480_usb == NULL) {
+               dev_err(dev, "Unable to allocate instance\n");
+               goto error;
+       }
+       i1480_usb_init(i1480_usb);
+
+       i1480 = &i1480_usb->i1480;
+       i1480->buf_size = 512;
+       i1480->cmd_buf = kmalloc(2 * i1480->buf_size, GFP_KERNEL);
+       if (i1480->cmd_buf == NULL) {
+               dev_err(dev, "Cannot allocate transfer buffers\n");
+               result = -ENOMEM;
+               goto error_buf_alloc;
+       }
+       i1480->evt_buf = i1480->cmd_buf + i1480->buf_size;
+
+       result = i1480_usb_create(i1480_usb, iface);
+       if (result < 0) {
+               dev_err(dev, "Cannot create instance: %d\n", result);
+               goto error_create;
+       }
+
+       /* setup the fops and upload the firmare */
+       i1480->pre_fw_name = "i1480-pre-phy-0.0.bin";
+       i1480->mac_fw_name = "i1480-usb-0.0.bin";
+       i1480->mac_fw_name_deprecate = "ptc-0.0.bin";
+       i1480->phy_fw_name = "i1480-phy-0.0.bin";
+       i1480->dev = &iface->dev;
+       i1480->write = i1480_usb_write;
+       i1480->read = i1480_usb_read;
+       i1480->rc_setup = NULL;
+       i1480->wait_init_done = i1480_usb_wait_init_done;
+       i1480->cmd = i1480_usb_cmd;
+
+       result = i1480_fw_upload(&i1480_usb->i1480);    /* the real thing */
+       if (result >= 0) {
+               usb_reset_device(i1480_usb->usb_dev);
+               result = -ENODEV;       /* we don't want to bind to the iface */
+       }
+       i1480_usb_destroy(i1480_usb);
+error_create:
+       kfree(i1480->cmd_buf);
+error_buf_alloc:
+       kfree(i1480_usb);
+error:
+       return result;
+}
+
+#define i1480_USB_DEV(v, p)                            \
+{                                                      \
+       .match_flags = USB_DEVICE_ID_MATCH_DEVICE       \
+                | USB_DEVICE_ID_MATCH_DEV_INFO         \
+                | USB_DEVICE_ID_MATCH_INT_INFO,        \
+       .idVendor = (v),                                \
+       .idProduct = (p),                               \
+       .bDeviceClass = 0xff,                           \
+       .bDeviceSubClass = 0xff,                        \
+       .bDeviceProtocol = 0xff,                        \
+       .bInterfaceClass = 0xff,                        \
+       .bInterfaceSubClass = 0xff,                     \
+       .bInterfaceProtocol = 0xff,                     \
+}
+
+
+/** USB device ID's that we handle */
+static struct usb_device_id i1480_usb_id_table[] = {
+       i1480_USB_DEV(0x8086, 0xdf3b),
+       i1480_USB_DEV(0x15a9, 0x0005),
+       i1480_USB_DEV(0x07d1, 0x3802),
+       i1480_USB_DEV(0x050d, 0x305a),
+       i1480_USB_DEV(0x3495, 0x3007),
+       {},
+};
+MODULE_DEVICE_TABLE(usb, i1480_usb_id_table);
+
+
+static struct usb_driver i1480_dfu_driver = {
+       .name =         "i1480-dfu-usb",
+       .id_table =     i1480_usb_id_table,
+       .probe =        i1480_usb_probe,
+       .disconnect =   NULL,
+};
+
+
+/*
+ * Initialize the i1480 DFU driver.
+ *
+ * We also need to register our function for guessing event sizes.
+ */
+static int __init i1480_dfu_driver_init(void)
+{
+       return usb_register(&i1480_dfu_driver);
+}
+module_init(i1480_dfu_driver_init);
+
+
+static void __exit i1480_dfu_driver_exit(void)
+{
+       usb_deregister(&i1480_dfu_driver);
+}
+module_exit(i1480_dfu_driver_exit);
+
+
+MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
+MODULE_DESCRIPTION("Intel Wireless UWB Link 1480 firmware uploader for USB");
+MODULE_LICENSE("GPL");
diff --git a/drivers/uwb/i1480/i1480-est.c b/drivers/uwb/i1480/i1480-est.c
new file mode 100644 (file)
index 0000000..7bf8c6f
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Intel Wireless UWB Link 1480
+ * Event Size tables for Wired Adaptors
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: docs
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/uwb.h>
+#include "dfu/i1480-dfu.h"
+
+
+/** Event size table for wEvents 0x00XX */
+static struct uwb_est_entry i1480_est_fd00[] = {
+       /* Anybody expecting this response has to use
+        * neh->extra_size to specify the real size that will
+        * come back. */
+       [i1480_EVT_CONFIRM] = { .size = sizeof(struct i1480_evt_confirm) },
+       [i1480_CMD_SET_IP_MAS] = { .size = sizeof(struct i1480_evt_confirm) },
+#ifdef i1480_RCEB_EXTENDED
+       [0x09] = {
+               .size = sizeof(struct i1480_rceb),
+               .offset = 1 + offsetof(struct i1480_rceb, wParamLength),
+       },
+#endif
+};
+
+/** Event size table for wEvents 0x01XX */
+static struct uwb_est_entry i1480_est_fd01[] = {
+       [0xff & i1480_EVT_RM_INIT_DONE] = { .size = sizeof(struct i1480_rceb) },
+       [0xff & i1480_EVT_DEV_ADD] = { .size = sizeof(struct i1480_rceb) + 9 },
+       [0xff & i1480_EVT_DEV_RM] = { .size = sizeof(struct i1480_rceb) + 9 },
+       [0xff & i1480_EVT_DEV_ID_CHANGE] = {
+               .size = sizeof(struct i1480_rceb) + 2 },
+};
+
+static int i1480_est_init(void)
+{
+       int result = uwb_est_register(i1480_CET_VS1, 0x00, 0x8086, 0x0c3b,
+                                     i1480_est_fd00,
+                                     ARRAY_SIZE(i1480_est_fd00));
+       if (result < 0) {
+               printk(KERN_ERR "Can't register EST table fd00: %d\n", result);
+               return result;
+       }
+       result = uwb_est_register(i1480_CET_VS1, 0x01, 0x8086, 0x0c3b,
+                                 i1480_est_fd01, ARRAY_SIZE(i1480_est_fd01));
+       if (result < 0) {
+               printk(KERN_ERR "Can't register EST table fd01: %d\n", result);
+               return result;
+       }
+       return 0;
+}
+module_init(i1480_est_init);
+
+static void i1480_est_exit(void)
+{
+       uwb_est_unregister(i1480_CET_VS1, 0x00, 0x8086, 0x0c3b,
+                          i1480_est_fd00, ARRAY_SIZE(i1480_est_fd00));
+       uwb_est_unregister(i1480_CET_VS1, 0x01, 0x8086, 0x0c3b,
+                          i1480_est_fd01, ARRAY_SIZE(i1480_est_fd01));
+}
+module_exit(i1480_est_exit);
+
+MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
+MODULE_DESCRIPTION("i1480's Vendor Specific Event Size Tables");
+MODULE_LICENSE("GPL");
+
+/**
+ * USB device ID's that we handle
+ *
+ * [so we are loaded when this kind device is connected]
+ */
+static struct usb_device_id i1480_est_id_table[] = {
+       { USB_DEVICE(0x8086, 0xdf3b), },
+       { USB_DEVICE(0x8086, 0x0c3b), },
+       { },
+};
+MODULE_DEVICE_TABLE(usb, i1480_est_id_table);