summaryrefslogtreecommitdiffstats
path: root/drivers/uwb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/uwb')
-rw-r--r--drivers/uwb/Kconfig90
-rw-r--r--drivers/uwb/Makefile29
-rw-r--r--drivers/uwb/address.c374
-rw-r--r--drivers/uwb/beacon.c644
-rw-r--r--drivers/uwb/driver.c144
-rw-r--r--drivers/uwb/drp-avail.c288
-rw-r--r--drivers/uwb/drp-ie.c232
-rw-r--r--drivers/uwb/drp.c461
-rw-r--r--drivers/uwb/est.c485
-rw-r--r--drivers/uwb/hwa-rc.c926
-rw-r--r--drivers/uwb/i1480/Makefile2
-rw-r--r--drivers/uwb/i1480/dfu/Makefile9
-rw-r--r--drivers/uwb/i1480/dfu/dfu.c217
-rw-r--r--drivers/uwb/i1480/dfu/i1480-dfu.h260
-rw-r--r--drivers/uwb/i1480/dfu/mac.c529
-rw-r--r--drivers/uwb/i1480/dfu/phy.c203
-rw-r--r--drivers/uwb/i1480/dfu/usb.c500
-rw-r--r--drivers/uwb/i1480/i1480-est.c99
-rw-r--r--drivers/uwb/i1480/i1480-wlp.h200
-rw-r--r--drivers/uwb/i1480/i1480u-wlp/Makefile8
-rw-r--r--drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h284
-rw-r--r--drivers/uwb/i1480/i1480u-wlp/lc.c421
-rw-r--r--drivers/uwb/i1480/i1480u-wlp/netdev.c368
-rw-r--r--drivers/uwb/i1480/i1480u-wlp/rx.c486
-rw-r--r--drivers/uwb/i1480/i1480u-wlp/sysfs.c408
-rw-r--r--drivers/uwb/i1480/i1480u-wlp/tx.c632
-rw-r--r--drivers/uwb/ie.c541
-rw-r--r--drivers/uwb/lc-dev.c492
-rw-r--r--drivers/uwb/lc-rc.c495
-rw-r--r--drivers/uwb/neh.c616
-rw-r--r--drivers/uwb/pal.c91
-rw-r--r--drivers/uwb/reset.c362
-rw-r--r--drivers/uwb/rsv.c680
-rw-r--r--drivers/uwb/scan.c133
-rw-r--r--drivers/uwb/umc-bus.c218
-rw-r--r--drivers/uwb/umc-dev.c104
-rw-r--r--drivers/uwb/umc-drv.c31
-rw-r--r--drivers/uwb/uwb-debug.c367
-rw-r--r--drivers/uwb/uwb-internal.h305
-rw-r--r--drivers/uwb/uwbd.c427
-rw-r--r--drivers/uwb/whc-rc.c528
-rw-r--r--drivers/uwb/whci.c269
-rw-r--r--drivers/uwb/wlp/Makefile10
-rw-r--r--drivers/uwb/wlp/driver.c43
-rw-r--r--drivers/uwb/wlp/eda.c449
-rw-r--r--drivers/uwb/wlp/messages.c1946
-rw-r--r--drivers/uwb/wlp/sysfs.c709
-rw-r--r--drivers/uwb/wlp/txrx.c374
-rw-r--r--drivers/uwb/wlp/wlp-internal.h228
-rw-r--r--drivers/uwb/wlp/wlp-lc.c585
-rw-r--r--drivers/uwb/wlp/wss-lc.c1055
51 files changed, 19357 insertions, 0 deletions
diff --git a/drivers/uwb/Kconfig b/drivers/uwb/Kconfig
new file mode 100644
index 000000000000..ca783127af36
--- /dev/null
+++ b/drivers/uwb/Kconfig
@@ -0,0 +1,90 @@
+#
+# UWB device configuration
+#
+
+menuconfig UWB
+ tristate "Ultra Wideband devices (EXPERIMENTAL)"
+ depends on EXPERIMENTAL
+ depends on PCI
+ default n
+ help
+ UWB is a high-bandwidth, low-power, point-to-point radio
+ technology using a wide spectrum (3.1-10.6GHz). It is
+ optimized for in-room use (480Mbps at 2 meters, 110Mbps at
+ 10m). It serves as the transport layer for other protocols,
+ such as Wireless USB (WUSB), IP (WLP) and upcoming
+ Bluetooth and 1394
+
+ The topology is peer to peer; however, higher level
+ protocols (such as WUSB) might impose a master/slave
+ relationship.
+
+ Say Y here if your computer has UWB radio controllers (USB or PCI)
+ based. You will need to enable the radio controllers
+ below. It is ok to select all of them, no harm done.
+
+ For more help check the UWB and WUSB related files in
+ <file:Documentation/usb/>.
+
+ To compile the UWB stack as a module, choose M here.
+
+if UWB
+
+config UWB_HWA
+ tristate "UWB Radio Control driver for WUSB-compliant USB dongles (HWA)"
+ depends on USB
+ help
+ This driver enables the radio controller for HWA USB
+ devices. HWA stands for Host Wire Adapter, and it is a UWB
+ Radio Controller connected to your system via USB. Most of
+ them come with a Wireless USB host controller also.
+
+ 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.
+
+config UWB_WHCI
+ tristate "UWB Radio Control driver for WHCI-compliant cards"
+ depends on PCI
+ help
+ This driver enables the radio controller for WHCI cards.
+
+ WHCI is an specification developed by Intel
+ (http://www.intel.com/technology/comms/wusb/whci.htm) much
+ in the spirit of USB's EHCI, but for UWB and Wireless USB
+ radio/host controllers connected via memmory mapping (eg:
+ PCI). Most of these cards come also with a Wireless USB host
+ controller.
+
+ 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.
+
+config UWB_WLP
+ tristate "Support WiMedia Link Protocol (Ethernet/IP over UWB)"
+ depends on UWB && NET
+ help
+ 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.
+
+config UWB_I1480U_WLP
+ tristate "Support for Intel Wireless UWB Link 1480 HWA's WLP interface"
+ depends on UWB_I1480U && UWB_WLP && NET
+ help
+ This driver enables WLP support for the i1480 when connected via
+ USB. WLP is the WiMedia Link Protocol, or IP over UWB.
+
+ To compile this driver select Y (built in) or M (module). It
+ is safe to select any even if you don't have the hardware.
+
+endif # UWB
diff --git a/drivers/uwb/Makefile b/drivers/uwb/Makefile
new file mode 100644
index 000000000000..257e6908304c
--- /dev/null
+++ b/drivers/uwb/Makefile
@@ -0,0 +1,29 @@
+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_I1480U) += i1480/
+
+uwb-objs := \
+ address.o \
+ beacon.o \
+ driver.o \
+ drp.o \
+ drp-avail.o \
+ drp-ie.o \
+ est.o \
+ ie.o \
+ lc-dev.o \
+ lc-rc.o \
+ neh.o \
+ pal.o \
+ reset.o \
+ rsv.o \
+ scan.o \
+ uwb-debug.o \
+ uwbd.o
+
+umc-objs := \
+ umc-bus.o \
+ umc-dev.o \
+ umc-drv.o
diff --git a/drivers/uwb/address.c b/drivers/uwb/address.c
new file mode 100644
index 000000000000..1664ae5f1706
--- /dev/null
+++ b/drivers/uwb/address.c
@@ -0,0 +1,374 @@
+/*
+ * Ultra Wide Band
+ * Address management
+ *
+ * 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/errno.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/random.h>
+#include <linux/etherdevice.h>
+#include <linux/uwb/debug.h>
+#include "uwb-internal.h"
+
+
+/** Device Address Management command */
+struct uwb_rc_cmd_dev_addr_mgmt {
+ struct uwb_rccb rccb;
+ u8 bmOperationType;
+ u8 baAddr[6];
+} __attribute__((packed));
+
+
+/**
+ * Low level command for setting/getting UWB radio's addresses
+ *
+ * @hwarc: HWA Radio Control interface instance
+ * @bmOperationType:
+ * Set/get, MAC/DEV (see WUSB1.0[8.6.2.2])
+ * @baAddr: address buffer--assumed to have enough data to hold
+ * the address type requested.
+ * @reply: Pointer to reply buffer (can be stack allocated)
+ * @returns: 0 if ok, < 0 errno code on error.
+ *
+ * @cmd has to be allocated because USB cannot grok USB or vmalloc
+ * buffers depending on your combination of host architecture.
+ */
+static
+int uwb_rc_dev_addr_mgmt(struct uwb_rc *rc,
+ u8 bmOperationType, const u8 *baAddr,
+ struct uwb_rc_evt_dev_addr_mgmt *reply)
+{
+ int result;
+ struct uwb_rc_cmd_dev_addr_mgmt *cmd;
+
+ result = -ENOMEM;
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (cmd == NULL)
+ goto error_kzalloc;
+ cmd->rccb.bCommandType = UWB_RC_CET_GENERAL;
+ cmd->rccb.wCommand = cpu_to_le16(UWB_RC_CMD_DEV_ADDR_MGMT);
+ cmd->bmOperationType = bmOperationType;
+ if (baAddr) {
+ size_t size = 0;
+ switch (bmOperationType >> 1) {
+ case 0: size = 2; break;
+ case 1: size = 6; break;
+ default: BUG();
+ }
+ memcpy(cmd->baAddr, baAddr, size);
+ }
+ reply->rceb.bEventType = UWB_RC_CET_GENERAL;
+ reply->rceb.wEvent = UWB_RC_CMD_DEV_ADDR_MGMT;
+ result = uwb_rc_cmd(rc, "DEV-ADDR-MGMT",
+ &cmd->rccb, sizeof(*cmd),
+ &reply->rceb, sizeof(*reply));
+ if (result < 0)
+ goto error_cmd;
+ if (result < sizeof(*reply)) {
+ dev_err(&rc->uwb_dev.dev,
+ "DEV-ADDR-MGMT: not enough data replied: "
+ "%d vs %zu bytes needed\n", result, sizeof(*reply));
+ result = -ENOMSG;
+ } else if (reply->bResultCode != UWB_RC_RES_SUCCESS) {
+ dev_err(&rc->uwb_dev.dev,
+ "DEV-ADDR-MGMT: command execution failed: %s (%d)\n",
+ uwb_rc_strerror(reply->bResultCode),
+ reply->bResultCode);
+ result = -EIO;
+ } else
+ result = 0;
+error_cmd:
+ kfree(cmd);
+error_kzalloc:
+ return result;
+}
+
+
+/**
+ * Set the UWB RC MAC or device address.
+ *
+ * @rc: UWB Radio Controller
+ * @_addr: Pointer to address to write [assumed to be either a
+ * 'struct uwb_mac_addr *' or a 'struct uwb_dev_addr *'].
+ * @type: Type of address to set (UWB_ADDR_DEV or UWB_ADDR_MAC).
+ * @returns: 0 if ok, < 0 errno code on error.
+ *
+ * Some anal retentivity here: even if both 'struct
+ * uwb_{dev,mac}_addr' have the actual byte array in the same offset
+ * and I could just pass _addr to hwarc_cmd_dev_addr_mgmt(), I prefer
+ * to use some syntatic sugar in case someday we decide to change the
+ * format of the structs. The compiler will optimize it out anyway.
+ */
+static int uwb_rc_addr_set(struct uwb_rc *rc,
+ const void *_addr, enum uwb_addr_type type)
+{
+ int result;
+ u8 bmOperationType = 0x1; /* Set address */
+ const struct uwb_dev_addr *dev_addr = _addr;
+ const struct uwb_mac_addr *mac_addr = _addr;
+ struct uwb_rc_evt_dev_addr_mgmt reply;
+ const u8 *baAddr;
+
+ result = -EINVAL;
+ switch (type) {
+ case UWB_ADDR_DEV:
+ baAddr = dev_addr->data;
+ break;
+ case UWB_ADDR_MAC:
+ baAddr = mac_addr->data;
+ bmOperationType |= 0x2;
+ break;
+ default:
+ return result;
+ }
+ return uwb_rc_dev_addr_mgmt(rc, bmOperationType, baAddr, &reply);
+}
+
+
+/**
+ * Get the UWB radio's MAC or device address.
+ *
+ * @rc: UWB Radio Controller
+ * @_addr: Where to write the address data [assumed to be either a
+ * 'struct uwb_mac_addr *' or a 'struct uwb_dev_addr *'].
+ * @type: Type of address to get (UWB_ADDR_DEV or UWB_ADDR_MAC).
+ * @returns: 0 if ok (and *_addr set), < 0 errno code on error.
+ *
+ * See comment in uwb_rc_addr_set() about anal retentivity in the
+ * type handling of the address variables.
+ */
+static int uwb_rc_addr_get(struct uwb_rc *rc,
+ void *_addr, enum uwb_addr_type type)
+{
+ int result;
+ u8 bmOperationType = 0x0; /* Get address */
+ struct uwb_rc_evt_dev_addr_mgmt evt;
+ struct uwb_dev_addr *dev_addr = _addr;
+ struct uwb_mac_addr *mac_addr = _addr;
+ u8 *baAddr;
+
+ result = -EINVAL;
+ switch (type) {
+ case UWB_ADDR_DEV:
+ baAddr = dev_addr->data;
+ break;
+ case UWB_ADDR_MAC:
+ bmOperationType |= 0x2;
+ baAddr = mac_addr->data;
+ break;
+ default:
+ return result;
+ }
+ result = uwb_rc_dev_addr_mgmt(rc, bmOperationType, baAddr, &evt);
+ if (result == 0)
+ switch (type) {
+ case UWB_ADDR_DEV:
+ memcpy(&dev_addr->data, evt.baAddr,
+ sizeof(dev_addr->data));
+ break;
+ case UWB_ADDR_MAC:
+ memcpy(&mac_addr->data, evt.baAddr,
+ sizeof(mac_addr->data));
+ break;
+ default: /* shut gcc up */
+ BUG();
+ }
+ return result;
+}
+
+
+/** Get @rc's MAC address to @addr */
+int uwb_rc_mac_addr_get(struct uwb_rc *rc,
+ struct uwb_mac_addr *addr) {
+ return uwb_rc_addr_get(rc, addr, UWB_ADDR_MAC);
+}
+EXPORT_SYMBOL_GPL(uwb_rc_mac_addr_get);
+
+
+/** Get @rc's device address to @addr */
+int uwb_rc_dev_addr_get(struct uwb_rc *rc,
+ struct uwb_dev_addr *addr) {
+ return uwb_rc_addr_get(rc, addr, UWB_ADDR_DEV);
+}
+EXPORT_SYMBOL_GPL(uwb_rc_dev_addr_get);
+
+
+/** Set @rc's address to @addr */
+int uwb_rc_mac_addr_set(struct uwb_rc *rc,
+ const struct uwb_mac_addr *addr)
+{
+ int result = -EINVAL;
+ mutex_lock(&rc->uwb_dev.mutex);
+ result = uwb_rc_addr_set(rc, addr, UWB_ADDR_MAC);
+ mutex_unlock(&rc->uwb_dev.mutex);
+ return result;
+}
+
+
+/** Set @rc's address to @addr */
+int uwb_rc_dev_addr_set(struct uwb_rc *rc,
+ const struct uwb_dev_addr *addr)
+{
+ int result = -EINVAL;
+ mutex_lock(&rc->uwb_dev.mutex);
+ result = uwb_rc_addr_set(rc, addr, UWB_ADDR_DEV);
+ rc->uwb_dev.dev_addr = *addr;
+ mutex_unlock(&rc->uwb_dev.mutex);
+ return result;
+}
+
+/* Returns !0 if given address is already assigned to device. */
+int __uwb_mac_addr_assigned_check(struct device *dev, void *_addr)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ struct uwb_mac_addr *addr = _addr;
+
+ if (!uwb_mac_addr_cmp(addr, &uwb_dev->mac_addr))
+ return !0;
+ return 0;
+}
+
+/* Returns !0 if given address is already assigned to device. */
+int __uwb_dev_addr_assigned_check(struct device *dev, void *_addr)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ struct uwb_dev_addr *addr = _addr;
+ if (!uwb_dev_addr_cmp(addr, &uwb_dev->dev_addr))
+ return !0;
+ return 0;
+}
+
+/**
+ * uwb_dev_addr_assign - assigned a generated DevAddr to a radio controller
+ * @rc: the (local) radio controller device requiring a new DevAddr
+ *
+ * A new DevAddr is required when:
+ * - first setting up a radio controller
+ * - if the hardware reports a DevAddr conflict
+ *
+ * The DevAddr is randomly generated in the generated DevAddr range
+ * [0x100, 0xfeff]. The number of devices in a beacon group is limited
+ * by mMaxBPLength (96) so this address space will never be exhausted.
+ *
+ * [ECMA-368] 17.1.1, 17.16.
+ */
+int uwb_rc_dev_addr_assign(struct uwb_rc *rc)
+{
+ struct uwb_dev_addr new_addr;
+
+ do {
+ get_random_bytes(new_addr.data, sizeof(new_addr.data));
+ } while (new_addr.data[0] == 0x00 || new_addr.data[0] == 0xff
+ || __uwb_dev_addr_assigned(rc, &new_addr));
+
+ return uwb_rc_dev_addr_set(rc, &new_addr);
+}
+
+/**
+ * uwbd_evt_handle_rc_dev_addr_conflict - handle a DEV_ADDR_CONFLICT event
+ * @evt: the DEV_ADDR_CONFLICT notification from the radio controller
+ *
+ * A new (non-conflicting) DevAddr is assigned to the radio controller.
+ *
+ * [ECMA-368] 17.1.1.1.
+ */
+int uwbd_evt_handle_rc_dev_addr_conflict(struct uwb_event *evt)
+{
+ struct uwb_rc *rc = evt->rc;
+
+ return uwb_rc_dev_addr_assign(rc);
+}
+
+/*
+ * Print the 48-bit EUI MAC address of the radio controller when
+ * reading /sys/class/uwb_rc/XX/mac_address
+ */
+static ssize_t uwb_rc_mac_addr_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ struct uwb_rc *rc = uwb_dev->rc;
+ struct uwb_mac_addr addr;
+ ssize_t result;
+
+ mutex_lock(&rc->uwb_dev.mutex);
+ result = uwb_rc_addr_get(rc, &addr, UWB_ADDR_MAC);
+ mutex_unlock(&rc->uwb_dev.mutex);
+ if (result >= 0) {
+ result = uwb_mac_addr_print(buf, UWB_ADDR_STRSIZE, &addr);
+ buf[result++] = '\n';
+ }
+ return result;
+}
+
+/*
+ * Parse a 48 bit address written to /sys/class/uwb_rc/XX/mac_address
+ * and if correct, set it.
+ */
+static ssize_t uwb_rc_mac_addr_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ struct uwb_rc *rc = uwb_dev->rc;
+ struct uwb_mac_addr addr;
+ ssize_t result;
+
+ result = sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx\n",
+ &addr.data[0], &addr.data[1], &addr.data[2],
+ &addr.data[3], &addr.data[4], &addr.data[5]);
+ if (result != 6) {
+ result = -EINVAL;
+ goto out;
+ }
+ if (is_multicast_ether_addr(addr.data)) {
+ dev_err(&rc->uwb_dev.dev, "refusing to set multicast "
+ "MAC address %s\n", buf);
+ result = -EINVAL;
+ goto out;
+ }
+ result = uwb_rc_mac_addr_set(rc, &addr);
+ if (result == 0)
+ rc->uwb_dev.mac_addr = addr;
+out:
+ return result < 0 ? result : size;
+}
+DEVICE_ATTR(mac_address, S_IRUGO | S_IWUSR, uwb_rc_mac_addr_show, uwb_rc_mac_addr_store);
+
+/** Print @addr to @buf, @return bytes written */
+size_t __uwb_addr_print(char *buf, size_t buf_size, const unsigned char *addr,
+ int type)
+{
+ size_t result;
+ if (type)
+ result = scnprintf(buf, buf_size,
+ "%02x:%02x:%02x:%02x:%02x:%02x",
+ addr[0], addr[1], addr[2],
+ addr[3], addr[4], addr[5]);
+ else
+ result = scnprintf(buf, buf_size, "%02x:%02x",
+ addr[1], addr[0]);
+ return result;
+}
+EXPORT_SYMBOL_GPL(__uwb_addr_print);
diff --git a/drivers/uwb/beacon.c b/drivers/uwb/beacon.c
new file mode 100644
index 000000000000..f65a52c0afac
--- /dev/null
+++ b/drivers/uwb/beacon.c
@@ -0,0 +1,644 @@
+/*
+ * Ultra Wide Band
+ * Beacon management
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kdev_t.h>
+#include "uwb-internal.h"
+
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+/** Start Beaconing command structure */
+struct uwb_rc_cmd_start_beacon {
+ struct uwb_rccb rccb;
+ __le16 wBPSTOffset;
+ u8 bChannelNumber;
+} __attribute__((packed));
+
+
+static int uwb_rc_start_beacon(struct uwb_rc *rc, u16 bpst_offset, u8 channel)
+{
+ int result;
+ struct uwb_rc_cmd_start_beacon *cmd;
+ struct uwb_rc_evt_confirm reply;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (cmd == NULL)
+ return -ENOMEM;
+ cmd->rccb.bCommandType = UWB_RC_CET_GENERAL;
+ cmd->rccb.wCommand = cpu_to_le16(UWB_RC_CMD_START_BEACON);
+ cmd->wBPSTOffset = cpu_to_le16(bpst_offset);
+ cmd->bChannelNumber = channel;
+ reply.rceb.bEventType = UWB_RC_CET_GENERAL;
+ reply.rceb.wEvent = UWB_RC_CMD_START_BEACON;
+ result = uwb_rc_cmd(rc, "START-BEACON", &cmd->rccb, sizeof(*cmd),
+ &reply.rceb, sizeof(reply));
+ if (result < 0)
+ goto error_cmd;
+ if (reply.bResultCode != UWB_RC_RES_SUCCESS) {
+ dev_err(&rc->uwb_dev.dev,
+ "START-BEACON: command execution failed: %s (%d)\n",
+ uwb_rc_strerror(reply.bResultCode), reply.bResultCode);
+ result = -EIO;
+ }
+error_cmd:
+ kfree(cmd);
+ return result;
+}
+
+static int uwb_rc_stop_beacon(struct uwb_rc *rc)
+{
+ int result;
+ struct uwb_rccb *cmd;
+ struct uwb_rc_evt_confirm reply;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (cmd == NULL)
+ return -ENOMEM;
+ cmd->bCommandType = UWB_RC_CET_GENERAL;
+ cmd->wCommand = cpu_to_le16(UWB_RC_CMD_STOP_BEACON);
+ reply.rceb.bEventType = UWB_RC_CET_GENERAL;
+ reply.rceb.wEvent = UWB_RC_CMD_STOP_BEACON;
+ result = uwb_rc_cmd(rc, "STOP-BEACON", cmd, sizeof(*cmd),
+ &reply.rceb, sizeof(reply));
+ if (result < 0)
+ goto error_cmd;
+ if (reply.bResultCode != UWB_RC_RES_SUCCESS) {
+ dev_err(&rc->uwb_dev.dev,
+ "STOP-BEACON: command execution failed: %s (%d)\n",
+ uwb_rc_strerror(reply.bResultCode), reply.bResultCode);
+ result = -EIO;
+ }
+error_cmd:
+ kfree(cmd);
+ return result;
+}
+
+/*
+ * Start/stop beacons
+ *
+ * @rc: UWB Radio Controller to operate on
+ * @channel: UWB channel on which to beacon (WUSB[table
+ * 5-12]). If -1, stop beaconing.
+ * @bpst_offset: Beacon Period Start Time offset; FIXME-do zero
+ *
+ * According to WHCI 0.95 [4.13.6] the driver will only receive the RCEB
+ * of a SET IE command after the device sent the first beacon that includes
+ * the IEs specified in the SET IE command. So, after we start beaconing we
+ * check if there is anything in the IE cache and call the SET IE command
+ * if needed.
+ */
+int uwb_rc_beacon(struct uwb_rc *rc, int channel, unsigned bpst_offset)
+{
+ int result;
+ struct device *dev = &rc->uwb_dev.dev;
+
+ mutex_lock(&rc->uwb_dev.mutex);
+ if (channel < 0)
+ channel = -1;
+ if (channel == -1)
+ result = uwb_rc_stop_beacon(rc);
+ else {
+ /* channel >= 0...dah */
+ result = uwb_rc_start_beacon(rc, bpst_offset, channel);
+ if (result < 0)
+ goto out_up;
+ if (le16_to_cpu(rc->ies->wIELength) > 0) {
+ result = uwb_rc_set_ie(rc, rc->ies);
+ if (result < 0) {
+ dev_err(dev, "Cannot set new IE on device: "
+ "%d\n", result);
+ result = uwb_rc_stop_beacon(rc);
+ channel = -1;
+ bpst_offset = 0;
+ } else
+ result = 0;
+ }
+ }
+
+ if (result < 0)
+ goto out_up;
+ rc->beaconing = channel;
+
+ uwb_notify(rc, NULL, uwb_bg_joined(rc) ? UWB_NOTIF_BG_JOIN : UWB_NOTIF_BG_LEAVE);
+
+out_up:
+ mutex_unlock(&rc->uwb_dev.mutex);
+ return result;
+}
+
+/*
+ * Beacon cache
+ *
+ * The purpose of this is to speed up the lookup of becon information
+ * when a new beacon arrives. The UWB Daemon uses it also to keep a
+ * tab of which devices are in radio distance and which not. When a
+ * device's beacon stays present for more than a certain amount of
+ * time, it is considered a new, usable device. When a beacon ceases
+ * to be received for a certain amount of time, it is considered that
+ * the device is gone.
+ *
+ * FIXME: use an allocator for the entries
+ * FIXME: use something faster for search than a list
+ */
+
+struct uwb_beca uwb_beca = {
+ .list = LIST_HEAD_INIT(uwb_beca.list),
+ .mutex = __MUTEX_INITIALIZER(uwb_beca.mutex)
+};
+
+
+void uwb_bce_kfree(struct kref *_bce)
+{
+ struct uwb_beca_e *bce = container_of(_bce, struct uwb_beca_e, refcnt);
+
+ kfree(bce->be);
+ kfree(bce);
+}
+
+
+/* Find a beacon by dev addr in the cache */
+static
+struct uwb_beca_e *__uwb_beca_find_bydev(const struct uwb_dev_addr *dev_addr)
+{
+ struct uwb_beca_e *bce, *next;
+ list_for_each_entry_safe(bce, next, &uwb_beca.list, node) {
+ d_printf(6, NULL, "looking for addr %02x:%02x in %02x:%02x\n",
+ dev_addr->data[0], dev_addr->data[1],
+ bce->dev_addr.data[0], bce->dev_addr.data[1]);
+ if (!memcmp(&bce->dev_addr, dev_addr, sizeof(bce->dev_addr)))
+ goto out;
+ }
+ bce = NULL;
+out:
+ return bce;
+}
+
+/* Find a beacon by dev addr in the cache */
+static
+struct uwb_beca_e *__uwb_beca_find_bymac(const struct uwb_mac_addr *mac_addr)
+{
+ struct uwb_beca_e *bce, *next;
+ list_for_each_entry_safe(bce, next, &uwb_beca.list, node) {
+ if (!memcmp(bce->mac_addr, mac_addr->data,
+ sizeof(bce->mac_addr)))
+ goto out;
+ }
+ bce = NULL;
+out:
+ return bce;
+}
+
+/**
+ * uwb_dev_get_by_devaddr - get a UWB device with a specific DevAddr
+ * @rc: the radio controller that saw the device
+ * @devaddr: DevAddr of the UWB device to find
+ *
+ * There may be more than one matching device (in the case of a
+ * DevAddr conflict), but only the first one is returned.
+ */
+struct uwb_dev *uwb_dev_get_by_devaddr(struct uwb_rc *rc,
+ const struct uwb_dev_addr *devaddr)
+{
+ struct uwb_dev *found = NULL;
+ struct uwb_beca_e *bce;
+
+ mutex_lock(&uwb_beca.mutex);
+ bce = __uwb_beca_find_bydev(devaddr);
+ if (bce)
+ found = uwb_dev_try_get(rc, bce->uwb_dev);
+ mutex_unlock(&uwb_beca.mutex);
+
+ return found;
+}
+
+/**
+ * uwb_dev_get_by_macaddr - get a UWB device with a specific EUI-48
+ * @rc: the radio controller that saw the device
+ * @devaddr: EUI-48 of the UWB device to find
+ */
+struct uwb_dev *uwb_dev_get_by_macaddr(struct uwb_rc *rc,
+ const struct uwb_mac_addr *macaddr)
+{
+ struct uwb_dev *found = NULL;
+ struct uwb_beca_e *bce;
+
+ mutex_lock(&uwb_beca.mutex);
+ bce = __uwb_beca_find_bymac(macaddr);
+ if (bce)
+ found = uwb_dev_try_get(rc, bce->uwb_dev);
+ mutex_unlock(&uwb_beca.mutex);
+
+ return found;
+}
+
+/* Initialize a beacon cache entry */
+static void uwb_beca_e_init(struct uwb_beca_e *bce)
+{
+ mutex_init(&bce->mutex);
+ kref_init(&bce->refcnt);
+ stats_init(&bce->lqe_stats);
+ stats_init(&bce->rssi_stats);
+}
+
+/*
+ * Add a beacon to the cache
+ *
+ * @be: Beacon event information
+ * @bf: Beacon frame (part of b, really)
+ * @ts_jiffies: Timestamp (in jiffies) when the beacon was received
+ */
+struct uwb_beca_e *__uwb_beca_add(struct uwb_rc_evt_beacon *be,
+ struct uwb_beacon_frame *bf,
+ unsigned long ts_jiffies)
+{
+ struct uwb_beca_e *bce;
+
+ bce = kzalloc(sizeof(*bce), GFP_KERNEL);
+ if (bce == NULL)
+ return NULL;
+ uwb_beca_e_init(bce);
+ bce->ts_jiffies = ts_jiffies;
+ bce->uwb_dev = NULL;
+ list_add(&bce->node, &uwb_beca.list);
+ return bce;
+}
+
+/*
+ * Wipe out beacon entries that became stale
+ *
+ * Remove associated devicest too.
+ */
+void uwb_beca_purge(void)
+{
+ struct uwb_beca_e *bce, *next;
+ unsigned long now = jiffies;
+ mutex_lock(&uwb_beca.mutex);
+ list_for_each_entry_safe(bce, next, &uwb_beca.list, node) {
+ if (now - bce->ts_jiffies
+ > msecs_to_jiffies(beacon_timeout_ms)) {
+ uwbd_dev_offair(bce);
+ list_del(&bce->node);
+ uwb_bce_put(bce);
+ }
+ }
+ mutex_unlock(&uwb_beca.mutex);
+}
+
+/* Clean up the whole beacon cache. Called on shutdown */
+void uwb_beca_release(void)
+{
+ struct uwb_beca_e *bce, *next;
+ mutex_lock(&uwb_beca.mutex);
+ list_for_each_entry_safe(bce, next, &uwb_beca.list, node) {
+ list_del(&bce->node);
+ uwb_bce_put(bce);
+ }
+ mutex_unlock(&uwb_beca.mutex);
+}
+
+static void uwb_beacon_print(struct uwb_rc *rc, struct uwb_rc_evt_beacon *be,
+ struct uwb_beacon_frame *bf)
+{
+ char macbuf[UWB_ADDR_STRSIZE];
+ char devbuf[UWB_ADDR_STRSIZE];
+ char dstbuf[UWB_ADDR_STRSIZE];
+
+ uwb_mac_addr_print(macbuf, sizeof(macbuf), &bf->Device_Identifier);
+ uwb_dev_addr_print(devbuf, sizeof(devbuf), &bf->hdr.SrcAddr);
+ uwb_dev_addr_print(dstbuf, sizeof(dstbuf), &bf->hdr.DestAddr);
+ dev_info(&rc->uwb_dev.dev,
+ "BEACON from %s to %s (ch%u offset %u slot %u MAC %s)\n",
+ devbuf, dstbuf, be->bChannelNumber, be->wBPSTOffset,
+ bf->Beacon_Slot_Number, macbuf);
+}
+
+/*
+ * @bce: beacon cache entry, referenced
+ */
+ssize_t uwb_bce_print_IEs(struct uwb_dev *uwb_dev, struct uwb_beca_e *bce,
+ char *buf, size_t size)
+{
+ ssize_t result = 0;
+ struct uwb_rc_evt_beacon *be;
+ struct uwb_beacon_frame *bf;
+ struct uwb_buf_ctx ctx = {
+ .buf = buf,
+ .bytes = 0,
+ .size = size
+ };
+
+ mutex_lock(&bce->mutex);
+ be = bce->be;
+ if (be == NULL)
+ goto out;
+ bf = (void *) be->BeaconInfo;
+ uwb_ie_for_each(uwb_dev, uwb_ie_dump_hex, &ctx,
+ bf->IEData, be->wBeaconInfoLength - sizeof(*bf));
+ result = ctx.bytes;
+out:
+ mutex_unlock(&bce->mutex);
+ return result;
+}
+
+/*
+ * Verify that the beacon event, frame and IEs are ok
+ */
+static int uwb_verify_beacon(struct uwb_rc *rc, struct uwb_event *evt,
+ struct uwb_rc_evt_beacon *be)
+{
+ int result = -EINVAL;
+ struct uwb_beacon_frame *bf;
+ struct device *dev = &rc->uwb_dev.dev;
+
+ /* Is there enough data to decode a beacon frame? */
+ if (evt->notif.size < sizeof(*be) + sizeof(*bf)) {
+ dev_err(dev, "BEACON event: Not enough data to decode "
+ "(%zu vs %zu bytes needed)\n", evt->notif.size,
+ sizeof(*be) + sizeof(*bf));
+ goto error;
+ }
+ /* FIXME: make sure beacon frame IEs are fine and that the whole thing
+ * is consistent */
+ result = 0;
+error:
+ return result;
+}
+
+/*
+ * Handle UWB_RC_EVT_BEACON events
+ *
+ * We check the beacon cache to see how the received beacon fares. If
+ * is there already we refresh the timestamp. If not we create a new
+ * entry.
+ *
+ * According to the WHCI and WUSB specs, only one beacon frame is
+ * allowed per notification block, so we don't bother about scanning
+ * for more.
+ */
+int uwbd_evt_handle_rc_beacon(struct uwb_event *evt)
+{
+ int result = -EINVAL;
+ struct uwb_rc *rc;
+ struct uwb_rc_evt_beacon *be;
+ struct uwb_beacon_frame *bf;
+ struct uwb_beca_e *bce;
+ struct device *dev = &evt->rc->uwb_dev.dev;
+ unsigned long last_ts;
+
+ rc = evt->rc;
+ be = container_of(evt->notif.rceb, struct uwb_rc_evt_beacon, rceb);
+ result = uwb_verify_beacon(rc, evt, be);
+ if (result < 0)
+ return result;
+
+ /* Ignore beacon if it is from an alien. */
+ if (be->bBeaconType == UWB_RC_BEACON_TYPE_OL_ALIEN ||
+ be->bBeaconType == UWB_RC_BEACON_TYPE_NOL_ALIEN) {
+ if (printk_ratelimit())
+ dev_err(dev, "BEACON received from ALIEN. Action? \n");
+ result = -ENOSYS;
+ return 0;
+ }
+ bf = (struct uwb_beacon_frame *) be->BeaconInfo;
+
+ /*
+ * Drop beacons from devices with a NULL EUI-48 -- they cannot
+ * be uniquely identified.
+ *
+ * It's expected that these will all be WUSB devices and they
+ * have a WUSB specific connection method so ignoring them
+ * here shouldn't be a problem.
+ */
+ if (uwb_mac_addr_bcast(&bf->Device_Identifier))
+ return 0;
+
+ mutex_lock(&uwb_beca.mutex);
+ bce = __uwb_beca_find_bymac(&bf->Device_Identifier);
+ if (bce == NULL) {
+ /* Not in there, a new device is pinging */
+ uwb_beacon_print(evt->rc, be, bf);
+ bce = __uwb_beca_add(be, bf, evt->ts_jiffies);
+ if (bce == NULL) {
+ mutex_unlock(&uwb_beca.mutex);
+ return -ENOMEM;
+ }
+ }
+ mutex_unlock(&uwb_beca.mutex);
+
+ mutex_lock(&bce->mutex);
+ /* purge old beacon data */
+ kfree(bce->be);
+
+ last_ts = bce->ts_jiffies;
+
+ /* Update commonly used fields */
+ bce->ts_jiffies = evt->ts_jiffies;
+ bce->be = be;
+ bce->dev_addr = bf->hdr.SrcAddr;
+ bce->mac_addr = &bf->Device_Identifier;
+ be->wBPSTOffset = le16_to_cpu(be->wBPSTOffset);
+ be->wBeaconInfoLength = le16_to_cpu(be->wBeaconInfoLength);
+ stats_add_sample(&bce->lqe_stats, be->bLQI - 7);
+ stats_add_sample(&bce->rssi_stats, be->bRSSI + 18);
+
+ /*
+ * This might be a beacon from a new device.
+ */
+ if (bce->uwb_dev == NULL)
+ uwbd_dev_onair(evt->rc, bce);
+
+ mutex_unlock(&bce->mutex);
+
+ return 1; /* we keep the event data */
+}
+
+/*
+ * Handle UWB_RC_EVT_BEACON_SIZE events
+ *
+ * XXXXX
+ */
+int uwbd_evt_handle_rc_beacon_size(struct uwb_event *evt)
+{
+ int result = -EINVAL;
+ struct device *dev = &evt->rc->uwb_dev.dev;
+ struct uwb_rc_evt_beacon_size *bs;
+
+ /* Is there enough data to decode the event? */
+ if (evt->notif.size < sizeof(*bs)) {
+ dev_err(dev, "BEACON SIZE notification: Not enough data to "
+ "decode (%zu vs %zu bytes needed)\n",
+ evt->notif.size, sizeof(*bs));
+ goto error;
+ }
+ bs = container_of(evt->notif.rceb, struct uwb_rc_evt_beacon_size, rceb);
+ if (0)
+ dev_info(dev, "Beacon size changed to %u bytes "
+ "(FIXME: action?)\n", le16_to_cpu(bs->wNewBeaconSize));
+ else {
+ /* temporary hack until we do something with this message... */
+ static unsigned count;
+ if (++count % 1000 == 0)
+ dev_info(dev, "Beacon size changed %u times "
+ "(FIXME: action?)\n", count);
+ }
+ result = 0;
+error:
+ return result;
+}
+
+/**
+ * uwbd_evt_handle_rc_bp_slot_change - handle a BP_SLOT_CHANGE event
+ * @evt: the BP_SLOT_CHANGE notification from the radio controller
+ *
+ * If the event indicates that no beacon period slots were available
+ * then radio controller has transitioned to a non-beaconing state.
+ * Otherwise, simply save the current beacon slot.
+ */
+int uwbd_evt_handle_rc_bp_slot_change(struct uwb_event *evt)
+{
+ struct uwb_rc *rc = evt->rc;
+ struct device *dev = &rc->uwb_dev.dev;
+ struct uwb_rc_evt_bp_slot_change *bpsc;
+
+ if (evt->notif.size < sizeof(*bpsc)) {
+ dev_err(dev, "BP SLOT CHANGE event: Not enough data\n");
+ return -EINVAL;
+ }
+ bpsc = container_of(evt->notif.rceb, struct uwb_rc_evt_bp_slot_change, rceb);
+
+ mutex_lock(&rc->uwb_dev.mutex);
+ if (uwb_rc_evt_bp_slot_change_no_slot(bpsc)) {
+ dev_info(dev, "stopped beaconing: No free slots in BP\n");
+ rc->beaconing = -1;
+ } else
+ rc->uwb_dev.beacon_slot = uwb_rc_evt_bp_slot_change_slot_num(bpsc);
+ mutex_unlock(&rc->uwb_dev.mutex);
+
+ return 0;
+}
+
+/**
+ * Handle UWB_RC_EVT_BPOIE_CHANGE events
+ *
+ * XXXXX
+ */
+struct uwb_ie_bpo {
+ struct uwb_ie_hdr hdr;
+ u8 bp_length;
+ u8 data[];
+} __attribute__((packed));
+
+int uwbd_evt_handle_rc_bpoie_change(struct uwb_event *evt)
+{
+ int result = -EINVAL;
+ struct device *dev = &evt->rc->uwb_dev.dev;
+ struct uwb_rc_evt_bpoie_change *bpoiec;
+ struct uwb_ie_bpo *bpoie;
+ static unsigned count; /* FIXME: this is a temp hack */
+ size_t iesize;
+
+ /* Is there enough data to decode it? */
+ if (evt->notif.size < sizeof(*bpoiec)) {
+ dev_err(dev, "BPOIEC notification: Not enough data to "
+ "decode (%zu vs %zu bytes needed)\n",
+ evt->notif.size, sizeof(*bpoiec));
+ goto error;
+ }
+ bpoiec = container_of(evt->notif.rceb, struct uwb_rc_evt_bpoie_change, rceb);
+ iesize = le16_to_cpu(bpoiec->wBPOIELength);
+ if (iesize < sizeof(*bpoie)) {
+ dev_err(dev, "BPOIEC notification: Not enough IE data to "
+ "decode (%zu vs %zu bytes needed)\n",
+ iesize, sizeof(*bpoie));
+ goto error;
+ }
+ if (++count % 1000 == 0) /* Lame placeholder */
+ dev_info(dev, "BPOIE: %u changes received\n", count);
+ /*
+ * FIXME: At this point we should go over all the IEs in the
+ * bpoiec->BPOIE array and act on each.
+ */
+ result = 0;
+error:
+ return result;
+}
+
+/**
+ * uwb_bg_joined - is the RC in a beacon group?
+ * @rc: the radio controller
+ *
+ * Returns true if the radio controller is in a beacon group (even if
+ * it's the sole member).
+ */
+int uwb_bg_joined(struct uwb_rc *rc)
+{
+ return rc->beaconing != -1;
+}
+EXPORT_SYMBOL_GPL(uwb_bg_joined);
+
+/*
+ * Print beaconing state.
+ */
+static ssize_t uwb_rc_beacon_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ struct uwb_rc *rc = uwb_dev->rc;
+ ssize_t result;
+
+ mutex_lock(&rc->uwb_dev.mutex);
+ result = sprintf(buf, "%d\n", rc->beaconing);
+ mutex_unlock(&rc->uwb_dev.mutex);
+ return result;
+}
+
+/*
+ * Start beaconing on the specified channel, or stop beaconing.
+ *
+ * The BPST offset of when to start searching for a beacon group to
+ * join may be specified.
+ */
+static ssize_t uwb_rc_beacon_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ struct uwb_rc *rc = uwb_dev->rc;
+ int channel;
+ unsigned bpst_offset = 0;
+ ssize_t result = -EINVAL;
+
+ result = sscanf(buf, "%d %u\n", &channel, &bpst_offset);
+ if (result >= 1)
+ result = uwb_rc_beacon(rc, channel, bpst_offset);
+
+ return result < 0 ? result : size;
+}
+DEVICE_ATTR(beacon, S_IRUGO | S_IWUSR, uwb_rc_beacon_show, uwb_rc_beacon_store);
diff --git a/drivers/uwb/driver.c b/drivers/uwb/driver.c
new file mode 100644
index 000000000000..521cdeb84971
--- /dev/null
+++ b/drivers/uwb/driver.c
@@ -0,0 +1,144 @@
+/*
+ * Ultra Wide Band
+ * Driver initialization, etc
+ *
+ * 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
+ *
+ * Life cycle: FIXME: explain
+ *
+ * UWB radio controller:
+ *
+ * 1. alloc a uwb_rc, zero it
+ * 2. call uwb_rc_init() on it to set it up + ops (won't do any
+ * kind of allocation)
+ * 3. register (now it is owned by the UWB stack--deregister before
+ * freeing/destroying).
+ * 4. It lives on it's own now (UWB stack handles)--when it
+ * disconnects, call unregister()
+ * 5. free it.
+ *
+ * Make sure you have a reference to the uwb_rc before calling
+ * any of the UWB API functions.
+ *
+ * TODO:
+ *
+ * 1. Locking and life cycle management is crappy still. All entry
+ * points to the UWB HCD API assume you have a reference on the
+ * uwb_rc structure and that it won't go away. They mutex lock it
+ * before doing anything.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kdev_t.h>
+#include <linux/random.h>
+#include <linux/uwb/debug.h>
+#include "uwb-internal.h"
+
+
+/* UWB stack attributes (or 'global' constants) */
+
+
+/**
+ * If a beacon dissapears for longer than this, then we consider the
+ * device who was represented by that beacon to be gone.
+ *
+ * ECMA-368[17.2.3, last para] establishes that a device must not
+ * consider a device to be its neighbour if he doesn't receive a beacon
+ * for more than mMaxLostBeacons. mMaxLostBeacons is defined in
+ * ECMA-368[17.16] as 3; because we can get only one beacon per
+ * superframe, that'd be 3 * 65ms = 195 ~ 200 ms. Let's give it time
+ * for jitter and stuff and make it 500 ms.
+ */
+unsigned long beacon_timeout_ms = 500;
+
+static
+ssize_t beacon_timeout_ms_show(struct class *class, char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%lu\n", beacon_timeout_ms);
+}
+
+static
+ssize_t beacon_timeout_ms_store(struct class *class,
+ const char *buf, size_t size)
+{
+ unsigned long bt;
+ ssize_t result;
+ result = sscanf(buf, "%lu", &bt);
+ if (result != 1)
+ return -EINVAL;
+ beacon_timeout_ms = bt;
+ return size;
+}
+
+static struct class_attribute uwb_class_attrs[] = {
+ __ATTR(beacon_timeout_ms, S_IWUSR | S_IRUGO,
+ beacon_timeout_ms_show, beacon_timeout_ms_store),
+ __ATTR_NULL,
+};
+
+/** Device model classes */
+struct class uwb_rc_class = {
+ .name = "uwb_rc",
+ .class_attrs = uwb_class_attrs,
+};
+
+
+static int __init uwb_subsys_init(void)
+{
+ int result = 0;
+
+ result = uwb_est_create();
+ if (result < 0) {
+ printk(KERN_ERR "uwb: Can't initialize EST subsystem\n");
+ goto error_est_init;
+ }
+
+ result = class_register(&uwb_rc_class);
+ if (result < 0)
+ goto error_uwb_rc_class_register;
+ uwbd_start();
+ uwb_dbg_init();
+ return 0;
+
+error_uwb_rc_class_register:
+ uwb_est_destroy();
+error_est_init:
+ return result;
+}
+module_init(uwb_subsys_init);
+
+static void __exit uwb_subsys_exit(void)
+{
+ uwb_dbg_exit();
+ uwbd_stop();
+ class_unregister(&uwb_rc_class);
+ uwb_est_destroy();
+ return;
+}
+module_exit(uwb_subsys_exit);
+
+MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
+MODULE_DESCRIPTION("Ultra Wide Band core");
+MODULE_LICENSE("GPL");
diff --git a/drivers/uwb/drp-avail.c b/drivers/uwb/drp-avail.c
new file mode 100644
index 000000000000..3febd8552808
--- /dev/null
+++ b/drivers/uwb/drp-avail.c
@@ -0,0 +1,288 @@
+/*
+ * Ultra Wide Band
+ * DRP availability management
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Reinette Chatre <reinette.chatre@intel.com>
+ * Copyright (C) 2008 Cambridge Silicon Radio Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Manage DRP Availability (the MAS available for DRP
+ * reservations). Thus:
+ *
+ * - Handle DRP Availability Change notifications
+ *
+ * - Allow the reservation manager to indicate MAS reserved/released
+ * by local (owned by/targeted at the radio controller)
+ * reservations.
+ *
+ * - Based on the two sources above, generate a DRP Availability IE to
+ * be included in the beacon.
+ *
+ * See also the documentation for struct uwb_drp_avail.
+ */
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/bitmap.h>
+#include "uwb-internal.h"
+
+/**
+ * uwb_drp_avail_init - initialize an RC's MAS availability
+ *
+ * All MAS are available initially. The RC will inform use which
+ * slots are used for the BP (it may change in size).
+ */
+void uwb_drp_avail_init(struct uwb_rc *rc)
+{
+ bitmap_fill(rc->drp_avail.global, UWB_NUM_MAS);
+ bitmap_fill(rc->drp_avail.local, UWB_NUM_MAS);
+ bitmap_fill(rc->drp_avail.pending, UWB_NUM_MAS);
+}
+
+/*
+ * Determine MAS available for new local reservations.
+ *
+ * avail = global & local & pending
+ */
+static void uwb_drp_available(struct uwb_rc *rc, struct uwb_mas_bm *avail)
+{
+ bitmap_and(avail->bm, rc->drp_avail.global, rc->drp_avail.local, UWB_NUM_MAS);
+ bitmap_and(avail->bm, avail->bm, rc->drp_avail.pending, UWB_NUM_MAS);
+}
+
+/**
+ * uwb_drp_avail_reserve_pending - reserve MAS for a new reservation
+ * @rc: the radio controller
+ * @mas: the MAS to reserve
+ *
+ * Returns 0 on success, or -EBUSY if the MAS requested aren't available.
+ */
+int uwb_drp_avail_reserve_pending(struct uwb_rc *rc, struct uwb_mas_bm *mas)
+{
+ struct uwb_mas_bm avail;
+
+ uwb_drp_available(rc, &avail);
+ if (!bitmap_subset(mas->bm, avail.bm, UWB_NUM_MAS))
+ return -EBUSY;
+
+ bitmap_andnot(rc->drp_avail.pending, rc->drp_avail.pending, mas->bm, UWB_NUM_MAS);
+ return 0;
+}
+
+/**
+ * uwb_drp_avail_reserve - reserve MAS for an established reservation
+ * @rc: the radio controller
+ * @mas: the MAS to reserve
+ */
+void uwb_drp_avail_reserve(struct uwb_rc *rc, struct uwb_mas_bm *mas)
+{
+ bitmap_or(rc->drp_avail.pending, rc->drp_avail.pending, mas->bm, UWB_NUM_MAS);
+ bitmap_andnot(rc->drp_avail.local, rc->drp_avail.local, mas->bm, UWB_NUM_MAS);
+ rc->drp_avail.ie_valid = false;
+}
+
+/**
+ * uwb_drp_avail_release - release MAS from a pending or established reservation
+ * @rc: the radio controller
+ * @mas: the MAS to release
+ */
+void uwb_drp_avail_release(struct uwb_rc *rc, struct uwb_mas_bm *mas)
+{
+ bitmap_or(rc->drp_avail.local, rc->drp_avail.local, mas->bm, UWB_NUM_MAS);
+ bitmap_or(rc->drp_avail.pending, rc->drp_avail.pending, mas->bm, UWB_NUM_MAS);
+ rc->drp_avail.ie_valid = false;
+}
+
+/**
+ * uwb_drp_avail_ie_update - update the DRP Availability IE
+ * @rc: the radio controller
+ *
+ * avail = global & local
+ */
+void uwb_drp_avail_ie_update(struct uwb_rc *rc)
+{
+ struct uwb_mas_bm avail;
+
+ bitmap_and(avail.bm, rc->drp_avail.global, rc->drp_avail.local, UWB_NUM_MAS);
+
+ rc->drp_avail.ie.hdr.element_id = UWB_IE_DRP_AVAILABILITY;
+ rc->drp_avail.ie.hdr.length = UWB_NUM_MAS / 8;
+ uwb_mas_bm_copy_le(rc->drp_avail.ie.bmp, &avail);
+ rc->drp_avail.ie_valid = true;
+}
+
+/**
+ * Create an unsigned long from a buffer containing a byte stream.
+ *
+ * @array: pointer to buffer
+ * @itr: index of buffer from where we start
+ * @len: the buffer's remaining size may not be exact multiple of
+ * sizeof(unsigned long), @len is the length of buffer that needs
+ * to be converted. This will be sizeof(unsigned long) or smaller
+ * (BUG if not). If it is smaller then we will pad the remaining
+ * space of the result with zeroes.
+ */
+static
+unsigned long get_val(u8 *array, size_t itr, size_t len)
+{
+ unsigned long val = 0;
+ size_t top = itr + len;
+
+ BUG_ON(len > sizeof(val));
+
+ while (itr < top) {
+ val <<= 8;
+ val |= array[top - 1];
+ top--;
+ }
+ val <<= 8 * (sizeof(val) - len); /* padding */
+ return val;
+}
+
+/**
+ * Initialize bitmap from data buffer.
+ *
+ * The bitmap to be converted could come from a IE, for example a
+ * DRP Availability IE.
+ * From ECMA-368 1.0 [16.8.7]: "
+ * octets: 1 1 N * (0 to 32)
+ * Element ID Length (=N) DRP Availability Bitmap
+ *
+ * The DRP Availability Bitmap field is up to 256 bits long, one
+ * bit for each MAS in the superframe, where the least-significant
+ * bit of the field corresponds to the first MAS in the superframe
+ * and successive bits correspond to successive MASs."
+ *
+ * The DRP Availability bitmap is in octets from 0 to 32, so octet
+ * 32 contains bits for MAS 1-8, etc. If the bitmap is smaller than 32
+ * octets, the bits in octets not included at the end of the bitmap are
+ * treated as zero. In this case (when the bitmap is smaller than 32
+ * octets) the MAS represented range from MAS 1 to MAS (size of bitmap)
+ * with the last octet still containing bits for MAS 1-8, etc.
+ *
+ * For example:
+ * F00F0102 03040506 0708090A 0B0C0D0E 0F010203
+ * ^^^^
+ * ||||
+ * ||||
+ * |||\LSB of byte is MAS 9
+ * ||\MSB of byte is MAS 16
+ * |\LSB of first byte is MAS 1
+ * \ MSB of byte is MAS 8
+ *
+ * An example of this encoding can be found in ECMA-368 Annex-D [Table D.11]
+ *
+ * The resulting bitmap will have the following mapping:
+ * bit position 0 == MAS 1
+ * bit position 1 == MAS 2
+ * ...
+ * bit position (UWB_NUM_MAS - 1) == MAS UWB_NUM_MAS
+ *
+ * @bmp_itr: pointer to bitmap (can be declared with DECLARE_BITMAP)
+ * @buffer: pointer to buffer containing bitmap data in big endian
+ * format (MSB first)
+ * @buffer_size:number of bytes with which bitmap should be initialized
+ */
+static
+void buffer_to_bmp(unsigned long *bmp_itr, void *_buffer,
+ size_t buffer_size)
+{
+ u8 *buffer = _buffer;
+ size_t itr, len;
+ unsigned long val;
+
+ itr = 0;
+ while (itr < buffer_size) {
+ len = buffer_size - itr >= sizeof(val) ?
+ sizeof(val) : buffer_size - itr;
+ val = get_val(buffer, itr, len);
+ bmp_itr[itr / sizeof(val)] = val;
+ itr += sizeof(val);
+ }
+}
+
+
+/**
+ * Extract DRP Availability bitmap from the notification.
+ *
+ * The notification that comes in contains a bitmap of (UWB_NUM_MAS / 8) bytes
+ * We convert that to our internal representation.
+ */
+static
+int uwbd_evt_get_drp_avail(struct uwb_event *evt, unsigned long *bmp)
+{
+ struct device *dev = &evt->rc->uwb_dev.dev;
+ struct uwb_rc_evt_drp_avail *drp_evt;
+ int result = -EINVAL;
+
+ /* Is there enough data to decode the event? */
+ if (evt->notif.size < sizeof(*drp_evt)) {
+ dev_err(dev, "DRP Availability Change: Not enough "
+ "data to decode event [%zu bytes, %zu "
+ "needed]\n", evt->notif.size, sizeof(*drp_evt));
+ goto error;
+ }
+ drp_evt = container_of(evt->notif.rceb, struct uwb_rc_evt_drp_avail, rceb);
+ buffer_to_bmp(bmp, drp_evt->bmp, UWB_NUM_MAS/8);
+ result = 0;
+error:
+ return result;
+}
+
+
+/**
+ * Process an incoming DRP Availability notification.
+ *
+ * @evt: Event information (packs the actual event data, which
+ * radio controller it came to, etc).
+ *
+ * @returns: 0 on success (so uwbd() frees the event buffer), < 0
+ * on error.
+ *
+ * According to ECMA-368 1.0 [16.8.7], bits set to ONE indicate that
+ * the MAS slot is available, bits set to ZERO indicate that the slot
+ * is busy.
+ *
+ * So we clear available slots, we set used slots :)
+ *
+ * The notification only marks non-availability based on the BP and
+ * received DRP IEs that are not for this radio controller. A copy of
+ * this bitmap is needed to generate the real availability (which
+ * includes local and pending reservations).
+ *
+ * The DRP Availability IE that this radio controller emits will need
+ * to be updated.
+ */
+int uwbd_evt_handle_rc_drp_avail(struct uwb_event *evt)
+{
+ int result;
+ struct uwb_rc *rc = evt->rc;
+ DECLARE_BITMAP(bmp, UWB_NUM_MAS);
+
+ result = uwbd_evt_get_drp_avail(evt, bmp);
+ if (result < 0)
+ return result;
+
+ mutex_lock(&rc->rsvs_mutex);
+ bitmap_copy(rc->drp_avail.global, bmp, UWB_NUM_MAS);
+ rc->drp_avail.ie_valid = false;
+ mutex_unlock(&rc->rsvs_mutex);
+
+ uwb_rsv_sched_update(rc);
+
+ return 0;
+}
diff --git a/drivers/uwb/drp-ie.c b/drivers/uwb/drp-ie.c
new file mode 100644
index 000000000000..882724c5f126
--- /dev/null
+++ b/drivers/uwb/drp-ie.c
@@ -0,0 +1,232 @@
+/*
+ * UWB DRP IE management.
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Copyright (C) 2008 Cambridge Silicon Radio Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/random.h>
+#include <linux/uwb.h>
+
+#include "uwb-internal.h"
+
+/*
+ * Allocate a DRP IE.
+ *
+ * To save having to free/allocate a DRP IE when its MAS changes,
+ * enough memory is allocated for the maxiumum number of DRP
+ * allocation fields. This gives an overhead per reservation of up to
+ * (UWB_NUM_ZONES - 1) * 4 = 60 octets.
+ */
+static struct uwb_ie_drp *uwb_drp_ie_alloc(void)
+{
+ struct uwb_ie_drp *drp_ie;
+ unsigned tiebreaker;
+
+ drp_ie = kzalloc(sizeof(struct uwb_ie_drp) +
+ UWB_NUM_ZONES * sizeof(struct uwb_drp_alloc),
+ GFP_KERNEL);
+ if (drp_ie) {
+ drp_ie->hdr.element_id = UWB_IE_DRP;
+
+ get_random_bytes(&tiebreaker, sizeof(unsigned));
+ uwb_ie_drp_set_tiebreaker(drp_ie, tiebreaker & 1);
+ }
+ return drp_ie;
+}
+
+
+/*
+ * Fill a DRP IE's allocation fields from a MAS bitmap.
+ */
+static void uwb_drp_ie_from_bm(struct uwb_ie_drp *drp_ie,
+ struct uwb_mas_bm *mas)
+{
+ int z, i, num_fields = 0, next = 0;
+ struct uwb_drp_alloc *zones;
+ __le16 current_bmp;
+ DECLARE_BITMAP(tmp_bmp, UWB_NUM_MAS);
+ DECLARE_BITMAP(tmp_mas_bm, UWB_MAS_PER_ZONE);
+
+ zones = drp_ie->allocs;
+
+ bitmap_copy(tmp_bmp, mas->bm, UWB_NUM_MAS);
+
+ /* Determine unique MAS bitmaps in zones from bitmap. */
+ for (z = 0; z < UWB_NUM_ZONES; z++) {
+ bitmap_copy(tmp_mas_bm, tmp_bmp, UWB_MAS_PER_ZONE);
+ if (bitmap_weight(tmp_mas_bm, UWB_MAS_PER_ZONE) > 0) {
+ bool found = false;
+ current_bmp = (__le16) *tmp_mas_bm;
+ for (i = 0; i < next; i++) {
+ if (current_bmp == zones[i].mas_bm) {
+ zones[i].zone_bm |= 1 << z;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ num_fields++;
+ zones[next].zone_bm = 1 << z;
+ zones[next].mas_bm = current_bmp;
+ next++;
+ }
+ }
+ bitmap_shift_right(tmp_bmp, tmp_bmp, UWB_MAS_PER_ZONE, UWB_NUM_MAS);
+ }
+
+ /* Store in format ready for transmission (le16). */
+ for (i = 0; i < num_fields; i++) {
+ drp_ie->allocs[i].zone_bm = cpu_to_le16(zones[i].zone_bm);
+ drp_ie->allocs[i].mas_bm = cpu_to_le16(zones[i].mas_bm);
+ }
+
+ drp_ie->hdr.length = sizeof(struct uwb_ie_drp) - sizeof(struct uwb_ie_hdr)
+ + num_fields * sizeof(struct uwb_drp_alloc);
+}
+
+/**
+ * uwb_drp_ie_update - update a reservation's DRP IE
+ * @rsv: the reservation
+ */
+int uwb_drp_ie_update(struct uwb_rsv *rsv)
+{
+ struct device *dev = &rsv->rc->uwb_dev.dev;
+ struct uwb_ie_drp *drp_ie;
+ int reason_code, status;
+
+ switch (rsv->state) {
+ case UWB_RSV_STATE_NONE:
+ kfree(rsv->drp_ie);
+ rsv->drp_ie = NULL;
+ return 0;
+ case UWB_RSV_STATE_O_INITIATED:
+ reason_code = UWB_DRP_REASON_ACCEPTED;
+ status = 0;
+ break;
+ case UWB_RSV_STATE_O_PENDING:
+ reason_code = UWB_DRP_REASON_ACCEPTED;
+ status = 0;
+ break;
+ case UWB_RSV_STATE_O_MODIFIED:
+ reason_code = UWB_DRP_REASON_MODIFIED;
+ status = 1;
+ break;
+ case UWB_RSV_STATE_O_ESTABLISHED:
+ reason_code = UWB_DRP_REASON_ACCEPTED;
+ status = 1;
+ break;
+ case UWB_RSV_STATE_T_ACCEPTED:
+ reason_code = UWB_DRP_REASON_ACCEPTED;
+ status = 1;
+ break;
+ case UWB_RSV_STATE_T_DENIED:
+ reason_code = UWB_DRP_REASON_DENIED;
+ status = 0;
+ break;
+ default:
+ dev_dbg(dev, "rsv with unhandled state (%d)\n", rsv->state);
+ return -EINVAL;
+ }
+
+ if (rsv->drp_ie == NULL) {
+ rsv->drp_ie = uwb_drp_ie_alloc();
+ if (rsv->drp_ie == NULL)
+ return -ENOMEM;
+ }
+ drp_ie = rsv->drp_ie;
+
+ uwb_ie_drp_set_owner(drp_ie, uwb_rsv_is_owner(rsv));
+ uwb_ie_drp_set_status(drp_ie, status);
+ uwb_ie_drp_set_reason_code(drp_ie, reason_code);
+ uwb_ie_drp_set_stream_index(drp_ie, rsv->stream);
+ uwb_ie_drp_set_type(drp_ie, rsv->type);
+
+ if (uwb_rsv_is_owner(rsv)) {
+ switch (rsv->target.type) {
+ case UWB_RSV_TARGET_DEV:
+ drp_ie->dev_addr = rsv->target.dev->dev_addr;
+ break;
+ case UWB_RSV_TARGET_DEVADDR:
+ drp_ie->dev_addr = rsv->target.devaddr;
+ break;
+ }
+ } else
+ drp_ie->dev_addr = rsv->owner->dev_addr;
+
+ uwb_drp_ie_from_bm(drp_ie, &rsv->mas);
+
+ rsv->ie_valid = true;
+ return 0;
+}
+
+/*
+ * Set MAS bits from given MAS bitmap in a single zone of large bitmap.
+ *
+ * We are given a zone id and the MAS bitmap of bits that need to be set in
+ * this zone. Note that this zone may already have bits set and this only
+ * adds settings - we cannot simply assign the MAS bitmap contents to the
+ * zone contents. We iterate over the the bits (MAS) in the zone and set the
+ * bits that are set in the given MAS bitmap.
+ */
+static
+void uwb_drp_ie_single_zone_to_bm(struct uwb_mas_bm *bm, u8 zone, u16 mas_bm)
+{
+ int mas;
+ u16 mas_mask;
+
+ for (mas = 0; mas < UWB_MAS_PER_ZONE; mas++) {
+ mas_mask = 1 << mas;
+ if (mas_bm & mas_mask)
+ set_bit(zone * UWB_NUM_ZONES + mas, bm->bm);
+ }
+}
+
+/**
+ * uwb_drp_ie_zones_to_bm - convert DRP allocation fields to a bitmap
+ * @mas: MAS bitmap that will be populated to correspond to the
+ * allocation fields in the DRP IE
+ * @drp_ie: the DRP IE that contains the allocation fields.
+ *
+ * The input format is an array of MAS allocation fields (16 bit Zone
+ * bitmap, 16 bit MAS bitmap) as described in [ECMA-368] section
+ * 16.8.6. The output is a full 256 bit MAS bitmap.
+ *
+ * We go over all the allocation fields, for each allocation field we
+ * know which zones are impacted. We iterate over all the zones
+ * impacted and call a function that will set the correct MAS bits in
+ * each zone.
+ */
+void uwb_drp_ie_to_bm(struct uwb_mas_bm *bm, const struct uwb_ie_drp *drp_ie)
+{
+ int numallocs = (drp_ie->hdr.length - 4) / 4;
+ const struct uwb_drp_alloc *alloc;
+ int cnt;
+ u16 zone_bm, mas_bm;
+ u8 zone;
+ u16 zone_mask;
+
+ for (cnt = 0; cnt < numallocs; cnt++) {
+ alloc = &drp_ie->allocs[cnt];
+ zone_bm = le16_to_cpu(alloc->zone_bm);
+ mas_bm = le16_to_cpu(alloc->mas_bm);
+ for (zone = 0; zone < UWB_NUM_ZONES; zone++) {
+ zone_mask = 1 << zone;
+ if (zone_bm & zone_mask)
+ uwb_drp_ie_single_zone_to_bm(bm, zone, mas_bm);
+ }
+ }
+}
diff --git a/drivers/uwb/drp.c b/drivers/uwb/drp.c
new file mode 100644
index 000000000000..c0b1e5e2bd08
--- /dev/null
+++ b/drivers/uwb/drp.c
@@ -0,0 +1,461 @@
+/*
+ * Ultra Wide Band
+ * Dynamic Reservation Protocol handling
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ * Copyright (C) 2008 Cambridge Silicon Radio Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/delay.h>
+#include "uwb-internal.h"
+
+/**
+ * Construct and send the SET DRP IE
+ *
+ * @rc: UWB Host controller
+ * @returns: >= 0 number of bytes still available in the beacon
+ * < 0 errno code on error.
+ *
+ * See WUSB[8.6.2.7]: The host must set all the DRP IEs that it wants the
+ * device to include in its beacon at the same time. We thus have to
+ * traverse all reservations and include the DRP IEs of all PENDING
+ * and NEGOTIATED reservations in a SET DRP command for transmission.
+ *
+ * A DRP Availability IE is appended.
+ *
+ * rc->uwb_dev.mutex is held
+ *
+ * FIXME We currently ignore the returned value indicating the remaining space
+ * in beacon. This could be used to deny reservation requests earlier if
+ * determined that they would cause the beacon space to be exceeded.
+ */
+static
+int uwb_rc_gen_send_drp_ie(struct uwb_rc *rc)
+{
+ int result;
+ struct device *dev = &rc->uwb_dev.dev;
+ struct uwb_rc_cmd_set_drp_ie *cmd;
+ struct uwb_rc_evt_set_drp_ie reply;
+ struct uwb_rsv *rsv;
+ int num_bytes = 0;
+ u8 *IEDataptr;
+
+ result = -ENOMEM;
+ /* First traverse all reservations to determine memory needed. */
+ list_for_each_entry(rsv, &rc->reservations, rc_node) {
+ if (rsv->drp_ie != NULL)
+ num_bytes += rsv->drp_ie->hdr.length + 2;
+ }
+ num_bytes += sizeof(rc->drp_avail.ie);
+ cmd = kzalloc(sizeof(*cmd) + num_bytes, GFP_KERNEL);
+ if (cmd == NULL)
+ goto error;
+ cmd->rccb.bCommandType = UWB_RC_CET_GENERAL;
+ cmd->rccb.wCommand = cpu_to_le16(UWB_RC_CMD_SET_DRP_IE);
+ cmd->wIELength = num_bytes;
+ IEDataptr = (u8 *)&cmd->IEData[0];
+
+ /* Next traverse all reservations to place IEs in allocated memory. */
+ list_for_each_entry(rsv, &rc->reservations, rc_node) {
+ if (rsv->drp_ie != NULL) {
+ memcpy(IEDataptr, rsv->drp_ie,
+ rsv->drp_ie->hdr.length + 2);
+ IEDataptr += rsv->drp_ie->hdr.length + 2;
+ }
+ }
+ memcpy(IEDataptr, &rc->drp_avail.ie, sizeof(rc->drp_avail.ie));
+
+ reply.rceb.bEventType = UWB_RC_CET_GENERAL;
+ reply.rceb.wEvent = UWB_RC_CMD_SET_DRP_IE;
+ result = uwb_rc_cmd(rc, "SET-DRP-IE", &cmd->rccb,
+ sizeof(*cmd) + num_bytes, &reply.rceb,
+ sizeof(reply));
+ if (result < 0)
+ goto error_cmd;
+ result = le16_to_cpu(reply.wRemainingSpace);
+ if (reply.bResultCode != UWB_RC_RES_SUCCESS) {
+ dev_err(&rc->uwb_dev.dev, "SET-DRP-IE: command execution "
+ "failed: %s (%d). RemainingSpace in beacon "
+ "= %d\n", uwb_rc_strerror(reply.bResultCode),
+ reply.bResultCode, result);
+ result = -EIO;
+ } else {
+ dev_dbg(dev, "SET-DRP-IE sent. RemainingSpace in beacon "
+ "= %d.\n", result);
+ result = 0;
+ }
+error_cmd:
+ kfree(cmd);
+error:
+ return result;
+
+}
+/**
+ * Send all DRP IEs associated with this host
+ *
+ * @returns: >= 0 number of bytes still available in the beacon
+ * < 0 errno code on error.
+ *
+ * As per the protocol we obtain the host controller device lock to access
+ * bandwidth structures.
+ */
+int uwb_rc_send_all_drp_ie(struct uwb_rc *rc)
+{
+ int result;
+
+ mutex_lock(&rc->uwb_dev.mutex);
+ result = uwb_rc_gen_send_drp_ie(rc);
+ mutex_unlock(&rc->uwb_dev.mutex);
+ return result;
+}
+
+void uwb_drp_handle_timeout(struct uwb_rsv *rsv)
+{
+ struct device *dev = &rsv->rc->uwb_dev.dev;
+
+ dev_dbg(dev, "reservation timeout in state %s (%d)\n",
+ uwb_rsv_state_str(rsv->state), rsv->state);
+
+ switch (rsv->state) {
+ case UWB_RSV_STATE_O_INITIATED:
+ if (rsv->is_multicast) {
+ uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED);
+ return;
+ }
+ break;
+ case UWB_RSV_STATE_O_ESTABLISHED:
+ if (rsv->is_multicast)
+ return;
+ break;
+ default:
+ break;
+ }
+ uwb_rsv_remove(rsv);
+}
+
+/*
+ * Based on the DRP IE, transition a target reservation to a new
+ * state.
+ */
+static void uwb_drp_process_target(struct uwb_rc *rc, struct uwb_rsv *rsv,
+ struct uwb_ie_drp *drp_ie)
+{
+ struct device *dev = &rc->uwb_dev.dev;
+ int status;
+ enum uwb_drp_reason reason_code;
+
+ status = uwb_ie_drp_status(drp_ie);
+ reason_code = uwb_ie_drp_reason_code(drp_ie);
+
+ if (status) {
+ switch (reason_code) {
+ case UWB_DRP_REASON_ACCEPTED:
+ uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_ACCEPTED);
+ break;
+ case UWB_DRP_REASON_MODIFIED:
+ dev_err(dev, "FIXME: unhandled reason code (%d/%d)\n",
+ reason_code, status);
+ break;
+ default:
+ dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n",
+ reason_code, status);
+ }
+ } else {
+ switch (reason_code) {
+ case UWB_DRP_REASON_ACCEPTED:
+ /* New reservations are handled in uwb_rsv_find(). */
+ break;
+ case UWB_DRP_REASON_DENIED:
+ uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE);
+ break;
+ case UWB_DRP_REASON_CONFLICT:
+ case UWB_DRP_REASON_MODIFIED:
+ dev_err(dev, "FIXME: unhandled reason code (%d/%d)\n",
+ reason_code, status);
+ break;
+ default:
+ dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n",
+ reason_code, status);
+ }
+ }
+}
+
+/*
+ * Based on the DRP IE, transition an owner reservation to a new
+ * state.
+ */
+static void uwb_drp_process_owner(struct uwb_rc *rc, struct uwb_rsv *rsv,
+ struct uwb_ie_drp *drp_ie)
+{
+ struct device *dev = &rc->uwb_dev.dev;
+ int status;
+ enum uwb_drp_reason reason_code;
+
+ status = uwb_ie_drp_status(drp_ie);
+ reason_code = uwb_ie_drp_reason_code(drp_ie);
+
+ if (status) {
+ switch (reason_code) {
+ case UWB_DRP_REASON_ACCEPTED:
+ uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED);
+ break;
+ case UWB_DRP_REASON_MODIFIED:
+ dev_err(dev, "FIXME: unhandled reason code (%d/%d)\n",
+ reason_code, status);
+ break;
+ default:
+ dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n",
+ reason_code, status);
+ }
+ } else {
+ switch (reason_code) {
+ case UWB_DRP_REASON_PENDING:
+ uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_PENDING);
+ break;
+ case UWB_DRP_REASON_DENIED:
+ uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE);
+ break;
+ case UWB_DRP_REASON_CONFLICT:
+ case UWB_DRP_REASON_MODIFIED:
+ dev_err(dev, "FIXME: unhandled reason code (%d/%d)\n",
+ reason_code, status);
+ break;
+ default:
+ dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n",
+ reason_code, status);
+ }
+ }
+}
+
+/*
+ * Process a received DRP IE, it's either for a reservation owned by
+ * the RC or targeted at it (or it's for a WUSB cluster reservation).
+ */
+static void uwb_drp_process(struct uwb_rc *rc, struct uwb_dev *src,
+ struct uwb_ie_drp *drp_ie)
+{
+ struct uwb_rsv *rsv;
+
+ rsv = uwb_rsv_find(rc, src, drp_ie);
+ if (!rsv) {
+ /*
+ * No reservation? It's either for a recently
+ * terminated reservation; or the DRP IE couldn't be
+ * processed (e.g., an invalid IE or out of memory).
+ */
+ return;
+ }
+
+ /*
+ * Do nothing with DRP IEs for reservations that have been
+ * terminated.
+ */
+ if (rsv->state == UWB_RSV_STATE_NONE) {
+ uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE);
+ return;
+ }
+
+ if (uwb_ie_drp_owner(drp_ie))
+ uwb_drp_process_target(rc, rsv, drp_ie);
+ else
+ uwb_drp_process_owner(rc, rsv, drp_ie);
+}
+
+
+/*
+ * Process all the DRP IEs (both DRP IEs and the DRP Availability IE)
+ * from a device.
+ */
+static
+void uwb_drp_process_all(struct uwb_rc *rc, struct uwb_rc_evt_drp *drp_evt,
+ size_t ielen, struct uwb_dev *src_dev)
+{
+ struct device *dev = &rc->uwb_dev.dev;
+ struct uwb_ie_hdr *ie_hdr;
+ void *ptr;
+
+ ptr = drp_evt->ie_data;
+ for (;;) {
+ ie_hdr = uwb_ie_next(&ptr, &ielen);
+ if (!ie_hdr)
+ break;
+
+ switch (ie_hdr->element_id) {
+ case UWB_IE_DRP_AVAILABILITY:
+ /* FIXME: does something need to be done with this? */
+ break;
+ case UWB_IE_DRP:
+ uwb_drp_process(rc, src_dev, (struct uwb_ie_drp *)ie_hdr);
+ break;
+ default:
+ dev_warn(dev, "unexpected IE in DRP notification\n");
+ break;
+ }
+ }
+
+ if (ielen > 0)
+ dev_warn(dev, "%d octets remaining in DRP notification\n",
+ (int)ielen);
+}
+
+
+/*
+ * Go through all the DRP IEs and find the ones that conflict with our
+ * reservations.
+ *
+ * FIXME: must resolve the conflict according the the rules in
+ * [ECMA-368].
+ */
+static
+void uwb_drp_process_conflict_all(struct uwb_rc *rc, struct uwb_rc_evt_drp *drp_evt,
+ size_t ielen, struct uwb_dev *src_dev)
+{
+ struct device *dev = &rc->uwb_dev.dev;
+ struct uwb_ie_hdr *ie_hdr;
+ struct uwb_ie_drp *drp_ie;
+ void *ptr;
+
+ ptr = drp_evt->ie_data;
+ for (;;) {
+ ie_hdr = uwb_ie_next(&ptr, &ielen);
+ if (!ie_hdr)
+ break;
+
+ drp_ie = container_of(ie_hdr, struct uwb_ie_drp, hdr);
+
+ /* FIXME: check if this DRP IE conflicts. */
+ }
+
+ if (ielen > 0)
+ dev_warn(dev, "%d octets remaining in DRP notification\n",
+ (int)ielen);
+}
+
+
+/*
+ * Terminate all reservations owned by, or targeted at, 'uwb_dev'.
+ */
+static void uwb_drp_terminate_all(struct uwb_rc *rc, struct uwb_dev *uwb_dev)
+{
+ struct uwb_rsv *rsv;
+
+ list_for_each_entry(rsv, &rc->reservations, rc_node) {
+ if (rsv->owner == uwb_dev
+ || (rsv->target.type == UWB_RSV_TARGET_DEV && rsv->target.dev == uwb_dev))
+ uwb_rsv_remove(rsv);
+ }
+}
+
+
+/**
+ * uwbd_evt_handle_rc_drp - handle a DRP_IE event
+ * @evt: the DRP_IE event from the radio controller
+ *
+ * This processes DRP notifications from the radio controller, either
+ * initiating a new reservation or transitioning an existing
+ * reservation into a different state.
+ *
+ * DRP notifications can occur for three different reasons:
+ *
+ * - UWB_DRP_NOTIF_DRP_IE_RECVD: one or more DRP IEs with the RC as
+ * the target or source have been recieved.
+ *
+ * These DRP IEs could be new or for an existing reservation.
+ *
+ * If the DRP IE for an existing reservation ceases to be to
+ * recieved for at least mMaxLostBeacons, the reservation should be
+ * considered to be terminated. Note that the TERMINATE reason (see
+ * below) may not always be signalled (e.g., the remote device has
+ * two or more reservations established with the RC).
+ *
+ * - UWB_DRP_NOTIF_CONFLICT: DRP IEs from any device in the beacon
+ * group conflict with the RC's reservations.
+ *
+ * - UWB_DRP_NOTIF_TERMINATE: DRP IEs are no longer being received
+ * from a device (i.e., it's terminated all reservations).
+ *
+ * Only the software state of the reservations is changed; the setting
+ * of the radio controller's DRP IEs is done after all the events in
+ * an event buffer are processed. This saves waiting multiple times
+ * for the SET_DRP_IE command to complete.
+ */
+int uwbd_evt_handle_rc_drp(struct uwb_event *evt)
+{
+ struct device *dev = &evt->rc->uwb_dev.dev;
+ struct uwb_rc *rc = evt->rc;
+ struct uwb_rc_evt_drp *drp_evt;
+ size_t ielength, bytes_left;
+ struct uwb_dev_addr src_addr;
+ struct uwb_dev *src_dev;
+ int reason;
+
+ /* Is there enough data to decode the event (and any IEs in
+ its payload)? */
+ if (evt->notif.size < sizeof(*drp_evt)) {
+ dev_err(dev, "DRP event: Not enough data to decode event "
+ "[%zu bytes left, %zu needed]\n",
+ evt->notif.size, sizeof(*drp_evt));
+ return 0;
+ }
+ bytes_left = evt->notif.size - sizeof(*drp_evt);
+ drp_evt = container_of(evt->notif.rceb, struct uwb_rc_evt_drp, rceb);
+ ielength = le16_to_cpu(drp_evt->ie_length);
+ if (bytes_left != ielength) {
+ dev_err(dev, "DRP event: Not enough data in payload [%zu"
+ "bytes left, %zu declared in the event]\n",
+ bytes_left, ielength);
+ return 0;
+ }
+
+ memcpy(src_addr.data, &drp_evt->src_addr, sizeof(src_addr));
+ src_dev = uwb_dev_get_by_devaddr(rc, &src_addr);
+ if (!src_dev) {
+ /*
+ * A DRP notification from an unrecognized device.
+ *
+ * This is probably from a WUSB device that doesn't
+ * have an EUI-48 and therefore doesn't show up in the
+ * UWB device database. It's safe to simply ignore
+ * these.
+ */
+ return 0;
+ }
+
+ mutex_lock(&rc->rsvs_mutex);
+
+ reason = uwb_rc_evt_drp_reason(drp_evt);
+
+ switch (reason) {
+ case UWB_DRP_NOTIF_DRP_IE_RCVD:
+ uwb_drp_process_all(rc, drp_evt, ielength, src_dev);
+ break;
+ case UWB_DRP_NOTIF_CONFLICT:
+ uwb_drp_process_conflict_all(rc, drp_evt, ielength, src_dev);
+ break;
+ case UWB_DRP_NOTIF_TERMINATE:
+ uwb_drp_terminate_all(rc, src_dev);
+ break;
+ default:
+ dev_warn(dev, "ignored DRP event with reason code: %d\n", reason);
+ break;
+ }
+
+ mutex_unlock(&rc->rsvs_mutex);
+
+ uwb_dev_put(src_dev);
+ return 0;
+}
diff --git a/drivers/uwb/est.c b/drivers/uwb/est.c
new file mode 100644
index 000000000000..3791cd95b63d
--- /dev/null
+++ b/drivers/uwb/est.c
@@ -0,0 +1,485 @@
+/*
+ * Ultra Wide Band Radio Control
+ * Event Size Tables management
+ *
+ * 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
+ *
+ * Infrastructure, code and data tables for guessing the size of
+ * events received on the notification endpoints of UWB radio
+ * controllers.
+ *
+ * You define a table of events and for each, its size and how to get
+ * the extra size.
+ *
+ * ENTRY POINTS:
+ *
+ * uwb_est_{init/destroy}(): To initialize/release the EST subsystem.
+ *
+ * uwb_est_[u]register(): To un/register event size tables
+ * uwb_est_grow()
+ *
+ * uwb_est_find_size(): Get the size of an event
+ * uwb_est_get_size()
+ */
+#include <linux/spinlock.h>
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+#include "uwb-internal.h"
+
+
+struct uwb_est {
+ u16 type_event_high;
+ u16 vendor, product;
+ u8 entries;
+ const struct uwb_est_entry *entry;
+};
+
+
+static struct uwb_est *uwb_est;
+static u8 uwb_est_size;
+static u8 uwb_est_used;
+static DEFINE_RWLOCK(uwb_est_lock);
+
+/**
+ * WUSB Standard Event Size Table, HWA-RC interface
+ *
+ * Sizes for events and notifications type 0 (general), high nibble 0.
+ */
+static
+struct uwb_est_entry uwb_est_00_00xx[] = {
+ [UWB_RC_EVT_IE_RCV] = {
+ .size = sizeof(struct uwb_rc_evt_ie_rcv),
+ .offset = 1 + offsetof(struct uwb_rc_evt_ie_rcv, wIELength),
+ },
+ [UWB_RC_EVT_BEACON] = {
+ .size = sizeof(struct uwb_rc_evt_beacon),
+ .offset = 1 + offsetof(struct uwb_rc_evt_beacon, wBeaconInfoLength),
+ },
+ [UWB_RC_EVT_BEACON_SIZE] = {
+ .size = sizeof(struct uwb_rc_evt_beacon_size),
+ },
+ [UWB_RC_EVT_BPOIE_CHANGE] = {
+ .size = sizeof(struct uwb_rc_evt_bpoie_change),
+ .offset = 1 + offsetof(struct uwb_rc_evt_bpoie_change,
+ wBPOIELength),
+ },
+ [UWB_RC_EVT_BP_SLOT_CHANGE] = {
+ .size = sizeof(struct uwb_rc_evt_bp_slot_change),
+ },
+ [UWB_RC_EVT_BP_SWITCH_IE_RCV] = {
+ .size = sizeof(struct uwb_rc_evt_bp_switch_ie_rcv),
+ .offset = 1 + offsetof(struct uwb_rc_evt_bp_switch_ie_rcv, wIELength),
+ },
+ [UWB_RC_EVT_DEV_ADDR_CONFLICT] = {
+ .size = sizeof(struct uwb_rc_evt_dev_addr_conflict),
+ },
+ [UWB_RC_EVT_DRP_AVAIL] = {
+ .size = sizeof(struct uwb_rc_evt_drp_avail)
+ },
+ [UWB_RC_EVT_DRP] = {
+ .size = sizeof(struct uwb_rc_evt_drp),
+ .offset = 1 + offsetof(struct uwb_rc_evt_drp, ie_length),
+ },
+ [UWB_RC_EVT_BP_SWITCH_STATUS] = {
+ .size = sizeof(struct uwb_rc_evt_bp_switch_status),
+ },
+ [UWB_RC_EVT_CMD_FRAME_RCV] = {
+ .size = sizeof(struct uwb_rc_evt_cmd_frame_rcv),
+ .offset = 1 + offsetof(struct uwb_rc_evt_cmd_frame_rcv, dataLength),
+ },
+ [UWB_RC_EVT_CHANNEL_CHANGE_IE_RCV] = {
+ .size = sizeof(struct uwb_rc_evt_channel_change_ie_rcv),
+ .offset = 1 + offsetof(struct uwb_rc_evt_channel_change_ie_rcv, wIELength),
+ },
+ [UWB_RC_CMD_CHANNEL_CHANGE] = {
+ .size = sizeof(struct uwb_rc_evt_confirm),
+ },
+ [UWB_RC_CMD_DEV_ADDR_MGMT] = {
+ .size = sizeof(struct uwb_rc_evt_dev_addr_mgmt) },
+ [UWB_RC_CMD_GET_IE] = {
+ .size = sizeof(struct uwb_rc_evt_get_ie),
+ .offset = 1 + offsetof(struct uwb_rc_evt_get_ie, wIELength),
+ },
+ [UWB_RC_CMD_RESET] = {
+ .size = sizeof(struct uwb_rc_evt_confirm),
+ },
+ [UWB_RC_CMD_SCAN] = {
+ .size = sizeof(struct uwb_rc_evt_confirm),
+ },
+ [UWB_RC_CMD_SET_BEACON_FILTER] = {
+ .size = sizeof(struct uwb_rc_evt_confirm),
+ },
+ [UWB_RC_CMD_SET_DRP_IE] = {
+ .size = sizeof(struct uwb_rc_evt_set_drp_ie),
+ },
+ [UWB_RC_CMD_SET_IE] = {
+ .size = sizeof(struct uwb_rc_evt_set_ie),
+ },
+ [UWB_RC_CMD_SET_NOTIFICATION_FILTER] = {
+ .size = sizeof(struct uwb_rc_evt_confirm),
+ },
+ [UWB_RC_CMD_SET_TX_POWER] = {
+ .size = sizeof(struct uwb_rc_evt_confirm),
+ },
+ [UWB_RC_CMD_SLEEP] = {
+ .size = sizeof(struct uwb_rc_evt_confirm),
+ },
+ [UWB_RC_CMD_START_BEACON] = {
+ .size = sizeof(struct uwb_rc_evt_confirm),
+ },
+ [UWB_RC_CMD_STOP_BEACON] = {
+ .size = sizeof(struct uwb_rc_evt_confirm),
+ },
+ [UWB_RC_CMD_BP_MERGE] = {
+ .size = sizeof(struct uwb_rc_evt_confirm),
+ },
+ [UWB_RC_CMD_SEND_COMMAND_FRAME] = {
+ .size = sizeof(struct uwb_rc_evt_confirm),
+ },
+ [UWB_RC_CMD_SET_ASIE_NOTIF] = {
+ .size = sizeof(struct uwb_rc_evt_confirm),
+ },
+};
+
+static
+struct uwb_est_entry uwb_est_01_00xx[] = {
+ [UWB_RC_DAA_ENERGY_DETECTED] = {
+ .size = sizeof(struct uwb_rc_evt_daa_energy_detected),
+ },
+ [UWB_RC_SET_DAA_ENERGY_MASK] = {
+ .size = sizeof(struct uwb_rc_evt_set_daa_energy_mask),
+ },
+ [UWB_RC_SET_NOTIFICATION_FILTER_EX] = {
+ .size = sizeof(struct uwb_rc_evt_set_notification_filter_ex),
+ },
+};
+
+/**
+ * Initialize the EST subsystem
+ *
+ * Register the standard tables also.
+ *
+ * FIXME: tag init
+ */
+int uwb_est_create(void)
+{
+ int result;
+
+ uwb_est_size = 2;
+ uwb_est_used = 0;
+ uwb_est = kzalloc(uwb_est_size * sizeof(uwb_est[0]), GFP_KERNEL);
+ if (uwb_est == NULL)
+ return -ENOMEM;
+
+ result = uwb_est_register(UWB_RC_CET_GENERAL, 0, 0xffff, 0xffff,
+ uwb_est_00_00xx, ARRAY_SIZE(uwb_est_00_00xx));
+ if (result < 0)
+ goto out;
+ result = uwb_est_register(UWB_RC_CET_EX_TYPE_1, 0, 0xffff, 0xffff,
+ uwb_est_01_00xx, ARRAY_SIZE(uwb_est_01_00xx));
+out:
+ return result;
+}
+
+
+/** Clean it up */
+void uwb_est_destroy(void)
+{
+ kfree(uwb_est);
+ uwb_est = NULL;
+ uwb_est_size = uwb_est_used = 0;
+}
+
+
+/**
+ * Double the capacity of the EST table
+ *
+ * @returns 0 if ok, < 0 errno no error.
+ */
+static
+int uwb_est_grow(void)
+{
+ size_t actual_size = uwb_est_size * sizeof(uwb_est[0]);
+ void *new = kmalloc(2 * actual_size, GFP_ATOMIC);
+ if (new == NULL)
+ return -ENOMEM;
+ memcpy(new, uwb_est, actual_size);
+ memset(new + actual_size, 0, actual_size);
+ kfree(uwb_est);
+ uwb_est = new;
+ uwb_est_size *= 2;
+ return 0;
+}
+
+
+/**
+ * Register an event size table
+ *
+ * Makes room for it if the table is full, and then inserts it in the
+ * right position (entries are sorted by type, event_high, vendor and
+ * then product).
+ *
+ * @vendor: vendor code for matching against the device (0x0000 and
+ * 0xffff mean any); use 0x0000 to force all to match without
+ * checking possible vendor specific ones, 0xfffff to match
+ * after checking vendor specific ones.
+ *
+ * @product: product code from that vendor; same matching rules, use
+ * 0x0000 for not allowing vendor specific matches, 0xffff
+ * for allowing.
+ *
+ * This arragement just makes the tables sort differenty. Because the
+ * table is sorted by growing type-event_high-vendor-product, a zero
+ * vendor will match before than a 0x456a vendor, that will match
+ * before a 0xfffff vendor.
+ *
+ * @returns 0 if ok, < 0 errno on error (-ENOENT if not found).
+ */
+/* FIXME: add bus type to vendor/product code */
+int uwb_est_register(u8 type, u8 event_high, u16 vendor, u16 product,
+ const struct uwb_est_entry *entry, size_t entries)
+{
+ unsigned long flags;
+ unsigned itr;
+ u16 type_event_high;
+ int result = 0;
+
+ write_lock_irqsave(&uwb_est_lock, flags);
+ if (uwb_est_used == uwb_est_size) {
+ result = uwb_est_grow();
+ if (result < 0)
+ goto out;
+ }
+ /* Find the right spot to insert it in */
+ type_event_high = type << 8 | event_high;
+ for (itr = 0; itr < uwb_est_used; itr++)
+ if (uwb_est[itr].type_event_high < type
+ && uwb_est[itr].vendor < vendor
+ && uwb_est[itr].product < product)
+ break;
+
+ /* Shift others to make room for the new one? */
+ if (itr < uwb_est_used)
+ memmove(&uwb_est[itr+1], &uwb_est[itr], uwb_est_used - itr);
+ uwb_est[itr].type_event_high = type << 8 | event_high;
+ uwb_est[itr].vendor = vendor;
+ uwb_est[itr].product = product;
+ uwb_est[itr].entry = entry;
+ uwb_est[itr].entries = entries;
+ uwb_est_used++;
+out:
+ write_unlock_irqrestore(&uwb_est_lock, flags);
+ return result;
+}
+EXPORT_SYMBOL_GPL(uwb_est_register);
+
+
+/**
+ * Unregister an event size table
+ *
+ * This just removes the specified entry and moves the ones after it
+ * to fill in the gap. This is needed to keep the list sorted; no
+ * reallocation is done to reduce the size of the table.
+ *
+ * We unregister by all the data we used to register instead of by
+ * pointer to the @entry array because we might have used the same
+ * table for a bunch of IDs (for example).
+ *
+ * @returns 0 if ok, < 0 errno on error (-ENOENT if not found).
+ */
+int uwb_est_unregister(u8 type, u8 event_high, u16 vendor, u16 product,
+ const struct uwb_est_entry *entry, size_t entries)
+{
+ unsigned long flags;
+ unsigned itr;
+ struct uwb_est est_cmp = {
+ .type_event_high = type << 8 | event_high,
+ .vendor = vendor,
+ .product = product,
+ .entry = entry,
+ .entries = entries
+ };
+ write_lock_irqsave(&uwb_est_lock, flags);
+ for (itr = 0; itr < uwb_est_used; itr++)
+ if (!memcmp(&uwb_est[itr], &est_cmp, sizeof(est_cmp)))
+ goto found;
+ write_unlock_irqrestore(&uwb_est_lock, flags);
+ return -ENOENT;
+
+found:
+ if (itr < uwb_est_used - 1) /* Not last one? move ones above */
+ memmove(&uwb_est[itr], &uwb_est[itr+1], uwb_est_used - itr - 1);
+ uwb_est_used--;
+ write_unlock_irqrestore(&uwb_est_lock, flags);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(uwb_est_unregister);
+
+
+/**
+ * Get the size of an event from a table
+ *
+ * @rceb: pointer to the buffer with the event
+ * @rceb_size: size of the area pointed to by @rceb in bytes.
+ * @returns: > 0 Size of the event
+ * -ENOSPC An area big enough was not provided to look
+ * ahead into the event's guts and guess the size.
+ * -EINVAL Unknown event code (wEvent).
+ *
+ * This will look at the received RCEB and guess what is the total
+ * size. For variable sized events, it will look further ahead into
+ * their length field to see how much data should be read.
+ *
+ * Note this size is *not* final--the neh (Notification/Event Handle)
+ * might specificy an extra size to add.
+ */
+static
+ssize_t uwb_est_get_size(struct uwb_rc *uwb_rc, struct uwb_est *est,
+ u8 event_low, const struct uwb_rceb *rceb,
+ size_t rceb_size)
+{
+ unsigned offset;
+ ssize_t size;
+ struct device *dev = &uwb_rc->uwb_dev.dev;
+ const struct uwb_est_entry *entry;
+
+ size = -ENOENT;
+ if (event_low >= est->entries) { /* in range? */
+ if (printk_ratelimit())
+ dev_err(dev, "EST %p 0x%04x/%04x/%04x[%u]: "
+ "event %u out of range\n",
+ est, est->type_event_high, est->vendor,
+ est->product, est->entries,
+ event_low);
+ goto out;
+ }
+ size = -ENOENT;
+ entry = &est->entry[event_low];
+ if (entry->size == 0 && entry->offset == 0) { /* unknown? */
+ if (printk_ratelimit())
+ dev_err(dev, "EST %p 0x%04x/%04x/%04x[%u]: "
+ "event %u unknown\n",
+ est, est->type_event_high, est->vendor,
+ est->product, est->entries, event_low);
+ goto out;
+ }
+ offset = entry->offset; /* extra fries with that? */
+ if (offset == 0)
+ size = entry->size;
+ else {
+ /* Ops, got an extra size field at 'offset'--read it */
+ const void *ptr = rceb;
+ size_t type_size = 0;
+ offset--;
+ size = -ENOSPC; /* enough data for more? */
+ switch (entry->type) {
+ case UWB_EST_16: type_size = sizeof(__le16); break;
+ case UWB_EST_8: type_size = sizeof(u8); break;
+ default: BUG();
+ }
+ if (offset + type_size > rceb_size) {
+ if (printk_ratelimit())
+ dev_err(dev, "EST %p 0x%04x/%04x/%04x[%u]: "
+ "not enough data to read extra size\n",
+ est, est->type_event_high, est->vendor,
+ est->product, est->entries);
+ goto out;
+ }
+ size = entry->size;
+ ptr += offset;
+ switch (entry->type) {
+ case UWB_EST_16: size += le16_to_cpu(*(__le16 *)ptr); break;
+ case UWB_EST_8: size += *(u8 *)ptr; break;
+ default: BUG();
+ }
+ }
+out:
+ return size;
+}
+
+
+/**
+ * Guesses the size of a WA event
+ *
+ * @rceb: pointer to the buffer with the event
+ * @rceb_size: size of the area pointed to by @rceb in bytes.
+ * @returns: > 0 Size of the event
+ * -ENOSPC An area big enough was not provided to look
+ * ahead into the event's guts and guess the size.
+ * -EINVAL Unknown event code (wEvent).
+ *
+ * This will look at the received RCEB and guess what is the total
+ * size by checking all the tables registered with
+ * uwb_est_register(). For variable sized events, it will look further
+ * ahead into their length field to see how much data should be read.
+ *
+ * Note this size is *not* final--the neh (Notification/Event Handle)
+ * might specificy an extra size to add or replace.
+ */
+ssize_t uwb_est_find_size(struct uwb_rc *rc, const struct uwb_rceb *rceb,
+ size_t rceb_size)
+{
+ /* FIXME: add vendor/product data */
+ ssize_t size;
+ struct device *dev = &rc->uwb_dev.dev;
+ unsigned long flags;
+ unsigned itr;
+ u16 type_event_high, event;
+ u8 *ptr = (u8 *) rceb;
+
+ read_lock_irqsave(&uwb_est_lock, flags);
+ d_printf(2, dev, "Size query for event 0x%02x/%04x/%02x,"
+ " buffer size %ld\n",
+ (unsigned) rceb->bEventType,
+ (unsigned) le16_to_cpu(rceb->wEvent),
+ (unsigned) rceb->bEventContext,
+ (long) rceb_size);
+ size = -ENOSPC;
+ if (rceb_size < sizeof(*rceb))
+ goto out;
+ event = le16_to_cpu(rceb->wEvent);
+ type_event_high = rceb->bEventType << 8 | (event & 0xff00) >> 8;
+ for (itr = 0; itr < uwb_est_used; itr++) {
+ d_printf(3, dev, "Checking EST 0x%04x/%04x/%04x\n",
+ uwb_est[itr].type_event_high, uwb_est[itr].vendor,
+ uwb_est[itr].product);
+ if (uwb_est[itr].type_event_high != type_event_high)
+ continue;
+ size = uwb_est_get_size(rc, &uwb_est[itr],
+ event & 0x00ff, rceb, rceb_size);
+ /* try more tables that might handle the same type */
+ if (size != -ENOENT)
+ goto out;
+ }
+ /* FIXME: downgrade to _dbg() */
+ if (printk_ratelimit())
+ dev_err(dev, "event 0x%02x/%04x/%02x: no handlers available; "
+ "RCEB %02x %02x %02x %02x\n",
+ (unsigned) rceb->bEventType,
+ (unsigned) le16_to_cpu(rceb->wEvent),
+ (unsigned) rceb->bEventContext,
+ ptr[0], ptr[1], ptr[2], ptr[3]);
+ size = -ENOENT;
+out:
+ read_unlock_irqrestore(&uwb_est_lock, flags);
+ return size;
+}
+EXPORT_SYMBOL_GPL(uwb_est_find_size);
diff --git a/drivers/uwb/hwa-rc.c b/drivers/uwb/hwa-rc.c
new file mode 100644
index 000000000000..3d26fa0f8ae1
--- /dev/null
+++ b/drivers/uwb/hwa-rc.c
@@ -0,0 +1,926 @@
+/*
+ * WUSB Host Wire Adapter: Radio Control Interface (WUSB[8.6])
+ * Radio Control command/event transport
+ *
+ * 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.
+ *
+ *
+ * Initialize the Radio Control interface Driver.
+ *
+ * For each device probed, creates an 'struct hwarc' which contains
+ * just the representation of the UWB Radio Controller, and the logic
+ * for reading notifications and passing them to the UWB Core.
+ *
+ * So we initialize all of those, register the UWB Radio Controller
+ * and setup the notification/event handle to pipe the notifications
+ * to the UWB management Daemon.
+ *
+ * Command and event filtering.
+ *
+ * This is the driver for the Radio Control Interface described in WUSB
+ * 1.0. The core UWB module assumes that all drivers are compliant to the
+ * WHCI 0.95 specification. We thus create a filter that parses all
+ * incoming messages from the (WUSB 1.0) device and manipulate them to
+ * conform to the WHCI 0.95 specification. Similarly, outgoing messages
+ * are parsed and manipulated to conform to the WUSB 1.0 compliant messages
+ * that the device expects. Only a few messages are affected:
+ * Affected events:
+ * UWB_RC_EVT_BEACON
+ * UWB_RC_EVT_BP_SLOT_CHANGE
+ * UWB_RC_EVT_DRP_AVAIL
+ * UWB_RC_EVT_DRP
+ * Affected commands:
+ * UWB_RC_CMD_SCAN
+ * UWB_RC_CMD_SET_DRP_IE
+ *
+ *
+ *
+ */
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/wusb.h>
+#include <linux/usb/wusb-wa.h>
+#include <linux/uwb.h>
+#include "uwb-internal.h"
+#define D_LOCAL 1
+#include <linux/uwb/debug.h>
+
+/* The device uses commands and events from the WHCI specification, although
+ * reporting itself as WUSB compliant. */
+#define WUSB_QUIRK_WHCI_CMD_EVT 0x01
+
+/**
+ * Descriptor for an instance of the UWB Radio Control Driver that
+ * attaches to the RCI interface of the Host Wired Adapter.
+ *
+ * Unless there is a lock specific to the 'data members', all access
+ * is protected by uwb_rc->mutex.
+ *
+ * The NEEP (Notification/Event EndPoint) URB (@neep_urb) writes to
+ * @rd_buffer. Note there is no locking because it is perfectly (heh!)
+ * serialized--probe() submits an URB, callback is called, processes
+ * the data (synchronously), submits another URB, and so on. There is
+ * no concurrent access to the buffer.
+ */
+struct hwarc {
+ struct usb_device *usb_dev;
+ struct usb_interface *usb_iface;
+ struct uwb_rc *uwb_rc; /* UWB host controller */
+ struct urb *neep_urb; /* Notification endpoint handling */
+ struct edc neep_edc;
+ void *rd_buffer; /* NEEP read buffer */
+};
+
+
+/* Beacon received notification (WUSB 1.0 [8.6.3.2]) */
+struct uwb_rc_evt_beacon_WUSB_0100 {
+ struct uwb_rceb rceb;
+ u8 bChannelNumber;
+ __le16 wBPSTOffset;
+ u8 bLQI;
+ u8 bRSSI;
+ __le16 wBeaconInfoLength;
+ u8 BeaconInfo[];
+} __attribute__((packed));
+
+/**
+ * Filter WUSB 1.0 BEACON RCV notification to be WHCI 0.95
+ *
+ * @header: the incoming event
+ * @buf_size: size of buffer containing incoming event
+ * @new_size: size of event after filtering completed
+ *
+ * The WHCI 0.95 spec has a "Beacon Type" field. This value is unknown at
+ * the time we receive the beacon from WUSB so we just set it to
+ * UWB_RC_BEACON_TYPE_NEIGHBOR as a default.
+ * The solution below allocates memory upon receipt of every beacon from a
+ * WUSB device. This will deteriorate performance. What is the right way to
+ * do this?
+ */
+static
+int hwarc_filter_evt_beacon_WUSB_0100(struct uwb_rc *rc,
+ struct uwb_rceb **header,
+ const size_t buf_size,
+ size_t *new_size)
+{
+ struct uwb_rc_evt_beacon_WUSB_0100 *be;
+ struct uwb_rc_evt_beacon *newbe;
+ size_t bytes_left, ielength;
+ struct device *dev = &rc->uwb_dev.dev;
+
+ be = container_of(*header, struct uwb_rc_evt_beacon_WUSB_0100, rceb);
+ bytes_left = buf_size;
+ if (bytes_left < sizeof(*be)) {
+ dev_err(dev, "Beacon Received Notification: Not enough data "
+ "to decode for filtering (%zu vs %zu bytes needed)\n",
+ bytes_left, sizeof(*be));
+ return -EINVAL;
+ }
+ bytes_left -= sizeof(*be);
+ ielength = le16_to_cpu(be->wBeaconInfoLength);
+ if (bytes_left < ielength) {
+ dev_err(dev, "Beacon Received Notification: Not enough data "
+ "to decode IEs (%zu vs %zu bytes needed)\n",
+ bytes_left, ielength);
+ return -EINVAL;
+ }
+ newbe = kzalloc(sizeof(*newbe) + ielength, GFP_ATOMIC);
+ if (newbe == NULL)
+ return -ENOMEM;
+ newbe->rceb = be->rceb;
+ newbe->bChannelNumber = be->bChannelNumber;
+ newbe->bBeaconType = UWB_RC_BEACON_TYPE_NEIGHBOR;
+ newbe->wBPSTOffset = be->wBPSTOffset;
+ newbe->bLQI = be->bLQI;
+ newbe->bRSSI = be->bRSSI;
+ newbe->wBeaconInfoLength = be->wBeaconInfoLength;
+ memcpy(newbe->BeaconInfo, be->BeaconInfo, ielength);
+ *header = &newbe->rceb;
+ *new_size = sizeof(*newbe) + ielength;
+ return 1; /* calling function will free memory */
+}
+
+
+/* DRP Availability change notification (WUSB 1.0 [8.6.3.8]) */
+struct uwb_rc_evt_drp_avail_WUSB_0100 {
+ struct uwb_rceb rceb;
+ __le16 wIELength;
+ u8 IEData[];
+} __attribute__((packed));
+
+/**
+ * Filter WUSB 1.0 DRP AVAILABILITY CHANGE notification to be WHCI 0.95
+ *
+ * @header: the incoming event
+ * @buf_size: size of buffer containing incoming event
+ * @new_size: size of event after filtering completed
+ */
+static
+int hwarc_filter_evt_drp_avail_WUSB_0100(struct uwb_rc *rc,
+ struct uwb_rceb **header,
+ const size_t buf_size,
+ size_t *new_size)
+{
+ struct uwb_rc_evt_drp_avail_WUSB_0100 *da;
+ struct uwb_rc_evt_drp_avail *newda;
+ struct uwb_ie_hdr *ie_hdr;
+ size_t bytes_left, ielength;
+ struct device *dev = &rc->uwb_dev.dev;
+
+
+ da = container_of(*header, struct uwb_rc_evt_drp_avail_WUSB_0100, rceb);
+ bytes_left = buf_size;
+ if (bytes_left < sizeof(*da)) {
+ dev_err(dev, "Not enough data to decode DRP Avail "
+ "Notification for filtering. Expected %zu, "
+ "received %zu.\n", (size_t)sizeof(*da), bytes_left);
+ return -EINVAL;
+ }
+ bytes_left -= sizeof(*da);
+ ielength = le16_to_cpu(da->wIELength);
+ if (bytes_left < ielength) {
+ dev_err(dev, "DRP Avail Notification filter: IE length "
+ "[%zu bytes] does not match actual length "
+ "[%zu bytes].\n", ielength, bytes_left);
+ return -EINVAL;
+ }
+ if (ielength < sizeof(*ie_hdr)) {
+ dev_err(dev, "DRP Avail Notification filter: Not enough "
+ "data to decode IE [%zu bytes, %zu needed]\n",
+ ielength, sizeof(*ie_hdr));
+ return -EINVAL;
+ }
+ ie_hdr = (void *) da->IEData;
+ if (ie_hdr->length > 32) {
+ dev_err(dev, "DRP Availability Change event has unexpected "
+ "length for filtering. Expected < 32 bytes, "
+ "got %zu bytes.\n", (size_t)ie_hdr->length);
+ return -EINVAL;
+ }
+ newda = kzalloc(sizeof(*newda), GFP_ATOMIC);
+ if (newda == NULL)
+ return -ENOMEM;
+ newda->rceb = da->rceb;
+ memcpy(newda->bmp, (u8 *) ie_hdr + sizeof(*ie_hdr), ie_hdr->length);
+ *header = &newda->rceb;
+ *new_size = sizeof(*newda);
+ return 1; /* calling function will free memory */
+}
+
+
+/* DRP notification (WUSB 1.0 [8.6.3.9]) */
+struct uwb_rc_evt_drp_WUSB_0100 {
+ struct uwb_rceb rceb;
+ struct uwb_dev_addr wSrcAddr;
+ u8 bExplicit;
+ __le16 wIELength;
+ u8 IEData[];
+} __attribute__((packed));
+
+/**
+ * Filter WUSB 1.0 DRP Notification to be WHCI 0.95
+ *
+ * @header: the incoming event
+ * @buf_size: size of buffer containing incoming event
+ * @new_size: size of event after filtering completed
+ *
+ * It is hard to manage DRP reservations without having a Reason code.
+ * Unfortunately there is none in the WUSB spec. We just set the default to
+ * DRP IE RECEIVED.
+ * We do not currently use the bBeaconSlotNumber value, so we set this to
+ * zero for now.
+ */
+static
+int hwarc_filter_evt_drp_WUSB_0100(struct uwb_rc *rc,
+ struct uwb_rceb **header,
+ const size_t buf_size,
+ size_t *new_size)
+{
+ struct uwb_rc_evt_drp_WUSB_0100 *drpev;
+ struct uwb_rc_evt_drp *newdrpev;
+ size_t bytes_left, ielength;
+ struct device *dev = &rc->uwb_dev.dev;
+
+ drpev = container_of(*header, struct uwb_rc_evt_drp_WUSB_0100, rceb);
+ bytes_left = buf_size;
+ if (bytes_left < sizeof(*drpev)) {
+ dev_err(dev, "Not enough data to decode DRP Notification "
+ "for filtering. Expected %zu, received %zu.\n",
+ (size_t)sizeof(*drpev), bytes_left);
+ return -EINVAL;
+ }
+ ielength = le16_to_cpu(drpev->wIELength);
+ bytes_left -= sizeof(*drpev);
+ if (bytes_left < ielength) {
+ dev_err(dev, "DRP Notification filter: header length [%zu "
+ "bytes] does not match actual length [%zu "
+ "bytes].\n", ielength, bytes_left);
+ return -EINVAL;
+ }
+ newdrpev = kzalloc(sizeof(*newdrpev) + ielength, GFP_ATOMIC);
+ if (newdrpev == NULL)
+ return -ENOMEM;
+ newdrpev->rceb = drpev->rceb;
+ newdrpev->src_addr = drpev->wSrcAddr;
+ newdrpev->reason = UWB_DRP_NOTIF_DRP_IE_RCVD;
+ newdrpev->beacon_slot_number = 0;
+ newdrpev->ie_length = drpev->wIELength;
+ memcpy(newdrpev->ie_data, drpev->IEData, ielength);
+ *header = &newdrpev->rceb;
+ *new_size = sizeof(*newdrpev) + ielength;
+ return 1; /* calling function will free memory */
+}
+
+
+/* Scan Command (WUSB 1.0 [8.6.2.5]) */
+struct uwb_rc_cmd_scan_WUSB_0100 {
+ struct uwb_rccb rccb;
+ u8 bChannelNumber;
+ u8 bScanState;
+} __attribute__((packed));
+
+/**
+ * Filter WHCI 0.95 SCAN command to be WUSB 1.0 SCAN command
+ *
+ * @header: command sent to device (compliant to WHCI 0.95)
+ * @size: size of command sent to device
+ *
+ * We only reduce the size by two bytes because the WUSB 1.0 scan command
+ * does not have the last field (wStarttime). Also, make sure we don't send
+ * the device an unexpected scan type.
+ */
+static
+int hwarc_filter_cmd_scan_WUSB_0100(struct uwb_rc *rc,
+ struct uwb_rccb **header,
+ size_t *size)
+{
+ struct uwb_rc_cmd_scan *sc;
+
+ sc = container_of(*header, struct uwb_rc_cmd_scan, rccb);
+
+ if (sc->bScanState == UWB_SCAN_ONLY_STARTTIME)
+ sc->bScanState = UWB_SCAN_ONLY;
+ /* Don't send the last two bytes. */
+ *size -= 2;
+ return 0;
+}
+
+
+/* SET DRP IE command (WUSB 1.0 [8.6.2.7]) */
+struct uwb_rc_cmd_set_drp_ie_WUSB_0100 {
+ struct uwb_rccb rccb;
+ u8 bExplicit;
+ __le16 wIELength;
+ struct uwb_ie_drp IEData[];
+} __attribute__((packed));
+
+/**
+ * Filter WHCI 0.95 SET DRP IE command to be WUSB 1.0 SET DRP IE command
+ *
+ * @header: command sent to device (compliant to WHCI 0.95)
+ * @size: size of command sent to device
+ *
+ * WUSB has an extra bExplicit field - we assume always explicit
+ * negotiation so this field is set. The command expected by the device is
+ * thus larger than the one prepared by the driver so we need to
+ * reallocate memory to accommodate this.
+ * We trust the driver to send us the correct data so no checking is done
+ * on incoming data - evn though it is variable length.
+ */
+static
+int hwarc_filter_cmd_set_drp_ie_WUSB_0100(struct uwb_rc *rc,
+ struct uwb_rccb **header,
+ size_t *size)
+{
+ struct uwb_rc_cmd_set_drp_ie *orgcmd;
+ struct uwb_rc_cmd_set_drp_ie_WUSB_0100 *cmd;
+ size_t ielength;
+
+ orgcmd = container_of(*header, struct uwb_rc_cmd_set_drp_ie, rccb);
+ ielength = le16_to_cpu(orgcmd->wIELength);
+ cmd = kzalloc(sizeof(*cmd) + ielength, GFP_KERNEL);
+ if (cmd == NULL)
+ return -ENOMEM;
+ cmd->rccb = orgcmd->rccb;
+ cmd->bExplicit = 0;
+ cmd->wIELength = orgcmd->wIELength;
+ memcpy(cmd->IEData, orgcmd->IEData, ielength);
+ *header = &cmd->rccb;
+ *size = sizeof(*cmd) + ielength;
+ return 1; /* calling function will free memory */
+}
+
+
+/**
+ * Filter data from WHCI driver to WUSB device
+ *
+ * @header: WHCI 0.95 compliant command from driver
+ * @size: length of command
+ *
+ * The routine managing commands to the device (uwb_rc_cmd()) will call the
+ * filtering function pointer (if it exists) before it passes any data to
+ * the device. At this time the command has been formatted according to
+ * WHCI 0.95 and is ready to be sent to the device.
+ *
+ * The filter function will be provided with the current command and its
+ * length. The function will manipulate the command if necessary and
+ * potentially reallocate memory for a command that needed more memory that
+ * the given command. If new memory was created the function will return 1
+ * to indicate to the calling function that the memory need to be freed
+ * when not needed any more. The size will contain the new length of the
+ * command.
+ * If memory has not been allocated we rely on the original mechanisms to
+ * free the memory of the command - even when we reduce the value of size.
+ */
+static
+int hwarc_filter_cmd_WUSB_0100(struct uwb_rc *rc, struct uwb_rccb **header,
+ size_t *size)
+{
+ int result;
+ struct uwb_rccb *rccb = *header;
+ int cmd = le16_to_cpu(rccb->wCommand);
+ switch (cmd) {
+ case UWB_RC_CMD_SCAN:
+ result = hwarc_filter_cmd_scan_WUSB_0100(rc, header, size);
+ break;
+ case UWB_RC_CMD_SET_DRP_IE:
+ result = hwarc_filter_cmd_set_drp_ie_WUSB_0100(rc, header, size);
+ break;
+ default:
+ result = -ENOANO;
+ break;
+ }
+ return result;
+}
+
+
+/**
+ * Filter data from WHCI driver to WUSB device
+ *
+ * @header: WHCI 0.95 compliant command from driver
+ * @size: length of command
+ *
+ * Filter commands based on which protocol the device supports. The WUSB
+ * errata should be the same as WHCI 0.95 so we do not filter that here -
+ * only WUSB 1.0.
+ */
+static
+int hwarc_filter_cmd(struct uwb_rc *rc, struct uwb_rccb **header,
+ size_t *size)
+{
+ int result = -ENOANO;
+ if (rc->version == 0x0100)
+ result = hwarc_filter_cmd_WUSB_0100(rc, header, size);
+ return result;
+}
+
+
+/**
+ * Compute return value as sum of incoming value and value at given offset
+ *
+ * @rceb: event for which we compute the size, it contains a variable
+ * length field.
+ * @core_size: size of the "non variable" part of the event
+ * @offset: place in event where the length of the variable part is stored
+ * @buf_size: total length of buffer in which event arrived - we need to make
+ * sure we read the offset in memory that is still part of the event
+ */
+static
+ssize_t hwarc_get_event_size(struct uwb_rc *rc, const struct uwb_rceb *rceb,
+ size_t core_size, size_t offset,
+ const size_t buf_size)
+{
+ ssize_t size = -ENOSPC;
+ const void *ptr = rceb;
+ size_t type_size = sizeof(__le16);
+ struct device *dev = &rc->uwb_dev.dev;
+
+ if (offset + type_size >= buf_size) {
+ dev_err(dev, "Not enough data to read extra size of event "
+ "0x%02x/%04x/%02x, only got %zu bytes.\n",
+ rceb->bEventType, le16_to_cpu(rceb->wEvent),
+ rceb->bEventContext, buf_size);
+ goto out;
+ }
+ ptr += offset;
+ size = core_size + le16_to_cpu(*(__le16 *)ptr);
+out:
+ return size;
+}
+
+
+/* Beacon slot change notification (WUSB 1.0 [8.6.3.5]) */
+struct uwb_rc_evt_bp_slot_change_WUSB_0100 {
+ struct uwb_rceb rceb;
+ u8 bSlotNumber;
+} __attribute__((packed));
+
+
+/**
+ * Filter data from WUSB device to WHCI driver
+ *
+ * @header: incoming event
+ * @buf_size: size of buffer in which event arrived
+ * @_event_size: actual size of event in the buffer
+ * @new_size: size of event after filtered
+ *
+ * We don't know how the buffer is constructed - there may be more than one
+ * event in it so buffer length does not determine event length. We first
+ * determine the expected size of the incoming event. This value is passed
+ * back only if the actual filtering succeeded (so we know the computed
+ * expected size is correct). This value will be zero if
+ * the event did not need any filtering.
+ *
+ * WHCI interprets the BP Slot Change event's data differently than
+ * WUSB. The event sizes are exactly the same. The data field
+ * indicates the new beacon slot in which a RC is transmitting its
+ * beacon. The maximum value of this is 96 (wMacBPLength ECMA-368
+ * 17.16 (Table 117)). We thus know that the WUSB value will not set
+ * the bit bNoSlot, so we don't really do anything (placeholder).
+ */
+static
+int hwarc_filter_event_WUSB_0100(struct uwb_rc *rc, struct uwb_rceb **header,
+ const size_t buf_size, size_t *_real_size,
+ size_t *_new_size)
+{
+ int result = -ENOANO;
+ struct uwb_rceb *rceb = *header;
+ int event = le16_to_cpu(rceb->wEvent);
+ size_t event_size;
+ size_t core_size, offset;
+
+ if (rceb->bEventType != UWB_RC_CET_GENERAL)
+ goto out;
+ switch (event) {
+ case UWB_RC_EVT_BEACON:
+ core_size = sizeof(struct uwb_rc_evt_beacon_WUSB_0100);
+ offset = offsetof(struct uwb_rc_evt_beacon_WUSB_0100,
+ wBeaconInfoLength);
+ event_size = hwarc_get_event_size(rc, rceb, core_size,
+ offset, buf_size);
+ if (event_size < 0)
+ goto out;
+ *_real_size = event_size;
+ result = hwarc_filter_evt_beacon_WUSB_0100(rc, header,
+ buf_size, _new_size);
+ break;
+ case UWB_RC_EVT_BP_SLOT_CHANGE:
+ *_new_size = *_real_size =
+ sizeof(struct uwb_rc_evt_bp_slot_change_WUSB_0100);
+ result = 0;
+ break;
+
+ case UWB_RC_EVT_DRP_AVAIL:
+ core_size = sizeof(struct uwb_rc_evt_drp_avail_WUSB_0100);
+ offset = offsetof(struct uwb_rc_evt_drp_avail_WUSB_0100,
+ wIELength);
+ event_size = hwarc_get_event_size(rc, rceb, core_size,
+ offset, buf_size);
+ if (event_size < 0)
+ goto out;
+ *_real_size = event_size;
+ result = hwarc_filter_evt_drp_avail_WUSB_0100(
+ rc, header, buf_size, _new_size);
+ break;
+
+ case UWB_RC_EVT_DRP:
+ core_size = sizeof(struct uwb_rc_evt_drp_WUSB_0100);
+ offset = offsetof(struct uwb_rc_evt_drp_WUSB_0100, wIELength);
+ event_size = hwarc_get_event_size(rc, rceb, core_size,
+ offset, buf_size);
+ if (event_size < 0)
+ goto out;
+ *_real_size = event_size;
+ result = hwarc_filter_evt_drp_WUSB_0100(rc, header,
+ buf_size, _new_size);
+ break;
+
+ default:
+ break;
+ }
+out:
+ return result;
+}
+
+/**
+ * Filter data from WUSB device to WHCI driver
+ *
+ * @header: incoming event
+ * @buf_size: size of buffer in which event arrived
+ * @_event_size: actual size of event in the buffer
+ * @_new_size: size of event after filtered
+ *
+ * Filter events based on which protocol the device supports. The WUSB
+ * errata should be the same as WHCI 0.95 so we do not filter that here -
+ * only WUSB 1.0.
+ *
+ * If we don't handle it, we return -ENOANO (why the weird error code?
+ * well, so if I get it, I can pinpoint in the code that raised
+ * it...after all, not too many places use the higher error codes).
+ */
+static
+int hwarc_filter_event(struct uwb_rc *rc, struct uwb_rceb **header,
+ const size_t buf_size, size_t *_real_size,
+ size_t *_new_size)
+{
+ int result = -ENOANO;
+ if (rc->version == 0x0100)
+ result = hwarc_filter_event_WUSB_0100(
+ rc, header, buf_size, _real_size, _new_size);
+ return result;
+}
+
+
+/**
+ * Execute an UWB RC command on HWA
+ *
+ * @rc: Instance of a Radio Controller that is a HWA
+ * @cmd: Buffer containing the RCCB and payload to execute
+ * @cmd_size: Size of the command buffer.
+ *
+ * NOTE: rc's mutex has to be locked
+ */
+static
+int hwarc_cmd(struct uwb_rc *uwb_rc, const struct uwb_rccb *cmd, size_t cmd_size)
+{
+ struct hwarc *hwarc = uwb_rc->priv;
+ return usb_control_msg(
+ hwarc->usb_dev, usb_sndctrlpipe(hwarc->usb_dev, 0),
+ WA_EXEC_RC_CMD, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0, hwarc->usb_iface->cur_altsetting->desc.bInterfaceNumber,
+ (void *) cmd, cmd_size, 100 /* FIXME: this is totally arbitrary */);
+}
+
+static
+int hwarc_reset(struct uwb_rc *uwb_rc)
+{
+ struct hwarc *hwarc = uwb_rc->priv;
+ return usb_reset_device(hwarc->usb_dev);
+}
+
+/**
+ * Callback for the notification and event endpoint
+ *
+ * Check's that everything is fine and then passes the read data to
+ * the notification/event handling mechanism (neh).
+ */
+static
+void hwarc_neep_cb(struct urb *urb)
+{
+ struct hwarc *hwarc = urb->context;
+ struct usb_interface *usb_iface = hwarc->usb_iface;
+ struct device *dev = &usb_iface->dev;
+ int result;
+
+ switch (result = urb->status) {
+ case 0:
+ d_printf(3, dev, "NEEP: receive stat %d, %zu bytes\n",
+ urb->status, (size_t)urb->actual_length);
+ uwb_rc_neh_grok(hwarc->uwb_rc, urb->transfer_buffer,
+ urb->actual_length);
+ break;
+ case -ECONNRESET: /* Not an error, but a controlled situation; */
+ case -ENOENT: /* (we killed the URB)...so, no broadcast */
+ d_printf(2, dev, "NEEP: URB reset/noent %d\n", urb->status);
+ goto out;
+ case -ESHUTDOWN: /* going away! */
+ d_printf(2, dev, "NEEP: URB down %d\n", urb->status);
+ goto out;
+ default: /* On general errors, retry unless it gets ugly */
+ if (edc_inc(&hwarc->neep_edc, EDC_MAX_ERRORS,
+ EDC_ERROR_TIMEFRAME))
+ goto error_exceeded;
+ dev_err(dev, "NEEP: URB error %d\n", urb->status);
+ }
+ result = usb_submit_urb(urb, GFP_ATOMIC);
+ d_printf(3, dev, "NEEP: submit %d\n", result);
+ if (result < 0) {
+ dev_err(dev, "NEEP: Can't resubmit URB (%d) resetting device\n",
+ result);
+ goto error;
+ }
+out:
+ return;
+
+error_exceeded:
+ dev_err(dev, "NEEP: URB max acceptable errors "
+ "exceeded, resetting device\n");
+error:
+ uwb_rc_neh_error(hwarc->uwb_rc, result);
+ uwb_rc_reset_all(hwarc->uwb_rc);
+ return;
+}
+
+static void hwarc_init(struct hwarc *hwarc)
+{
+ edc_init(&hwarc->neep_edc);
+}
+
+/**
+ * Initialize the notification/event endpoint stuff
+ *
+ * Note this is effectively a parallel thread; it knows that
+ * hwarc->uwb_rc always exists because the existence of a 'hwarc'
+ * means that there is a reverence on the hwarc->uwb_rc (see
+ * _probe()), and thus _neep_cb() can execute safely.
+ */
+static int hwarc_neep_init(struct uwb_rc *rc)
+{
+ struct hwarc *hwarc = rc->priv;
+ struct usb_interface *iface = hwarc->usb_iface;
+ struct usb_device *usb_dev = interface_to_usbdev(iface);
+ struct device *dev = &iface->dev;
+ int result;
+ struct usb_endpoint_descriptor *epd;
+
+ epd = &iface->cur_altsetting->endpoint[0].desc;
+ hwarc->rd_buffer = (void *) __get_free_page(GFP_KERNEL);
+ if (hwarc->rd_buffer == NULL) {
+ dev_err(dev, "Unable to allocate notification's read buffer\n");
+ goto error_rd_buffer;
+ }
+ hwarc->neep_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (hwarc->neep_urb == NULL) {
+ dev_err(dev, "Unable to allocate notification URB\n");
+ goto error_urb_alloc;
+ }
+ usb_fill_int_urb(hwarc->neep_urb, usb_dev,
+ usb_rcvintpipe(usb_dev, epd->bEndpointAddress),
+ hwarc->rd_buffer, PAGE_SIZE,
+ hwarc_neep_cb, hwarc, epd->bInterval);
+ result = usb_submit_urb(hwarc->neep_urb, GFP_ATOMIC);
+ if (result < 0) {
+ dev_err(dev, "Cannot submit notification URB: %d\n", result);
+ goto error_neep_submit;
+ }
+ return 0;
+
+error_neep_submit:
+ usb_free_urb(hwarc->neep_urb);
+error_urb_alloc:
+ free_page((unsigned long)hwarc->rd_buffer);
+error_rd_buffer:
+ return -ENOMEM;
+}
+
+
+/** Clean up all the notification endpoint resources */
+static void hwarc_neep_release(struct uwb_rc *rc)
+{
+ struct hwarc *hwarc = rc->priv;
+
+ usb_kill_urb(hwarc->neep_urb);
+ usb_free_urb(hwarc->neep_urb);
+ free_page((unsigned long)hwarc->rd_buffer);
+}
+
+/**
+ * Get the version from class-specific descriptor
+ *
+ * NOTE: this descriptor comes with the big bundled configuration
+ * descriptor that includes the interfaces' and endpoints', so
+ * we just look for it in the cached copy kept by the USB stack.
+ *
+ * NOTE2: We convert LE fields to CPU order.
+ */
+static int hwarc_get_version(struct uwb_rc *rc)
+{
+ int result;
+
+ struct hwarc *hwarc = rc->priv;
+ struct uwb_rc_control_intf_class_desc *descr;
+ struct device *dev = &rc->uwb_dev.dev;
+ struct usb_device *usb_dev = hwarc->usb_dev;
+ char *itr;
+ struct usb_descriptor_header *hdr;
+ size_t itr_size, actconfig_idx;
+ u16 version;
+
+ actconfig_idx = (usb_dev->actconfig - usb_dev->config) /
+ sizeof(usb_dev->config[0]);
+ itr = usb_dev->rawdescriptors[actconfig_idx];
+ itr_size = le16_to_cpu(usb_dev->actconfig->desc.wTotalLength);
+ while (itr_size >= sizeof(*hdr)) {
+ hdr = (struct usb_descriptor_header *) itr;
+ d_printf(3, dev, "Extra device descriptor: "
+ "type %02x/%u bytes @ %zu (%zu left)\n",
+ hdr->bDescriptorType, hdr->bLength,
+ (itr - usb_dev->rawdescriptors[actconfig_idx]),
+ itr_size);
+ if (hdr->bDescriptorType == USB_DT_CS_RADIO_CONTROL)
+ goto found;
+ itr += hdr->bLength;
+ itr_size -= hdr->bLength;
+ }
+ dev_err(dev, "cannot find Radio Control Interface Class descriptor\n");
+ return -ENODEV;
+
+found:
+ result = -EINVAL;
+ if (hdr->bLength > itr_size) { /* is it available? */
+ dev_err(dev, "incomplete Radio Control Interface Class "
+ "descriptor (%zu bytes left, %u needed)\n",
+ itr_size, hdr->bLength);
+ goto error;
+ }
+ if (hdr->bLength < sizeof(*descr)) {
+ dev_err(dev, "short Radio Control Interface Class "
+ "descriptor\n");
+ goto error;
+ }
+ descr = (struct uwb_rc_control_intf_class_desc *) hdr;
+ /* Make LE fields CPU order */
+ version = __le16_to_cpu(descr->bcdRCIVersion);
+ if (version != 0x0100) {
+ dev_err(dev, "Device reports protocol version 0x%04x. We "
+ "do not support that. \n", version);
+ result = -EINVAL;
+ goto error;
+ }
+ rc->version = version;
+ d_printf(3, dev, "Device supports WUSB protocol version 0x%04x \n",
+ rc->version);
+ result = 0;
+error:
+ return result;
+}
+
+/*
+ * By creating a 'uwb_rc', we have a reference on it -- that reference
+ * is the one we drop when we disconnect.
+ *
+ * No need to switch altsettings; according to WUSB1.0[8.6.1.1], there
+ * is only one altsetting allowed.
+ */
+static int hwarc_probe(struct usb_interface *iface,
+ const struct usb_device_id *id)
+{
+ int result;
+ struct uwb_rc *uwb_rc;
+ struct hwarc *hwarc;
+ struct device *dev = &iface->dev;
+
+ result = -ENOMEM;
+ uwb_rc = uwb_rc_alloc();
+ if (uwb_rc == NULL) {
+ dev_err(dev, "unable to allocate RC instance\n");
+ goto error_rc_alloc;
+ }
+ hwarc = kzalloc(sizeof(*hwarc), GFP_KERNEL);
+ if (hwarc == NULL) {
+ dev_err(dev, "unable to allocate HWA RC instance\n");
+ goto error_alloc;
+ }
+ hwarc_init(hwarc);
+ hwarc->usb_dev = usb_get_dev(interface_to_usbdev(iface));
+ hwarc->usb_iface = usb_get_intf(iface);
+ hwarc->uwb_rc = uwb_rc;
+
+ uwb_rc->owner = THIS_MODULE;
+ uwb_rc->start = hwarc_neep_init;
+ uwb_rc->stop = hwarc_neep_release;
+ uwb_rc->cmd = hwarc_cmd;
+ uwb_rc->reset = hwarc_reset;
+ if (id->driver_info & WUSB_QUIRK_WHCI_CMD_EVT) {
+ uwb_rc->filter_cmd = NULL;
+ uwb_rc->filter_event = NULL;
+ } else {
+ uwb_rc->filter_cmd = hwarc_filter_cmd;
+ uwb_rc->filter_event = hwarc_filter_event;
+ }
+
+ result = uwb_rc_add(uwb_rc, dev, hwarc);
+ if (result < 0)
+ goto error_rc_add;
+ result = hwarc_get_version(uwb_rc);
+ if (result < 0) {
+ dev_err(dev, "cannot retrieve version of RC \n");
+ goto error_get_version;
+ }
+ usb_set_intfdata(iface, hwarc);
+ return 0;
+
+error_get_version:
+ uwb_rc_rm(uwb_rc);
+error_rc_add:
+ usb_put_intf(iface);
+ usb_put_dev(hwarc->usb_dev);
+error_alloc:
+ uwb_rc_put(uwb_rc);
+error_rc_alloc:
+ return result;
+}
+
+static void hwarc_disconnect(struct usb_interface *iface)
+{
+ struct hwarc *hwarc = usb_get_intfdata(iface);
+ struct uwb_rc *uwb_rc = hwarc->uwb_rc;
+
+ usb_set_intfdata(hwarc->usb_iface, NULL);
+ uwb_rc_rm(uwb_rc);
+ usb_put_intf(hwarc->usb_iface);
+ usb_put_dev(hwarc->usb_dev);
+ d_printf(1, &hwarc->usb_iface->dev, "freed hwarc %p\n", hwarc);
+ kfree(hwarc);
+ uwb_rc_put(uwb_rc); /* when creating the device, refcount = 1 */
+}
+
+/** USB device ID's that we handle */
+static struct usb_device_id hwarc_id_table[] = {
+ /* D-Link DUB-1210 */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3d02, 0xe0, 0x01, 0x02),
+ .driver_info = WUSB_QUIRK_WHCI_CMD_EVT },
+ /* Intel i1480 (using firmware 1.3PA2-20070828) */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x8086, 0x0c3b, 0xe0, 0x01, 0x02),
+ .driver_info = WUSB_QUIRK_WHCI_CMD_EVT },
+ /* Generic match for the Radio Control interface */
+ { USB_INTERFACE_INFO(0xe0, 0x01, 0x02), },
+ { },
+};
+MODULE_DEVICE_TABLE(usb, hwarc_id_table);
+
+static struct usb_driver hwarc_driver = {
+ .name = "hwa-rc",
+ .probe = hwarc_probe,
+ .disconnect = hwarc_disconnect,
+ .id_table = hwarc_id_table,
+};
+
+static int __init hwarc_driver_init(void)
+{
+ int result;
+ result = usb_register(&hwarc_driver);
+ if (result < 0)
+ printk(KERN_ERR "HWA-RC: Cannot register USB driver: %d\n",
+ result);
+ return result;
+
+}
+module_init(hwarc_driver_init);
+
+static void __exit hwarc_driver_exit(void)
+{
+ usb_deregister(&hwarc_driver);
+}
+module_exit(hwarc_driver_exit);
+
+MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
+MODULE_DESCRIPTION("Host Wireless Adapter Radio Control Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/uwb/i1480/Makefile b/drivers/uwb/i1480/Makefile
new file mode 100644
index 000000000000..212bbc7d4c32
--- /dev/null
+++ b/drivers/uwb/i1480/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_UWB_I1480U) += dfu/ i1480-est.o
+obj-$(CONFIG_UWB_I1480U_WLP) += i1480u-wlp/
diff --git a/drivers/uwb/i1480/dfu/Makefile b/drivers/uwb/i1480/dfu/Makefile
new file mode 100644
index 000000000000..bd1b9f25424c
--- /dev/null
+++ b/drivers/uwb/i1480/dfu/Makefile
@@ -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
index 000000000000..9097b3b30385
--- /dev/null
+++ b/drivers/uwb/i1480/dfu/dfu.c
@@ -0,0 +1,217 @@
+/*
+ * 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>
+
+/**
+ * i1480_rceb_check - Check RCEB for expected field values
+ * @i1480: pointer to device for which RCEB is being checked
+ * @rceb: RCEB being checked
+ * @cmd: which command the RCEB is related to
+ * @context: expected context
+ * @expected_type: expected event type
+ * @expected_event: expected event
+ *
+ * If @cmd is NULL, do not print error messages, but still return an error
+ * code.
+ *
+ * Return 0 if @rceb matches the expected values, -EINVAL otherwise.
+ */
+int i1480_rceb_check(const struct i1480 *i1480, const struct uwb_rceb *rceb,
+ const char *cmd, u8 context, u8 expected_type,
+ unsigned expected_event)
+{
+ int result = 0;
+ struct device *dev = i1480->dev;
+ if (rceb->bEventContext != context) {
+ if (cmd)
+ dev_err(dev, "%s: unexpected context id 0x%02x "
+ "(expected 0x%02x)\n", cmd,
+ rceb->bEventContext, context);
+ result = -EINVAL;
+ }
+ if (rceb->bEventType != expected_type) {
+ if (cmd)
+ 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) {
+ if (cmd)
+ 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;
+ }
+ /*
+ * Firmware versions >= 1.4.12224 for IOGear GUWA100U generate a
+ * spurious notification after firmware is downloaded. So check whether
+ * the receibed RCEB is such notification before assuming that the
+ * command has failed.
+ */
+ if (i1480_rceb_check(i1480, i1480->evt_buf, NULL,
+ 0, 0xfd, 0x0022) == 0) {
+ /* Now wait for the actual RCEB for this command. */
+ result = i1480->wait_init_done(i1480);
+ if (result < 0)
+ goto error;
+ result = i1480->evt_result;
+ }
+ 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);
+
+
+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;
+ }
+ /*
+ * FIXME: find some reliable way to check whether firmware is running
+ * properly. Maybe use some standard request that has no side effects?
+ */
+ 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
index 000000000000..46f45e800f36
--- /dev/null
+++ b/drivers/uwb/i1480/dfu/i1480-dfu.h
@@ -0,0 +1,260 @@
+/*
+ * 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()]
+ *
+ * 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;
+};
+
+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,
+ u8, 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
index 000000000000..8d069907a3b5
--- /dev/null
+++ b/drivers/uwb/i1480/dfu/mac.c
@@ -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 (i1480_rceb_check(i1480, &rcebe->rceb, NULL, 0, i1480_CET_VS1,
+ i1480_EVT_RM_INIT_DONE) < 0) {
+ 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
index 000000000000..3b1a87de8e63
--- /dev/null
+++ b/drivers/uwb/i1480/dfu/phy.c
@@ -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
index 000000000000..98eeeff051aa
--- /dev/null
+++ b/drivers/uwb/i1480/dfu/usb.c
@@ -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
index 000000000000..7bf8c6febae7
--- /dev/null
+++ b/drivers/uwb/i1480/i1480-est.c
@@ -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);
diff --git a/drivers/uwb/i1480/i1480-wlp.h b/drivers/uwb/i1480/i1480-wlp.h
new file mode 100644
index 000000000000..18a8b0e4567b
--- /dev/null
+++ b/drivers/uwb/i1480/i1480-wlp.h
@@ -0,0 +1,200 @@
+/*
+ * Intel 1480 Wireless UWB Link
+ * WLP specific definitions
+ *
+ *
+ * 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
+ */
+
+#ifndef __i1480_wlp_h__
+#define __i1480_wlp_h__
+
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/uwb.h>
+#include <linux/if_ether.h>
+#include <asm/byteorder.h>
+
+/* New simplified header format? */
+#undef WLP_HDR_FMT_2 /* FIXME: rename */
+
+/**
+ * Values of the Delivery ID & Type field when PCA or DRP
+ *
+ * The Delivery ID & Type field in the WLP TX header indicates whether
+ * the frame is PCA or DRP. This is done based on the high level bit of
+ * this field.
+ * We use this constant to test if the traffic is PCA or DRP as follows:
+ * if (wlp_tx_hdr_delivery_id_type(wlp_tx_hdr) & WLP_DRP)
+ * this is DRP traffic
+ * else
+ * this is PCA traffic
+ */
+enum deliver_id_type_bit {
+ WLP_DRP = 8,
+};
+
+/**
+ * WLP TX header
+ *
+ * Indicates UWB/WLP-specific transmission parameters for a network
+ * packet.
+ */
+struct wlp_tx_hdr {
+ /* dword 0 */
+ struct uwb_dev_addr dstaddr;
+ u8 key_index;
+ u8 mac_params;
+ /* dword 1 */
+ u8 phy_params;
+#ifndef WLP_HDR_FMT_2
+ u8 reserved;
+ __le16 oui01; /* FIXME: not so sure if __le16 or u8[2] */
+ /* dword 2 */
+ u8 oui2; /* if all LE, it could be merged */
+ __le16 prid;
+#endif
+} __attribute__((packed));
+
+static inline int wlp_tx_hdr_delivery_id_type(const struct wlp_tx_hdr *hdr)
+{
+ return hdr->mac_params & 0x0f;
+}
+
+static inline int wlp_tx_hdr_ack_policy(const struct wlp_tx_hdr *hdr)
+{
+ return (hdr->mac_params >> 4) & 0x07;
+}
+
+static inline int wlp_tx_hdr_rts_cts(const struct wlp_tx_hdr *hdr)
+{
+ return (hdr->mac_params >> 7) & 0x01;
+}
+
+static inline void wlp_tx_hdr_set_delivery_id_type(struct wlp_tx_hdr *hdr, int id)
+{
+ hdr->mac_params = (hdr->mac_params & ~0x0f) | id;
+}
+
+static inline void wlp_tx_hdr_set_ack_policy(struct wlp_tx_hdr *hdr,
+ enum uwb_ack_pol policy)
+{
+ hdr->mac_params = (hdr->mac_params & ~0x70) | (policy << 4);
+}
+
+static inline void wlp_tx_hdr_set_rts_cts(struct wlp_tx_hdr *hdr, int rts_cts)
+{
+ hdr->mac_params = (hdr->mac_params & ~0x80) | (rts_cts << 7);
+}
+
+static inline enum uwb_phy_rate wlp_tx_hdr_phy_rate(const struct wlp_tx_hdr *hdr)
+{
+ return hdr->phy_params & 0x0f;
+}
+
+static inline int wlp_tx_hdr_tx_power(const struct wlp_tx_hdr *hdr)
+{
+ return (hdr->phy_params >> 4) & 0x0f;
+}
+
+static inline void wlp_tx_hdr_set_phy_rate(struct wlp_tx_hdr *hdr, enum uwb_phy_rate rate)
+{
+ hdr->phy_params = (hdr->phy_params & ~0x0f) | rate;
+}
+
+static inline void wlp_tx_hdr_set_tx_power(struct wlp_tx_hdr *hdr, int pwr)
+{
+ hdr->phy_params = (hdr->phy_params & ~0xf0) | (pwr << 4);
+}
+
+
+/**
+ * WLP RX header
+ *
+ * Provides UWB/WLP-specific transmission data for a received
+ * network packet.
+ */
+struct wlp_rx_hdr {
+ /* dword 0 */
+ struct uwb_dev_addr dstaddr;
+ struct uwb_dev_addr srcaddr;
+ /* dword 1 */
+ u8 LQI;
+ s8 RSSI;
+ u8 reserved3;
+#ifndef WLP_HDR_FMT_2
+ u8 oui0;
+ /* dword 2 */
+ __le16 oui12;
+ __le16 prid;
+#endif
+} __attribute__((packed));
+
+
+/** User configurable options for WLP */
+struct wlp_options {
+ struct mutex mutex; /* access to user configurable options*/
+ struct wlp_tx_hdr def_tx_hdr; /* default tx hdr */
+ u8 pca_base_priority;
+ u8 bw_alloc; /*index into bw_allocs[] for PCA/DRP reservations*/
+};
+
+
+static inline
+void wlp_options_init(struct wlp_options *options)
+{
+ mutex_init(&options->mutex);
+ wlp_tx_hdr_set_ack_policy(&options->def_tx_hdr, UWB_ACK_INM);
+ wlp_tx_hdr_set_rts_cts(&options->def_tx_hdr, 1);
+ /* FIXME: default to phy caps */
+ wlp_tx_hdr_set_phy_rate(&options->def_tx_hdr, UWB_PHY_RATE_480);
+#ifndef WLP_HDR_FMT_2
+ options->def_tx_hdr.prid = cpu_to_le16(0x0000);
+#endif
+}
+
+
+/* sysfs helpers */
+
+extern ssize_t uwb_pca_base_priority_store(struct wlp_options *,
+ const char *, size_t);
+extern ssize_t uwb_pca_base_priority_show(const struct wlp_options *, char *);
+extern ssize_t uwb_bw_alloc_store(struct wlp_options *, const char *, size_t);
+extern ssize_t uwb_bw_alloc_show(const struct wlp_options *, char *);
+extern ssize_t uwb_ack_policy_store(struct wlp_options *,
+ const char *, size_t);
+extern ssize_t uwb_ack_policy_show(const struct wlp_options *, char *);
+extern ssize_t uwb_rts_cts_store(struct wlp_options *, const char *, size_t);
+extern ssize_t uwb_rts_cts_show(const struct wlp_options *, char *);
+extern ssize_t uwb_phy_rate_store(struct wlp_options *, const char *, size_t);
+extern ssize_t uwb_phy_rate_show(const struct wlp_options *, char *);
+
+
+/** Simple bandwidth allocation (temporary and too simple) */
+struct wlp_bw_allocs {
+ const char *name;
+ struct {
+ u8 mask, stream;
+ } tx, rx;
+};
+
+
+#endif /* #ifndef __i1480_wlp_h__ */
diff --git a/drivers/uwb/i1480/i1480u-wlp/Makefile b/drivers/uwb/i1480/i1480u-wlp/Makefile
new file mode 100644
index 000000000000..fe6709b8e68b
--- /dev/null
+++ b/drivers/uwb/i1480/i1480u-wlp/Makefile
@@ -0,0 +1,8 @@
+obj-$(CONFIG_UWB_I1480U_WLP) += i1480u-wlp.o
+
+i1480u-wlp-objs := \
+ lc.o \
+ netdev.o \
+ rx.o \
+ sysfs.o \
+ tx.o
diff --git a/drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h b/drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h
new file mode 100644
index 000000000000..5f1b2951bb83
--- /dev/null
+++ b/drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h
@@ -0,0 +1,284 @@
+/*
+ * Intel 1480 Wireless UWB Link USB
+ * Header formats, constants, general internal interfaces
+ *
+ *
+ * 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 is not an standard interface.
+ *
+ * FIXME: docs
+ *
+ * i1480u-wlp is pretty simple: two endpoints, one for tx, one for
+ * rx. rx is polled. Network packets (ethernet, whatever) are wrapped
+ * in i1480 TX or RX headers (for sending over the air), and these
+ * packets are wrapped in UNTD headers (for sending to the WLP UWB
+ * controller).
+ *
+ * UNTD packets (UNTD hdr + i1480 hdr + network packet) packets
+ * cannot be bigger than i1480u_MAX_FRG_SIZE. When this happens, the
+ * i1480 packet is broken in chunks/packets:
+ *
+ * UNTD-1st.hdr + i1480.hdr + payload
+ * UNTD-next.hdr + payload
+ * ...
+ * UNTD-last.hdr + payload
+ *
+ * so that each packet is smaller or equal than i1480u_MAX_FRG_SIZE.
+ *
+ * All HW structures and bitmaps are little endian, so we need to play
+ * ugly tricks when defining bitfields. Hoping for the day GCC
+ * implements __attribute__((endian(1234))).
+ *
+ * FIXME: ROADMAP to the whole implementation
+ */
+
+#ifndef __i1480u_wlp_h__
+#define __i1480u_wlp_h__
+
+#include <linux/usb.h>
+#include <linux/netdevice.h>
+#include <linux/uwb.h> /* struct uwb_rc, struct uwb_notifs_handler */
+#include <linux/wlp.h>
+#include "../i1480-wlp.h"
+
+#undef i1480u_FLOW_CONTROL /* Enable flow control code */
+
+/**
+ * Basic flow control
+ */
+enum {
+ i1480u_TX_INFLIGHT_MAX = 1000,
+ i1480u_TX_INFLIGHT_THRESHOLD = 100,
+};
+
+/** Maximum size of a transaction that we can tx/rx */
+enum {
+ /* Maximum packet size computed as follows: max UNTD header (8) +
+ * i1480 RX header (8) + max Ethernet header and payload (4096) +
+ * Padding added by skb_reserve (2) to make post Ethernet payload
+ * start on 16 byte boundary*/
+ i1480u_MAX_RX_PKT_SIZE = 4114,
+ i1480u_MAX_FRG_SIZE = 512,
+ i1480u_RX_BUFS = 9,
+};
+
+
+/**
+ * UNTD packet type
+ *
+ * We need to fragment any payload whose UNTD packet is going to be
+ * bigger than i1480u_MAX_FRG_SIZE.
+ */
+enum i1480u_pkt_type {
+ i1480u_PKT_FRAG_1ST = 0x1,
+ i1480u_PKT_FRAG_NXT = 0x0,
+ i1480u_PKT_FRAG_LST = 0x2,
+ i1480u_PKT_FRAG_CMP = 0x3
+};
+enum {
+ i1480u_PKT_NONE = 0x4,
+};
+
+/** USB Network Transfer Descriptor - common */
+struct untd_hdr {
+ u8 type;
+ __le16 len;
+} __attribute__((packed));
+
+static inline enum i1480u_pkt_type untd_hdr_type(const struct untd_hdr *hdr)
+{
+ return hdr->type & 0x03;
+}
+
+static inline int untd_hdr_rx_tx(const struct untd_hdr *hdr)
+{
+ return (hdr->type >> 2) & 0x01;
+}
+
+static inline void untd_hdr_set_type(struct untd_hdr *hdr, enum i1480u_pkt_type type)
+{
+ hdr->type = (hdr->type & ~0x03) | type;
+}
+
+static inline void untd_hdr_set_rx_tx(struct untd_hdr *hdr, int rx_tx)
+{
+ hdr->type = (hdr->type & ~0x04) | (rx_tx << 2);
+}
+
+
+/**
+ * USB Network Transfer Descriptor - Complete Packet
+ *
+ * This is for a packet that is smaller (header + payload) than
+ * i1480u_MAX_FRG_SIZE.
+ *
+ * @hdr.total_len is the size of the payload; the payload doesn't
+ * count this header nor the padding, but includes the size of i1480
+ * header.
+ */
+struct untd_hdr_cmp {
+ struct untd_hdr hdr;
+ u8 padding;
+} __attribute__((packed));
+
+
+/**
+ * USB Network Transfer Descriptor - First fragment
+ *
+ * @hdr.len is the size of the *whole packet* (excluding UNTD
+ * headers); @fragment_len is the size of the payload (excluding UNTD
+ * headers, but including i1480 headers).
+ */
+struct untd_hdr_1st {
+ struct untd_hdr hdr;
+ __le16 fragment_len;
+ u8 padding[3];
+} __attribute__((packed));
+
+
+/**
+ * USB Network Transfer Descriptor - Next / Last [Rest]
+ *
+ * @hdr.len is the size of the payload, not including headrs.
+ */
+struct untd_hdr_rst {
+ struct untd_hdr hdr;
+ u8 padding;
+} __attribute__((packed));
+
+
+/**
+ * Transmission context
+ *
+ * Wraps all the stuff needed to track a pending/active tx
+ * operation.
+ */
+struct i1480u_tx {
+ struct list_head list_node;
+ struct i1480u *i1480u;
+ struct urb *urb;
+
+ struct sk_buff *skb;
+ struct wlp_tx_hdr *wlp_tx_hdr;
+
+ void *buf; /* if NULL, no new buf was used */
+ size_t buf_size;
+};
+
+/**
+ * Basic flow control
+ *
+ * We maintain a basic flow control counter. "count" how many TX URBs are
+ * outstanding. Only allow "max"
+ * TX URBs to be outstanding. If this value is reached the queue will be
+ * stopped. The queue will be restarted when there are
+ * "threshold" URBs outstanding.
+ * Maintain a counter of how many time the TX queue needed to be restarted
+ * due to the "max" being exceeded and the "threshold" reached again. The
+ * timestamp "restart_ts" is to keep track from when the counter was last
+ * queried (see sysfs handling of file wlp_tx_inflight).
+ */
+struct i1480u_tx_inflight {
+ atomic_t count;
+ unsigned long max;
+ unsigned long threshold;
+ unsigned long restart_ts;
+ atomic_t restart_count;
+};
+
+/**
+ * Instance of a i1480u WLP interface
+ *
+ * Keeps references to the USB device that wraps it, as well as it's
+ * interface and associated UWB host controller. As well, it also
+ * keeps a link to the netdevice for integration into the networking
+ * stack.
+ * We maintian separate error history for the tx and rx endpoints because
+ * the implementation does not rely on locking - having one shared
+ * structure between endpoints may cause problems. Adding locking to the
+ * implementation will have higher cost than adding a separate structure.
+ */
+struct i1480u {
+ struct usb_device *usb_dev;
+ struct usb_interface *usb_iface;
+ struct net_device *net_dev;
+
+ spinlock_t lock;
+ struct net_device_stats stats;
+
+ /* RX context handling */
+ struct sk_buff *rx_skb;
+ struct uwb_dev_addr rx_srcaddr;
+ size_t rx_untd_pkt_size;
+ struct i1480u_rx_buf {
+ struct i1480u *i1480u; /* back pointer */
+ struct urb *urb;
+ struct sk_buff *data; /* i1480u_MAX_RX_PKT_SIZE each */
+ } rx_buf[i1480u_RX_BUFS]; /* N bufs */
+
+ spinlock_t tx_list_lock; /* TX context */
+ struct list_head tx_list;
+ u8 tx_stream;
+
+ struct stats lqe_stats, rssi_stats; /* radio statistics */
+
+ /* Options we can set from sysfs */
+ struct wlp_options options;
+ struct uwb_notifs_handler uwb_notifs_handler;
+ struct edc tx_errors;
+ struct edc rx_errors;
+ struct wlp wlp;
+#ifdef i1480u_FLOW_CONTROL
+ struct urb *notif_urb;
+ struct edc notif_edc; /* error density counter */
+ u8 notif_buffer[1];
+#endif
+ struct i1480u_tx_inflight tx_inflight;
+};
+
+/* Internal interfaces */
+extern void i1480u_rx_cb(struct urb *urb);
+extern int i1480u_rx_setup(struct i1480u *);
+extern void i1480u_rx_release(struct i1480u *);
+extern void i1480u_tx_release(struct i1480u *);
+extern int i1480u_xmit_frame(struct wlp *, struct sk_buff *,
+ struct uwb_dev_addr *);
+extern void i1480u_stop_queue(struct wlp *);
+extern void i1480u_start_queue(struct wlp *);
+extern int i1480u_sysfs_setup(struct i1480u *);
+extern void i1480u_sysfs_release(struct i1480u *);
+
+/* netdev interface */
+extern int i1480u_open(struct net_device *);
+extern int i1480u_stop(struct net_device *);
+extern int i1480u_hard_start_xmit(struct sk_buff *, struct net_device *);
+extern void i1480u_tx_timeout(struct net_device *);
+extern int i1480u_set_config(struct net_device *, struct ifmap *);
+extern struct net_device_stats *i1480u_get_stats(struct net_device *);
+extern int i1480u_change_mtu(struct net_device *, int);
+extern void i1480u_uwb_notifs_cb(void *, struct uwb_dev *, enum uwb_notifs);
+
+/* bandwidth allocation callback */
+extern void i1480u_bw_alloc_cb(struct uwb_rsv *);
+
+/* Sys FS */
+extern struct attribute_group i1480u_wlp_attr_group;
+
+#endif /* #ifndef __i1480u_wlp_h__ */
diff --git a/drivers/uwb/i1480/i1480u-wlp/lc.c b/drivers/uwb/i1480/i1480u-wlp/lc.c
new file mode 100644
index 000000000000..737d60cd5b73
--- /dev/null
+++ b/drivers/uwb/i1480/i1480u-wlp/lc.c
@@ -0,0 +1,421 @@
+/*
+ * WUSB Wire Adapter: WLP interface
+ * Driver for the Linux Network stack.
+ *
+ * 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
+ *
+ * This implements a very simple network driver for the WLP USB
+ * device that is associated to a UWB (Ultra Wide Band) host.
+ *
+ * This is seen as an interface of a composite device. Once the UWB
+ * host has an association to another WLP capable device, the
+ * networking interface (aka WLP) can start to send packets back and
+ * forth.
+ *
+ * Limitations:
+ *
+ * - Hand cranked; can't ifup the interface until there is an association
+ *
+ * - BW allocation very simplistic [see i1480u_mas_set() and callees].
+ *
+ *
+ * ROADMAP:
+ *
+ * ENTRY POINTS (driver model):
+ *
+ * i1480u_driver_{exit,init}(): initialization of the driver.
+ *
+ * i1480u_probe(): called by the driver code when a device
+ * matching 'i1480u_id_table' is connected.
+ *
+ * This allocs a netdev instance, inits with
+ * i1480u_add(), then registers_netdev().
+ * i1480u_init()
+ * i1480u_add()
+ *
+ * i1480u_disconnect(): device has been disconnected/module
+ * is being removed.
+ * i1480u_rm()
+ */
+#include <linux/version.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/uwb/debug.h>
+#include "i1480u-wlp.h"
+
+
+
+static inline
+void i1480u_init(struct i1480u *i1480u)
+{
+ /* nothing so far... doesn't it suck? */
+ spin_lock_init(&i1480u->lock);
+ INIT_LIST_HEAD(&i1480u->tx_list);
+ spin_lock_init(&i1480u->tx_list_lock);
+ wlp_options_init(&i1480u->options);
+ edc_init(&i1480u->tx_errors);
+ edc_init(&i1480u->rx_errors);
+#ifdef i1480u_FLOW_CONTROL
+ edc_init(&i1480u->notif_edc);
+#endif
+ stats_init(&i1480u->lqe_stats);
+ stats_init(&i1480u->rssi_stats);
+ wlp_init(&i1480u->wlp);
+}
+
+/**
+ * Fill WLP device information structure
+ *
+ * The structure will contain a few character arrays, each ending with a
+ * null terminated string. Each string has to fit (excluding terminating
+ * character) into a specified range obtained from the WLP substack.
+ *
+ * It is still not clear exactly how this device information should be
+ * obtained. Until we find out we use the USB device descriptor as backup, some
+ * information elements have intuitive mappings, other not.
+ */
+static
+void i1480u_fill_device_info(struct wlp *wlp, struct wlp_device_info *dev_info)
+{
+ struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp);
+ struct usb_device *usb_dev = i1480u->usb_dev;
+ /* Treat device name and model name the same */
+ if (usb_dev->descriptor.iProduct) {
+ usb_string(usb_dev, usb_dev->descriptor.iProduct,
+ dev_info->name, sizeof(dev_info->name));
+ usb_string(usb_dev, usb_dev->descriptor.iProduct,
+ dev_info->model_name, sizeof(dev_info->model_name));
+ }
+ if (usb_dev->descriptor.iManufacturer)
+ usb_string(usb_dev, usb_dev->descriptor.iManufacturer,
+ dev_info->manufacturer,
+ sizeof(dev_info->manufacturer));
+ scnprintf(dev_info->model_nr, sizeof(dev_info->model_nr), "%04x",
+ __le16_to_cpu(usb_dev->descriptor.bcdDevice));
+ if (usb_dev->descriptor.iSerialNumber)
+ usb_string(usb_dev, usb_dev->descriptor.iSerialNumber,
+ dev_info->serial, sizeof(dev_info->serial));
+ /* FIXME: where should we obtain category? */
+ dev_info->prim_dev_type.category = cpu_to_le16(WLP_DEV_CAT_OTHER);
+ /* FIXME: Complete OUI and OUIsubdiv attributes */
+}
+
+#ifdef i1480u_FLOW_CONTROL
+/**
+ * Callback for the notification endpoint
+ *
+ * This mostly controls the xon/xoff protocol. In case of hard error,
+ * we stop the queue. If not, we always retry.
+ */
+static
+void i1480u_notif_cb(struct urb *urb, struct pt_regs *regs)
+{
+ struct i1480u *i1480u = urb->context;
+ struct usb_interface *usb_iface = i1480u->usb_iface;
+ struct device *dev = &usb_iface->dev;
+ int result;
+
+ switch (urb->status) {
+ case 0: /* Got valid data, do xon/xoff */
+ switch (i1480u->notif_buffer[0]) {
+ case 'N':
+ dev_err(dev, "XOFF STOPPING queue at %lu\n", jiffies);
+ netif_stop_queue(i1480u->net_dev);
+ break;
+ case 'A':
+ dev_err(dev, "XON STARTING queue at %lu\n", jiffies);
+ netif_start_queue(i1480u->net_dev);
+ break;
+ default:
+ dev_err(dev, "NEP: unknown data 0x%02hhx\n",
+ i1480u->notif_buffer[0]);
+ }
+ break;
+ case -ECONNRESET: /* Controlled situation ... */
+ case -ENOENT: /* we killed the URB... */
+ dev_err(dev, "NEP: URB reset/noent %d\n", urb->status);
+ goto error;
+ case -ESHUTDOWN: /* going away! */
+ dev_err(dev, "NEP: URB down %d\n", urb->status);
+ goto error;
+ default: /* Retry unless it gets ugly */
+ if (edc_inc(&i1480u->notif_edc, EDC_MAX_ERRORS,
+ EDC_ERROR_TIMEFRAME)) {
+ dev_err(dev, "NEP: URB max acceptable errors "
+ "exceeded; resetting device\n");
+ goto error_reset;
+ }
+ dev_err(dev, "NEP: URB error %d\n", urb->status);
+ break;
+ }
+ result = usb_submit_urb(urb, GFP_ATOMIC);
+ if (result < 0) {
+ dev_err(dev, "NEP: Can't resubmit URB: %d; resetting device\n",
+ result);
+ goto error_reset;
+ }
+ return;
+
+error_reset:
+ wlp_reset_all(&i1480-wlp);
+error:
+ netif_stop_queue(i1480u->net_dev);
+ return;
+}
+#endif
+
+static
+int i1480u_add(struct i1480u *i1480u, struct usb_interface *iface)
+{
+ int result = -ENODEV;
+ struct wlp *wlp = &i1480u->wlp;
+ struct usb_device *usb_dev = interface_to_usbdev(iface);
+ struct net_device *net_dev = i1480u->net_dev;
+ struct uwb_rc *rc;
+ struct uwb_dev *uwb_dev;
+#ifdef i1480u_FLOW_CONTROL
+ struct usb_endpoint_descriptor *epd;
+#endif
+
+ i1480u->usb_dev = usb_get_dev(usb_dev);
+ i1480u->usb_iface = iface;
+ rc = uwb_rc_get_by_grandpa(&i1480u->usb_dev->dev);
+ if (rc == NULL) {
+ dev_err(&iface->dev, "Cannot get associated UWB Radio "
+ "Controller\n");
+ goto out;
+ }
+ wlp->xmit_frame = i1480u_xmit_frame;
+ wlp->fill_device_info = i1480u_fill_device_info;
+ wlp->stop_queue = i1480u_stop_queue;
+ wlp->start_queue = i1480u_start_queue;
+ result = wlp_setup(wlp, rc);
+ if (result < 0) {
+ dev_err(&iface->dev, "Cannot setup WLP\n");
+ goto error_wlp_setup;
+ }
+ result = 0;
+ ether_setup(net_dev); /* make it an etherdevice */
+ uwb_dev = &rc->uwb_dev;
+ /* FIXME: hookup address change notifications? */
+
+ memcpy(net_dev->dev_addr, uwb_dev->mac_addr.data,
+ sizeof(net_dev->dev_addr));
+
+ net_dev->hard_header_len = sizeof(struct untd_hdr_cmp)
+ + sizeof(struct wlp_tx_hdr)
+ + WLP_DATA_HLEN
+ + ETH_HLEN;
+ net_dev->mtu = 3500;
+ net_dev->tx_queue_len = 20; /* FIXME: maybe use 1000? */
+
+/* net_dev->flags &= ~IFF_BROADCAST; FIXME: BUG in firmware */
+ /* FIXME: multicast disabled */
+ net_dev->flags &= ~IFF_MULTICAST;
+ net_dev->features &= ~NETIF_F_SG;
+ net_dev->features &= ~NETIF_F_FRAGLIST;
+ /* All NETIF_F_*_CSUM disabled */
+ net_dev->features |= NETIF_F_HIGHDMA;
+ net_dev->watchdog_timeo = 5*HZ; /* FIXME: a better default? */
+
+ net_dev->open = i1480u_open;
+ net_dev->stop = i1480u_stop;
+ net_dev->hard_start_xmit = i1480u_hard_start_xmit;
+ net_dev->tx_timeout = i1480u_tx_timeout;
+ net_dev->get_stats = i1480u_get_stats;
+ net_dev->set_config = i1480u_set_config;
+ net_dev->change_mtu = i1480u_change_mtu;
+
+#ifdef i1480u_FLOW_CONTROL
+ /* Notification endpoint setup (submitted when we open the device) */
+ i1480u->notif_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (i1480u->notif_urb == NULL) {
+ dev_err(&iface->dev, "Unable to allocate notification URB\n");
+ result = -ENOMEM;
+ goto error_urb_alloc;
+ }
+ epd = &iface->cur_altsetting->endpoint[0].desc;
+ usb_fill_int_urb(i1480u->notif_urb, usb_dev,
+ usb_rcvintpipe(usb_dev, epd->bEndpointAddress),
+ i1480u->notif_buffer, sizeof(i1480u->notif_buffer),
+ i1480u_notif_cb, i1480u, epd->bInterval);
+
+#endif
+
+ i1480u->tx_inflight.max = i1480u_TX_INFLIGHT_MAX;
+ i1480u->tx_inflight.threshold = i1480u_TX_INFLIGHT_THRESHOLD;
+ i1480u->tx_inflight.restart_ts = jiffies;
+ usb_set_intfdata(iface, i1480u);
+ return result;
+
+#ifdef i1480u_FLOW_CONTROL
+error_urb_alloc:
+#endif
+ wlp_remove(wlp);
+error_wlp_setup:
+ uwb_rc_put(rc);
+out:
+ usb_put_dev(i1480u->usb_dev);
+ return result;
+}
+
+static void i1480u_rm(struct i1480u *i1480u)
+{
+ struct uwb_rc *rc = i1480u->wlp.rc;
+ usb_set_intfdata(i1480u->usb_iface, NULL);
+#ifdef i1480u_FLOW_CONTROL
+ usb_kill_urb(i1480u->notif_urb);
+ usb_free_urb(i1480u->notif_urb);
+#endif
+ wlp_remove(&i1480u->wlp);
+ uwb_rc_put(rc);
+ usb_put_dev(i1480u->usb_dev);
+}
+
+/** Just setup @net_dev's i1480u private data */
+static void i1480u_netdev_setup(struct net_device *net_dev)
+{
+ struct i1480u *i1480u = netdev_priv(net_dev);
+ /* Initialize @i1480u */
+ memset(i1480u, 0, sizeof(*i1480u));
+ i1480u_init(i1480u);
+}
+
+/**
+ * Probe a i1480u interface and register it
+ *
+ * @iface: USB interface to link to
+ * @id: USB class/subclass/protocol id
+ * @returns: 0 if ok, < 0 errno code on error.
+ *
+ * Does basic housekeeping stuff and then allocs a netdev with space
+ * for the i1480u data. Initializes, registers in i1480u, registers in
+ * netdev, ready to go.
+ */
+static int i1480u_probe(struct usb_interface *iface,
+ const struct usb_device_id *id)
+{
+ int result;
+ struct net_device *net_dev;
+ struct device *dev = &iface->dev;
+ struct i1480u *i1480u;
+
+ /* Allocate instance [calls i1480u_netdev_setup() on it] */
+ result = -ENOMEM;
+ net_dev = alloc_netdev(sizeof(*i1480u), "wlp%d", i1480u_netdev_setup);
+ if (net_dev == NULL) {
+ dev_err(dev, "no memory for network device instance\n");
+ goto error_alloc_netdev;
+ }
+ SET_NETDEV_DEV(net_dev, dev);
+ i1480u = netdev_priv(net_dev);
+ i1480u->net_dev = net_dev;
+ result = i1480u_add(i1480u, iface); /* Now setup all the wlp stuff */
+ if (result < 0) {
+ dev_err(dev, "cannot add i1480u device: %d\n", result);
+ goto error_i1480u_add;
+ }
+ result = register_netdev(net_dev); /* Okey dokey, bring it up */
+ if (result < 0) {
+ dev_err(dev, "cannot register network device: %d\n", result);
+ goto error_register_netdev;
+ }
+ i1480u_sysfs_setup(i1480u);
+ if (result < 0)
+ goto error_sysfs_init;
+ return 0;
+
+error_sysfs_init:
+ unregister_netdev(net_dev);
+error_register_netdev:
+ i1480u_rm(i1480u);
+error_i1480u_add:
+ free_netdev(net_dev);
+error_alloc_netdev:
+ return result;
+}
+
+
+/**
+ * Disconect a i1480u from the system.
+ *
+ * i1480u_stop() has been called before, so al the rx and tx contexts
+ * have been taken down already. Make sure the queue is stopped,
+ * unregister netdev and i1480u, free and kill.
+ */
+static void i1480u_disconnect(struct usb_interface *iface)
+{
+ struct i1480u *i1480u;
+ struct net_device *net_dev;
+
+ i1480u = usb_get_intfdata(iface);
+ net_dev = i1480u->net_dev;
+ netif_stop_queue(net_dev);
+#ifdef i1480u_FLOW_CONTROL
+ usb_kill_urb(i1480u->notif_urb);
+#endif
+ i1480u_sysfs_release(i1480u);
+ unregister_netdev(net_dev);
+ i1480u_rm(i1480u);
+ free_netdev(net_dev);
+}
+
+static struct usb_device_id i1480u_id_table[] = {
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE \
+ | USB_DEVICE_ID_MATCH_DEV_INFO \
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x8086,
+ .idProduct = 0x0c3b,
+ .bDeviceClass = 0xef,
+ .bDeviceSubClass = 0x02,
+ .bDeviceProtocol = 0x02,
+ .bInterfaceClass = 0xff,
+ .bInterfaceSubClass = 0xff,
+ .bInterfaceProtocol = 0xff,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(usb, i1480u_id_table);
+
+static struct usb_driver i1480u_driver = {
+ .name = KBUILD_MODNAME,
+ .probe = i1480u_probe,
+ .disconnect = i1480u_disconnect,
+ .id_table = i1480u_id_table,
+};
+
+static int __init i1480u_driver_init(void)
+{
+ return usb_register(&i1480u_driver);
+}
+module_init(i1480u_driver_init);
+
+
+static void __exit i1480u_driver_exit(void)
+{
+ usb_deregister(&i1480u_driver);
+}
+module_exit(i1480u_driver_exit);
+
+MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
+MODULE_DESCRIPTION("i1480 Wireless UWB Link WLP networking for USB");
+MODULE_LICENSE("GPL");
diff --git a/drivers/uwb/i1480/i1480u-wlp/netdev.c b/drivers/uwb/i1480/i1480u-wlp/netdev.c
new file mode 100644
index 000000000000..8802ac43d872
--- /dev/null
+++ b/drivers/uwb/i1480/i1480u-wlp/netdev.c
@@ -0,0 +1,368 @@
+/*
+ * WUSB Wire Adapter: WLP interface
+ * Driver for the Linux Network stack.
+ *
+ * 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
+ *
+ * Implementation of the netdevice linkage (except tx and rx related stuff).
+ *
+ * ROADMAP:
+ *
+ * ENTRY POINTS (Net device):
+ *
+ * i1480u_open(): Called when we ifconfig up the interface;
+ * associates to a UWB host controller, reserves
+ * bandwidth (MAS), sets up RX USB URB and starts
+ * the queue.
+ *
+ * i1480u_stop(): Called when we ifconfig down a interface;
+ * reverses _open().
+ *
+ * i1480u_set_config():
+ */
+
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/uwb/debug.h>
+#include "i1480u-wlp.h"
+
+struct i1480u_cmd_set_ip_mas {
+ struct uwb_rccb rccb;
+ struct uwb_dev_addr addr;
+ u8 stream;
+ u8 owner;
+ u8 type; /* enum uwb_drp_type */
+ u8 baMAS[32];
+} __attribute__((packed));
+
+
+static
+int i1480u_set_ip_mas(
+ struct uwb_rc *rc,
+ const struct uwb_dev_addr *dstaddr,
+ u8 stream, u8 owner, u8 type, unsigned long *mas)
+{
+
+ int result;
+ struct i1480u_cmd_set_ip_mas *cmd;
+ struct uwb_rc_evt_confirm reply;
+
+ result = -ENOMEM;
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (cmd == NULL)
+ goto error_kzalloc;
+ cmd->rccb.bCommandType = 0xfd;
+ cmd->rccb.wCommand = cpu_to_le16(0x000e);
+ cmd->addr = *dstaddr;
+ cmd->stream = stream;
+ cmd->owner = owner;
+ cmd->type = type;
+ if (mas == NULL)
+ memset(cmd->baMAS, 0x00, sizeof(cmd->baMAS));
+ else
+ memcpy(cmd->baMAS, mas, sizeof(cmd->baMAS));
+ reply.rceb.bEventType = 0xfd;
+ reply.rceb.wEvent = cpu_to_le16(0x000e);
+ result = uwb_rc_cmd(rc, "SET-IP-MAS", &cmd->rccb, sizeof(*cmd),
+ &reply.rceb, sizeof(reply));
+ if (result < 0)
+ goto error_cmd;
+ if (reply.bResultCode != UWB_RC_RES_FAIL) {
+ dev_err(&rc->uwb_dev.dev,
+ "SET-IP-MAS: command execution failed: %d\n",
+ reply.bResultCode);
+ result = -EIO;
+ }
+error_cmd:
+ kfree(cmd);
+error_kzalloc:
+ return result;
+}
+
+/*
+ * Inform a WLP interface of a MAS reservation
+ *
+ * @rc is assumed refcnted.
+ */
+/* FIXME: detect if remote device is WLP capable? */
+static int i1480u_mas_set_dev(struct uwb_dev *uwb_dev, struct uwb_rc *rc,
+ u8 stream, u8 owner, u8 type, unsigned long *mas)
+{
+ int result = 0;
+ struct device *dev = &rc->uwb_dev.dev;
+
+ result = i1480u_set_ip_mas(rc, &uwb_dev->dev_addr, stream, owner,
+ type, mas);
+ if (result < 0) {
+ char rcaddrbuf[UWB_ADDR_STRSIZE], devaddrbuf[UWB_ADDR_STRSIZE];
+ uwb_dev_addr_print(rcaddrbuf, sizeof(rcaddrbuf),
+ &rc->uwb_dev.dev_addr);
+ uwb_dev_addr_print(devaddrbuf, sizeof(devaddrbuf),
+ &uwb_dev->dev_addr);
+ dev_err(dev, "Set IP MAS (%s to %s) failed: %d\n",
+ rcaddrbuf, devaddrbuf, result);
+ }
+ return result;
+}
+
+/**
+ * Called by bandwidth allocator when change occurs in reservation.
+ *
+ * @rsv: The reservation that is being established, modified, or
+ * terminated.
+ *
+ * When a reservation is established, modified, or terminated the upper layer
+ * (WLP here) needs set/update the currently available Media Access Slots
+ * that can be use for IP traffic.
+ *
+ * Our action taken during failure depends on how the reservation is being
+ * changed:
+ * - if reservation is being established we do nothing if we cannot set the
+ * new MAS to be used
+ * - if reservation is being terminated we revert back to PCA whether the
+ * SET IP MAS command succeeds or not.
+ */
+void i1480u_bw_alloc_cb(struct uwb_rsv *rsv)
+{
+ int result = 0;
+ struct i1480u *i1480u = rsv->pal_priv;
+ struct device *dev = &i1480u->usb_iface->dev;
+ struct uwb_dev *target_dev = rsv->target.dev;
+ struct uwb_rc *rc = i1480u->wlp.rc;
+ u8 stream = rsv->stream;
+ int type = rsv->type;
+ int is_owner = rsv->owner == &rc->uwb_dev;
+ unsigned long *bmp = rsv->mas.bm;
+
+ dev_err(dev, "WLP callback called - sending set ip mas\n");
+ /*user cannot change options while setting configuration*/
+ mutex_lock(&i1480u->options.mutex);
+ switch (rsv->state) {
+ case UWB_RSV_STATE_T_ACCEPTED:
+ case UWB_RSV_STATE_O_ESTABLISHED:
+ result = i1480u_mas_set_dev(target_dev, rc, stream, is_owner,
+ type, bmp);
+ if (result < 0) {
+ dev_err(dev, "MAS reservation failed: %d\n", result);
+ goto out;
+ }
+ if (is_owner) {
+ wlp_tx_hdr_set_delivery_id_type(&i1480u->options.def_tx_hdr,
+ WLP_DRP | stream);
+ wlp_tx_hdr_set_rts_cts(&i1480u->options.def_tx_hdr, 0);
+ }
+ break;
+ case UWB_RSV_STATE_NONE:
+ /* revert back to PCA */
+ result = i1480u_mas_set_dev(target_dev, rc, stream, is_owner,
+ type, bmp);
+ if (result < 0)
+ dev_err(dev, "MAS reservation failed: %d\n", result);
+ /* Revert to PCA even though SET IP MAS failed. */
+ wlp_tx_hdr_set_delivery_id_type(&i1480u->options.def_tx_hdr,
+ i1480u->options.pca_base_priority);
+ wlp_tx_hdr_set_rts_cts(&i1480u->options.def_tx_hdr, 1);
+ break;
+ default:
+ dev_err(dev, "unexpected WLP reservation state: %s (%d).\n",
+ uwb_rsv_state_str(rsv->state), rsv->state);
+ break;
+ }
+out:
+ mutex_unlock(&i1480u->options.mutex);
+ return;
+}
+
+/**
+ *
+ * Called on 'ifconfig up'
+ */
+int i1480u_open(struct net_device *net_dev)
+{
+ int result;
+ struct i1480u *i1480u = netdev_priv(net_dev);
+ struct wlp *wlp = &i1480u->wlp;
+ struct uwb_rc *rc;
+ struct device *dev = &i1480u->usb_iface->dev;
+
+ rc = wlp->rc;
+ result = i1480u_rx_setup(i1480u); /* Alloc RX stuff */
+ if (result < 0)
+ goto error_rx_setup;
+ netif_wake_queue(net_dev);
+#ifdef i1480u_FLOW_CONTROL
+ result = usb_submit_urb(i1480u->notif_urb, GFP_KERNEL);;
+ if (result < 0) {
+ dev_err(dev, "Can't submit notification URB: %d\n", result);
+ goto error_notif_urb_submit;
+ }
+#endif
+ i1480u->uwb_notifs_handler.cb = i1480u_uwb_notifs_cb;
+ i1480u->uwb_notifs_handler.data = i1480u;
+ if (uwb_bg_joined(rc))
+ netif_carrier_on(net_dev);
+ else
+ netif_carrier_off(net_dev);
+ uwb_notifs_register(rc, &i1480u->uwb_notifs_handler);
+ /* Interface is up with an address, now we can create WSS */
+ result = wlp_wss_setup(net_dev, &wlp->wss);
+ if (result < 0) {
+ dev_err(dev, "Can't create WSS: %d. \n", result);
+ goto error_notif_deregister;
+ }
+ return 0;
+error_notif_deregister:
+ uwb_notifs_deregister(rc, &i1480u->uwb_notifs_handler);
+#ifdef i1480u_FLOW_CONTROL
+error_notif_urb_submit:
+#endif
+ netif_stop_queue(net_dev);
+ i1480u_rx_release(i1480u);
+error_rx_setup:
+ return result;
+}
+
+
+/**
+ * Called on 'ifconfig down'
+ */
+int i1480u_stop(struct net_device *net_dev)
+{
+ struct i1480u *i1480u = netdev_priv(net_dev);
+ struct wlp *wlp = &i1480u->wlp;
+ struct uwb_rc *rc = wlp->rc;
+
+ BUG_ON(wlp->rc == NULL);
+ wlp_wss_remove(&wlp->wss);
+ uwb_notifs_deregister(rc, &i1480u->uwb_notifs_handler);
+ netif_carrier_off(net_dev);
+#ifdef i1480u_FLOW_CONTROL
+ usb_kill_urb(i1480u->notif_urb);
+#endif
+ netif_stop_queue(net_dev);
+ i1480u_rx_release(i1480u);
+ i1480u_tx_release(i1480u);
+ return 0;
+}
+
+
+/** Report statistics */
+struct net_device_stats *i1480u_get_stats(struct net_device *net_dev)
+{
+ struct i1480u *i1480u = netdev_priv(net_dev);
+ return &i1480u->stats;
+}
+
+
+/**
+ *
+ * Change the interface config--we probably don't have to do anything.
+ */
+int i1480u_set_config(struct net_device *net_dev, struct ifmap *map)
+{
+ int result;
+ struct i1480u *i1480u = netdev_priv(net_dev);
+ BUG_ON(i1480u->wlp.rc == NULL);
+ result = 0;
+ return result;
+}
+
+/**
+ * Change the MTU of the interface
+ */
+int i1480u_change_mtu(struct net_device *net_dev, int mtu)
+{
+ static union {
+ struct wlp_tx_hdr tx;
+ struct wlp_rx_hdr rx;
+ } i1480u_all_hdrs;
+
+ if (mtu < ETH_HLEN) /* We encap eth frames */
+ return -ERANGE;
+ if (mtu > 4000 - sizeof(i1480u_all_hdrs))
+ return -ERANGE;
+ net_dev->mtu = mtu;
+ return 0;
+}
+
+
+/**
+ * Callback function to handle events from UWB
+ * When we see other devices we know the carrier is ok,
+ * if we are the only device in the beacon group we set the carrier
+ * state to off.
+ * */
+void i1480u_uwb_notifs_cb(void *data, struct uwb_dev *uwb_dev,
+ enum uwb_notifs event)
+{
+ struct i1480u *i1480u = data;
+ struct net_device *net_dev = i1480u->net_dev;
+ struct device *dev = &i1480u->usb_iface->dev;
+ switch (event) {
+ case UWB_NOTIF_BG_JOIN:
+ netif_carrier_on(net_dev);
+ dev_info(dev, "Link is up\n");
+ break;
+ case UWB_NOTIF_BG_LEAVE:
+ netif_carrier_off(net_dev);
+ dev_info(dev, "Link is down\n");
+ break;
+ default:
+ dev_err(dev, "don't know how to handle event %d from uwb\n",
+ event);
+ }
+}
+
+/**
+ * Stop the network queue
+ *
+ * Enable WLP substack to stop network queue. We also set the flow control
+ * threshold at this time to prevent the flow control from restarting the
+ * queue.
+ *
+ * we are loosing the current threshold value here ... FIXME?
+ */
+void i1480u_stop_queue(struct wlp *wlp)
+{
+ struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp);
+ struct net_device *net_dev = i1480u->net_dev;
+ i1480u->tx_inflight.threshold = 0;
+ netif_stop_queue(net_dev);
+}
+
+/**
+ * Start the network queue
+ *
+ * Enable WLP substack to start network queue. Also re-enable the flow
+ * control to manage the queue again.
+ *
+ * We re-enable the flow control by storing the default threshold in the
+ * flow control threshold. This means that if the user modified the
+ * threshold before the queue was stopped and restarted that information
+ * will be lost. FIXME?
+ */
+void i1480u_start_queue(struct wlp *wlp)
+{
+ struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp);
+ struct net_device *net_dev = i1480u->net_dev;
+ i1480u->tx_inflight.threshold = i1480u_TX_INFLIGHT_THRESHOLD;
+ netif_start_queue(net_dev);
+}
diff --git a/drivers/uwb/i1480/i1480u-wlp/rx.c b/drivers/uwb/i1480/i1480u-wlp/rx.c
new file mode 100644
index 000000000000..9fc035354a76
--- /dev/null
+++ b/drivers/uwb/i1480/i1480u-wlp/rx.c
@@ -0,0 +1,486 @@
+/*
+ * WUSB Wire Adapter: WLP interface
+ * Driver for the Linux Network stack.
+ *
+ * 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.
+ *
+ *
+ * i1480u's RX handling is simple. i1480u will send the received
+ * network packets broken up in fragments; 1 to N fragments make a
+ * packet, we assemble them together and deliver the packet with netif_rx().
+ *
+ * Beacuse each USB transfer is a *single* fragment (except when the
+ * transfer contains a first fragment), each URB called thus
+ * back contains one or two fragments. So we queue N URBs, each with its own
+ * fragment buffer. When a URB is done, we process it (adding to the
+ * current skb from the fragment buffer until complete). Once
+ * processed, we requeue the URB. There is always a bunch of URBs
+ * ready to take data, so the intergap should be minimal.
+ *
+ * An URB's transfer buffer is the data field of a socket buffer. This
+ * reduces copying as data can be passed directly to network layer. If a
+ * complete packet or 1st fragment is received the URB's transfer buffer is
+ * taken away from it and used to send data to the network layer. In this
+ * case a new transfer buffer is allocated to the URB before being requeued.
+ * If a "NEXT" or "LAST" fragment is received, the fragment contents is
+ * appended to the RX packet under construction and the transfer buffer
+ * is reused. To be able to use this buffer to assemble complete packets
+ * we set each buffer's size to that of the MAX ethernet packet that can
+ * be received. There is thus room for improvement in memory usage.
+ *
+ * When the max tx fragment size increases, we should be able to read
+ * data into the skbs directly with very simple code.
+ *
+ * ROADMAP:
+ *
+ * ENTRY POINTS:
+ *
+ * i1480u_rx_setup(): setup RX context [from i1480u_open()]
+ *
+ * i1480u_rx_release(): release RX context [from i1480u_stop()]
+ *
+ * i1480u_rx_cb(): called when the RX USB URB receives a
+ * packet. It removes the header and pushes it up
+ * the Linux netdev stack with netif_rx().
+ *
+ * i1480u_rx_buffer()
+ * i1480u_drop() and i1480u_fix()
+ * i1480u_skb_deliver
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include "i1480u-wlp.h"
+
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+
+/**
+ * Setup the RX context
+ *
+ * Each URB is provided with a transfer_buffer that is the data field
+ * of a new socket buffer.
+ */
+int i1480u_rx_setup(struct i1480u *i1480u)
+{
+ int result, cnt;
+ struct device *dev = &i1480u->usb_iface->dev;
+ struct net_device *net_dev = i1480u->net_dev;
+ struct usb_endpoint_descriptor *epd;
+ struct sk_buff *skb;
+
+ /* Alloc RX stuff */
+ i1480u->rx_skb = NULL; /* not in process of receiving packet */
+ result = -ENOMEM;
+ epd = &i1480u->usb_iface->cur_altsetting->endpoint[1].desc;
+ for (cnt = 0; cnt < i1480u_RX_BUFS; cnt++) {
+ struct i1480u_rx_buf *rx_buf = &i1480u->rx_buf[cnt];
+ rx_buf->i1480u = i1480u;
+ skb = dev_alloc_skb(i1480u_MAX_RX_PKT_SIZE);
+ if (!skb) {
+ dev_err(dev,
+ "RX: cannot allocate RX buffer %d\n", cnt);
+ result = -ENOMEM;
+ goto error;
+ }
+ skb->dev = net_dev;
+ skb->ip_summed = CHECKSUM_NONE;
+ skb_reserve(skb, 2);
+ rx_buf->data = skb;
+ rx_buf->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (unlikely(rx_buf->urb == NULL)) {
+ dev_err(dev, "RX: cannot allocate URB %d\n", cnt);
+ result = -ENOMEM;
+ goto error;
+ }
+ usb_fill_bulk_urb(rx_buf->urb, i1480u->usb_dev,
+ usb_rcvbulkpipe(i1480u->usb_dev, epd->bEndpointAddress),
+ rx_buf->data->data, i1480u_MAX_RX_PKT_SIZE - 2,
+ i1480u_rx_cb, rx_buf);
+ result = usb_submit_urb(rx_buf->urb, GFP_NOIO);
+ if (unlikely(result < 0)) {
+ dev_err(dev, "RX: cannot submit URB %d: %d\n",
+ cnt, result);
+ goto error;
+ }
+ }
+ return 0;
+
+error:
+ i1480u_rx_release(i1480u);
+ return result;
+}
+
+
+/** Release resources associated to the rx context */
+void i1480u_rx_release(struct i1480u *i1480u)
+{
+ int cnt;
+ for (cnt = 0; cnt < i1480u_RX_BUFS; cnt++) {
+ if (i1480u->rx_buf[cnt].data)
+ dev_kfree_skb(i1480u->rx_buf[cnt].data);
+ if (i1480u->rx_buf[cnt].urb) {
+ usb_kill_urb(i1480u->rx_buf[cnt].urb);
+ usb_free_urb(i1480u->rx_buf[cnt].urb);
+ }
+ }
+ if (i1480u->rx_skb != NULL)
+ dev_kfree_skb(i1480u->rx_skb);
+}
+
+static
+void i1480u_rx_unlink_urbs(struct i1480u *i1480u)
+{
+ int cnt;
+ for (cnt = 0; cnt < i1480u_RX_BUFS; cnt++) {
+ if (i1480u->rx_buf[cnt].urb)
+ usb_unlink_urb(i1480u->rx_buf[cnt].urb);
+ }
+}
+
+/** Fix an out-of-sequence packet */
+#define i1480u_fix(i1480u, msg...) \
+do { \
+ if (printk_ratelimit()) \
+ dev_err(&i1480u->usb_iface->dev, msg); \
+ dev_kfree_skb_irq(i1480u->rx_skb); \
+ i1480u->rx_skb = NULL; \
+ i1480u->rx_untd_pkt_size = 0; \
+} while (0)
+
+
+/** Drop an out-of-sequence packet */
+#define i1480u_drop(i1480u, msg...) \
+do { \
+ if (printk_ratelimit()) \
+ dev_err(&i1480u->usb_iface->dev, msg); \
+ i1480u->stats.rx_dropped++; \
+} while (0)
+
+
+
+
+/** Finalizes setting up the SKB and delivers it
+ *
+ * We first pass the incoming frame to WLP substack for verification. It
+ * may also be a WLP association frame in which case WLP will take over the
+ * processing. If WLP does not take it over it will still verify it, if the
+ * frame is invalid the skb will be freed by WLP and we will not continue
+ * parsing.
+ * */
+static
+void i1480u_skb_deliver(struct i1480u *i1480u)
+{
+ int should_parse;
+ struct net_device *net_dev = i1480u->net_dev;
+ struct device *dev = &i1480u->usb_iface->dev;
+
+ d_printf(6, dev, "RX delivered pre skb(%p), %u bytes\n",
+ i1480u->rx_skb, i1480u->rx_skb->len);
+ d_dump(7, dev, i1480u->rx_skb->data, i1480u->rx_skb->len);
+ should_parse = wlp_receive_frame(dev, &i1480u->wlp, i1480u->rx_skb,
+ &i1480u->rx_srcaddr);
+ if (!should_parse)
+ goto out;
+ i1480u->rx_skb->protocol = eth_type_trans(i1480u->rx_skb, net_dev);
+ d_printf(5, dev, "RX delivered skb(%p), %u bytes\n",
+ i1480u->rx_skb, i1480u->rx_skb->len);
+ d_dump(7, dev, i1480u->rx_skb->data,
+ i1480u->rx_skb->len > 72 ? 72 : i1480u->rx_skb->len);
+ i1480u->stats.rx_packets++;
+ i1480u->stats.rx_bytes += i1480u->rx_untd_pkt_size;
+ net_dev->last_rx = jiffies;
+ /* FIXME: flow control: check netif_rx() retval */
+
+ netif_rx(i1480u->rx_skb); /* deliver */
+out:
+ i1480u->rx_skb = NULL;
+ i1480u->rx_untd_pkt_size = 0;
+}
+
+
+/**
+ * Process a buffer of data received from the USB RX endpoint
+ *
+ * First fragment arrives with next or last fragment. All other fragments
+ * arrive alone.
+ *
+ * /me hates long functions.
+ */
+static
+void i1480u_rx_buffer(struct i1480u_rx_buf *rx_buf)
+{
+ unsigned pkt_completed = 0; /* !0 when we got all pkt fragments */
+ size_t untd_hdr_size, untd_frg_size;
+ size_t i1480u_hdr_size;
+ struct wlp_rx_hdr *i1480u_hdr = NULL;
+
+ struct i1480u *i1480u = rx_buf->i1480u;
+ struct sk_buff *skb = rx_buf->data;
+ int size_left = rx_buf->urb->actual_length;
+ void *ptr = rx_buf->urb->transfer_buffer; /* also rx_buf->data->data */
+ struct untd_hdr *untd_hdr;
+
+ struct net_device *net_dev = i1480u->net_dev;
+ struct device *dev = &i1480u->usb_iface->dev;
+ struct sk_buff *new_skb;
+
+#if 0
+ dev_fnstart(dev,
+ "(i1480u %p ptr %p size_left %zu)\n", i1480u, ptr, size_left);
+ dev_err(dev, "RX packet, %zu bytes\n", size_left);
+ dump_bytes(dev, ptr, size_left);
+#endif
+ i1480u_hdr_size = sizeof(struct wlp_rx_hdr);
+
+ while (size_left > 0) {
+ if (pkt_completed) {
+ i1480u_drop(i1480u, "RX: fragment follows completed"
+ "packet in same buffer. Dropping\n");
+ break;
+ }
+ untd_hdr = ptr;
+ if (size_left < sizeof(*untd_hdr)) { /* Check the UNTD header */
+ i1480u_drop(i1480u, "RX: short UNTD header! Dropping\n");
+ goto out;
+ }
+ if (unlikely(untd_hdr_rx_tx(untd_hdr) == 0)) { /* Paranoia: TX set? */
+ i1480u_drop(i1480u, "RX: TX bit set! Dropping\n");
+ goto out;
+ }
+ switch (untd_hdr_type(untd_hdr)) { /* Check the UNTD header type */
+ case i1480u_PKT_FRAG_1ST: {
+ struct untd_hdr_1st *untd_hdr_1st = (void *) untd_hdr;
+ dev_dbg(dev, "1st fragment\n");
+ untd_hdr_size = sizeof(struct untd_hdr_1st);
+ if (i1480u->rx_skb != NULL)
+ i1480u_fix(i1480u, "RX: 1st fragment out of "
+ "sequence! Fixing\n");
+ if (size_left < untd_hdr_size + i1480u_hdr_size) {
+ i1480u_drop(i1480u, "RX: short 1st fragment! "
+ "Dropping\n");
+ goto out;
+ }
+ i1480u->rx_untd_pkt_size = le16_to_cpu(untd_hdr->len)
+ - i1480u_hdr_size;
+ untd_frg_size = le16_to_cpu(untd_hdr_1st->fragment_len);
+ if (size_left < untd_hdr_size + untd_frg_size) {
+ i1480u_drop(i1480u,
+ "RX: short payload! Dropping\n");
+ goto out;
+ }
+ i1480u->rx_skb = skb;
+ i1480u_hdr = (void *) untd_hdr_1st + untd_hdr_size;
+ i1480u->rx_srcaddr = i1480u_hdr->srcaddr;
+ skb_put(i1480u->rx_skb, untd_hdr_size + untd_frg_size);
+ skb_pull(i1480u->rx_skb, untd_hdr_size + i1480u_hdr_size);
+ stats_add_sample(&i1480u->lqe_stats, (s8) i1480u_hdr->LQI - 7);
+ stats_add_sample(&i1480u->rssi_stats, i1480u_hdr->RSSI + 18);
+ rx_buf->data = NULL; /* need to create new buffer */
+ break;
+ }
+ case i1480u_PKT_FRAG_NXT: {
+ dev_dbg(dev, "nxt fragment\n");
+ untd_hdr_size = sizeof(struct untd_hdr_rst);
+ if (i1480u->rx_skb == NULL) {
+ i1480u_drop(i1480u, "RX: next fragment out of "
+ "sequence! Dropping\n");
+ goto out;
+ }
+ if (size_left < untd_hdr_size) {
+ i1480u_drop(i1480u, "RX: short NXT fragment! "
+ "Dropping\n");
+ goto out;
+ }
+ untd_frg_size = le16_to_cpu(untd_hdr->len);
+ if (size_left < untd_hdr_size + untd_frg_size) {
+ i1480u_drop(i1480u,
+ "RX: short payload! Dropping\n");
+ goto out;
+ }
+ memmove(skb_put(i1480u->rx_skb, untd_frg_size),
+ ptr + untd_hdr_size, untd_frg_size);
+ break;
+ }
+ case i1480u_PKT_FRAG_LST: {
+ dev_dbg(dev, "Lst fragment\n");
+ untd_hdr_size = sizeof(struct untd_hdr_rst);
+ if (i1480u->rx_skb == NULL) {
+ i1480u_drop(i1480u, "RX: last fragment out of "
+ "sequence! Dropping\n");
+ goto out;
+ }
+ if (size_left < untd_hdr_size) {
+ i1480u_drop(i1480u, "RX: short LST fragment! "
+ "Dropping\n");
+ goto out;
+ }
+ untd_frg_size = le16_to_cpu(untd_hdr->len);
+ if (size_left < untd_frg_size + untd_hdr_size) {
+ i1480u_drop(i1480u,
+ "RX: short payload! Dropping\n");
+ goto out;
+ }
+ memmove(skb_put(i1480u->rx_skb, untd_frg_size),
+ ptr + untd_hdr_size, untd_frg_size);
+ pkt_completed = 1;
+ break;
+ }
+ case i1480u_PKT_FRAG_CMP: {
+ dev_dbg(dev, "cmp fragment\n");
+ untd_hdr_size = sizeof(struct untd_hdr_cmp);
+ if (i1480u->rx_skb != NULL)
+ i1480u_fix(i1480u, "RX: fix out-of-sequence CMP"
+ " fragment!\n");
+ if (size_left < untd_hdr_size + i1480u_hdr_size) {
+ i1480u_drop(i1480u, "RX: short CMP fragment! "
+ "Dropping\n");
+ goto out;
+ }
+ i1480u->rx_untd_pkt_size = le16_to_cpu(untd_hdr->len);
+ untd_frg_size = i1480u->rx_untd_pkt_size;
+ if (size_left < i1480u->rx_untd_pkt_size + untd_hdr_size) {
+ i1480u_drop(i1480u,
+ "RX: short payload! Dropping\n");
+ goto out;
+ }
+ i1480u->rx_skb = skb;
+ i1480u_hdr = (void *) untd_hdr + untd_hdr_size;
+ i1480u->rx_srcaddr = i1480u_hdr->srcaddr;
+ stats_add_sample(&i1480u->lqe_stats, (s8) i1480u_hdr->LQI - 7);
+ stats_add_sample(&i1480u->rssi_stats, i1480u_hdr->RSSI + 18);
+ skb_put(i1480u->rx_skb, untd_hdr_size + i1480u->rx_untd_pkt_size);
+ skb_pull(i1480u->rx_skb, untd_hdr_size + i1480u_hdr_size);
+ rx_buf->data = NULL; /* for hand off skb to network stack */
+ pkt_completed = 1;
+ i1480u->rx_untd_pkt_size -= i1480u_hdr_size; /* accurate stat */
+ break;
+ }
+ default:
+ i1480u_drop(i1480u, "RX: unknown packet type %u! "
+ "Dropping\n", untd_hdr_type(untd_hdr));
+ goto out;
+ }
+ size_left -= untd_hdr_size + untd_frg_size;
+ if (size_left > 0)
+ ptr += untd_hdr_size + untd_frg_size;
+ }
+ if (pkt_completed)
+ i1480u_skb_deliver(i1480u);
+out:
+ /* recreate needed RX buffers*/
+ if (rx_buf->data == NULL) {
+ /* buffer is being used to receive packet, create new */
+ new_skb = dev_alloc_skb(i1480u_MAX_RX_PKT_SIZE);
+ if (!new_skb) {
+ if (printk_ratelimit())
+ dev_err(dev,
+ "RX: cannot allocate RX buffer\n");
+ } else {
+ new_skb->dev = net_dev;
+ new_skb->ip_summed = CHECKSUM_NONE;
+ skb_reserve(new_skb, 2);
+ rx_buf->data = new_skb;
+ }
+ }
+ return;
+}
+
+
+/**
+ * Called when an RX URB has finished receiving or has found some kind
+ * of error condition.
+ *
+ * LIMITATIONS:
+ *
+ * - We read USB-transfers, each transfer contains a SINGLE fragment
+ * (can contain a complete packet, or a 1st, next, or last fragment
+ * of a packet).
+ * Looks like a transfer can contain more than one fragment (07/18/06)
+ *
+ * - Each transfer buffer is the size of the maximum packet size (minus
+ * headroom), i1480u_MAX_PKT_SIZE - 2
+ *
+ * - We always read the full USB-transfer, no partials.
+ *
+ * - Each transfer is read directly into a skb. This skb will be used to
+ * send data to the upper layers if it is the first fragment or a complete
+ * packet. In the other cases the data will be copied from the skb to
+ * another skb that is being prepared for the upper layers from a prev
+ * first fragment.
+ *
+ * It is simply too much of a pain. Gosh, there should be a unified
+ * SG infrastructure for *everything* [so that I could declare a SG
+ * buffer, pass it to USB for receiving, append some space to it if
+ * I wish, receive more until I have the whole chunk, adapt
+ * pointers on each fragment to remove hardware headers and then
+ * attach that to an skbuff and netif_rx()].
+ */
+void i1480u_rx_cb(struct urb *urb)
+{
+ int result;
+ int do_parse_buffer = 1;
+ struct i1480u_rx_buf *rx_buf = urb->context;
+ struct i1480u *i1480u = rx_buf->i1480u;
+ struct device *dev = &i1480u->usb_iface->dev;
+ unsigned long flags;
+ u8 rx_buf_idx = rx_buf - i1480u->rx_buf;
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ECONNRESET: /* Not an error, but a controlled situation; */
+ case -ENOENT: /* (we killed the URB)...so, no broadcast */
+ case -ESHUTDOWN: /* going away! */
+ dev_err(dev, "RX URB[%u]: goind down %d\n",
+ rx_buf_idx, urb->status);
+ goto error;
+ default:
+ dev_err(dev, "RX URB[%u]: unknown status %d\n",
+ rx_buf_idx, urb->status);
+ if (edc_inc(&i1480u->rx_errors, EDC_MAX_ERRORS,
+ EDC_ERROR_TIMEFRAME)) {
+ dev_err(dev, "RX: max acceptable errors exceeded,"
+ " resetting device.\n");
+ i1480u_rx_unlink_urbs(i1480u);
+ wlp_reset_all(&i1480u->wlp);
+ goto error;
+ }
+ do_parse_buffer = 0;
+ break;
+ }
+ spin_lock_irqsave(&i1480u->lock, flags);
+ /* chew the data fragments, extract network packets */
+ if (do_parse_buffer) {
+ i1480u_rx_buffer(rx_buf);
+ if (rx_buf->data) {
+ rx_buf->urb->transfer_buffer = rx_buf->data->data;
+ result = usb_submit_urb(rx_buf->urb, GFP_ATOMIC);
+ if (result < 0) {
+ dev_err(dev, "RX URB[%u]: cannot submit %d\n",
+ rx_buf_idx, result);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&i1480u->lock, flags);
+error:
+ return;
+}
+
diff --git a/drivers/uwb/i1480/i1480u-wlp/sysfs.c b/drivers/uwb/i1480/i1480u-wlp/sysfs.c
new file mode 100644
index 000000000000..a1d8ca6ac935
--- /dev/null
+++ b/drivers/uwb/i1480/i1480u-wlp/sysfs.c
@@ -0,0 +1,408 @@
+/*
+ * WUSB Wire Adapter: WLP interface
+ * Sysfs interfaces
+ *
+ * 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/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/uwb/debug.h>
+#include <linux/device.h>
+#include "i1480u-wlp.h"
+
+
+/**
+ *
+ * @dev: Class device from the net_device; assumed refcnted.
+ *
+ * Yes, I don't lock--we assume it is refcounted and I am getting a
+ * single byte value that is kind of atomic to read.
+ */
+ssize_t uwb_phy_rate_show(const struct wlp_options *options, char *buf)
+{
+ return sprintf(buf, "%u\n",
+ wlp_tx_hdr_phy_rate(&options->def_tx_hdr));
+}
+EXPORT_SYMBOL_GPL(uwb_phy_rate_show);
+
+
+ssize_t uwb_phy_rate_store(struct wlp_options *options,
+ const char *buf, size_t size)
+{
+ ssize_t result;
+ unsigned rate;
+
+ result = sscanf(buf, "%u\n", &rate);
+ if (result != 1) {
+ result = -EINVAL;
+ goto out;
+ }
+ result = -EINVAL;
+ if (rate >= UWB_PHY_RATE_INVALID)
+ goto out;
+ wlp_tx_hdr_set_phy_rate(&options->def_tx_hdr, rate);
+ result = 0;
+out:
+ return result < 0 ? result : size;
+}
+EXPORT_SYMBOL_GPL(uwb_phy_rate_store);
+
+
+ssize_t uwb_rts_cts_show(const struct wlp_options *options, char *buf)
+{
+ return sprintf(buf, "%u\n",
+ wlp_tx_hdr_rts_cts(&options->def_tx_hdr));
+}
+EXPORT_SYMBOL_GPL(uwb_rts_cts_show);
+
+
+ssize_t uwb_rts_cts_store(struct wlp_options *options,
+ const char *buf, size_t size)
+{
+ ssize_t result;
+ unsigned value;
+
+ result = sscanf(buf, "%u\n", &value);
+ if (result != 1) {
+ result = -EINVAL;
+ goto out;
+ }
+ result = -EINVAL;
+ wlp_tx_hdr_set_rts_cts(&options->def_tx_hdr, !!value);
+ result = 0;
+out:
+ return result < 0 ? result : size;
+}
+EXPORT_SYMBOL_GPL(uwb_rts_cts_store);
+
+
+ssize_t uwb_ack_policy_show(const struct wlp_options *options, char *buf)
+{
+ return sprintf(buf, "%u\n",
+ wlp_tx_hdr_ack_policy(&options->def_tx_hdr));
+}
+EXPORT_SYMBOL_GPL(uwb_ack_policy_show);
+
+
+ssize_t uwb_ack_policy_store(struct wlp_options *options,
+ const char *buf, size_t size)
+{
+ ssize_t result;
+ unsigned value;
+
+ result = sscanf(buf, "%u\n", &value);
+ if (result != 1 || value > UWB_ACK_B_REQ) {
+ result = -EINVAL;
+ goto out;
+ }
+ wlp_tx_hdr_set_ack_policy(&options->def_tx_hdr, value);
+ result = 0;
+out:
+ return result < 0 ? result : size;
+}
+EXPORT_SYMBOL_GPL(uwb_ack_policy_store);
+
+
+/**
+ * Show the PCA base priority.
+ *
+ * We can access without locking, as the value is (for now) orthogonal
+ * to other values.
+ */
+ssize_t uwb_pca_base_priority_show(const struct wlp_options *options,
+ char *buf)
+{
+ return sprintf(buf, "%u\n",
+ options->pca_base_priority);
+}
+EXPORT_SYMBOL_GPL(uwb_pca_base_priority_show);
+
+
+/**
+ * Set the PCA base priority.
+ *
+ * We can access without locking, as the value is (for now) orthogonal
+ * to other values.
+ */
+ssize_t uwb_pca_base_priority_store(struct wlp_options *options,
+ const char *buf, size_t size)
+{
+ ssize_t result = -EINVAL;
+ u8 pca_base_priority;
+
+ result = sscanf(buf, "%hhu\n", &pca_base_priority);
+ if (result != 1) {
+ result = -EINVAL;
+ goto out;
+ }
+ result = -EINVAL;
+ if (pca_base_priority >= 8)
+ goto out;
+ options->pca_base_priority = pca_base_priority;
+ /* Update TX header if we are currently using PCA. */
+ if (result >= 0 && (wlp_tx_hdr_delivery_id_type(&options->def_tx_hdr) & WLP_DRP) == 0)
+ wlp_tx_hdr_set_delivery_id_type(&options->def_tx_hdr, options->pca_base_priority);
+ result = 0;
+out:
+ return result < 0 ? result : size;
+}
+EXPORT_SYMBOL_GPL(uwb_pca_base_priority_store);
+
+/**
+ * Show current inflight values
+ *
+ * Will print the current MAX and THRESHOLD values for the basic flow
+ * control. In addition it will report how many times the TX queue needed
+ * to be restarted since the last time this query was made.
+ */
+static ssize_t wlp_tx_inflight_show(struct i1480u_tx_inflight *inflight,
+ char *buf)
+{
+ ssize_t result;
+ unsigned long sec_elapsed = (jiffies - inflight->restart_ts)/HZ;
+ unsigned long restart_count = atomic_read(&inflight->restart_count);
+
+ result = scnprintf(buf, PAGE_SIZE, "%lu %lu %d %lu %lu %lu\n"
+ "#read: threshold max inflight_count restarts "
+ "seconds restarts/sec\n"
+ "#write: threshold max\n",
+ inflight->threshold, inflight->max,
+ atomic_read(&inflight->count),
+ restart_count, sec_elapsed,
+ sec_elapsed == 0 ? 0 : restart_count/sec_elapsed);
+ inflight->restart_ts = jiffies;
+ atomic_set(&inflight->restart_count, 0);
+ return result;
+}
+
+static
+ssize_t wlp_tx_inflight_store(struct i1480u_tx_inflight *inflight,
+ const char *buf, size_t size)
+{
+ unsigned long in_threshold, in_max;
+ ssize_t result;
+ result = sscanf(buf, "%lu %lu", &in_threshold, &in_max);
+ if (result != 2)
+ return -EINVAL;
+ if (in_max <= in_threshold)
+ return -EINVAL;
+ inflight->max = in_max;
+ inflight->threshold = in_threshold;
+ return size;
+}
+/*
+ * Glue (or function adaptors) for accesing info on sysfs
+ *
+ * [we need this indirection because the PCI driver does almost the
+ * same]
+ *
+ * Linux 2.6.21 changed how 'struct netdevice' does attributes (from
+ * having a 'struct class_dev' to having a 'struct device'). That is
+ * quite of a pain.
+ *
+ * So we try to abstract that here. i1480u_SHOW() and i1480u_STORE()
+ * create adaptors for extracting the 'struct i1480u' from a 'struct
+ * dev' and calling a function for doing a sysfs operation (as we have
+ * them factorized already). i1480u_ATTR creates the attribute file
+ * (CLASS_DEVICE_ATTR or DEVICE_ATTR) and i1480u_ATTR_NAME produces a
+ * class_device_attr_NAME or device_attr_NAME (for group registration).
+ */
+#include <linux/version.h>
+
+#define i1480u_SHOW(name, fn, param) \
+static ssize_t i1480u_show_##name(struct device *dev, \
+ struct device_attribute *attr,\
+ char *buf) \
+{ \
+ struct i1480u *i1480u = netdev_priv(to_net_dev(dev)); \
+ return fn(&i1480u->param, buf); \
+}
+
+#define i1480u_STORE(name, fn, param) \
+static ssize_t i1480u_store_##name(struct device *dev, \
+ struct device_attribute *attr,\
+ const char *buf, size_t size)\
+{ \
+ struct i1480u *i1480u = netdev_priv(to_net_dev(dev)); \
+ return fn(&i1480u->param, buf, size); \
+}
+
+#define i1480u_ATTR(name, perm) static DEVICE_ATTR(name, perm, \
+ i1480u_show_##name,\
+ i1480u_store_##name)
+
+#define i1480u_ATTR_SHOW(name) static DEVICE_ATTR(name, \
+ S_IRUGO, \
+ i1480u_show_##name, NULL)
+
+#define i1480u_ATTR_NAME(a) (dev_attr_##a)
+
+
+/*
+ * Sysfs adaptors
+ */
+i1480u_SHOW(uwb_phy_rate, uwb_phy_rate_show, options);
+i1480u_STORE(uwb_phy_rate, uwb_phy_rate_store, options);
+i1480u_ATTR(uwb_phy_rate, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(uwb_rts_cts, uwb_rts_cts_show, options);
+i1480u_STORE(uwb_rts_cts, uwb_rts_cts_store, options);
+i1480u_ATTR(uwb_rts_cts, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(uwb_ack_policy, uwb_ack_policy_show, options);
+i1480u_STORE(uwb_ack_policy, uwb_ack_policy_store, options);
+i1480u_ATTR(uwb_ack_policy, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(uwb_pca_base_priority, uwb_pca_base_priority_show, options);
+i1480u_STORE(uwb_pca_base_priority, uwb_pca_base_priority_store, options);
+i1480u_ATTR(uwb_pca_base_priority, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(wlp_eda, wlp_eda_show, wlp);
+i1480u_STORE(wlp_eda, wlp_eda_store, wlp);
+i1480u_ATTR(wlp_eda, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(wlp_uuid, wlp_uuid_show, wlp);
+i1480u_STORE(wlp_uuid, wlp_uuid_store, wlp);
+i1480u_ATTR(wlp_uuid, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(wlp_dev_name, wlp_dev_name_show, wlp);
+i1480u_STORE(wlp_dev_name, wlp_dev_name_store, wlp);
+i1480u_ATTR(wlp_dev_name, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(wlp_dev_manufacturer, wlp_dev_manufacturer_show, wlp);
+i1480u_STORE(wlp_dev_manufacturer, wlp_dev_manufacturer_store, wlp);
+i1480u_ATTR(wlp_dev_manufacturer, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(wlp_dev_model_name, wlp_dev_model_name_show, wlp);
+i1480u_STORE(wlp_dev_model_name, wlp_dev_model_name_store, wlp);
+i1480u_ATTR(wlp_dev_model_name, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(wlp_dev_model_nr, wlp_dev_model_nr_show, wlp);
+i1480u_STORE(wlp_dev_model_nr, wlp_dev_model_nr_store, wlp);
+i1480u_ATTR(wlp_dev_model_nr, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(wlp_dev_serial, wlp_dev_serial_show, wlp);
+i1480u_STORE(wlp_dev_serial, wlp_dev_serial_store, wlp);
+i1480u_ATTR(wlp_dev_serial, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(wlp_dev_prim_category, wlp_dev_prim_category_show, wlp);
+i1480u_STORE(wlp_dev_prim_category, wlp_dev_prim_category_store, wlp);
+i1480u_ATTR(wlp_dev_prim_category, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(wlp_dev_prim_OUI, wlp_dev_prim_OUI_show, wlp);
+i1480u_STORE(wlp_dev_prim_OUI, wlp_dev_prim_OUI_store, wlp);
+i1480u_ATTR(wlp_dev_prim_OUI, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(wlp_dev_prim_OUI_sub, wlp_dev_prim_OUI_sub_show, wlp);
+i1480u_STORE(wlp_dev_prim_OUI_sub, wlp_dev_prim_OUI_sub_store, wlp);
+i1480u_ATTR(wlp_dev_prim_OUI_sub, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(wlp_dev_prim_subcat, wlp_dev_prim_subcat_show, wlp);
+i1480u_STORE(wlp_dev_prim_subcat, wlp_dev_prim_subcat_store, wlp);
+i1480u_ATTR(wlp_dev_prim_subcat, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(wlp_neighborhood, wlp_neighborhood_show, wlp);
+i1480u_ATTR_SHOW(wlp_neighborhood);
+
+i1480u_SHOW(wss_activate, wlp_wss_activate_show, wlp.wss);
+i1480u_STORE(wss_activate, wlp_wss_activate_store, wlp.wss);
+i1480u_ATTR(wss_activate, S_IRUGO | S_IWUSR);
+
+/*
+ * Show the (min, max, avg) Line Quality Estimate (LQE, in dB) as over
+ * the last 256 received WLP frames (ECMA-368 13.3).
+ *
+ * [the -7dB that have to be substracted from the LQI to make the LQE
+ * are already taken into account].
+ */
+i1480u_SHOW(wlp_lqe, stats_show, lqe_stats);
+i1480u_STORE(wlp_lqe, stats_store, lqe_stats);
+i1480u_ATTR(wlp_lqe, S_IRUGO | S_IWUSR);
+
+/*
+ * Show the Receive Signal Strength Indicator averaged over all the
+ * received WLP frames (ECMA-368 13.3). Still is not clear what
+ * this value is, but is kind of a percentage of the signal strength
+ * at the antenna.
+ */
+i1480u_SHOW(wlp_rssi, stats_show, rssi_stats);
+i1480u_STORE(wlp_rssi, stats_store, rssi_stats);
+i1480u_ATTR(wlp_rssi, S_IRUGO | S_IWUSR);
+
+/**
+ * We maintain a basic flow control counter. "count" how many TX URBs are
+ * outstanding. Only allow "max"
+ * TX URBs to be outstanding. If this value is reached the queue will be
+ * stopped. The queue will be restarted when there are
+ * "threshold" URBs outstanding.
+ */
+i1480u_SHOW(wlp_tx_inflight, wlp_tx_inflight_show, tx_inflight);
+i1480u_STORE(wlp_tx_inflight, wlp_tx_inflight_store, tx_inflight);
+i1480u_ATTR(wlp_tx_inflight, S_IRUGO | S_IWUSR);
+
+static struct attribute *i1480u_attrs[] = {
+ &i1480u_ATTR_NAME(uwb_phy_rate).attr,
+ &i1480u_ATTR_NAME(uwb_rts_cts).attr,
+ &i1480u_ATTR_NAME(uwb_ack_policy).attr,
+ &i1480u_ATTR_NAME(uwb_pca_base_priority).attr,
+ &i1480u_ATTR_NAME(wlp_lqe).attr,
+ &i1480u_ATTR_NAME(wlp_rssi).attr,
+ &i1480u_ATTR_NAME(wlp_eda).attr,
+ &i1480u_ATTR_NAME(wlp_uuid).attr,
+ &i1480u_ATTR_NAME(wlp_dev_name).attr,
+ &i1480u_ATTR_NAME(wlp_dev_manufacturer).attr,
+ &i1480u_ATTR_NAME(wlp_dev_model_name).attr,
+ &i1480u_ATTR_NAME(wlp_dev_model_nr).attr,
+ &i1480u_ATTR_NAME(wlp_dev_serial).attr,
+ &i1480u_ATTR_NAME(wlp_dev_prim_category).attr,
+ &i1480u_ATTR_NAME(wlp_dev_prim_OUI).attr,
+ &i1480u_ATTR_NAME(wlp_dev_prim_OUI_sub).attr,
+ &i1480u_ATTR_NAME(wlp_dev_prim_subcat).attr,
+ &i1480u_ATTR_NAME(wlp_neighborhood).attr,
+ &i1480u_ATTR_NAME(wss_activate).attr,
+ &i1480u_ATTR_NAME(wlp_tx_inflight).attr,
+ NULL,
+};
+
+static struct attribute_group i1480u_attr_group = {
+ .name = NULL, /* we want them in the same directory */
+ .attrs = i1480u_attrs,
+};
+
+int i1480u_sysfs_setup(struct i1480u *i1480u)
+{
+ int result;
+ struct device *dev = &i1480u->usb_iface->dev;
+ result = sysfs_create_group(&i1480u->net_dev->dev.kobj,
+ &i1480u_attr_group);
+ if (result < 0)
+ dev_err(dev, "cannot initialize sysfs attributes: %d\n",
+ result);
+ return result;
+}
+
+
+void i1480u_sysfs_release(struct i1480u *i1480u)
+{
+ sysfs_remove_group(&i1480u->net_dev->dev.kobj,
+ &i1480u_attr_group);
+}
diff --git a/drivers/uwb/i1480/i1480u-wlp/tx.c b/drivers/uwb/i1480/i1480u-wlp/tx.c
new file mode 100644
index 000000000000..3426bfb68240
--- /dev/null
+++ b/drivers/uwb/i1480/i1480u-wlp/tx.c
@@ -0,0 +1,632 @@
+/*
+ * WUSB Wire Adapter: WLP interface
+ * Deal with TX (massaging data to transmit, handling it)
+ *
+ * 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.
+ *
+ *
+ * Transmission engine. Get an skb, create from that a WLP transmit
+ * context, add a WLP TX header (which we keep prefilled in the
+ * device's instance), fill out the target-specific fields and
+ * fire it.
+ *
+ * ROADMAP:
+ *
+ * Entry points:
+ *
+ * i1480u_tx_release(): called by i1480u_disconnect() to release
+ * pending tx contexts.
+ *
+ * i1480u_tx_cb(): callback for TX contexts (USB URBs)
+ * i1480u_tx_destroy():
+ *
+ * i1480u_tx_timeout(): called for timeout handling from the
+ * network stack.
+ *
+ * i1480u_hard_start_xmit(): called for transmitting an skb from
+ * the network stack. Will interact with WLP
+ * substack to verify and prepare frame.
+ * i1480u_xmit_frame(): actual transmission on hardware
+ *
+ * i1480u_tx_create() Creates TX context
+ * i1480u_tx_create_1() For packets in 1 fragment
+ * i1480u_tx_create_n() For packets in >1 fragments
+ *
+ * TODO:
+ *
+ * - FIXME: rewrite using usb_sg_*(), add asynch support to
+ * usb_sg_*(). It might not make too much sense as most of
+ * the times the MTU will be smaller than one page...
+ */
+
+#include "i1480u-wlp.h"
+#define D_LOCAL 5
+#include <linux/uwb/debug.h>
+
+enum {
+ /* This is only for Next and Last TX packets */
+ i1480u_MAX_PL_SIZE = i1480u_MAX_FRG_SIZE
+ - sizeof(struct untd_hdr_rst),
+};
+
+/** Free resources allocated to a i1480u tx context. */
+static
+void i1480u_tx_free(struct i1480u_tx *wtx)
+{
+ kfree(wtx->buf);
+ if (wtx->skb)
+ dev_kfree_skb_irq(wtx->skb);
+ usb_free_urb(wtx->urb);
+ kfree(wtx);
+}
+
+static
+void i1480u_tx_destroy(struct i1480u *i1480u, struct i1480u_tx *wtx)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&i1480u->tx_list_lock, flags); /* not active any more */
+ list_del(&wtx->list_node);
+ i1480u_tx_free(wtx);
+ spin_unlock_irqrestore(&i1480u->tx_list_lock, flags);
+}
+
+static
+void i1480u_tx_unlink_urbs(struct i1480u *i1480u)
+{
+ unsigned long flags;
+ struct i1480u_tx *wtx, *next;
+
+ spin_lock_irqsave(&i1480u->tx_list_lock, flags);
+ list_for_each_entry_safe(wtx, next, &i1480u->tx_list, list_node) {
+ usb_unlink_urb(wtx->urb);
+ }
+ spin_unlock_irqrestore(&i1480u->tx_list_lock, flags);
+}
+
+
+/**
+ * Callback for a completed tx USB URB.
+ *
+ * TODO:
+ *
+ * - FIXME: recover errors more gracefully
+ * - FIXME: handle NAKs (I dont think they come here) for flow ctl
+ */
+static
+void i1480u_tx_cb(struct urb *urb)
+{
+ struct i1480u_tx *wtx = urb->context;
+ struct i1480u *i1480u = wtx->i1480u;
+ struct net_device *net_dev = i1480u->net_dev;
+ struct device *dev = &i1480u->usb_iface->dev;
+ unsigned long flags;
+
+ switch (urb->status) {
+ case 0:
+ spin_lock_irqsave(&i1480u->lock, flags);
+ i1480u->stats.tx_packets++;
+ i1480u->stats.tx_bytes += urb->actual_length;
+ spin_unlock_irqrestore(&i1480u->lock, flags);
+ break;
+ case -ECONNRESET: /* Not an error, but a controlled situation; */
+ case -ENOENT: /* (we killed the URB)...so, no broadcast */
+ dev_dbg(dev, "notif endp: reset/noent %d\n", urb->status);
+ netif_stop_queue(net_dev);
+ break;
+ case -ESHUTDOWN: /* going away! */
+ dev_dbg(dev, "notif endp: down %d\n", urb->status);
+ netif_stop_queue(net_dev);
+ break;
+ default:
+ dev_err(dev, "TX: unknown URB status %d\n", urb->status);
+ if (edc_inc(&i1480u->tx_errors, EDC_MAX_ERRORS,
+ EDC_ERROR_TIMEFRAME)) {
+ dev_err(dev, "TX: max acceptable errors exceeded."
+ "Reset device.\n");
+ netif_stop_queue(net_dev);
+ i1480u_tx_unlink_urbs(i1480u);
+ wlp_reset_all(&i1480u->wlp);
+ }
+ break;
+ }
+ i1480u_tx_destroy(i1480u, wtx);
+ if (atomic_dec_return(&i1480u->tx_inflight.count)
+ <= i1480u->tx_inflight.threshold
+ && netif_queue_stopped(net_dev)
+ && i1480u->tx_inflight.threshold != 0) {
+ if (d_test(2) && printk_ratelimit())
+ d_printf(2, dev, "Restart queue. \n");
+ netif_start_queue(net_dev);
+ atomic_inc(&i1480u->tx_inflight.restart_count);
+ }
+ return;
+}
+
+
+/**
+ * Given a buffer that doesn't fit in a single fragment, create an
+ * scatter/gather structure for delivery to the USB pipe.
+ *
+ * Implements functionality of i1480u_tx_create().
+ *
+ * @wtx: tx descriptor
+ * @skb: skb to send
+ * @gfp_mask: gfp allocation mask
+ * @returns: Pointer to @wtx if ok, NULL on error.
+ *
+ * Sorry, TOO LONG a function, but breaking it up is kind of hard
+ *
+ * This will break the buffer in chunks smaller than
+ * i1480u_MAX_FRG_SIZE (including the header) and add proper headers
+ * to each:
+ *
+ * 1st header \
+ * i1480 tx header | fragment 1
+ * fragment data /
+ * nxt header \ fragment 2
+ * fragment data /
+ * ..
+ * ..
+ * last header \ fragment 3
+ * last fragment data /
+ *
+ * This does not fill the i1480 TX header, it is left up to the
+ * caller to do that; you can get it from @wtx->wlp_tx_hdr.
+ *
+ * This function consumes the skb unless there is an error.
+ */
+static
+int i1480u_tx_create_n(struct i1480u_tx *wtx, struct sk_buff *skb,
+ gfp_t gfp_mask)
+{
+ int result;
+ void *pl;
+ size_t pl_size;
+
+ void *pl_itr, *buf_itr;
+ size_t pl_size_left, frgs, pl_size_1st, frg_pl_size = 0;
+ struct untd_hdr_1st *untd_hdr_1st;
+ struct wlp_tx_hdr *wlp_tx_hdr;
+ struct untd_hdr_rst *untd_hdr_rst;
+
+ wtx->skb = NULL;
+ pl = skb->data;
+ pl_itr = pl;
+ pl_size = skb->len;
+ pl_size_left = pl_size; /* payload size */
+ /* First fragment; fits as much as i1480u_MAX_FRG_SIZE minus
+ * the headers */
+ pl_size_1st = i1480u_MAX_FRG_SIZE
+ - sizeof(struct untd_hdr_1st) - sizeof(struct wlp_tx_hdr);
+ BUG_ON(pl_size_1st > pl_size);
+ pl_size_left -= pl_size_1st;
+ /* The rest have an smaller header (no i1480 TX header). We
+ * need to break up the payload in blocks smaller than
+ * i1480u_MAX_PL_SIZE (payload excluding header). */
+ frgs = (pl_size_left + i1480u_MAX_PL_SIZE - 1) / i1480u_MAX_PL_SIZE;
+ /* Allocate space for the new buffer. In this new buffer we'll
+ * place the headers followed by the data fragment, headers,
+ * data fragments, etc..
+ */
+ result = -ENOMEM;
+ wtx->buf_size = sizeof(*untd_hdr_1st)
+ + sizeof(*wlp_tx_hdr)
+ + frgs * sizeof(*untd_hdr_rst)
+ + pl_size;
+ wtx->buf = kmalloc(wtx->buf_size, gfp_mask);
+ if (wtx->buf == NULL)
+ goto error_buf_alloc;
+
+ buf_itr = wtx->buf; /* We got the space, let's fill it up */
+ /* Fill 1st fragment */
+ untd_hdr_1st = buf_itr;
+ buf_itr += sizeof(*untd_hdr_1st);
+ untd_hdr_set_type(&untd_hdr_1st->hdr, i1480u_PKT_FRAG_1ST);
+ untd_hdr_set_rx_tx(&untd_hdr_1st->hdr, 0);
+ untd_hdr_1st->hdr.len = cpu_to_le16(pl_size + sizeof(*wlp_tx_hdr));
+ untd_hdr_1st->fragment_len =
+ cpu_to_le16(pl_size_1st + sizeof(*wlp_tx_hdr));
+ memset(untd_hdr_1st->padding, 0, sizeof(untd_hdr_1st->padding));
+ /* Set up i1480 header info */
+ wlp_tx_hdr = wtx->wlp_tx_hdr = buf_itr;
+ buf_itr += sizeof(*wlp_tx_hdr);
+ /* Copy the first fragment */
+ memcpy(buf_itr, pl_itr, pl_size_1st);
+ pl_itr += pl_size_1st;
+ buf_itr += pl_size_1st;
+
+ /* Now do each remaining fragment */
+ result = -EINVAL;
+ while (pl_size_left > 0) {
+ d_printf(5, NULL, "ITR HDR: pl_size_left %zu buf_itr %zu\n",
+ pl_size_left, buf_itr - wtx->buf);
+ if (buf_itr + sizeof(*untd_hdr_rst) - wtx->buf
+ > wtx->buf_size) {
+ printk(KERN_ERR "BUG: no space for header\n");
+ goto error_bug;
+ }
+ d_printf(5, NULL, "ITR HDR 2: pl_size_left %zu buf_itr %zu\n",
+ pl_size_left, buf_itr - wtx->buf);
+ untd_hdr_rst = buf_itr;
+ buf_itr += sizeof(*untd_hdr_rst);
+ if (pl_size_left > i1480u_MAX_PL_SIZE) {
+ frg_pl_size = i1480u_MAX_PL_SIZE;
+ untd_hdr_set_type(&untd_hdr_rst->hdr, i1480u_PKT_FRAG_NXT);
+ } else {
+ frg_pl_size = pl_size_left;
+ untd_hdr_set_type(&untd_hdr_rst->hdr, i1480u_PKT_FRAG_LST);
+ }
+ d_printf(5, NULL,
+ "ITR PL: pl_size_left %zu buf_itr %zu frg_pl_size %zu\n",
+ pl_size_left, buf_itr - wtx->buf, frg_pl_size);
+ untd_hdr_set_rx_tx(&untd_hdr_rst->hdr, 0);
+ untd_hdr_rst->hdr.len = cpu_to_le16(frg_pl_size);
+ untd_hdr_rst->padding = 0;
+ if (buf_itr + frg_pl_size - wtx->buf
+ > wtx->buf_size) {
+ printk(KERN_ERR "BUG: no space for payload\n");
+ goto error_bug;
+ }
+ memcpy(buf_itr, pl_itr, frg_pl_size);
+ buf_itr += frg_pl_size;
+ pl_itr += frg_pl_size;
+ pl_size_left -= frg_pl_size;
+ d_printf(5, NULL,
+ "ITR PL 2: pl_size_left %zu buf_itr %zu frg_pl_size %zu\n",
+ pl_size_left, buf_itr - wtx->buf, frg_pl_size);
+ }
+ dev_kfree_skb_irq(skb);
+ return 0;
+
+error_bug:
+ printk(KERN_ERR
+ "BUG: skb %u bytes\n"
+ "BUG: frg_pl_size %zd i1480u_MAX_FRG_SIZE %u\n"
+ "BUG: buf_itr %zu buf_size %zu pl_size_left %zu\n",
+ skb->len,
+ frg_pl_size, i1480u_MAX_FRG_SIZE,
+ buf_itr - wtx->buf, wtx->buf_size, pl_size_left);
+
+ kfree(wtx->buf);
+error_buf_alloc:
+ return result;
+}
+
+
+/**
+ * Given a buffer that fits in a single fragment, fill out a @wtx
+ * struct for transmitting it down the USB pipe.
+ *
+ * Uses the fact that we have space reserved in front of the skbuff
+ * for hardware headers :]
+ *
+ * This does not fill the i1480 TX header, it is left up to the
+ * caller to do that; you can get it from @wtx->wlp_tx_hdr.
+ *
+ * @pl: pointer to payload data
+ * @pl_size: size of the payuload
+ *
+ * This function does not consume the @skb.
+ */
+static
+int i1480u_tx_create_1(struct i1480u_tx *wtx, struct sk_buff *skb,
+ gfp_t gfp_mask)
+{
+ struct untd_hdr_cmp *untd_hdr_cmp;
+ struct wlp_tx_hdr *wlp_tx_hdr;
+
+ wtx->buf = NULL;
+ wtx->skb = skb;
+ BUG_ON(skb_headroom(skb) < sizeof(*wlp_tx_hdr));
+ wlp_tx_hdr = (void *) __skb_push(skb, sizeof(*wlp_tx_hdr));
+ wtx->wlp_tx_hdr = wlp_tx_hdr;
+ BUG_ON(skb_headroom(skb) < sizeof(*untd_hdr_cmp));
+ untd_hdr_cmp = (void *) __skb_push(skb, sizeof(*untd_hdr_cmp));
+
+ untd_hdr_set_type(&untd_hdr_cmp->hdr, i1480u_PKT_FRAG_CMP);
+ untd_hdr_set_rx_tx(&untd_hdr_cmp->hdr, 0);
+ untd_hdr_cmp->hdr.len = cpu_to_le16(skb->len - sizeof(*untd_hdr_cmp));
+ untd_hdr_cmp->padding = 0;
+ return 0;
+}
+
+
+/**
+ * Given a skb to transmit, massage it to become palatable for the TX pipe
+ *
+ * This will break the buffer in chunks smaller than
+ * i1480u_MAX_FRG_SIZE and add proper headers to each.
+ *
+ * 1st header \
+ * i1480 tx header | fragment 1
+ * fragment data /
+ * nxt header \ fragment 2
+ * fragment data /
+ * ..
+ * ..
+ * last header \ fragment 3
+ * last fragment data /
+ *
+ * Each fragment will be always smaller or equal to i1480u_MAX_FRG_SIZE.
+ *
+ * If the first fragment is smaller than i1480u_MAX_FRG_SIZE, then the
+ * following is composed:
+ *
+ * complete header \
+ * i1480 tx header | single fragment
+ * packet data /
+ *
+ * We were going to use s/g support, but because the interface is
+ * synch and at the end there is plenty of overhead to do it, it
+ * didn't seem that worth for data that is going to be smaller than
+ * one page.
+ */
+static
+struct i1480u_tx *i1480u_tx_create(struct i1480u *i1480u,
+ struct sk_buff *skb, gfp_t gfp_mask)
+{
+ int result;
+ struct usb_endpoint_descriptor *epd;
+ int usb_pipe;
+ unsigned long flags;
+
+ struct i1480u_tx *wtx;
+ const size_t pl_max_size =
+ i1480u_MAX_FRG_SIZE - sizeof(struct untd_hdr_cmp)
+ - sizeof(struct wlp_tx_hdr);
+
+ wtx = kmalloc(sizeof(*wtx), gfp_mask);
+ if (wtx == NULL)
+ goto error_wtx_alloc;
+ wtx->urb = usb_alloc_urb(0, gfp_mask);
+ if (wtx->urb == NULL)
+ goto error_urb_alloc;
+ epd = &i1480u->usb_iface->cur_altsetting->endpoint[2].desc;
+ usb_pipe = usb_sndbulkpipe(i1480u->usb_dev, epd->bEndpointAddress);
+ /* Fits in a single complete packet or need to split? */
+ if (skb->len > pl_max_size) {
+ result = i1480u_tx_create_n(wtx, skb, gfp_mask);
+ if (result < 0)
+ goto error_create;
+ usb_fill_bulk_urb(wtx->urb, i1480u->usb_dev, usb_pipe,
+ wtx->buf, wtx->buf_size, i1480u_tx_cb, wtx);
+ } else {
+ result = i1480u_tx_create_1(wtx, skb, gfp_mask);
+ if (result < 0)
+ goto error_create;
+ usb_fill_bulk_urb(wtx->urb, i1480u->usb_dev, usb_pipe,
+ skb->data, skb->len, i1480u_tx_cb, wtx);
+ }
+ spin_lock_irqsave(&i1480u->tx_list_lock, flags);
+ list_add(&wtx->list_node, &i1480u->tx_list);
+ spin_unlock_irqrestore(&i1480u->tx_list_lock, flags);
+ return wtx;
+
+error_create:
+ kfree(wtx->urb);
+error_urb_alloc:
+ kfree(wtx);
+error_wtx_alloc:
+ return NULL;
+}
+
+/**
+ * Actual fragmentation and transmission of frame
+ *
+ * @wlp: WLP substack data structure
+ * @skb: To be transmitted
+ * @dst: Device address of destination
+ * @returns: 0 on success, <0 on failure
+ *
+ * This function can also be called directly (not just from
+ * hard_start_xmit), so we also check here if the interface is up before
+ * taking sending anything.
+ */
+int i1480u_xmit_frame(struct wlp *wlp, struct sk_buff *skb,
+ struct uwb_dev_addr *dst)
+{
+ int result = -ENXIO;
+ struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp);
+ struct device *dev = &i1480u->usb_iface->dev;
+ struct net_device *net_dev = i1480u->net_dev;
+ struct i1480u_tx *wtx;
+ struct wlp_tx_hdr *wlp_tx_hdr;
+ static unsigned char dev_bcast[2] = { 0xff, 0xff };
+#if 0
+ int lockup = 50;
+#endif
+
+ d_fnstart(6, dev, "(skb %p (%u), net_dev %p)\n", skb, skb->len,
+ net_dev);
+ BUG_ON(i1480u->wlp.rc == NULL);
+ if ((net_dev->flags & IFF_UP) == 0)
+ goto out;
+ result = -EBUSY;
+ if (atomic_read(&i1480u->tx_inflight.count) >= i1480u->tx_inflight.max) {
+ if (d_test(2) && printk_ratelimit())
+ d_printf(2, dev, "Max frames in flight "
+ "stopping queue.\n");
+ netif_stop_queue(net_dev);
+ goto error_max_inflight;
+ }
+ result = -ENOMEM;
+ wtx = i1480u_tx_create(i1480u, skb, GFP_ATOMIC);
+ if (unlikely(wtx == NULL)) {
+ if (printk_ratelimit())
+ dev_err(dev, "TX: no memory for WLP TX URB,"
+ "dropping packet (in flight %d)\n",
+ atomic_read(&i1480u->tx_inflight.count));
+ netif_stop_queue(net_dev);
+ goto error_wtx_alloc;
+ }
+ wtx->i1480u = i1480u;
+ /* Fill out the i1480 header; @i1480u->def_tx_hdr read without
+ * locking. We do so because they are kind of orthogonal to
+ * each other (and thus not changed in an atomic batch).
+ * The ETH header is right after the WLP TX header. */
+ wlp_tx_hdr = wtx->wlp_tx_hdr;
+ *wlp_tx_hdr = i1480u->options.def_tx_hdr;
+ wlp_tx_hdr->dstaddr = *dst;
+ if (!memcmp(&wlp_tx_hdr->dstaddr, dev_bcast, sizeof(dev_bcast))
+ && (wlp_tx_hdr_delivery_id_type(wlp_tx_hdr) & WLP_DRP)) {
+ /*Broadcast message directed to DRP host. Send as best effort
+ * on PCA. */
+ wlp_tx_hdr_set_delivery_id_type(wlp_tx_hdr, i1480u->options.pca_base_priority);
+ }
+
+#if 0
+ dev_info(dev, "TX delivering skb -> USB, %zu bytes\n", skb->len);
+ dump_bytes(dev, skb->data, skb->len > 72 ? 72 : skb->len);
+#endif
+#if 0
+ /* simulates a device lockup after every lockup# packets */
+ if (lockup && ((i1480u->stats.tx_packets + 1) % lockup) == 0) {
+ /* Simulate a dropped transmit interrupt */
+ net_dev->trans_start = jiffies;
+ netif_stop_queue(net_dev);
+ dev_err(dev, "Simulate lockup at %ld\n", jiffies);
+ return result;
+ }
+#endif
+
+ result = usb_submit_urb(wtx->urb, GFP_ATOMIC); /* Go baby */
+ if (result < 0) {
+ dev_err(dev, "TX: cannot submit URB: %d\n", result);
+ /* We leave the freeing of skb to calling function */
+ wtx->skb = NULL;
+ goto error_tx_urb_submit;
+ }
+ atomic_inc(&i1480u->tx_inflight.count);
+ net_dev->trans_start = jiffies;
+ d_fnend(6, dev, "(skb %p (%u), net_dev %p) = %d\n", skb, skb->len,
+ net_dev, result);
+ return result;
+
+error_tx_urb_submit:
+ i1480u_tx_destroy(i1480u, wtx);
+error_wtx_alloc:
+error_max_inflight:
+out:
+ d_fnend(6, dev, "(skb %p (%u), net_dev %p) = %d\n", skb, skb->len,
+ net_dev, result);
+ return result;
+}
+
+
+/**
+ * Transmit an skb Called when an skbuf has to be transmitted
+ *
+ * The skb is first passed to WLP substack to ensure this is a valid
+ * frame. If valid the device address of destination will be filled and
+ * the WLP header prepended to the skb. If this step fails we fake sending
+ * the frame, if we return an error the network stack will just keep trying.
+ *
+ * Broadcast frames inside a WSS needs to be treated special as multicast is
+ * not supported. A broadcast frame is sent as unicast to each member of the
+ * WSS - this is done by the WLP substack when it finds a broadcast frame.
+ * So, we test if the WLP substack took over the skb and only transmit it
+ * if it has not (been taken over).
+ *
+ * @net_dev->xmit_lock is held
+ */
+int i1480u_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev)
+{
+ int result;
+ struct i1480u *i1480u = netdev_priv(net_dev);
+ struct device *dev = &i1480u->usb_iface->dev;
+ struct uwb_dev_addr dst;
+
+ d_fnstart(6, dev, "(skb %p (%u), net_dev %p)\n", skb, skb->len,
+ net_dev);
+ BUG_ON(i1480u->wlp.rc == NULL);
+ if ((net_dev->flags & IFF_UP) == 0)
+ goto error;
+ result = wlp_prepare_tx_frame(dev, &i1480u->wlp, skb, &dst);
+ if (result < 0) {
+ dev_err(dev, "WLP verification of TX frame failed (%d). "
+ "Dropping packet.\n", result);
+ goto error;
+ } else if (result == 1) {
+ d_printf(6, dev, "WLP will transmit frame. \n");
+ /* trans_start time will be set when WLP actually transmits
+ * the frame */
+ goto out;
+ }
+ d_printf(6, dev, "Transmitting frame. \n");
+ result = i1480u_xmit_frame(&i1480u->wlp, skb, &dst);
+ if (result < 0) {
+ dev_err(dev, "Frame TX failed (%d).\n", result);
+ goto error;
+ }
+ d_fnend(6, dev, "(skb %p (%u), net_dev %p) = %d\n", skb, skb->len,
+ net_dev, result);
+ return NETDEV_TX_OK;
+error:
+ dev_kfree_skb_any(skb);
+ i1480u->stats.tx_dropped++;
+out:
+ d_fnend(6, dev, "(skb %p (%u), net_dev %p) = %d\n", skb, skb->len,
+ net_dev, result);
+ return NETDEV_TX_OK;
+}
+
+
+/**
+ * Called when a pkt transmission doesn't complete in a reasonable period
+ * Device reset may sleep - do it outside of interrupt context (delayed)
+ */
+void i1480u_tx_timeout(struct net_device *net_dev)
+{
+ struct i1480u *i1480u = netdev_priv(net_dev);
+
+ wlp_reset_all(&i1480u->wlp);
+}
+
+
+void i1480u_tx_release(struct i1480u *i1480u)
+{
+ unsigned long flags;
+ struct i1480u_tx *wtx, *next;
+ int count = 0, empty;
+
+ spin_lock_irqsave(&i1480u->tx_list_lock, flags);
+ list_for_each_entry_safe(wtx, next, &i1480u->tx_list, list_node) {
+ count++;
+ usb_unlink_urb(wtx->urb);
+ }
+ spin_unlock_irqrestore(&i1480u->tx_list_lock, flags);
+ count = count*10; /* i1480ut 200ms per unlinked urb (intervals of 20ms) */
+ /*
+ * We don't like this sollution too much (dirty as it is), but
+ * it is cheaper than putting a refcount on each i1480u_tx and
+ * i1480uting for all of them to go away...
+ *
+ * Called when no more packets can be added to tx_list
+ * so can i1480ut for it to be empty.
+ */
+ while (1) {
+ spin_lock_irqsave(&i1480u->tx_list_lock, flags);
+ empty = list_empty(&i1480u->tx_list);
+ spin_unlock_irqrestore(&i1480u->tx_list_lock, flags);
+ if (empty)
+ break;
+ count--;
+ BUG_ON(count == 0);
+ msleep(20);
+ }
+}
diff --git a/drivers/uwb/ie.c b/drivers/uwb/ie.c
new file mode 100644
index 000000000000..cf6f3d152b9d
--- /dev/null
+++ b/drivers/uwb/ie.c
@@ -0,0 +1,541 @@
+/*
+ * Ultra Wide Band
+ * Information Element Handling
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ * Reinette Chatre <reinette.chatre@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 "uwb-internal.h"
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+/**
+ * uwb_ie_next - get the next IE in a buffer
+ * @ptr: start of the buffer containing the IE data
+ * @len: length of the buffer
+ *
+ * Both @ptr and @len are updated so subsequent calls to uwb_ie_next()
+ * will get the next IE.
+ *
+ * NULL is returned (and @ptr and @len will not be updated) if there
+ * are no more IEs in the buffer or the buffer is too short.
+ */
+struct uwb_ie_hdr *uwb_ie_next(void **ptr, size_t *len)
+{
+ struct uwb_ie_hdr *hdr;
+ size_t ie_len;
+
+ if (*len < sizeof(struct uwb_ie_hdr))
+ return NULL;
+
+ hdr = *ptr;
+ ie_len = sizeof(struct uwb_ie_hdr) + hdr->length;
+
+ if (*len < ie_len)
+ return NULL;
+
+ *ptr += ie_len;
+ *len -= ie_len;
+
+ return hdr;
+}
+EXPORT_SYMBOL_GPL(uwb_ie_next);
+
+/**
+ * Get the IEs that a radio controller is sending in its beacon
+ *
+ * @uwb_rc: UWB Radio Controller
+ * @returns: Size read from the system
+ *
+ * We don't need to lock the uwb_rc's mutex because we don't modify
+ * anything. Once done with the iedata buffer, call
+ * uwb_rc_ie_release(iedata). Don't call kfree on it.
+ */
+ssize_t uwb_rc_get_ie(struct uwb_rc *uwb_rc, struct uwb_rc_evt_get_ie **pget_ie)
+{
+ ssize_t result;
+ struct device *dev = &uwb_rc->uwb_dev.dev;
+ struct uwb_rccb *cmd = NULL;
+ struct uwb_rceb *reply = NULL;
+ struct uwb_rc_evt_get_ie *get_ie;
+
+ d_fnstart(3, dev, "(%p, %p)\n", uwb_rc, pget_ie);
+ result = -ENOMEM;
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (cmd == NULL)
+ goto error_kzalloc;
+ cmd->bCommandType = UWB_RC_CET_GENERAL;
+ cmd->wCommand = cpu_to_le16(UWB_RC_CMD_GET_IE);
+ result = uwb_rc_vcmd(uwb_rc, "GET_IE", cmd, sizeof(*cmd),
+ UWB_RC_CET_GENERAL, UWB_RC_CMD_GET_IE,
+ &reply);
+ if (result < 0)
+ goto error_cmd;
+ get_ie = container_of(reply, struct uwb_rc_evt_get_ie, rceb);
+ if (result < sizeof(*get_ie)) {
+ dev_err(dev, "not enough data returned for decoding GET IE "
+ "(%zu bytes received vs %zu needed)\n",
+ result, sizeof(*get_ie));
+ result = -EINVAL;
+ } else if (result < sizeof(*get_ie) + le16_to_cpu(get_ie->wIELength)) {
+ dev_err(dev, "not enough data returned for decoding GET IE "
+ "payload (%zu bytes received vs %zu needed)\n", result,
+ sizeof(*get_ie) + le16_to_cpu(get_ie->wIELength));
+ result = -EINVAL;
+ } else
+ *pget_ie = get_ie;
+error_cmd:
+ kfree(cmd);
+error_kzalloc:
+ d_fnend(3, dev, "(%p, %p) = %d\n", uwb_rc, pget_ie, (int)result);
+ return result;
+}
+EXPORT_SYMBOL_GPL(uwb_rc_get_ie);
+
+
+/*
+ * Given a pointer to an IE, print it in ASCII/hex followed by a new line
+ *
+ * @ie_hdr: pointer to the IE header. Length is in there, and it is
+ * guaranteed that the ie_hdr->length bytes following it are
+ * safely accesible.
+ *
+ * @_data: context data passed from uwb_ie_for_each(), an struct output_ctx
+ */
+int uwb_ie_dump_hex(struct uwb_dev *uwb_dev, const struct uwb_ie_hdr *ie_hdr,
+ size_t offset, void *_ctx)
+{
+ struct uwb_buf_ctx *ctx = _ctx;
+ const u8 *pl = (void *)(ie_hdr + 1);
+ u8 pl_itr;
+
+ ctx->bytes += scnprintf(ctx->buf + ctx->bytes, ctx->size - ctx->bytes,
+ "%02x %02x ", (unsigned) ie_hdr->element_id,
+ (unsigned) ie_hdr->length);
+ pl_itr = 0;
+ while (pl_itr < ie_hdr->length && ctx->bytes < ctx->size)
+ ctx->bytes += scnprintf(ctx->buf + ctx->bytes,
+ ctx->size - ctx->bytes,
+ "%02x ", (unsigned) pl[pl_itr++]);
+ if (ctx->bytes < ctx->size)
+ ctx->buf[ctx->bytes++] = '\n';
+ return 0;
+}
+EXPORT_SYMBOL_GPL(uwb_ie_dump_hex);
+
+
+/**
+ * Verify that a pointer in a buffer points to valid IE
+ *
+ * @start: pointer to start of buffer in which IE appears
+ * @itr: pointer to IE inside buffer that will be verified
+ * @top: pointer to end of buffer
+ *
+ * @returns: 0 if IE is valid, <0 otherwise
+ *
+ * Verification involves checking that the buffer can contain a
+ * header and the amount of data reported in the IE header can be found in
+ * the buffer.
+ */
+static
+int uwb_rc_ie_verify(struct uwb_dev *uwb_dev, const void *start,
+ const void *itr, const void *top)
+{
+ struct device *dev = &uwb_dev->dev;
+ const struct uwb_ie_hdr *ie_hdr;
+
+ if (top - itr < sizeof(*ie_hdr)) {
+ dev_err(dev, "Bad IE: no data to decode header "
+ "(%zu bytes left vs %zu needed) at offset %zu\n",
+ top - itr, sizeof(*ie_hdr), itr - start);
+ return -EINVAL;
+ }
+ ie_hdr = itr;
+ itr += sizeof(*ie_hdr);
+ if (top - itr < ie_hdr->length) {
+ dev_err(dev, "Bad IE: not enough data for payload "
+ "(%zu bytes left vs %zu needed) at offset %zu\n",
+ top - itr, (size_t)ie_hdr->length,
+ (void *)ie_hdr - start);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+/**
+ * Walk a buffer filled with consecutive IE's a buffer
+ *
+ * @uwb_dev: UWB device this IEs belong to (for err messages mainly)
+ *
+ * @fn: function to call with each IE; if it returns 0, we keep
+ * traversing the buffer. If it returns !0, we'll stop and return
+ * that value.
+ *
+ * @data: pointer passed to @fn
+ *
+ * @buf: buffer where the consecutive IEs are located
+ *
+ * @size: size of @buf
+ *
+ * Each IE is checked for basic correctness (there is space left for
+ * the header and the payload). If that test is failed, we stop
+ * processing. For every good IE, @fn is called.
+ */
+ssize_t uwb_ie_for_each(struct uwb_dev *uwb_dev, uwb_ie_f fn, void *data,
+ const void *buf, size_t size)
+{
+ ssize_t result = 0;
+ const struct uwb_ie_hdr *ie_hdr;
+ const void *itr = buf, *top = itr + size;
+
+ while (itr < top) {
+ if (uwb_rc_ie_verify(uwb_dev, buf, itr, top) != 0)
+ break;
+ ie_hdr = itr;
+ itr += sizeof(*ie_hdr) + ie_hdr->length;
+ result = fn(uwb_dev, ie_hdr, itr - buf, data);
+ if (result != 0)
+ break;
+ }
+ return result;
+}
+EXPORT_SYMBOL_GPL(uwb_ie_for_each);
+
+
+/**
+ * Replace all IEs currently being transmitted by a device
+ *
+ * @cmd: pointer to the SET-IE command with the IEs to set
+ * @size: size of @buf
+ */
+int uwb_rc_set_ie(struct uwb_rc *rc, struct uwb_rc_cmd_set_ie *cmd)
+{
+ int result;
+ struct device *dev = &rc->uwb_dev.dev;
+ struct uwb_rc_evt_set_ie reply;
+
+ reply.rceb.bEventType = UWB_RC_CET_GENERAL;
+ reply.rceb.wEvent = UWB_RC_CMD_SET_IE;
+ result = uwb_rc_cmd(rc, "SET-IE", &cmd->rccb,
+ sizeof(*cmd) + le16_to_cpu(cmd->wIELength),
+ &reply.rceb, sizeof(reply));
+ if (result < 0)
+ goto error_cmd;
+ else if (result != sizeof(reply)) {
+ dev_err(dev, "SET-IE: not enough data to decode reply "
+ "(%d bytes received vs %zu needed)\n",
+ result, sizeof(reply));
+ result = -EIO;
+ } else if (reply.bResultCode != UWB_RC_RES_SUCCESS) {
+ dev_err(dev, "SET-IE: command execution failed: %s (%d)\n",
+ uwb_rc_strerror(reply.bResultCode), reply.bResultCode);
+ result = -EIO;
+ } else
+ result = 0;
+error_cmd:
+ return result;
+}
+
+/**
+ * Determine by IE id if IE is host settable
+ * WUSB 1.0 [8.6.2.8 Table 8.85]
+ *
+ * EXCEPTION:
+ * All but UWB_IE_WLP appears in Table 8.85 from WUSB 1.0. Setting this IE
+ * is required for the WLP substack to perform association with its WSS so
+ * we hope that the WUSB spec will be changed to reflect this.
+ */
+static
+int uwb_rc_ie_is_host_settable(enum uwb_ie element_id)
+{
+ if (element_id == UWB_PCA_AVAILABILITY ||
+ element_id == UWB_BP_SWITCH_IE ||
+ element_id == UWB_MAC_CAPABILITIES_IE ||
+ element_id == UWB_PHY_CAPABILITIES_IE ||
+ element_id == UWB_APP_SPEC_PROBE_IE ||
+ element_id == UWB_IDENTIFICATION_IE ||
+ element_id == UWB_MASTER_KEY_ID_IE ||
+ element_id == UWB_IE_WLP ||
+ element_id == UWB_APP_SPEC_IE)
+ return 1;
+ return 0;
+}
+
+
+/**
+ * Extract Host Settable IEs from IE
+ *
+ * @ie_data: pointer to buffer containing all IEs
+ * @size: size of buffer
+ *
+ * @returns: length of buffer that only includes host settable IEs
+ *
+ * Given a buffer of IEs we move all Host Settable IEs to front of buffer
+ * by overwriting the IEs that are not Host Settable.
+ * Buffer length is adjusted accordingly.
+ */
+static
+ssize_t uwb_rc_parse_host_settable_ie(struct uwb_dev *uwb_dev,
+ void *ie_data, size_t size)
+{
+ size_t new_len = size;
+ struct uwb_ie_hdr *ie_hdr;
+ size_t ie_length;
+ void *itr = ie_data, *top = itr + size;
+
+ while (itr < top) {
+ if (uwb_rc_ie_verify(uwb_dev, ie_data, itr, top) != 0)
+ break;
+ ie_hdr = itr;
+ ie_length = sizeof(*ie_hdr) + ie_hdr->length;
+ if (uwb_rc_ie_is_host_settable(ie_hdr->element_id)) {
+ itr += ie_length;
+ } else {
+ memmove(itr, itr + ie_length, top - (itr + ie_length));
+ new_len -= ie_length;
+ top -= ie_length;
+ }
+ }
+ return new_len;
+}
+
+
+/* Cleanup the whole IE management subsystem */
+void uwb_rc_ie_init(struct uwb_rc *uwb_rc)
+{
+ mutex_init(&uwb_rc->ies_mutex);
+}
+
+
+/**
+ * Set up cache for host settable IEs currently being transmitted
+ *
+ * First we just call GET-IE to get the current IEs being transmitted
+ * (or we workaround and pretend we did) and (because the format is
+ * the same) reuse that as the IE cache (with the command prefix, as
+ * explained in 'struct uwb_rc').
+ *
+ * @returns: size of cache created
+ */
+ssize_t uwb_rc_ie_setup(struct uwb_rc *uwb_rc)
+{
+ struct device *dev = &uwb_rc->uwb_dev.dev;
+ ssize_t result;
+ size_t capacity;
+ struct uwb_rc_evt_get_ie *ie_info;
+
+ d_fnstart(3, dev, "(%p)\n", uwb_rc);
+ mutex_lock(&uwb_rc->ies_mutex);
+ result = uwb_rc_get_ie(uwb_rc, &ie_info);
+ if (result < 0)
+ goto error_get_ie;
+ capacity = result;
+ d_printf(5, dev, "Got IEs %zu bytes (%zu long at %p)\n", result,
+ (size_t)le16_to_cpu(ie_info->wIELength), ie_info);
+
+ /* Remove IEs that host should not set. */
+ result = uwb_rc_parse_host_settable_ie(&uwb_rc->uwb_dev,
+ ie_info->IEData, le16_to_cpu(ie_info->wIELength));
+ if (result < 0)
+ goto error_parse;
+ d_printf(5, dev, "purged non-settable IEs to %zu bytes\n", result);
+ uwb_rc->ies = (void *) ie_info;
+ uwb_rc->ies->rccb.bCommandType = UWB_RC_CET_GENERAL;
+ uwb_rc->ies->rccb.wCommand = cpu_to_le16(UWB_RC_CMD_SET_IE);
+ uwb_rc->ies_capacity = capacity;
+ d_printf(5, dev, "IE cache at %p %zu bytes, %zu capacity\n",
+ ie_info, result, capacity);
+ result = 0;
+error_parse:
+error_get_ie:
+ mutex_unlock(&uwb_rc->ies_mutex);
+ d_fnend(3, dev, "(%p) = %zu\n", uwb_rc, result);
+ return result;
+}
+
+
+/* Cleanup the whole IE management subsystem */
+void uwb_rc_ie_release(struct uwb_rc *uwb_rc)
+{
+ kfree(uwb_rc->ies);
+ uwb_rc->ies = NULL;
+ uwb_rc->ies_capacity = 0;
+}
+
+
+static
+int __acc_size(struct uwb_dev *uwb_dev, const struct uwb_ie_hdr *ie_hdr,
+ size_t offset, void *_ctx)
+{
+ size_t *acc_size = _ctx;
+ *acc_size += sizeof(*ie_hdr) + ie_hdr->length;
+ d_printf(6, &uwb_dev->dev, "new acc size %zu\n", *acc_size);
+ return 0;
+}
+
+
+/**
+ * Add a new IE to IEs currently being transmitted by device
+ *
+ * @ies: the buffer containing the new IE or IEs to be added to
+ * the device's beacon. The buffer will be verified for
+ * consistence (meaning the headers should be right) and
+ * consistent with the buffer size.
+ * @size: size of @ies (in bytes, total buffer size)
+ * @returns: 0 if ok, <0 errno code on error
+ *
+ * According to WHCI 0.95 [4.13.6] the driver will only receive the RCEB
+ * after the device sent the first beacon that includes the IEs specified
+ * in the SET IE command. We thus cannot send this command if the device is
+ * not beaconing. Instead, a SET IE command will be sent later right after
+ * we start beaconing.
+ *
+ * Setting an IE on the device will overwrite all current IEs in device. So
+ * we take the current IEs being transmitted by the device, append the
+ * new one, and call SET IE with all the IEs needed.
+ *
+ * The local IE cache will only be updated with the new IE if SET IE
+ * completed successfully.
+ */
+int uwb_rc_ie_add(struct uwb_rc *uwb_rc,
+ const struct uwb_ie_hdr *ies, size_t size)
+{
+ int result = 0;
+ struct device *dev = &uwb_rc->uwb_dev.dev;
+ struct uwb_rc_cmd_set_ie *new_ies;
+ size_t ies_size, total_size, acc_size = 0;
+
+ if (uwb_rc->ies == NULL)
+ return -ESHUTDOWN;
+ uwb_ie_for_each(&uwb_rc->uwb_dev, __acc_size, &acc_size, ies, size);
+ if (acc_size != size) {
+ dev_err(dev, "BUG: bad IEs, misconstructed headers "
+ "[%zu bytes reported vs %zu calculated]\n",
+ size, acc_size);
+ WARN_ON(1);
+ return -EINVAL;
+ }
+ mutex_lock(&uwb_rc->ies_mutex);
+ ies_size = le16_to_cpu(uwb_rc->ies->wIELength);
+ total_size = sizeof(*uwb_rc->ies) + ies_size;
+ if (total_size + size > uwb_rc->ies_capacity) {
+ d_printf(4, dev, "Reallocating IE cache from %p capacity %zu "
+ "to capacity %zu\n", uwb_rc->ies, uwb_rc->ies_capacity,
+ total_size + size);
+ new_ies = kzalloc(total_size + size, GFP_KERNEL);
+ if (new_ies == NULL) {
+ dev_err(dev, "No memory for adding new IE\n");
+ result = -ENOMEM;
+ goto error_alloc;
+ }
+ memcpy(new_ies, uwb_rc->ies, total_size);
+ uwb_rc->ies_capacity = total_size + size;
+ kfree(uwb_rc->ies);
+ uwb_rc->ies = new_ies;
+ d_printf(4, dev, "New IE cache at %p capacity %zu\n",
+ uwb_rc->ies, uwb_rc->ies_capacity);
+ }
+ memcpy((void *)uwb_rc->ies + total_size, ies, size);
+ uwb_rc->ies->wIELength = cpu_to_le16(ies_size + size);
+ if (uwb_rc->beaconing != -1) {
+ result = uwb_rc_set_ie(uwb_rc, uwb_rc->ies);
+ if (result < 0) {
+ dev_err(dev, "Cannot set new IE on device: %d\n",
+ result);
+ uwb_rc->ies->wIELength = cpu_to_le16(ies_size);
+ } else
+ result = 0;
+ }
+ d_printf(4, dev, "IEs now occupy %hu bytes of %zu capacity at %p\n",
+ le16_to_cpu(uwb_rc->ies->wIELength), uwb_rc->ies_capacity,
+ uwb_rc->ies);
+error_alloc:
+ mutex_unlock(&uwb_rc->ies_mutex);
+ return result;
+}
+EXPORT_SYMBOL_GPL(uwb_rc_ie_add);
+
+
+/*
+ * Remove an IE from internal cache
+ *
+ * We are dealing with our internal IE cache so no need to verify that the
+ * IEs are valid (it has been done already).
+ *
+ * Should be called with ies_mutex held
+ *
+ * We do not break out once an IE is found in the cache. It is currently
+ * possible to have more than one IE with the same ID included in the
+ * beacon. We don't reallocate, we just mark the size smaller.
+ */
+static
+int uwb_rc_ie_cache_rm(struct uwb_rc *uwb_rc, enum uwb_ie to_remove)
+{
+ struct uwb_ie_hdr *ie_hdr;
+ size_t new_len = le16_to_cpu(uwb_rc->ies->wIELength);
+ void *itr = uwb_rc->ies->IEData;
+ void *top = itr + new_len;
+
+ while (itr < top) {
+ ie_hdr = itr;
+ if (ie_hdr->element_id != to_remove) {
+ itr += sizeof(*ie_hdr) + ie_hdr->length;
+ } else {
+ int ie_length;
+ ie_length = sizeof(*ie_hdr) + ie_hdr->length;
+ if (top - itr != ie_length)
+ memmove(itr, itr + ie_length, top - itr + ie_length);
+ top -= ie_length;
+ new_len -= ie_length;
+ }
+ }
+ uwb_rc->ies->wIELength = cpu_to_le16(new_len);
+ return 0;
+}
+
+
+/**
+ * Remove an IE currently being transmitted by device
+ *
+ * @element_id: id of IE to be removed from device's beacon
+ */
+int uwb_rc_ie_rm(struct uwb_rc *uwb_rc, enum uwb_ie element_id)
+{
+ struct device *dev = &uwb_rc->uwb_dev.dev;
+ int result;
+
+ if (uwb_rc->ies == NULL)
+ return -ESHUTDOWN;
+ mutex_lock(&uwb_rc->ies_mutex);
+ result = uwb_rc_ie_cache_rm(uwb_rc, element_id);
+ if (result < 0)
+ dev_err(dev, "Cannot remove IE from cache.\n");
+ if (uwb_rc->beaconing != -1) {
+ result = uwb_rc_set_ie(uwb_rc, uwb_rc->ies);
+ if (result < 0)
+ dev_err(dev, "Cannot set new IE on device.\n");
+ }
+ mutex_unlock(&uwb_rc->ies_mutex);
+ return result;
+}
+EXPORT_SYMBOL_GPL(uwb_rc_ie_rm);
diff --git a/drivers/uwb/lc-dev.c b/drivers/uwb/lc-dev.c
new file mode 100644
index 000000000000..a6cb8ad731a6
--- /dev/null
+++ b/drivers/uwb/lc-dev.c
@@ -0,0 +1,492 @@
+/*
+ * Ultra Wide Band
+ * Life cycle of devices
+ *
+ * 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/kernel.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kdev_t.h>
+#include <linux/random.h>
+#include "uwb-internal.h"
+
+#define D_LOCAL 1
+#include <linux/uwb/debug.h>
+
+
+/* We initialize addresses to 0xff (invalid, as it is bcast) */
+static inline void uwb_dev_addr_init(struct uwb_dev_addr *addr)
+{
+ memset(&addr->data, 0xff, sizeof(addr->data));
+}
+
+static inline void uwb_mac_addr_init(struct uwb_mac_addr *addr)
+{
+ memset(&addr->data, 0xff, sizeof(addr->data));
+}
+
+/* @returns !0 if a device @addr is a broadcast address */
+static inline int uwb_dev_addr_bcast(const struct uwb_dev_addr *addr)
+{
+ static const struct uwb_dev_addr bcast = { .data = { 0xff, 0xff } };
+ return !uwb_dev_addr_cmp(addr, &bcast);
+}
+
+/*
+ * Add callback @new to be called when an event occurs in @rc.
+ */
+int uwb_notifs_register(struct uwb_rc *rc, struct uwb_notifs_handler *new)
+{
+ if (mutex_lock_interruptible(&rc->notifs_chain.mutex))
+ return -ERESTARTSYS;
+ list_add(&new->list_node, &rc->notifs_chain.list);
+ mutex_unlock(&rc->notifs_chain.mutex);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(uwb_notifs_register);
+
+/*
+ * Remove event handler (callback)
+ */
+int uwb_notifs_deregister(struct uwb_rc *rc, struct uwb_notifs_handler *entry)
+{
+ if (mutex_lock_interruptible(&rc->notifs_chain.mutex))
+ return -ERESTARTSYS;
+ list_del(&entry->list_node);
+ mutex_unlock(&rc->notifs_chain.mutex);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(uwb_notifs_deregister);
+
+/*
+ * Notify all event handlers of a given event on @rc
+ *
+ * We are called with a valid reference to the device, or NULL if the
+ * event is not for a particular event (e.g., a BG join event).
+ */
+void uwb_notify(struct uwb_rc *rc, struct uwb_dev *uwb_dev, enum uwb_notifs event)
+{
+ struct uwb_notifs_handler *handler;
+ if (mutex_lock_interruptible(&rc->notifs_chain.mutex))
+ return;
+ if (!list_empty(&rc->notifs_chain.list)) {
+ list_for_each_entry(handler, &rc->notifs_chain.list, list_node) {
+ handler->cb(handler->data, uwb_dev, event);
+ }
+ }
+ mutex_unlock(&rc->notifs_chain.mutex);
+}
+
+/*
+ * Release the backing device of a uwb_dev that has been dynamically allocated.
+ */
+static void uwb_dev_sys_release(struct device *dev)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+
+ d_fnstart(4, NULL, "(dev %p uwb_dev %p)\n", dev, uwb_dev);
+ uwb_bce_put(uwb_dev->bce);
+ d_printf(0, &uwb_dev->dev, "uwb_dev %p freed\n", uwb_dev);
+ memset(uwb_dev, 0x69, sizeof(*uwb_dev));
+ kfree(uwb_dev);
+ d_fnend(4, NULL, "(dev %p uwb_dev %p) = void\n", dev, uwb_dev);
+}
+
+/*
+ * Initialize a UWB device instance
+ *
+ * Alloc, zero and call this function.
+ */
+void uwb_dev_init(struct uwb_dev *uwb_dev)
+{
+ mutex_init(&uwb_dev->mutex);
+ device_initialize(&uwb_dev->dev);
+ uwb_dev->dev.release = uwb_dev_sys_release;
+ uwb_dev_addr_init(&uwb_dev->dev_addr);
+ uwb_mac_addr_init(&uwb_dev->mac_addr);
+ bitmap_fill(uwb_dev->streams, UWB_NUM_GLOBAL_STREAMS);
+}
+
+static ssize_t uwb_dev_EUI_48_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ char addr[UWB_ADDR_STRSIZE];
+
+ uwb_mac_addr_print(addr, sizeof(addr), &uwb_dev->mac_addr);
+ return sprintf(buf, "%s\n", addr);
+}
+static DEVICE_ATTR(EUI_48, S_IRUGO, uwb_dev_EUI_48_show, NULL);
+
+static ssize_t uwb_dev_DevAddr_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ char addr[UWB_ADDR_STRSIZE];
+
+ uwb_dev_addr_print(addr, sizeof(addr), &uwb_dev->dev_addr);
+ return sprintf(buf, "%s\n", addr);
+}
+static DEVICE_ATTR(DevAddr, S_IRUGO, uwb_dev_DevAddr_show, NULL);
+
+/*
+ * Show the BPST of this device.
+ *
+ * Calculated from the receive time of the device's beacon and it's
+ * slot number.
+ */
+static ssize_t uwb_dev_BPST_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ struct uwb_beca_e *bce;
+ struct uwb_beacon_frame *bf;
+ u16 bpst;
+
+ bce = uwb_dev->bce;
+ mutex_lock(&bce->mutex);
+ bf = (struct uwb_beacon_frame *)bce->be->BeaconInfo;
+ bpst = bce->be->wBPSTOffset
+ - (u16)(bf->Beacon_Slot_Number * UWB_BEACON_SLOT_LENGTH_US);
+ mutex_unlock(&bce->mutex);
+
+ return sprintf(buf, "%d\n", bpst);
+}
+static DEVICE_ATTR(BPST, S_IRUGO, uwb_dev_BPST_show, NULL);
+
+/*
+ * Show the IEs a device is beaconing
+ *
+ * We need to access the beacon cache, so we just lock it really
+ * quick, print the IEs and unlock.
+ *
+ * We have a reference on the cache entry, so that should be
+ * quite safe.
+ */
+static ssize_t uwb_dev_IEs_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+
+ return uwb_bce_print_IEs(uwb_dev, uwb_dev->bce, buf, PAGE_SIZE);
+}
+static DEVICE_ATTR(IEs, S_IRUGO | S_IWUSR, uwb_dev_IEs_show, NULL);
+
+static ssize_t uwb_dev_LQE_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ struct uwb_beca_e *bce = uwb_dev->bce;
+ size_t result;
+
+ mutex_lock(&bce->mutex);
+ result = stats_show(&uwb_dev->bce->lqe_stats, buf);
+ mutex_unlock(&bce->mutex);
+ return result;
+}
+
+static ssize_t uwb_dev_LQE_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ struct uwb_beca_e *bce = uwb_dev->bce;
+ ssize_t result;
+
+ mutex_lock(&bce->mutex);
+ result = stats_store(&uwb_dev->bce->lqe_stats, buf, size);
+ mutex_unlock(&bce->mutex);
+ return result;
+}
+static DEVICE_ATTR(LQE, S_IRUGO | S_IWUSR, uwb_dev_LQE_show, uwb_dev_LQE_store);
+
+static ssize_t uwb_dev_RSSI_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ struct uwb_beca_e *bce = uwb_dev->bce;
+ size_t result;
+
+ mutex_lock(&bce->mutex);
+ result = stats_show(&uwb_dev->bce->rssi_stats, buf);
+ mutex_unlock(&bce->mutex);
+ return result;
+}
+
+static ssize_t uwb_dev_RSSI_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ struct uwb_beca_e *bce = uwb_dev->bce;
+ ssize_t result;
+
+ mutex_lock(&bce->mutex);
+ result = stats_store(&uwb_dev->bce->rssi_stats, buf, size);
+ mutex_unlock(&bce->mutex);
+ return result;
+}
+static DEVICE_ATTR(RSSI, S_IRUGO | S_IWUSR, uwb_dev_RSSI_show, uwb_dev_RSSI_store);
+
+
+static struct attribute *dev_attrs[] = {
+ &dev_attr_EUI_48.attr,
+ &dev_attr_DevAddr.attr,
+ &dev_attr_BPST.attr,
+ &dev_attr_IEs.attr,
+ &dev_attr_LQE.attr,
+ &dev_attr_RSSI.attr,
+ NULL,
+};
+
+static struct attribute_group dev_attr_group = {
+ .attrs = dev_attrs,
+};
+
+static struct attribute_group *groups[] = {
+ &dev_attr_group,
+ NULL,
+};
+
+/**
+ * Device SYSFS registration
+ *
+ *
+ */
+static int __uwb_dev_sys_add(struct uwb_dev *uwb_dev, struct device *parent_dev)
+{
+ int result;
+ struct device *dev;
+
+ d_fnstart(4, NULL, "(uwb_dev %p parent_dev %p)\n", uwb_dev, parent_dev);
+ BUG_ON(parent_dev == NULL);
+
+ dev = &uwb_dev->dev;
+ /* Device sysfs files are only useful for neighbor devices not
+ local radio controllers. */
+ if (&uwb_dev->rc->uwb_dev != uwb_dev)
+ dev->groups = groups;
+ dev->parent = parent_dev;
+ dev_set_drvdata(dev, uwb_dev);
+
+ result = device_add(dev);
+ d_fnend(4, NULL, "(uwb_dev %p parent_dev %p) = %d\n", uwb_dev, parent_dev, result);
+ return result;
+}
+
+
+static void __uwb_dev_sys_rm(struct uwb_dev *uwb_dev)
+{
+ d_fnstart(4, NULL, "(uwb_dev %p)\n", uwb_dev);
+ dev_set_drvdata(&uwb_dev->dev, NULL);
+ device_del(&uwb_dev->dev);
+ d_fnend(4, NULL, "(uwb_dev %p) = void\n", uwb_dev);
+}
+
+
+/**
+ * Register and initialize a new UWB device
+ *
+ * Did you call uwb_dev_init() on it?
+ *
+ * @parent_rc: is the parent radio controller who has the link to the
+ * device. When registering the UWB device that is a UWB
+ * Radio Controller, we point back to it.
+ *
+ * If registering the device that is part of a radio, caller has set
+ * rc->uwb_dev->dev. Otherwise it is to be left NULL--a new one will
+ * be allocated.
+ */
+int uwb_dev_add(struct uwb_dev *uwb_dev, struct device *parent_dev,
+ struct uwb_rc *parent_rc)
+{
+ int result;
+ struct device *dev;
+
+ BUG_ON(uwb_dev == NULL);
+ BUG_ON(parent_dev == NULL);
+ BUG_ON(parent_rc == NULL);
+
+ mutex_lock(&uwb_dev->mutex);
+ dev = &uwb_dev->dev;
+ uwb_dev->rc = parent_rc;
+ result = __uwb_dev_sys_add(uwb_dev, parent_dev);
+ if (result < 0)
+ printk(KERN_ERR "UWB: unable to register dev %s with sysfs: %d\n",
+ dev_name(dev), result);
+ mutex_unlock(&uwb_dev->mutex);
+ return result;
+}
+
+
+void uwb_dev_rm(struct uwb_dev *uwb_dev)
+{
+ mutex_lock(&uwb_dev->mutex);
+ __uwb_dev_sys_rm(uwb_dev);
+ mutex_unlock(&uwb_dev->mutex);
+}
+
+
+static
+int __uwb_dev_try_get(struct device *dev, void *__target_uwb_dev)
+{
+ struct uwb_dev *target_uwb_dev = __target_uwb_dev;
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ if (uwb_dev == target_uwb_dev) {
+ uwb_dev_get(uwb_dev);
+ return 1;
+ } else
+ return 0;
+}
+
+
+/**
+ * Given a UWB device descriptor, validate and refcount it
+ *
+ * @returns NULL if the device does not exist or is quiescing; the ptr to
+ * it otherwise.
+ */
+struct uwb_dev *uwb_dev_try_get(struct uwb_rc *rc, struct uwb_dev *uwb_dev)
+{
+ if (uwb_dev_for_each(rc, __uwb_dev_try_get, uwb_dev))
+ return uwb_dev;
+ else
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(uwb_dev_try_get);
+
+
+/**
+ * Remove a device from the system [grunt for other functions]
+ */
+int __uwb_dev_offair(struct uwb_dev *uwb_dev, struct uwb_rc *rc)
+{
+ struct device *dev = &uwb_dev->dev;
+ char macbuf[UWB_ADDR_STRSIZE], devbuf[UWB_ADDR_STRSIZE];
+
+ d_fnstart(3, NULL, "(dev %p [uwb_dev %p], uwb_rc %p)\n", dev, uwb_dev, rc);
+ uwb_mac_addr_print(macbuf, sizeof(macbuf), &uwb_dev->mac_addr);
+ uwb_dev_addr_print(devbuf, sizeof(devbuf), &uwb_dev->dev_addr);
+ dev_info(dev, "uwb device (mac %s dev %s) disconnected from %s %s\n",
+ macbuf, devbuf,
+ rc ? rc->uwb_dev.dev.parent->bus->name : "n/a",
+ rc ? dev_name(rc->uwb_dev.dev.parent) : "");
+ uwb_dev_rm(uwb_dev);
+ uwb_dev_put(uwb_dev); /* for the creation in _onair() */
+ d_fnend(3, NULL, "(dev %p [uwb_dev %p], uwb_rc %p) = 0\n", dev, uwb_dev, rc);
+ return 0;
+}
+
+
+/**
+ * A device went off the air, clean up after it!
+ *
+ * This is called by the UWB Daemon (through the beacon purge function
+ * uwb_bcn_cache_purge) when it is detected that a device has been in
+ * radio silence for a while.
+ *
+ * If this device is actually a local radio controller we don't need
+ * to go through the offair process, as it is not registered as that.
+ *
+ * NOTE: uwb_bcn_cache.mutex is held!
+ */
+void uwbd_dev_offair(struct uwb_beca_e *bce)
+{
+ struct uwb_dev *uwb_dev;
+
+ uwb_dev = bce->uwb_dev;
+ if (uwb_dev) {
+ uwb_notify(uwb_dev->rc, uwb_dev, UWB_NOTIF_OFFAIR);
+ __uwb_dev_offair(uwb_dev, uwb_dev->rc);
+ }
+}
+
+
+/**
+ * A device went on the air, start it up!
+ *
+ * This is called by the UWB Daemon when it is detected that a device
+ * has popped up in the radio range of the radio controller.
+ *
+ * It will just create the freaking device, register the beacon and
+ * stuff and yatla, done.
+ *
+ *
+ * NOTE: uwb_beca.mutex is held, bce->mutex is held
+ */
+void uwbd_dev_onair(struct uwb_rc *rc, struct uwb_beca_e *bce)
+{
+ int result;
+ struct device *dev = &rc->uwb_dev.dev;
+ struct uwb_dev *uwb_dev;
+ char macbuf[UWB_ADDR_STRSIZE], devbuf[UWB_ADDR_STRSIZE];
+
+ uwb_mac_addr_print(macbuf, sizeof(macbuf), bce->mac_addr);
+ uwb_dev_addr_print(devbuf, sizeof(devbuf), &bce->dev_addr);
+ uwb_dev = kcalloc(1, sizeof(*uwb_dev), GFP_KERNEL);
+ if (uwb_dev == NULL) {
+ dev_err(dev, "new device %s: Cannot allocate memory\n",
+ macbuf);
+ return;
+ }
+ uwb_dev_init(uwb_dev); /* This sets refcnt to one, we own it */
+ uwb_dev->mac_addr = *bce->mac_addr;
+ uwb_dev->dev_addr = bce->dev_addr;
+ dev_set_name(&uwb_dev->dev, macbuf);
+ result = uwb_dev_add(uwb_dev, &rc->uwb_dev.dev, rc);
+ if (result < 0) {
+ dev_err(dev, "new device %s: cannot instantiate device\n",
+ macbuf);
+ goto error_dev_add;
+ }
+ /* plug the beacon cache */
+ bce->uwb_dev = uwb_dev;
+ uwb_dev->bce = bce;
+ uwb_bce_get(bce); /* released in uwb_dev_sys_release() */
+ dev_info(dev, "uwb device (mac %s dev %s) connected to %s %s\n",
+ macbuf, devbuf, rc->uwb_dev.dev.parent->bus->name,
+ dev_name(rc->uwb_dev.dev.parent));
+ uwb_notify(rc, uwb_dev, UWB_NOTIF_ONAIR);
+ return;
+
+error_dev_add:
+ kfree(uwb_dev);
+ return;
+}
+
+/**
+ * Iterate over the list of UWB devices, calling a @function on each
+ *
+ * See docs for bus_for_each()....
+ *
+ * @rc: radio controller for the devices.
+ * @function: function to call.
+ * @priv: data to pass to @function.
+ * @returns: 0 if no invocation of function() returned a value
+ * different to zero. That value otherwise.
+ */
+int uwb_dev_for_each(struct uwb_rc *rc, uwb_dev_for_each_f function, void *priv)
+{
+ return device_for_each_child(&rc->uwb_dev.dev, priv, function);
+}
+EXPORT_SYMBOL_GPL(uwb_dev_for_each);
diff --git a/drivers/uwb/lc-rc.c b/drivers/uwb/lc-rc.c
new file mode 100644
index 000000000000..ee5772f00d42
--- /dev/null
+++ b/drivers/uwb/lc-rc.c
@@ -0,0 +1,495 @@
+/*
+ * Ultra Wide Band
+ * Life cycle of radio controllers
+ *
+ * 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
+ *
+ * A UWB radio controller is also a UWB device, so it embeds one...
+ *
+ * List of RCs comes from the 'struct class uwb_rc_class'.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/random.h>
+#include <linux/kdev_t.h>
+#include <linux/etherdevice.h>
+#include <linux/usb.h>
+
+#define D_LOCAL 1
+#include <linux/uwb/debug.h>
+#include "uwb-internal.h"
+
+static int uwb_rc_index_match(struct device *dev, void *data)
+{
+ int *index = data;
+ struct uwb_rc *rc = dev_get_drvdata(dev);
+
+ if (rc->index == *index)
+ return 1;
+ return 0;
+}
+
+static struct uwb_rc *uwb_rc_find_by_index(int index)
+{
+ struct device *dev;
+ struct uwb_rc *rc = NULL;
+
+ dev = class_find_device(&uwb_rc_class, NULL, &index, uwb_rc_index_match);
+ if (dev)
+ rc = dev_get_drvdata(dev);
+ return rc;
+}
+
+static int uwb_rc_new_index(void)
+{
+ int index = 0;
+
+ for (;;) {
+ if (!uwb_rc_find_by_index(index))
+ return index;
+ if (++index < 0)
+ index = 0;
+ }
+}
+
+/**
+ * Release the backing device of a uwb_rc that has been dynamically allocated.
+ */
+static void uwb_rc_sys_release(struct device *dev)
+{
+ struct uwb_dev *uwb_dev = container_of(dev, struct uwb_dev, dev);
+ struct uwb_rc *rc = container_of(uwb_dev, struct uwb_rc, uwb_dev);
+
+ uwb_rc_neh_destroy(rc);
+ uwb_rc_ie_release(rc);
+ d_printf(1, dev, "freed uwb_rc %p\n", rc);
+ kfree(rc);
+}
+
+
+void uwb_rc_init(struct uwb_rc *rc)
+{
+ struct uwb_dev *uwb_dev = &rc->uwb_dev;
+
+ uwb_dev_init(uwb_dev);
+ rc->uwb_dev.dev.class = &uwb_rc_class;
+ rc->uwb_dev.dev.release = uwb_rc_sys_release;
+ uwb_rc_neh_create(rc);
+ rc->beaconing = -1;
+ rc->scan_type = UWB_SCAN_DISABLED;
+ INIT_LIST_HEAD(&rc->notifs_chain.list);
+ mutex_init(&rc->notifs_chain.mutex);
+ uwb_drp_avail_init(rc);
+ uwb_rc_ie_init(rc);
+ uwb_rsv_init(rc);
+ uwb_rc_pal_init(rc);
+}
+EXPORT_SYMBOL_GPL(uwb_rc_init);
+
+
+struct uwb_rc *uwb_rc_alloc(void)
+{
+ struct uwb_rc *rc;
+ rc = kzalloc(sizeof(*rc), GFP_KERNEL);
+ if (rc == NULL)
+ return NULL;
+ uwb_rc_init(rc);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(uwb_rc_alloc);
+
+static struct attribute *rc_attrs[] = {
+ &dev_attr_mac_address.attr,
+ &dev_attr_scan.attr,
+ &dev_attr_beacon.attr,
+ NULL,
+};
+
+static struct attribute_group rc_attr_group = {
+ .attrs = rc_attrs,
+};
+
+/*
+ * Registration of sysfs specific stuff
+ */
+static int uwb_rc_sys_add(struct uwb_rc *rc)
+{
+ return sysfs_create_group(&rc->uwb_dev.dev.kobj, &rc_attr_group);
+}
+
+
+static void __uwb_rc_sys_rm(struct uwb_rc *rc)
+{
+ sysfs_remove_group(&rc->uwb_dev.dev.kobj, &rc_attr_group);
+}
+
+/**
+ * uwb_rc_mac_addr_setup - get an RC's EUI-48 address or set it
+ * @rc: the radio controller.
+ *
+ * If the EUI-48 address is 00:00:00:00:00:00 or FF:FF:FF:FF:FF:FF
+ * then a random locally administered EUI-48 is generated and set on
+ * the device. The probability of address collisions is sufficiently
+ * unlikely (1/2^40 = 9.1e-13) that they're not checked for.
+ */
+static
+int uwb_rc_mac_addr_setup(struct uwb_rc *rc)
+{
+ int result;
+ struct device *dev = &rc->uwb_dev.dev;
+ struct uwb_dev *uwb_dev = &rc->uwb_dev;
+ char devname[UWB_ADDR_STRSIZE];
+ struct uwb_mac_addr addr;
+
+ result = uwb_rc_mac_addr_get(rc, &addr);
+ if (result < 0) {
+ dev_err(dev, "cannot retrieve UWB EUI-48 address: %d\n", result);
+ return result;
+ }
+
+ if (uwb_mac_addr_unset(&addr) || uwb_mac_addr_bcast(&addr)) {
+ addr.data[0] = 0x02; /* locally adminstered and unicast */
+ get_random_bytes(&addr.data[1], sizeof(addr.data)-1);
+
+ result = uwb_rc_mac_addr_set(rc, &addr);
+ if (result < 0) {
+ uwb_mac_addr_print(devname, sizeof(devname), &addr);
+ dev_err(dev, "cannot set EUI-48 address %s: %d\n",
+ devname, result);
+ return result;
+ }
+ }
+ uwb_dev->mac_addr = addr;
+ return 0;
+}
+
+
+
+static int uwb_rc_setup(struct uwb_rc *rc)
+{
+ int result;
+ struct device *dev = &rc->uwb_dev.dev;
+
+ result = uwb_rc_reset(rc);
+ if (result < 0) {
+ dev_err(dev, "cannot reset UWB radio: %d\n", result);
+ goto error;
+ }
+ result = uwb_rc_mac_addr_setup(rc);
+ if (result < 0) {
+ dev_err(dev, "cannot setup UWB MAC address: %d\n", result);
+ goto error;
+ }
+ result = uwb_rc_dev_addr_assign(rc);
+ if (result < 0) {
+ dev_err(dev, "cannot assign UWB DevAddr: %d\n", result);
+ goto error;
+ }
+ result = uwb_rc_ie_setup(rc);
+ if (result < 0) {
+ dev_err(dev, "cannot setup IE subsystem: %d\n", result);
+ goto error_ie_setup;
+ }
+ result = uwb_rsv_setup(rc);
+ if (result < 0) {
+ dev_err(dev, "cannot setup reservation subsystem: %d\n", result);
+ goto error_rsv_setup;
+ }
+ uwb_dbg_add_rc(rc);
+ return 0;
+
+error_rsv_setup:
+ uwb_rc_ie_release(rc);
+error_ie_setup:
+error:
+ return result;
+}
+
+
+/**
+ * Register a new UWB radio controller
+ *
+ * Did you call uwb_rc_init() on your rc?
+ *
+ * We assume that this is being called with a > 0 refcount on
+ * it [through ops->{get|put}_device(). We'll take our own, though.
+ *
+ * @parent_dev is our real device, the one that provides the actual UWB device
+ */
+int uwb_rc_add(struct uwb_rc *rc, struct device *parent_dev, void *priv)
+{
+ int result;
+ struct device *dev;
+ char macbuf[UWB_ADDR_STRSIZE], devbuf[UWB_ADDR_STRSIZE];
+
+ rc->index = uwb_rc_new_index();
+
+ dev = &rc->uwb_dev.dev;
+ dev_set_name(dev, "uwb%d", rc->index);
+
+ rc->priv = priv;
+
+ result = rc->start(rc);
+ if (result < 0)
+ goto error_rc_start;
+
+ result = uwb_rc_setup(rc);
+ if (result < 0) {
+ dev_err(dev, "cannot setup UWB radio controller: %d\n", result);
+ goto error_rc_setup;
+ }
+
+ result = uwb_dev_add(&rc->uwb_dev, parent_dev, rc);
+ if (result < 0 && result != -EADDRNOTAVAIL)
+ goto error_dev_add;
+
+ result = uwb_rc_sys_add(rc);
+ if (result < 0) {
+ dev_err(parent_dev, "cannot register UWB radio controller "
+ "dev attributes: %d\n", result);
+ goto error_sys_add;
+ }
+
+ uwb_mac_addr_print(macbuf, sizeof(macbuf), &rc->uwb_dev.mac_addr);
+ uwb_dev_addr_print(devbuf, sizeof(devbuf), &rc->uwb_dev.dev_addr);
+ dev_info(dev,
+ "new uwb radio controller (mac %s dev %s) on %s %s\n",
+ macbuf, devbuf, parent_dev->bus->name, dev_name(parent_dev));
+ rc->ready = 1;
+ return 0;
+
+error_sys_add:
+ uwb_dev_rm(&rc->uwb_dev);
+error_dev_add:
+error_rc_setup:
+ rc->stop(rc);
+ uwbd_flush(rc);
+error_rc_start:
+ return result;
+}
+EXPORT_SYMBOL_GPL(uwb_rc_add);
+
+
+static int uwb_dev_offair_helper(struct device *dev, void *priv)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+
+ return __uwb_dev_offair(uwb_dev, uwb_dev->rc);
+}
+
+/*
+ * Remove a Radio Controller; stop beaconing/scanning, disconnect all children
+ */
+void uwb_rc_rm(struct uwb_rc *rc)
+{
+ rc->ready = 0;
+
+ uwb_dbg_del_rc(rc);
+ uwb_rsv_cleanup(rc);
+ uwb_rc_ie_rm(rc, UWB_IDENTIFICATION_IE);
+ if (rc->beaconing >= 0)
+ uwb_rc_beacon(rc, -1, 0);
+ if (rc->scan_type != UWB_SCAN_DISABLED)
+ uwb_rc_scan(rc, rc->scanning, UWB_SCAN_DISABLED, 0);
+ uwb_rc_reset(rc);
+
+ rc->stop(rc);
+ uwbd_flush(rc);
+
+ uwb_dev_lock(&rc->uwb_dev);
+ rc->priv = NULL;
+ rc->cmd = NULL;
+ uwb_dev_unlock(&rc->uwb_dev);
+ mutex_lock(&uwb_beca.mutex);
+ uwb_dev_for_each(rc, uwb_dev_offair_helper, NULL);
+ __uwb_rc_sys_rm(rc);
+ mutex_unlock(&uwb_beca.mutex);
+ uwb_dev_rm(&rc->uwb_dev);
+}
+EXPORT_SYMBOL_GPL(uwb_rc_rm);
+
+static int find_rc_try_get(struct device *dev, void *data)
+{
+ struct uwb_rc *target_rc = data;
+ struct uwb_rc *rc = dev_get_drvdata(dev);
+
+ if (rc == NULL) {
+ WARN_ON(1);
+ return 0;
+ }
+ if (rc == target_rc) {
+ if (rc->ready == 0)
+ return 0;
+ else
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * Given a radio controller descriptor, validate and refcount it
+ *
+ * @returns NULL if the rc does not exist or is quiescing; the ptr to
+ * it otherwise.
+ */
+struct uwb_rc *__uwb_rc_try_get(struct uwb_rc *target_rc)
+{
+ struct device *dev;
+ struct uwb_rc *rc = NULL;
+
+ dev = class_find_device(&uwb_rc_class, NULL, target_rc,
+ find_rc_try_get);
+ if (dev) {
+ rc = dev_get_drvdata(dev);
+ __uwb_rc_get(rc);
+ }
+ return rc;
+}
+EXPORT_SYMBOL_GPL(__uwb_rc_try_get);
+
+/*
+ * RC get for external refcount acquirers...
+ *
+ * Increments the refcount of the device and it's backend modules
+ */
+static inline struct uwb_rc *uwb_rc_get(struct uwb_rc *rc)
+{
+ if (rc->ready == 0)
+ return NULL;
+ uwb_dev_get(&rc->uwb_dev);
+ return rc;
+}
+
+static int find_rc_grandpa(struct device *dev, void *data)
+{
+ struct device *grandpa_dev = data;
+ struct uwb_rc *rc = dev_get_drvdata(dev);
+
+ if (rc->uwb_dev.dev.parent->parent == grandpa_dev) {
+ rc = uwb_rc_get(rc);
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * Locate and refcount a radio controller given a common grand-parent
+ *
+ * @grandpa_dev Pointer to the 'grandparent' device structure.
+ * @returns NULL If the rc does not exist or is quiescing; the ptr to
+ * it otherwise, properly referenced.
+ *
+ * The Radio Control interface (or the UWB Radio Controller) is always
+ * an interface of a device. The parent is the interface, the
+ * grandparent is the device that encapsulates the interface.
+ *
+ * There is no need to lock around as the "grandpa" would be
+ * refcounted by the target, and to remove the referemes, the
+ * uwb_rc_class->sem would have to be taken--we hold it, ergo we
+ * should be safe.
+ */
+struct uwb_rc *uwb_rc_get_by_grandpa(const struct device *grandpa_dev)
+{
+ struct device *dev;
+ struct uwb_rc *rc = NULL;
+
+ dev = class_find_device(&uwb_rc_class, NULL, (void *)grandpa_dev,
+ find_rc_grandpa);
+ if (dev)
+ rc = dev_get_drvdata(dev);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(uwb_rc_get_by_grandpa);
+
+/**
+ * Find a radio controller by device address
+ *
+ * @returns the pointer to the radio controller, properly referenced
+ */
+static int find_rc_dev(struct device *dev, void *data)
+{
+ struct uwb_dev_addr *addr = data;
+ struct uwb_rc *rc = dev_get_drvdata(dev);
+
+ if (rc == NULL) {
+ WARN_ON(1);
+ return 0;
+ }
+ if (!uwb_dev_addr_cmp(&rc->uwb_dev.dev_addr, addr)) {
+ rc = uwb_rc_get(rc);
+ return 1;
+ }
+ return 0;
+}
+
+struct uwb_rc *uwb_rc_get_by_dev(const struct uwb_dev_addr *addr)
+{
+ struct device *dev;
+ struct uwb_rc *rc = NULL;
+
+ dev = class_find_device(&uwb_rc_class, NULL, (void *)addr,
+ find_rc_dev);
+ if (dev)
+ rc = dev_get_drvdata(dev);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(uwb_rc_get_by_dev);
+
+/**
+ * Drop a reference on a radio controller
+ *
+ * This is the version that should be done by entities external to the
+ * UWB Radio Control stack (ie: clients of the API).
+ */
+void uwb_rc_put(struct uwb_rc *rc)
+{
+ __uwb_rc_put(rc);
+}
+EXPORT_SYMBOL_GPL(uwb_rc_put);
+
+/*
+ *
+ *
+ */
+ssize_t uwb_rc_print_IEs(struct uwb_rc *uwb_rc, char *buf, size_t size)
+{
+ ssize_t result;
+ struct uwb_rc_evt_get_ie *ie_info;
+ struct uwb_buf_ctx ctx;
+
+ result = uwb_rc_get_ie(uwb_rc, &ie_info);
+ if (result < 0)
+ goto error_get_ie;
+ ctx.buf = buf;
+ ctx.size = size;
+ ctx.bytes = 0;
+ uwb_ie_for_each(&uwb_rc->uwb_dev, uwb_ie_dump_hex, &ctx,
+ ie_info->IEData, result - sizeof(*ie_info));
+ result = ctx.bytes;
+ kfree(ie_info);
+error_get_ie:
+ return result;
+}
+
diff --git a/drivers/uwb/neh.c b/drivers/uwb/neh.c
new file mode 100644
index 000000000000..91b61480e1ee
--- /dev/null
+++ b/drivers/uwb/neh.c
@@ -0,0 +1,616 @@
+/*
+ * WUSB Wire Adapter: Radio Control Interface (WUSB[8])
+ * Notification and Event Handling
+ *
+ * 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.
+ *
+ *
+ * The RC interface of the Host Wire Adapter (USB dongle) or WHCI PCI
+ * card delivers a stream of notifications and events to the
+ * notification end event endpoint or area. This code takes care of
+ * getting a buffer with that data, breaking it up in separate
+ * notifications and events and then deliver those.
+ *
+ * Events are answers to commands and they carry a context ID that
+ * associates them to the command. Notifications are that,
+ * notifications, they come out of the blue and have a context ID of
+ * zero. Think of the context ID kind of like a handler. The
+ * uwb_rc_neh_* code deals with managing context IDs.
+ *
+ * This is why you require a handle to operate on a UWB host. When you
+ * open a handle a context ID is assigned to you.
+ *
+ * So, as it is done is:
+ *
+ * 1. Add an event handler [uwb_rc_neh_add()] (assigns a ctx id)
+ * 2. Issue command [rc->cmd(rc, ...)]
+ * 3. Arm the timeout timer [uwb_rc_neh_arm()]
+ * 4, Release the reference to the neh [uwb_rc_neh_put()]
+ * 5. Wait for the callback
+ * 6. Command result (RCEB) is passed to the callback
+ *
+ * If (2) fails, you should remove the handle [uwb_rc_neh_rm()]
+ * instead of arming the timer.
+ *
+ * Handles are for using in *serialized* code, single thread.
+ *
+ * When the notification/event comes, the IRQ handler/endpoint
+ * callback passes the data read to uwb_rc_neh_grok() which will break
+ * it up in a discrete series of events, look up who is listening for
+ * them and execute the pertinent callbacks.
+ *
+ * If the reader detects an error while reading the data stream, call
+ * uwb_rc_neh_error().
+ *
+ * CONSTRAINTS/ASSUMPTIONS:
+ *
+ * - Most notifications/events are small (less thank .5k), copying
+ * around is ok.
+ *
+ * - Notifications/events are ALWAYS smaller than PAGE_SIZE
+ *
+ * - Notifications/events always come in a single piece (ie: a buffer
+ * will always contain entire notifications/events).
+ *
+ * - we cannot know in advance how long each event is (because they
+ * lack a length field in their header--smart move by the standards
+ * body, btw). So we need a facility to get the event size given the
+ * header. This is what the EST code does (notif/Event Size
+ * Tables), check nest.c--as well, you can associate the size to
+ * the handle [w/ neh->extra_size()].
+ *
+ * - Most notifications/events are fixed size; only a few are variable
+ * size (NEST takes care of that).
+ *
+ * - Listeners of events expect them, so they usually provide a
+ * buffer, as they know the size. Listeners to notifications don't,
+ * so we allocate their buffers dynamically.
+ */
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/err.h>
+
+#include "uwb-internal.h"
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+/*
+ * UWB Radio Controller Notification/Event Handle
+ *
+ * Represents an entity waiting for an event coming from the UWB Radio
+ * Controller with a given context id (context) and type (evt_type and
+ * evt). On reception of the notification/event, the callback (cb) is
+ * called with the event.
+ *
+ * If the timer expires before the event is received, the callback is
+ * called with -ETIMEDOUT as the event size.
+ */
+struct uwb_rc_neh {
+ struct kref kref;
+
+ struct uwb_rc *rc;
+ u8 evt_type;
+ __le16 evt;
+ u8 context;
+ uwb_rc_cmd_cb_f cb;
+ void *arg;
+
+ struct timer_list timer;
+ struct list_head list_node;
+};
+
+static void uwb_rc_neh_timer(unsigned long arg);
+
+static void uwb_rc_neh_release(struct kref *kref)
+{
+ struct uwb_rc_neh *neh = container_of(kref, struct uwb_rc_neh, kref);
+
+ kfree(neh);
+}
+
+static void uwb_rc_neh_get(struct uwb_rc_neh *neh)
+{
+ kref_get(&neh->kref);
+}
+
+/**
+ * uwb_rc_neh_put - release reference to a neh
+ * @neh: the neh
+ */
+void uwb_rc_neh_put(struct uwb_rc_neh *neh)
+{
+ kref_put(&neh->kref, uwb_rc_neh_release);
+}
+
+
+/**
+ * Assigns @neh a context id from @rc's pool
+ *
+ * @rc: UWB Radio Controller descriptor; @rc->neh_lock taken
+ * @neh: Notification/Event Handle
+ * @returns 0 if context id was assigned ok; < 0 errno on error (if
+ * all the context IDs are taken).
+ *
+ * (assumes @wa is locked).
+ *
+ * NOTE: WUSB spec reserves context ids 0x00 for notifications and
+ * 0xff is invalid, so they must not be used. Initialization
+ * fills up those two in the bitmap so they are not allocated.
+ *
+ * We spread the allocation around to reduce the posiblity of two
+ * consecutive opened @neh's getting the same context ID assigned (to
+ * avoid surprises with late events that timed out long time ago). So
+ * first we search from where @rc->ctx_roll is, if not found, we
+ * search from zero.
+ */
+static
+int __uwb_rc_ctx_get(struct uwb_rc *rc, struct uwb_rc_neh *neh)
+{
+ int result;
+ result = find_next_zero_bit(rc->ctx_bm, UWB_RC_CTX_MAX,
+ rc->ctx_roll++);
+ if (result < UWB_RC_CTX_MAX)
+ goto found;
+ result = find_first_zero_bit(rc->ctx_bm, UWB_RC_CTX_MAX);
+ if (result < UWB_RC_CTX_MAX)
+ goto found;
+ return -ENFILE;
+found:
+ set_bit(result, rc->ctx_bm);
+ neh->context = result;
+ return 0;
+}
+
+
+/** Releases @neh's context ID back to @rc (@rc->neh_lock is locked). */
+static
+void __uwb_rc_ctx_put(struct uwb_rc *rc, struct uwb_rc_neh *neh)
+{
+ struct device *dev = &rc->uwb_dev.dev;
+ if (neh->context == 0)
+ return;
+ if (test_bit(neh->context, rc->ctx_bm) == 0) {
+ dev_err(dev, "context %u not set in bitmap\n",
+ neh->context);
+ WARN_ON(1);
+ }
+ clear_bit(neh->context, rc->ctx_bm);
+ neh->context = 0;
+}
+
+/**
+ * uwb_rc_neh_add - add a neh for a radio controller command
+ * @rc: the radio controller
+ * @cmd: the radio controller command
+ * @expected_type: the type of the expected response event
+ * @expected_event: the expected event ID
+ * @cb: callback for when the event is received
+ * @arg: argument for the callback
+ *
+ * Creates a neh and adds it to the list of those waiting for an
+ * event. A context ID will be assigned to the command.
+ */
+struct uwb_rc_neh *uwb_rc_neh_add(struct uwb_rc *rc, struct uwb_rccb *cmd,
+ u8 expected_type, u16 expected_event,
+ uwb_rc_cmd_cb_f cb, void *arg)
+{
+ int result;
+ unsigned long flags;
+ struct device *dev = &rc->uwb_dev.dev;
+ struct uwb_rc_neh *neh;
+
+ neh = kzalloc(sizeof(*neh), GFP_KERNEL);
+ if (neh == NULL) {
+ result = -ENOMEM;
+ goto error_kzalloc;
+ }
+
+ kref_init(&neh->kref);
+ INIT_LIST_HEAD(&neh->list_node);
+ init_timer(&neh->timer);
+ neh->timer.function = uwb_rc_neh_timer;
+ neh->timer.data = (unsigned long)neh;
+
+ neh->rc = rc;
+ neh->evt_type = expected_type;
+ neh->evt = cpu_to_le16(expected_event);
+ neh->cb = cb;
+ neh->arg = arg;
+
+ spin_lock_irqsave(&rc->neh_lock, flags);
+ result = __uwb_rc_ctx_get(rc, neh);
+ if (result >= 0) {
+ cmd->bCommandContext = neh->context;
+ list_add_tail(&neh->list_node, &rc->neh_list);
+ uwb_rc_neh_get(neh);
+ }
+ spin_unlock_irqrestore(&rc->neh_lock, flags);
+ if (result < 0)
+ goto error_ctx_get;
+
+ return neh;
+
+error_ctx_get:
+ kfree(neh);
+error_kzalloc:
+ dev_err(dev, "cannot open handle to radio controller: %d\n", result);
+ return ERR_PTR(result);
+}
+
+static void __uwb_rc_neh_rm(struct uwb_rc *rc, struct uwb_rc_neh *neh)
+{
+ del_timer(&neh->timer);
+ __uwb_rc_ctx_put(rc, neh);
+ list_del(&neh->list_node);
+}
+
+/**
+ * uwb_rc_neh_rm - remove a neh.
+ * @rc: the radio controller
+ * @neh: the neh to remove
+ *
+ * Remove an active neh immediately instead of waiting for the event
+ * (or a time out).
+ */
+void uwb_rc_neh_rm(struct uwb_rc *rc, struct uwb_rc_neh *neh)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&rc->neh_lock, flags);
+ __uwb_rc_neh_rm(rc, neh);
+ spin_unlock_irqrestore(&rc->neh_lock, flags);
+
+ uwb_rc_neh_put(neh);
+}
+
+/**
+ * uwb_rc_neh_arm - arm an event handler timeout timer
+ *
+ * @rc: UWB Radio Controller
+ * @neh: Notification/event handler for @rc
+ *
+ * The timer is only armed if the neh is active.
+ */
+void uwb_rc_neh_arm(struct uwb_rc *rc, struct uwb_rc_neh *neh)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&rc->neh_lock, flags);
+ if (neh->context)
+ mod_timer(&neh->timer,
+ jiffies + msecs_to_jiffies(UWB_RC_CMD_TIMEOUT_MS));
+ spin_unlock_irqrestore(&rc->neh_lock, flags);
+}
+
+static void uwb_rc_neh_cb(struct uwb_rc_neh *neh, struct uwb_rceb *rceb, size_t size)
+{
+ (*neh->cb)(neh->rc, neh->arg, rceb, size);
+ uwb_rc_neh_put(neh);
+}
+
+static bool uwb_rc_neh_match(struct uwb_rc_neh *neh, const struct uwb_rceb *rceb)
+{
+ return neh->evt_type == rceb->bEventType
+ && neh->evt == rceb->wEvent
+ && neh->context == rceb->bEventContext;
+}
+
+/**
+ * Find the handle waiting for a RC Radio Control Event
+ *
+ * @rc: UWB Radio Controller
+ * @rceb: Pointer to the RCEB buffer
+ * @event_size: Pointer to the size of the RCEB buffer. Might be
+ * adjusted to take into account the @neh->extra_size
+ * settings.
+ *
+ * If the listener has no buffer (NULL buffer), one is allocated for
+ * the right size (the amount of data received). @neh->ptr will point
+ * to the event payload, which always starts with a 'struct
+ * uwb_rceb'. kfree() it when done.
+ */
+static
+struct uwb_rc_neh *uwb_rc_neh_lookup(struct uwb_rc *rc,
+ const struct uwb_rceb *rceb)
+{
+ struct uwb_rc_neh *neh = NULL, *h;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rc->neh_lock, flags);
+
+ list_for_each_entry(h, &rc->neh_list, list_node) {
+ if (uwb_rc_neh_match(h, rceb)) {
+ neh = h;
+ break;
+ }
+ }
+
+ if (neh)
+ __uwb_rc_neh_rm(rc, neh);
+
+ spin_unlock_irqrestore(&rc->neh_lock, flags);
+
+ return neh;
+}
+
+
+/**
+ * Process notifications coming from the radio control interface
+ *
+ * @rc: UWB Radio Control Interface descriptor
+ * @neh: Notification/Event Handler @neh->ptr points to
+ * @uwb_evt->buffer.
+ *
+ * This function is called by the event/notif handling subsystem when
+ * notifications arrive (hwarc_probe() arms a notification/event handle
+ * that calls back this function for every received notification; this
+ * function then will rearm itself).
+ *
+ * Notification data buffers are dynamically allocated by the NEH
+ * handling code in neh.c [uwb_rc_neh_lookup()]. What is actually
+ * allocated is space to contain the notification data.
+ *
+ * Buffers are prefixed with a Radio Control Event Block (RCEB) as
+ * defined by the WUSB Wired-Adapter Radio Control interface. We
+ * just use it for the notification code.
+ *
+ * On each case statement we just transcode endianess of the different
+ * fields. We declare a pointer to a RCI definition of an event, and
+ * then to a UWB definition of the same event (which are the same,
+ * remember). Event if we use different pointers
+ */
+static
+void uwb_rc_notif(struct uwb_rc *rc, struct uwb_rceb *rceb, ssize_t size)
+{
+ struct device *dev = &rc->uwb_dev.dev;
+ struct uwb_event *uwb_evt;
+
+ if (size == -ESHUTDOWN)
+ return;
+ if (size < 0) {
+ dev_err(dev, "ignoring event with error code %zu\n",
+ size);
+ return;
+ }
+
+ uwb_evt = kzalloc(sizeof(*uwb_evt), GFP_ATOMIC);
+ if (unlikely(uwb_evt == NULL)) {
+ dev_err(dev, "no memory to queue event 0x%02x/%04x/%02x\n",
+ rceb->bEventType, le16_to_cpu(rceb->wEvent),
+ rceb->bEventContext);
+ return;
+ }
+ uwb_evt->rc = __uwb_rc_get(rc); /* will be put by uwbd's uwbd_event_handle() */
+ uwb_evt->ts_jiffies = jiffies;
+ uwb_evt->type = UWB_EVT_TYPE_NOTIF;
+ uwb_evt->notif.size = size;
+ uwb_evt->notif.rceb = rceb;
+
+ switch (le16_to_cpu(rceb->wEvent)) {
+ /* Trap some vendor specific events
+ *
+ * FIXME: move this to handling in ptc-est, where we
+ * register a NULL event handler for these two guys
+ * using the Intel IDs.
+ */
+ case 0x0103:
+ dev_info(dev, "FIXME: DEVICE ADD\n");
+ return;
+ case 0x0104:
+ dev_info(dev, "FIXME: DEVICE RM\n");
+ return;
+ default:
+ break;
+ }
+
+ uwbd_event_queue(uwb_evt);
+}
+
+static void uwb_rc_neh_grok_event(struct uwb_rc *rc, struct uwb_rceb *rceb, size_t size)
+{
+ struct device *dev = &rc->uwb_dev.dev;
+ struct uwb_rc_neh *neh;
+ struct uwb_rceb *notif;
+
+ if (rceb->bEventContext == 0) {
+ notif = kmalloc(size, GFP_ATOMIC);
+ if (notif) {
+ memcpy(notif, rceb, size);
+ uwb_rc_notif(rc, notif, size);
+ } else
+ dev_err(dev, "event 0x%02x/%04x/%02x (%zu bytes): no memory\n",
+ rceb->bEventType, le16_to_cpu(rceb->wEvent),
+ rceb->bEventContext, size);
+ } else {
+ neh = uwb_rc_neh_lookup(rc, rceb);
+ if (neh)
+ uwb_rc_neh_cb(neh, rceb, size);
+ else if (printk_ratelimit())
+ dev_warn(dev, "event 0x%02x/%04x/%02x (%zu bytes): nobody cared\n",
+ rceb->bEventType, le16_to_cpu(rceb->wEvent),
+ rceb->bEventContext, size);
+ }
+}
+
+/**
+ * Given a buffer with one or more UWB RC events/notifications, break
+ * them up and dispatch them.
+ *
+ * @rc: UWB Radio Controller
+ * @buf: Buffer with the stream of notifications/events
+ * @buf_size: Amount of data in the buffer
+ *
+ * Note each notification/event starts always with a 'struct
+ * uwb_rceb', so the minimum size if 4 bytes.
+ *
+ * The device may pass us events formatted differently than expected.
+ * These are first filtered, potentially creating a new event in a new
+ * memory location. If a new event is created by the filter it is also
+ * freed here.
+ *
+ * For each notif/event, tries to guess the size looking at the EST
+ * tables, then looks for a neh that is waiting for that event and if
+ * found, copies the payload to the neh's buffer and calls it back. If
+ * not, the data is ignored.
+ *
+ * Note that if we can't find a size description in the EST tables, we
+ * still might find a size in the 'neh' handle in uwb_rc_neh_lookup().
+ *
+ * Assumptions:
+ *
+ * @rc->neh_lock is NOT taken
+ *
+ * We keep track of various sizes here:
+ * size: contains the size of the buffer that is processed for the
+ * incoming event. this buffer may contain events that are not
+ * formatted as WHCI.
+ * real_size: the actual space taken by this event in the buffer.
+ * We need to keep track of the real size of an event to be able to
+ * advance the buffer correctly.
+ * event_size: the size of the event as expected by the core layer
+ * [OR] the size of the event after filtering. if the filtering
+ * created a new event in a new memory location then this is
+ * effectively the size of a new event buffer
+ */
+void uwb_rc_neh_grok(struct uwb_rc *rc, void *buf, size_t buf_size)
+{
+ struct device *dev = &rc->uwb_dev.dev;
+ void *itr;
+ struct uwb_rceb *rceb;
+ size_t size, real_size, event_size;
+ int needtofree;
+
+ d_fnstart(3, dev, "(rc %p buf %p %zu buf_size)\n", rc, buf, buf_size);
+ d_printf(2, dev, "groking event block: %zu bytes\n", buf_size);
+ itr = buf;
+ size = buf_size;
+ while (size > 0) {
+ if (size < sizeof(*rceb)) {
+ dev_err(dev, "not enough data in event buffer to "
+ "process incoming events (%zu left, minimum is "
+ "%zu)\n", size, sizeof(*rceb));
+ break;
+ }
+
+ rceb = itr;
+ if (rc->filter_event) {
+ needtofree = rc->filter_event(rc, &rceb, size,
+ &real_size, &event_size);
+ if (needtofree < 0 && needtofree != -ENOANO) {
+ dev_err(dev, "BUG: Unable to filter event "
+ "(0x%02x/%04x/%02x) from "
+ "device. \n", rceb->bEventType,
+ le16_to_cpu(rceb->wEvent),
+ rceb->bEventContext);
+ break;
+ }
+ } else
+ needtofree = -ENOANO;
+ /* do real processing if there was no filtering or the
+ * filtering didn't act */
+ if (needtofree == -ENOANO) {
+ ssize_t ret = uwb_est_find_size(rc, rceb, size);
+ if (ret < 0)
+ break;
+ if (ret > size) {
+ dev_err(dev, "BUG: hw sent incomplete event "
+ "0x%02x/%04x/%02x (%zd bytes), only got "
+ "%zu bytes. We don't handle that.\n",
+ rceb->bEventType, le16_to_cpu(rceb->wEvent),
+ rceb->bEventContext, ret, size);
+ break;
+ }
+ real_size = event_size = ret;
+ }
+ uwb_rc_neh_grok_event(rc, rceb, event_size);
+
+ if (needtofree == 1)
+ kfree(rceb);
+
+ itr += real_size;
+ size -= real_size;
+ d_printf(2, dev, "consumed %zd bytes, %zu left\n",
+ event_size, size);
+ }
+ d_fnend(3, dev, "(rc %p buf %p %zu buf_size) = void\n", rc, buf, buf_size);
+}
+EXPORT_SYMBOL_GPL(uwb_rc_neh_grok);
+
+
+/**
+ * The entity that reads from the device notification/event channel has
+ * detected an error.
+ *
+ * @rc: UWB Radio Controller
+ * @error: Errno error code
+ *
+ */
+void uwb_rc_neh_error(struct uwb_rc *rc, int error)
+{
+ struct uwb_rc_neh *neh, *next;
+ unsigned long flags;
+
+ BUG_ON(error >= 0);
+ spin_lock_irqsave(&rc->neh_lock, flags);
+ list_for_each_entry_safe(neh, next, &rc->neh_list, list_node) {
+ __uwb_rc_neh_rm(rc, neh);
+ uwb_rc_neh_cb(neh, NULL, error);
+ }
+ spin_unlock_irqrestore(&rc->neh_lock, flags);
+}
+EXPORT_SYMBOL_GPL(uwb_rc_neh_error);
+
+
+static void uwb_rc_neh_timer(unsigned long arg)
+{
+ struct uwb_rc_neh *neh = (struct uwb_rc_neh *)arg;
+ struct uwb_rc *rc = neh->rc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rc->neh_lock, flags);
+ __uwb_rc_neh_rm(rc, neh);
+ spin_unlock_irqrestore(&rc->neh_lock, flags);
+
+ uwb_rc_neh_cb(neh, NULL, -ETIMEDOUT);
+}
+
+/** Initializes the @rc's neh subsystem
+ */
+void uwb_rc_neh_create(struct uwb_rc *rc)
+{
+ spin_lock_init(&rc->neh_lock);
+ INIT_LIST_HEAD(&rc->neh_list);
+ set_bit(0, rc->ctx_bm); /* 0 is reserved (see [WUSB] table 8-65) */
+ set_bit(0xff, rc->ctx_bm); /* and 0xff is invalid */
+ rc->ctx_roll = 1;
+}
+
+
+/** Release's the @rc's neh subsystem */
+void uwb_rc_neh_destroy(struct uwb_rc *rc)
+{
+ unsigned long flags;
+ struct uwb_rc_neh *neh, *next;
+
+ spin_lock_irqsave(&rc->neh_lock, flags);
+ list_for_each_entry_safe(neh, next, &rc->neh_list, list_node) {
+ __uwb_rc_neh_rm(rc, neh);
+ uwb_rc_neh_put(neh);
+ }
+ spin_unlock_irqrestore(&rc->neh_lock, flags);
+}
diff --git a/drivers/uwb/pal.c b/drivers/uwb/pal.c
new file mode 100644
index 000000000000..1afb38eacb9a
--- /dev/null
+++ b/drivers/uwb/pal.c
@@ -0,0 +1,91 @@
+/*
+ * UWB PAL support.
+ *
+ * Copyright (C) 2008 Cambridge Silicon Radio Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kernel.h>
+#include <linux/uwb.h>
+
+#include "uwb-internal.h"
+
+/**
+ * uwb_pal_init - initialize a UWB PAL
+ * @pal: the PAL to initialize
+ */
+void uwb_pal_init(struct uwb_pal *pal)
+{
+ INIT_LIST_HEAD(&pal->node);
+}
+EXPORT_SYMBOL_GPL(uwb_pal_init);
+
+/**
+ * uwb_pal_register - register a UWB PAL
+ * @rc: the radio controller the PAL will be using
+ * @pal: the PAL
+ *
+ * The PAL must be initialized with uwb_pal_init().
+ */
+int uwb_pal_register(struct uwb_rc *rc, struct uwb_pal *pal)
+{
+ int ret;
+
+ if (pal->device) {
+ ret = sysfs_create_link(&pal->device->kobj,
+ &rc->uwb_dev.dev.kobj, "uwb_rc");
+ if (ret < 0)
+ return ret;
+ ret = sysfs_create_link(&rc->uwb_dev.dev.kobj,
+ &pal->device->kobj, pal->name);
+ if (ret < 0) {
+ sysfs_remove_link(&pal->device->kobj, "uwb_rc");
+ return ret;
+ }
+ }
+
+ spin_lock(&rc->pal_lock);
+ list_add(&pal->node, &rc->pals);
+ spin_unlock(&rc->pal_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(uwb_pal_register);
+
+/**
+ * uwb_pal_register - unregister a UWB PAL
+ * @rc: the radio controller the PAL was using
+ * @pal: the PAL
+ */
+void uwb_pal_unregister(struct uwb_rc *rc, struct uwb_pal *pal)
+{
+ spin_lock(&rc->pal_lock);
+ list_del(&pal->node);
+ spin_unlock(&rc->pal_lock);
+
+ if (pal->device) {
+ sysfs_remove_link(&rc->uwb_dev.dev.kobj, pal->name);
+ sysfs_remove_link(&pal->device->kobj, "uwb_rc");
+ }
+}
+EXPORT_SYMBOL_GPL(uwb_pal_unregister);
+
+/**
+ * uwb_rc_pal_init - initialize the PAL related parts of a radio controller
+ * @rc: the radio controller
+ */
+void uwb_rc_pal_init(struct uwb_rc *rc)
+{
+ spin_lock_init(&rc->pal_lock);
+ INIT_LIST_HEAD(&rc->pals);
+}
diff --git a/drivers/uwb/reset.c b/drivers/uwb/reset.c
new file mode 100644
index 000000000000..8de856fa7958
--- /dev/null
+++ b/drivers/uwb/reset.c
@@ -0,0 +1,362 @@
+/*
+ * Ultra Wide Band
+ * UWB basic command support and radio reset
+ *
+ * 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
+ *
+ * - Now we are serializing (using the uwb_dev->mutex) the command
+ * execution; it should be parallelized as much as possible some
+ * day.
+ */
+#include <linux/kernel.h>
+#include <linux/err.h>
+
+#include "uwb-internal.h"
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+/**
+ * Command result codes (WUSB1.0[T8-69])
+ */
+static
+const char *__strerror[] = {
+ "success",
+ "failure",
+ "hardware failure",
+ "no more slots",
+ "beacon is too large",
+ "invalid parameter",
+ "unsupported power level",
+ "time out (wa) or invalid ie data (whci)",
+ "beacon size exceeded",
+ "cancelled",
+ "invalid state",
+ "invalid size",
+ "ack not recieved",
+ "no more asie notification",
+};
+
+
+/** Return a string matching the given error code */
+const char *uwb_rc_strerror(unsigned code)
+{
+ if (code == 255)
+ return "time out";
+ if (code >= ARRAY_SIZE(__strerror))
+ return "unknown error";
+ return __strerror[code];
+}
+
+int uwb_rc_cmd_async(struct uwb_rc *rc, const char *cmd_name,
+ struct uwb_rccb *cmd, size_t cmd_size,
+ u8 expected_type, u16 expected_event,
+ uwb_rc_cmd_cb_f cb, void *arg)
+{
+ struct device *dev = &rc->uwb_dev.dev;
+ struct uwb_rc_neh *neh;
+ int needtofree = 0;
+ int result;
+
+ uwb_dev_lock(&rc->uwb_dev); /* Protect against rc->priv being removed */
+ if (rc->priv == NULL) {
+ uwb_dev_unlock(&rc->uwb_dev);
+ return -ESHUTDOWN;
+ }
+
+ if (rc->filter_cmd) {
+ needtofree = rc->filter_cmd(rc, &cmd, &cmd_size);
+ if (needtofree < 0 && needtofree != -ENOANO) {
+ dev_err(dev, "%s: filter error: %d\n",
+ cmd_name, needtofree);
+ uwb_dev_unlock(&rc->uwb_dev);
+ return needtofree;
+ }
+ }
+
+ neh = uwb_rc_neh_add(rc, cmd, expected_type, expected_event, cb, arg);
+ if (IS_ERR(neh)) {
+ result = PTR_ERR(neh);
+ goto out;
+ }
+
+ result = rc->cmd(rc, cmd, cmd_size);
+ uwb_dev_unlock(&rc->uwb_dev);
+ if (result < 0)
+ uwb_rc_neh_rm(rc, neh);
+ else
+ uwb_rc_neh_arm(rc, neh);
+ uwb_rc_neh_put(neh);
+out:
+ if (needtofree == 1)
+ kfree(cmd);
+ return result < 0 ? result : 0;
+}
+EXPORT_SYMBOL_GPL(uwb_rc_cmd_async);
+
+struct uwb_rc_cmd_done_params {
+ struct completion completion;
+ struct uwb_rceb *reply;
+ ssize_t reply_size;
+};
+
+static void uwb_rc_cmd_done(struct uwb_rc *rc, void *arg,
+ struct uwb_rceb *reply, ssize_t reply_size)
+{
+ struct uwb_rc_cmd_done_params *p = (struct uwb_rc_cmd_done_params *)arg;
+
+ if (reply_size > 0) {
+ if (p->reply)
+ reply_size = min(p->reply_size, reply_size);
+ else
+ p->reply = kmalloc(reply_size, GFP_ATOMIC);
+
+ if (p->reply)
+ memcpy(p->reply, reply, reply_size);
+ else
+ reply_size = -ENOMEM;
+ }
+ p->reply_size = reply_size;
+ complete(&p->completion);
+}
+
+
+/**
+ * Generic function for issuing commands to the Radio Control Interface
+ *
+ * @rc: UWB Radio Control descriptor
+ * @cmd_name: Name of the command being issued (for error messages)
+ * @cmd: Pointer to rccb structure containing the command;
+ * normally you embed this structure as the first member of
+ * the full command structure.
+ * @cmd_size: Size of the whole command buffer pointed to by @cmd.
+ * @reply: Pointer to where to store the reply
+ * @reply_size: @reply's size
+ * @expected_type: Expected type in the return event
+ * @expected_event: Expected event code in the return event
+ * @preply: Here a pointer to where the event data is received will
+ * be stored. Once done with the data, free with kfree().
+ *
+ * This function is generic; it works for commands that return a fixed
+ * and known size or for commands that return a variable amount of data.
+ *
+ * If a buffer is provided, that is used, although it could be chopped
+ * to the maximum size of the buffer. If the buffer is NULL, then one
+ * be allocated in *preply with the whole contents of the reply.
+ *
+ * @rc needs to be referenced
+ */
+static
+ssize_t __uwb_rc_cmd(struct uwb_rc *rc, const char *cmd_name,
+ struct uwb_rccb *cmd, size_t cmd_size,
+ struct uwb_rceb *reply, size_t reply_size,
+ u8 expected_type, u16 expected_event,
+ struct uwb_rceb **preply)
+{
+ ssize_t result = 0;
+ struct device *dev = &rc->uwb_dev.dev;
+ struct uwb_rc_cmd_done_params params;
+
+ init_completion(&params.completion);
+ params.reply = reply;
+ params.reply_size = reply_size;
+
+ result = uwb_rc_cmd_async(rc, cmd_name, cmd, cmd_size,
+ expected_type, expected_event,
+ uwb_rc_cmd_done, &params);
+ if (result)
+ return result;
+
+ wait_for_completion(&params.completion);
+
+ if (preply)
+ *preply = params.reply;
+
+ if (params.reply_size < 0)
+ dev_err(dev, "%s: confirmation event 0x%02x/%04x/%02x "
+ "reception failed: %d\n", cmd_name,
+ expected_type, expected_event, cmd->bCommandContext,
+ (int)params.reply_size);
+ return params.reply_size;
+}
+
+
+/**
+ * Generic function for issuing commands to the Radio Control Interface
+ *
+ * @rc: UWB Radio Control descriptor
+ * @cmd_name: Name of the command being issued (for error messages)
+ * @cmd: Pointer to rccb structure containing the command;
+ * normally you embed this structure as the first member of
+ * the full command structure.
+ * @cmd_size: Size of the whole command buffer pointed to by @cmd.
+ * @reply: Pointer to the beginning of the confirmation event
+ * buffer. Normally bigger than an 'struct hwarc_rceb'.
+ * You need to fill out reply->bEventType and reply->wEvent (in
+ * cpu order) as the function will use them to verify the
+ * confirmation event.
+ * @reply_size: Size of the reply buffer
+ *
+ * The function checks that the length returned in the reply is at
+ * least as big as @reply_size; if not, it will be deemed an error and
+ * -EIO returned.
+ *
+ * @rc needs to be referenced
+ */
+ssize_t uwb_rc_cmd(struct uwb_rc *rc, const char *cmd_name,
+ struct uwb_rccb *cmd, size_t cmd_size,
+ struct uwb_rceb *reply, size_t reply_size)
+{
+ struct device *dev = &rc->uwb_dev.dev;
+ ssize_t result;
+
+ result = __uwb_rc_cmd(rc, cmd_name,
+ cmd, cmd_size, reply, reply_size,
+ reply->bEventType, reply->wEvent, NULL);
+
+ if (result > 0 && result < reply_size) {
+ dev_err(dev, "%s: not enough data returned for decoding reply "
+ "(%zu bytes received vs at least %zu needed)\n",
+ cmd_name, result, reply_size);
+ result = -EIO;
+ }
+ return result;
+}
+EXPORT_SYMBOL_GPL(uwb_rc_cmd);
+
+
+/**
+ * Generic function for issuing commands to the Radio Control
+ * Interface that return an unknown amount of data
+ *
+ * @rc: UWB Radio Control descriptor
+ * @cmd_name: Name of the command being issued (for error messages)
+ * @cmd: Pointer to rccb structure containing the command;
+ * normally you embed this structure as the first member of
+ * the full command structure.
+ * @cmd_size: Size of the whole command buffer pointed to by @cmd.
+ * @expected_type: Expected type in the return event
+ * @expected_event: Expected event code in the return event
+ * @preply: Here a pointer to where the event data is received will
+ * be stored. Once done with the data, free with kfree().
+ *
+ * The function checks that the length returned in the reply is at
+ * least as big as a 'struct uwb_rceb *'; if not, it will be deemed an
+ * error and -EIO returned.
+ *
+ * @rc needs to be referenced
+ */
+ssize_t uwb_rc_vcmd(struct uwb_rc *rc, const char *cmd_name,
+ struct uwb_rccb *cmd, size_t cmd_size,
+ u8 expected_type, u16 expected_event,
+ struct uwb_rceb **preply)
+{
+ return __uwb_rc_cmd(rc, cmd_name, cmd, cmd_size, NULL, 0,
+ expected_type, expected_event, preply);
+}
+EXPORT_SYMBOL_GPL(uwb_rc_vcmd);
+
+
+/**
+ * Reset a UWB Host Controller (and all radio settings)
+ *
+ * @rc: Host Controller descriptor
+ * @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.
+ */
+int uwb_rc_reset(struct uwb_rc *rc)
+{
+ int result = -ENOMEM;
+ struct uwb_rc_evt_confirm reply;
+ struct uwb_rccb *cmd;
+ size_t cmd_size = sizeof(*cmd);
+
+ mutex_lock(&rc->uwb_dev.mutex);
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (cmd == NULL)
+ goto error_kzalloc;
+ 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 = uwb_rc_cmd(rc, "RESET", cmd, cmd_size,
+ &reply.rceb, sizeof(reply));
+ if (result < 0)
+ goto error_cmd;
+ if (reply.bResultCode != UWB_RC_RES_SUCCESS) {
+ dev_err(&rc->uwb_dev.dev,
+ "RESET: command execution failed: %s (%d)\n",
+ uwb_rc_strerror(reply.bResultCode), reply.bResultCode);
+ result = -EIO;
+ }
+error_cmd:
+ kfree(cmd);
+error_kzalloc:
+ mutex_unlock(&rc->uwb_dev.mutex);
+ return result;
+}
+
+int uwbd_msg_handle_reset(struct uwb_event *evt)
+{
+ struct uwb_rc *rc = evt->rc;
+ int ret;
+
+ /* Need to prevent the RC hardware module going away while in
+ the rc->reset() call. */
+ if (!try_module_get(rc->owner))
+ return 0;
+
+ dev_info(&rc->uwb_dev.dev, "resetting radio controller\n");
+ ret = rc->reset(rc);
+ if (ret)
+ dev_err(&rc->uwb_dev.dev, "failed to reset hardware: %d\n", ret);
+
+ module_put(rc->owner);
+ return ret;
+}
+
+/**
+ * uwb_rc_reset_all - request a reset of the radio controller and PALs
+ * @rc: the radio controller of the hardware device to be reset.
+ *
+ * The full hardware reset of the radio controller and all the PALs
+ * will be scheduled.
+ */
+void uwb_rc_reset_all(struct uwb_rc *rc)
+{
+ struct uwb_event *evt;
+
+ evt = kzalloc(sizeof(struct uwb_event), GFP_ATOMIC);
+ if (unlikely(evt == NULL))
+ return;
+
+ evt->rc = __uwb_rc_get(rc); /* will be put by uwbd's uwbd_event_handle() */
+ evt->ts_jiffies = jiffies;
+ evt->type = UWB_EVT_TYPE_MSG;
+ evt->message = UWB_EVT_MSG_RESET;
+
+ uwbd_event_queue(evt);
+}
+EXPORT_SYMBOL_GPL(uwb_rc_reset_all);
diff --git a/drivers/uwb/rsv.c b/drivers/uwb/rsv.c
new file mode 100644
index 000000000000..bae16204576d
--- /dev/null
+++ b/drivers/uwb/rsv.c
@@ -0,0 +1,680 @@
+/*
+ * UWB reservation management.
+ *
+ * Copyright (C) 2008 Cambridge Silicon Radio Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/uwb.h>
+
+#include "uwb-internal.h"
+
+static void uwb_rsv_timer(unsigned long arg);
+
+static const char *rsv_states[] = {
+ [UWB_RSV_STATE_NONE] = "none",
+ [UWB_RSV_STATE_O_INITIATED] = "initiated",
+ [UWB_RSV_STATE_O_PENDING] = "pending",
+ [UWB_RSV_STATE_O_MODIFIED] = "modified",
+ [UWB_RSV_STATE_O_ESTABLISHED] = "established",
+ [UWB_RSV_STATE_T_ACCEPTED] = "accepted",
+ [UWB_RSV_STATE_T_DENIED] = "denied",
+ [UWB_RSV_STATE_T_PENDING] = "pending",
+};
+
+static const char *rsv_types[] = {
+ [UWB_DRP_TYPE_ALIEN_BP] = "alien-bp",
+ [UWB_DRP_TYPE_HARD] = "hard",
+ [UWB_DRP_TYPE_SOFT] = "soft",
+ [UWB_DRP_TYPE_PRIVATE] = "private",
+ [UWB_DRP_TYPE_PCA] = "pca",
+};
+
+/**
+ * uwb_rsv_state_str - return a string for a reservation state
+ * @state: the reservation state.
+ */
+const char *uwb_rsv_state_str(enum uwb_rsv_state state)
+{
+ if (state < UWB_RSV_STATE_NONE || state >= UWB_RSV_STATE_LAST)
+ return "unknown";
+ return rsv_states[state];
+}
+EXPORT_SYMBOL_GPL(uwb_rsv_state_str);
+
+/**
+ * uwb_rsv_type_str - return a string for a reservation type
+ * @type: the reservation type
+ */
+const char *uwb_rsv_type_str(enum uwb_drp_type type)
+{
+ if (type < UWB_DRP_TYPE_ALIEN_BP || type > UWB_DRP_TYPE_PCA)
+ return "invalid";
+ return rsv_types[type];
+}
+EXPORT_SYMBOL_GPL(uwb_rsv_type_str);
+
+static void uwb_rsv_dump(struct uwb_rsv *rsv)
+{
+ struct device *dev = &rsv->rc->uwb_dev.dev;
+ struct uwb_dev_addr devaddr;
+ char owner[UWB_ADDR_STRSIZE], target[UWB_ADDR_STRSIZE];
+
+ uwb_dev_addr_print(owner, sizeof(owner), &rsv->owner->dev_addr);
+ if (rsv->target.type == UWB_RSV_TARGET_DEV)
+ devaddr = rsv->target.dev->dev_addr;
+ else
+ devaddr = rsv->target.devaddr;
+ uwb_dev_addr_print(target, sizeof(target), &devaddr);
+
+ dev_dbg(dev, "rsv %s -> %s: %s\n", owner, target, uwb_rsv_state_str(rsv->state));
+}
+
+/*
+ * Get a free stream index for a reservation.
+ *
+ * If the target is a DevAddr (e.g., a WUSB cluster reservation) then
+ * the stream is allocated from a pool of per-RC stream indexes,
+ * otherwise a unique stream index for the target is selected.
+ */
+static int uwb_rsv_get_stream(struct uwb_rsv *rsv)
+{
+ struct uwb_rc *rc = rsv->rc;
+ unsigned long *streams_bm;
+ int stream;
+
+ switch (rsv->target.type) {
+ case UWB_RSV_TARGET_DEV:
+ streams_bm = rsv->target.dev->streams;
+ break;
+ case UWB_RSV_TARGET_DEVADDR:
+ streams_bm = rc->uwb_dev.streams;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ stream = find_first_zero_bit(streams_bm, UWB_NUM_STREAMS);
+ if (stream >= UWB_NUM_STREAMS)
+ return -EBUSY;
+
+ rsv->stream = stream;
+ set_bit(stream, streams_bm);
+
+ return 0;
+}
+
+static void uwb_rsv_put_stream(struct uwb_rsv *rsv)
+{
+ struct uwb_rc *rc = rsv->rc;
+ unsigned long *streams_bm;
+
+ switch (rsv->target.type) {
+ case UWB_RSV_TARGET_DEV:
+ streams_bm = rsv->target.dev->streams;
+ break;
+ case UWB_RSV_TARGET_DEVADDR:
+ streams_bm = rc->uwb_dev.streams;
+ break;
+ default:
+ return;
+ }
+
+ clear_bit(rsv->stream, streams_bm);
+}
+
+/*
+ * Generate a MAS allocation with a single row component.
+ */
+static void uwb_rsv_gen_alloc_row(struct uwb_mas_bm *mas,
+ int first_mas, int mas_per_zone,
+ int zs, int ze)
+{
+ struct uwb_mas_bm col;
+ int z;
+
+ bitmap_zero(mas->bm, UWB_NUM_MAS);
+ bitmap_zero(col.bm, UWB_NUM_MAS);
+ bitmap_fill(col.bm, mas_per_zone);
+ bitmap_shift_left(col.bm, col.bm, first_mas + zs * UWB_MAS_PER_ZONE, UWB_NUM_MAS);
+
+ for (z = zs; z <= ze; z++) {
+ bitmap_or(mas->bm, mas->bm, col.bm, UWB_NUM_MAS);
+ bitmap_shift_left(col.bm, col.bm, UWB_MAS_PER_ZONE, UWB_NUM_MAS);
+ }
+}
+
+/*
+ * Allocate some MAS for this reservation based on current local
+ * availability, the reservation parameters (max_mas, min_mas,
+ * sparsity), and the WiMedia rules for MAS allocations.
+ *
+ * Returns -EBUSY is insufficient free MAS are available.
+ *
+ * FIXME: to simplify this, only safe reservations with a single row
+ * component in zones 1 to 15 are tried (zone 0 is skipped to avoid
+ * problems with the MAS reserved for the BP).
+ *
+ * [ECMA-368] section B.2.
+ */
+static int uwb_rsv_alloc_mas(struct uwb_rsv *rsv)
+{
+ static const int safe_mas_in_row[UWB_NUM_ZONES] = {
+ 8, 7, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 2, 1,
+ };
+ int n, r;
+ struct uwb_mas_bm mas;
+ bool found = false;
+
+ /*
+ * Search all valid safe allocations until either: too few MAS
+ * are available; or the smallest allocation with sufficient
+ * MAS is found.
+ *
+ * The top of the zones are preferred, so space for larger
+ * allocations is available in the bottom of the zone (e.g., a
+ * 15 MAS allocation should start in row 14 leaving space for
+ * a 120 MAS allocation at row 0).
+ */
+ for (n = safe_mas_in_row[0]; n >= 1; n--) {
+ int num_mas;
+
+ num_mas = n * (UWB_NUM_ZONES - 1);
+ if (num_mas < rsv->min_mas)
+ break;
+ if (found && num_mas < rsv->max_mas)
+ break;
+
+ for (r = UWB_MAS_PER_ZONE-1; r >= 0; r--) {
+ if (safe_mas_in_row[r] < n)
+ continue;
+ uwb_rsv_gen_alloc_row(&mas, r, n, 1, UWB_NUM_ZONES);
+ if (uwb_drp_avail_reserve_pending(rsv->rc, &mas) == 0) {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ return -EBUSY;
+
+ bitmap_copy(rsv->mas.bm, mas.bm, UWB_NUM_MAS);
+ return 0;
+}
+
+static void uwb_rsv_stroke_timer(struct uwb_rsv *rsv)
+{
+ int sframes = UWB_MAX_LOST_BEACONS;
+
+ /*
+ * Multicast reservations can become established within 1
+ * super frame and should not be terminated if no response is
+ * received.
+ */
+ if (rsv->is_multicast) {
+ if (rsv->state == UWB_RSV_STATE_O_INITIATED)
+ sframes = 1;
+ if (rsv->state == UWB_RSV_STATE_O_ESTABLISHED)
+ sframes = 0;
+ }
+
+ rsv->expired = false;
+ if (sframes > 0) {
+ /*
+ * Add an additional 2 superframes to account for the
+ * time to send the SET DRP IE command.
+ */
+ unsigned timeout_us = (sframes + 2) * UWB_SUPERFRAME_LENGTH_US;
+ mod_timer(&rsv->timer, jiffies + usecs_to_jiffies(timeout_us));
+ } else
+ del_timer(&rsv->timer);
+}
+
+/*
+ * Update a reservations state, and schedule an update of the
+ * transmitted DRP IEs.
+ */
+static void uwb_rsv_state_update(struct uwb_rsv *rsv,
+ enum uwb_rsv_state new_state)
+{
+ rsv->state = new_state;
+ rsv->ie_valid = false;
+
+ uwb_rsv_dump(rsv);
+
+ uwb_rsv_stroke_timer(rsv);
+ uwb_rsv_sched_update(rsv->rc);
+}
+
+static void uwb_rsv_callback(struct uwb_rsv *rsv)
+{
+ if (rsv->callback)
+ rsv->callback(rsv);
+}
+
+void uwb_rsv_set_state(struct uwb_rsv *rsv, enum uwb_rsv_state new_state)
+{
+ if (rsv->state == new_state) {
+ switch (rsv->state) {
+ case UWB_RSV_STATE_O_ESTABLISHED:
+ case UWB_RSV_STATE_T_ACCEPTED:
+ case UWB_RSV_STATE_NONE:
+ uwb_rsv_stroke_timer(rsv);
+ break;
+ default:
+ /* Expecting a state transition so leave timer
+ as-is. */
+ break;
+ }
+ return;
+ }
+
+ switch (new_state) {
+ case UWB_RSV_STATE_NONE:
+ uwb_drp_avail_release(rsv->rc, &rsv->mas);
+ uwb_rsv_put_stream(rsv);
+ uwb_rsv_state_update(rsv, UWB_RSV_STATE_NONE);
+ uwb_rsv_callback(rsv);
+ break;
+ case UWB_RSV_STATE_O_INITIATED:
+ uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_INITIATED);
+ break;
+ case UWB_RSV_STATE_O_PENDING:
+ uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_PENDING);
+ break;
+ case UWB_RSV_STATE_O_ESTABLISHED:
+ uwb_drp_avail_reserve(rsv->rc, &rsv->mas);
+ uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_ESTABLISHED);
+ uwb_rsv_callback(rsv);
+ break;
+ case UWB_RSV_STATE_T_ACCEPTED:
+ uwb_drp_avail_reserve(rsv->rc, &rsv->mas);
+ uwb_rsv_state_update(rsv, UWB_RSV_STATE_T_ACCEPTED);
+ uwb_rsv_callback(rsv);
+ break;
+ case UWB_RSV_STATE_T_DENIED:
+ uwb_rsv_state_update(rsv, UWB_RSV_STATE_T_DENIED);
+ break;
+ default:
+ dev_err(&rsv->rc->uwb_dev.dev, "unhandled state: %s (%d)\n",
+ uwb_rsv_state_str(new_state), new_state);
+ }
+}
+
+static struct uwb_rsv *uwb_rsv_alloc(struct uwb_rc *rc)
+{
+ struct uwb_rsv *rsv;
+
+ rsv = kzalloc(sizeof(struct uwb_rsv), GFP_KERNEL);
+ if (!rsv)
+ return NULL;
+
+ INIT_LIST_HEAD(&rsv->rc_node);
+ INIT_LIST_HEAD(&rsv->pal_node);
+ init_timer(&rsv->timer);
+ rsv->timer.function = uwb_rsv_timer;
+ rsv->timer.data = (unsigned long)rsv;
+
+ rsv->rc = rc;
+
+ return rsv;
+}
+
+static void uwb_rsv_free(struct uwb_rsv *rsv)
+{
+ uwb_dev_put(rsv->owner);
+ if (rsv->target.type == UWB_RSV_TARGET_DEV)
+ uwb_dev_put(rsv->target.dev);
+ kfree(rsv);
+}
+
+/**
+ * uwb_rsv_create - allocate and initialize a UWB reservation structure
+ * @rc: the radio controller
+ * @cb: callback to use when the reservation completes or terminates
+ * @pal_priv: data private to the PAL to be passed in the callback
+ *
+ * The callback is called when the state of the reservation changes from:
+ *
+ * - pending to accepted
+ * - pending to denined
+ * - accepted to terminated
+ * - pending to terminated
+ */
+struct uwb_rsv *uwb_rsv_create(struct uwb_rc *rc, uwb_rsv_cb_f cb, void *pal_priv)
+{
+ struct uwb_rsv *rsv;
+
+ rsv = uwb_rsv_alloc(rc);
+ if (!rsv)
+ return NULL;
+
+ rsv->callback = cb;
+ rsv->pal_priv = pal_priv;
+
+ return rsv;
+}
+EXPORT_SYMBOL_GPL(uwb_rsv_create);
+
+void uwb_rsv_remove(struct uwb_rsv *rsv)
+{
+ if (rsv->state != UWB_RSV_STATE_NONE)
+ uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE);
+ del_timer_sync(&rsv->timer);
+ list_del(&rsv->rc_node);
+ uwb_rsv_free(rsv);
+}
+
+/**
+ * uwb_rsv_destroy - free a UWB reservation structure
+ * @rsv: the reservation to free
+ *
+ * The reservation will be terminated if it is pending or established.
+ */
+void uwb_rsv_destroy(struct uwb_rsv *rsv)
+{
+ struct uwb_rc *rc = rsv->rc;
+
+ mutex_lock(&rc->rsvs_mutex);
+ uwb_rsv_remove(rsv);
+ mutex_unlock(&rc->rsvs_mutex);
+}
+EXPORT_SYMBOL_GPL(uwb_rsv_destroy);
+
+/**
+ * usb_rsv_establish - start a reservation establishment
+ * @rsv: the reservation
+ *
+ * The PAL should fill in @rsv's owner, target, type, max_mas,
+ * min_mas, sparsity and is_multicast fields. If the target is a
+ * uwb_dev it must be referenced.
+ *
+ * The reservation's callback will be called when the reservation is
+ * accepted, denied or times out.
+ */
+int uwb_rsv_establish(struct uwb_rsv *rsv)
+{
+ struct uwb_rc *rc = rsv->rc;
+ int ret;
+
+ mutex_lock(&rc->rsvs_mutex);
+
+ ret = uwb_rsv_get_stream(rsv);
+ if (ret)
+ goto out;
+
+ ret = uwb_rsv_alloc_mas(rsv);
+ if (ret) {
+ uwb_rsv_put_stream(rsv);
+ goto out;
+ }
+
+ list_add_tail(&rsv->rc_node, &rc->reservations);
+ rsv->owner = &rc->uwb_dev;
+ uwb_dev_get(rsv->owner);
+ uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_INITIATED);
+out:
+ mutex_unlock(&rc->rsvs_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(uwb_rsv_establish);
+
+/**
+ * uwb_rsv_modify - modify an already established reservation
+ * @rsv: the reservation to modify
+ * @max_mas: new maximum MAS to reserve
+ * @min_mas: new minimum MAS to reserve
+ * @sparsity: new sparsity to use
+ *
+ * FIXME: implement this once there are PALs that use it.
+ */
+int uwb_rsv_modify(struct uwb_rsv *rsv, int max_mas, int min_mas, int sparsity)
+{
+ return -ENOSYS;
+}
+EXPORT_SYMBOL_GPL(uwb_rsv_modify);
+
+/**
+ * uwb_rsv_terminate - terminate an established reservation
+ * @rsv: the reservation to terminate
+ *
+ * A reservation is terminated by removing the DRP IE from the beacon,
+ * the other end will consider the reservation to be terminated when
+ * it does not see the DRP IE for at least mMaxLostBeacons.
+ *
+ * If applicable, the reference to the target uwb_dev will be released.
+ */
+void uwb_rsv_terminate(struct uwb_rsv *rsv)
+{
+ struct uwb_rc *rc = rsv->rc;
+
+ mutex_lock(&rc->rsvs_mutex);
+
+ uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE);
+
+ mutex_unlock(&rc->rsvs_mutex);
+}
+EXPORT_SYMBOL_GPL(uwb_rsv_terminate);
+
+/**
+ * uwb_rsv_accept - accept a new reservation from a peer
+ * @rsv: the reservation
+ * @cb: call back for reservation changes
+ * @pal_priv: data to be passed in the above call back
+ *
+ * Reservation requests from peers are denied unless a PAL accepts it
+ * by calling this function.
+ */
+void uwb_rsv_accept(struct uwb_rsv *rsv, uwb_rsv_cb_f cb, void *pal_priv)
+{
+ rsv->callback = cb;
+ rsv->pal_priv = pal_priv;
+ rsv->state = UWB_RSV_STATE_T_ACCEPTED;
+}
+EXPORT_SYMBOL_GPL(uwb_rsv_accept);
+
+/*
+ * Is a received DRP IE for this reservation?
+ */
+static bool uwb_rsv_match(struct uwb_rsv *rsv, struct uwb_dev *src,
+ struct uwb_ie_drp *drp_ie)
+{
+ struct uwb_dev_addr *rsv_src;
+ int stream;
+
+ stream = uwb_ie_drp_stream_index(drp_ie);
+
+ if (rsv->stream != stream)
+ return false;
+
+ switch (rsv->target.type) {
+ case UWB_RSV_TARGET_DEVADDR:
+ return rsv->stream == stream;
+ case UWB_RSV_TARGET_DEV:
+ if (uwb_ie_drp_owner(drp_ie))
+ rsv_src = &rsv->owner->dev_addr;
+ else
+ rsv_src = &rsv->target.dev->dev_addr;
+ return uwb_dev_addr_cmp(&src->dev_addr, rsv_src) == 0;
+ }
+ return false;
+}
+
+static struct uwb_rsv *uwb_rsv_new_target(struct uwb_rc *rc,
+ struct uwb_dev *src,
+ struct uwb_ie_drp *drp_ie)
+{
+ struct uwb_rsv *rsv;
+ struct uwb_pal *pal;
+ enum uwb_rsv_state state;
+
+ rsv = uwb_rsv_alloc(rc);
+ if (!rsv)
+ return NULL;
+
+ rsv->rc = rc;
+ rsv->owner = src;
+ uwb_dev_get(rsv->owner);
+ rsv->target.type = UWB_RSV_TARGET_DEV;
+ rsv->target.dev = &rc->uwb_dev;
+ rsv->type = uwb_ie_drp_type(drp_ie);
+ rsv->stream = uwb_ie_drp_stream_index(drp_ie);
+ set_bit(rsv->stream, rsv->owner->streams);
+ uwb_drp_ie_to_bm(&rsv->mas, drp_ie);
+
+ /*
+ * See if any PALs are interested in this reservation. If not,
+ * deny the request.
+ */
+ rsv->state = UWB_RSV_STATE_T_DENIED;
+ spin_lock(&rc->pal_lock);
+ list_for_each_entry(pal, &rc->pals, node) {
+ if (pal->new_rsv)
+ pal->new_rsv(rsv);
+ if (rsv->state == UWB_RSV_STATE_T_ACCEPTED)
+ break;
+ }
+ spin_unlock(&rc->pal_lock);
+
+ list_add_tail(&rsv->rc_node, &rc->reservations);
+ state = rsv->state;
+ rsv->state = UWB_RSV_STATE_NONE;
+ uwb_rsv_set_state(rsv, state);
+
+ return rsv;
+}
+
+/**
+ * uwb_rsv_find - find a reservation for a received DRP IE.
+ * @rc: the radio controller
+ * @src: source of the DRP IE
+ * @drp_ie: the DRP IE
+ *
+ * If the reservation cannot be found and the DRP IE is from a peer
+ * attempting to establish a new reservation, create a new reservation
+ * and add it to the list.
+ */
+struct uwb_rsv *uwb_rsv_find(struct uwb_rc *rc, struct uwb_dev *src,
+ struct uwb_ie_drp *drp_ie)
+{
+ struct uwb_rsv *rsv;
+
+ list_for_each_entry(rsv, &rc->reservations, rc_node) {
+ if (uwb_rsv_match(rsv, src, drp_ie))
+ return rsv;
+ }
+
+ if (uwb_ie_drp_owner(drp_ie))
+ return uwb_rsv_new_target(rc, src, drp_ie);
+
+ return NULL;
+}
+
+/*
+ * Go through all the reservations and check for timeouts and (if
+ * necessary) update their DRP IEs.
+ *
+ * FIXME: look at building the SET_DRP_IE command here rather than
+ * having to rescan the list in uwb_rc_send_all_drp_ie().
+ */
+static bool uwb_rsv_update_all(struct uwb_rc *rc)
+{
+ struct uwb_rsv *rsv, *t;
+ bool ie_updated = false;
+
+ list_for_each_entry_safe(rsv, t, &rc->reservations, rc_node) {
+ if (rsv->expired)
+ uwb_drp_handle_timeout(rsv);
+ if (!rsv->ie_valid) {
+ uwb_drp_ie_update(rsv);
+ ie_updated = true;
+ }
+ }
+
+ return ie_updated;
+}
+
+void uwb_rsv_sched_update(struct uwb_rc *rc)
+{
+ queue_work(rc->rsv_workq, &rc->rsv_update_work);
+}
+
+/*
+ * Update DRP IEs and, if necessary, the DRP Availability IE and send
+ * the updated IEs to the radio controller.
+ */
+static void uwb_rsv_update_work(struct work_struct *work)
+{
+ struct uwb_rc *rc = container_of(work, struct uwb_rc, rsv_update_work);
+ bool ie_updated;
+
+ mutex_lock(&rc->rsvs_mutex);
+
+ ie_updated = uwb_rsv_update_all(rc);
+
+ if (!rc->drp_avail.ie_valid) {
+ uwb_drp_avail_ie_update(rc);
+ ie_updated = true;
+ }
+
+ if (ie_updated)
+ uwb_rc_send_all_drp_ie(rc);
+
+ mutex_unlock(&rc->rsvs_mutex);
+}
+
+static void uwb_rsv_timer(unsigned long arg)
+{
+ struct uwb_rsv *rsv = (struct uwb_rsv *)arg;
+
+ rsv->expired = true;
+ uwb_rsv_sched_update(rsv->rc);
+}
+
+void uwb_rsv_init(struct uwb_rc *rc)
+{
+ INIT_LIST_HEAD(&rc->reservations);
+ mutex_init(&rc->rsvs_mutex);
+ INIT_WORK(&rc->rsv_update_work, uwb_rsv_update_work);
+
+ bitmap_complement(rc->uwb_dev.streams, rc->uwb_dev.streams, UWB_NUM_STREAMS);
+}
+
+int uwb_rsv_setup(struct uwb_rc *rc)
+{
+ char name[16];
+
+ snprintf(name, sizeof(name), "%s_rsvd", dev_name(&rc->uwb_dev.dev));
+ rc->rsv_workq = create_singlethread_workqueue(name);
+ if (rc->rsv_workq == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void uwb_rsv_cleanup(struct uwb_rc *rc)
+{
+ struct uwb_rsv *rsv, *t;
+
+ mutex_lock(&rc->rsvs_mutex);
+ list_for_each_entry_safe(rsv, t, &rc->reservations, rc_node) {
+ uwb_rsv_remove(rsv);
+ }
+ mutex_unlock(&rc->rsvs_mutex);
+
+ cancel_work_sync(&rc->rsv_update_work);
+ destroy_workqueue(rc->rsv_workq);
+}
diff --git a/drivers/uwb/scan.c b/drivers/uwb/scan.c
new file mode 100644
index 000000000000..2d270748f32b
--- /dev/null
+++ b/drivers/uwb/scan.c
@@ -0,0 +1,133 @@
+/*
+ * Ultra Wide Band
+ * Scanning management
+ *
+ * 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
+ * FIXME: there are issues here on how BEACON and SCAN on USB RCI deal
+ * with each other. Currently seems that START_BEACON while
+ * SCAN_ONLY will cancel the scan, so we need to update the
+ * state here. Clarification request sent by email on
+ * 10/05/2005.
+ * 10/28/2005 No clear answer heard--maybe we'll hack the API
+ * so that when we start beaconing, if the HC is
+ * scanning in a mode not compatible with beaconing
+ * we just fail.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include "uwb-internal.h"
+
+
+/**
+ * Start/stop scanning in a radio controller
+ *
+ * @rc: UWB Radio Controlller
+ * @channel: Channel to scan; encodings in WUSB1.0[Table 5.12]
+ * @type: Type of scanning to do.
+ * @bpst_offset: value at which to start scanning (if type ==
+ * UWB_SCAN_ONLY_STARTTIME)
+ * @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.
+ */
+int uwb_rc_scan(struct uwb_rc *rc,
+ unsigned channel, enum uwb_scan_type type,
+ unsigned bpst_offset)
+{
+ int result;
+ struct uwb_rc_cmd_scan *cmd;
+ struct uwb_rc_evt_confirm reply;
+
+ result = -ENOMEM;
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (cmd == NULL)
+ goto error_kzalloc;
+ mutex_lock(&rc->uwb_dev.mutex);
+ cmd->rccb.bCommandType = UWB_RC_CET_GENERAL;
+ cmd->rccb.wCommand = cpu_to_le16(UWB_RC_CMD_SCAN);
+ cmd->bChannelNumber = channel;
+ cmd->bScanState = type;
+ cmd->wStartTime = cpu_to_le16(bpst_offset);
+ reply.rceb.bEventType = UWB_RC_CET_GENERAL;
+ reply.rceb.wEvent = UWB_RC_CMD_SCAN;
+ result = uwb_rc_cmd(rc, "SCAN", &cmd->rccb, sizeof(*cmd),
+ &reply.rceb, sizeof(reply));
+ if (result < 0)
+ goto error_cmd;
+ if (reply.bResultCode != UWB_RC_RES_SUCCESS) {
+ dev_err(&rc->uwb_dev.dev,
+ "SCAN: command execution failed: %s (%d)\n",
+ uwb_rc_strerror(reply.bResultCode), reply.bResultCode);
+ result = -EIO;
+ goto error_cmd;
+ }
+ rc->scanning = channel;
+ rc->scan_type = type;
+error_cmd:
+ mutex_unlock(&rc->uwb_dev.mutex);
+ kfree(cmd);
+error_kzalloc:
+ return result;
+}
+
+/*
+ * Print scanning state
+ */
+static ssize_t uwb_rc_scan_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ struct uwb_rc *rc = uwb_dev->rc;
+ ssize_t result;
+
+ mutex_lock(&rc->uwb_dev.mutex);
+ result = sprintf(buf, "%d %d\n", rc->scanning, rc->scan_type);
+ mutex_unlock(&rc->uwb_dev.mutex);
+ return result;
+}
+
+/*
+ *
+ */
+static ssize_t uwb_rc_scan_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ struct uwb_rc *rc = uwb_dev->rc;
+ unsigned channel;
+ unsigned type;
+ unsigned bpst_offset = 0;
+ ssize_t result = -EINVAL;
+
+ result = sscanf(buf, "%u %u %u\n", &channel, &type, &bpst_offset);
+ if (result >= 2 && type < UWB_SCAN_TOP)
+ result = uwb_rc_scan(rc, channel, type, bpst_offset);
+
+ return result < 0 ? result : size;
+}
+
+/** Radio Control sysfs interface (declaration) */
+DEVICE_ATTR(scan, S_IRUGO | S_IWUSR, uwb_rc_scan_show, uwb_rc_scan_store);
diff --git a/drivers/uwb/umc-bus.c b/drivers/uwb/umc-bus.c
new file mode 100644
index 000000000000..2d8d62d9f53e
--- /dev/null
+++ b/drivers/uwb/umc-bus.c
@@ -0,0 +1,218 @@
+/*
+ * Bus for UWB Multi-interface Controller capabilities.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
+ *
+ * This file is released under the GNU GPL v2.
+ */
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/workqueue.h>
+#include <linux/uwb/umc.h>
+#include <linux/pci.h>
+
+static int umc_bus_unbind_helper(struct device *dev, void *data)
+{
+ struct device *parent = data;
+
+ if (dev->parent == parent && dev->driver)
+ device_release_driver(dev);
+ return 0;
+}
+
+/**
+ * umc_controller_reset - reset the whole UMC controller
+ * @umc: the UMC device for the radio controller.
+ *
+ * Drivers will be unbound from all UMC devices belonging to the
+ * controller and then the radio controller will be rebound. The
+ * radio controller is expected to do a full hardware reset when it is
+ * probed.
+ *
+ * If this is called while a probe() or remove() is in progress it
+ * will return -EAGAIN and not perform the reset.
+ */
+int umc_controller_reset(struct umc_dev *umc)
+{
+ struct device *parent = umc->dev.parent;
+ int ret;
+
+ if (down_trylock(&parent->sem))
+ return -EAGAIN;
+ bus_for_each_dev(&umc_bus_type, NULL, parent, umc_bus_unbind_helper);
+ ret = device_attach(&umc->dev);
+ if (ret == 1)
+ ret = 0;
+ up(&parent->sem);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(umc_controller_reset);
+
+/**
+ * umc_match_pci_id - match a UMC driver to a UMC device's parent PCI device.
+ * @umc_drv: umc driver with match_data pointing to a zero-terminated
+ * table of pci_device_id's.
+ * @umc: umc device whose parent is to be matched.
+ */
+int umc_match_pci_id(struct umc_driver *umc_drv, struct umc_dev *umc)
+{
+ const struct pci_device_id *id_table = umc_drv->match_data;
+ struct pci_dev *pci;
+
+ if (umc->dev.parent->bus != &pci_bus_type)
+ return 0;
+
+ pci = to_pci_dev(umc->dev.parent);
+ return pci_match_id(id_table, pci) != NULL;
+}
+EXPORT_SYMBOL_GPL(umc_match_pci_id);
+
+static int umc_bus_rescan_helper(struct device *dev, void *data)
+{
+ int ret = 0;
+
+ if (!dev->driver)
+ ret = device_attach(dev);
+
+ return ret < 0 ? ret : 0;
+}
+
+static void umc_bus_rescan(void)
+{
+ int err;
+
+ /*
+ * We can't use bus_rescan_devices() here as it deadlocks when
+ * it tries to retake the dev->parent semaphore.
+ */
+ err = bus_for_each_dev(&umc_bus_type, NULL, NULL, umc_bus_rescan_helper);
+ if (err < 0)
+ printk(KERN_WARNING "%s: rescan of bus failed: %d\n",
+ KBUILD_MODNAME, err);
+}
+
+static int umc_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct umc_dev *umc = to_umc_dev(dev);
+ struct umc_driver *umc_driver = to_umc_driver(drv);
+
+ if (umc->cap_id == umc_driver->cap_id) {
+ if (umc_driver->match)
+ return umc_driver->match(umc_driver, umc);
+ else
+ return 1;
+ }
+ return 0;
+}
+
+static int umc_device_probe(struct device *dev)
+{
+ struct umc_dev *umc;
+ struct umc_driver *umc_driver;
+ int err;
+
+ umc_driver = to_umc_driver(dev->driver);
+ umc = to_umc_dev(dev);
+
+ get_device(dev);
+ err = umc_driver->probe(umc);
+ if (err)
+ put_device(dev);
+ else
+ umc_bus_rescan();
+
+ return err;
+}
+
+static int umc_device_remove(struct device *dev)
+{
+ struct umc_dev *umc;
+ struct umc_driver *umc_driver;
+
+ umc_driver = to_umc_driver(dev->driver);
+ umc = to_umc_dev(dev);
+
+ umc_driver->remove(umc);
+ put_device(dev);
+ return 0;
+}
+
+static int umc_device_suspend(struct device *dev, pm_message_t state)
+{
+ struct umc_dev *umc;
+ struct umc_driver *umc_driver;
+ int err = 0;
+
+ umc = to_umc_dev(dev);
+
+ if (dev->driver) {
+ umc_driver = to_umc_driver(dev->driver);
+ if (umc_driver->suspend)
+ err = umc_driver->suspend(umc, state);
+ }
+ return err;
+}
+
+static int umc_device_resume(struct device *dev)
+{
+ struct umc_dev *umc;
+ struct umc_driver *umc_driver;
+ int err = 0;
+
+ umc = to_umc_dev(dev);
+
+ if (dev->driver) {
+ umc_driver = to_umc_driver(dev->driver);
+ if (umc_driver->resume)
+ err = umc_driver->resume(umc);
+ }
+ return err;
+}
+
+static ssize_t capability_id_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct umc_dev *umc = to_umc_dev(dev);
+
+ return sprintf(buf, "0x%02x\n", umc->cap_id);
+}
+
+static ssize_t version_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct umc_dev *umc = to_umc_dev(dev);
+
+ return sprintf(buf, "0x%04x\n", umc->version);
+}
+
+static struct device_attribute umc_dev_attrs[] = {
+ __ATTR_RO(capability_id),
+ __ATTR_RO(version),
+ __ATTR_NULL,
+};
+
+struct bus_type umc_bus_type = {
+ .name = "umc",
+ .match = umc_bus_match,
+ .probe = umc_device_probe,
+ .remove = umc_device_remove,
+ .suspend = umc_device_suspend,
+ .resume = umc_device_resume,
+ .dev_attrs = umc_dev_attrs,
+};
+EXPORT_SYMBOL_GPL(umc_bus_type);
+
+static int __init umc_bus_init(void)
+{
+ return bus_register(&umc_bus_type);
+}
+module_init(umc_bus_init);
+
+static void __exit umc_bus_exit(void)
+{
+ bus_unregister(&umc_bus_type);
+}
+module_exit(umc_bus_exit);
+
+MODULE_DESCRIPTION("UWB Multi-interface Controller capability bus");
+MODULE_AUTHOR("Cambridge Silicon Radio Ltd.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/uwb/umc-dev.c b/drivers/uwb/umc-dev.c
new file mode 100644
index 000000000000..aa44e1c1a102
--- /dev/null
+++ b/drivers/uwb/umc-dev.c
@@ -0,0 +1,104 @@
+/*
+ * UWB Multi-interface Controller device management.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
+ *
+ * This file is released under the GNU GPL v2.
+ */
+#include <linux/kernel.h>
+#include <linux/uwb/umc.h>
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+static void umc_device_release(struct device *dev)
+{
+ struct umc_dev *umc = to_umc_dev(dev);
+
+ kfree(umc);
+}
+
+/**
+ * umc_device_create - allocate a child UMC device
+ * @parent: parent of the new UMC device.
+ * @n: index of the new device.
+ *
+ * The new UMC device will have a bus ID of the parent with '-n'
+ * appended.
+ */
+struct umc_dev *umc_device_create(struct device *parent, int n)
+{
+ struct umc_dev *umc;
+
+ umc = kzalloc(sizeof(struct umc_dev), GFP_KERNEL);
+ if (umc) {
+ snprintf(umc->dev.bus_id, sizeof(umc->dev.bus_id), "%s-%d",
+ parent->bus_id, n);
+ umc->dev.parent = parent;
+ umc->dev.bus = &umc_bus_type;
+ umc->dev.release = umc_device_release;
+
+ umc->dev.dma_mask = parent->dma_mask;
+ }
+ return umc;
+}
+EXPORT_SYMBOL_GPL(umc_device_create);
+
+/**
+ * umc_device_register - register a UMC device
+ * @umc: pointer to the UMC device
+ *
+ * The memory resource for the UMC device is acquired and the device
+ * registered with the system.
+ */
+int umc_device_register(struct umc_dev *umc)
+{
+ int err;
+
+ d_fnstart(3, &umc->dev, "(umc_dev %p)\n", umc);
+
+ err = request_resource(umc->resource.parent, &umc->resource);
+ if (err < 0) {
+ dev_err(&umc->dev, "can't allocate resource range "
+ "%016Lx to %016Lx: %d\n",
+ (unsigned long long)umc->resource.start,
+ (unsigned long long)umc->resource.end,
+ err);
+ goto error_request_resource;
+ }
+
+ err = device_register(&umc->dev);
+ if (err < 0)
+ goto error_device_register;
+ d_fnend(3, &umc->dev, "(umc_dev %p) = 0\n", umc);
+ return 0;
+
+error_device_register:
+ release_resource(&umc->resource);
+error_request_resource:
+ d_fnend(3, &umc->dev, "(umc_dev %p) = %d\n", umc, err);
+ return err;
+}
+EXPORT_SYMBOL_GPL(umc_device_register);
+
+/**
+ * umc_device_unregister - unregister a UMC device
+ * @umc: pointer to the UMC device
+ *
+ * First we unregister the device, make sure the driver can do it's
+ * resource release thing and then we try to release any left over
+ * resources. We take a ref to the device, to make sure it doesn't
+ * dissapear under our feet.
+ */
+void umc_device_unregister(struct umc_dev *umc)
+{
+ struct device *dev;
+ if (!umc)
+ return;
+ dev = get_device(&umc->dev);
+ d_fnstart(3, dev, "(umc_dev %p)\n", umc);
+ device_unregister(&umc->dev);
+ release_resource(&umc->resource);
+ d_fnend(3, dev, "(umc_dev %p) = void\n", umc);
+ put_device(dev);
+}
+EXPORT_SYMBOL_GPL(umc_device_unregister);
diff --git a/drivers/uwb/umc-drv.c b/drivers/uwb/umc-drv.c
new file mode 100644
index 000000000000..367b5eb85d60
--- /dev/null
+++ b/drivers/uwb/umc-drv.c
@@ -0,0 +1,31 @@
+/*
+ * UWB Multi-interface Controller driver management.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
+ *
+ * This file is released under the GNU GPL v2.
+ */
+#include <linux/kernel.h>
+#include <linux/uwb/umc.h>
+
+int __umc_driver_register(struct umc_driver *umc_drv, struct module *module,
+ const char *mod_name)
+{
+ umc_drv->driver.name = umc_drv->name;
+ umc_drv->driver.owner = module;
+ umc_drv->driver.mod_name = mod_name;
+ umc_drv->driver.bus = &umc_bus_type;
+
+ return driver_register(&umc_drv->driver);
+}
+EXPORT_SYMBOL_GPL(__umc_driver_register);
+
+/**
+ * umc_driver_register - unregister a UMC capabiltity driver.
+ * @umc_drv: pointer to the driver.
+ */
+void umc_driver_unregister(struct umc_driver *umc_drv)
+{
+ driver_unregister(&umc_drv->driver);
+}
+EXPORT_SYMBOL_GPL(umc_driver_unregister);
diff --git a/drivers/uwb/uwb-debug.c b/drivers/uwb/uwb-debug.c
new file mode 100644
index 000000000000..6d232c35d07d
--- /dev/null
+++ b/drivers/uwb/uwb-debug.c
@@ -0,0 +1,367 @@
+/*
+ * Ultra Wide Band
+ * Debug support
+ *
+ * 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: doc
+ */
+
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/notifier.h>
+#include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/seq_file.h>
+
+#include <linux/uwb/debug-cmd.h>
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+#include "uwb-internal.h"
+
+void dump_bytes(struct device *dev, const void *_buf, size_t rsize)
+{
+ const char *buf = _buf;
+ char line[32];
+ size_t offset = 0;
+ int cnt, cnt2;
+ for (cnt = 0; cnt < rsize; cnt += 8) {
+ size_t rtop = rsize - cnt < 8 ? rsize - cnt : 8;
+ for (offset = cnt2 = 0; cnt2 < rtop; cnt2++) {
+ offset += scnprintf(line + offset, sizeof(line) - offset,
+ "%02x ", buf[cnt + cnt2] & 0xff);
+ }
+ if (dev)
+ dev_info(dev, "%s\n", line);
+ else
+ printk(KERN_INFO "%s\n", line);
+ }
+}
+EXPORT_SYMBOL_GPL(dump_bytes);
+
+/*
+ * Debug interface
+ *
+ * Per radio controller debugfs files (in uwb/uwbN/):
+ *
+ * command: Flexible command interface (see <linux/uwb/debug-cmd.h>).
+ *
+ * reservations: information on reservations.
+ *
+ * accept: Set to true (Y or 1) to accept reservation requests from
+ * peers.
+ *
+ * drp_avail: DRP availability information.
+ */
+
+struct uwb_dbg {
+ struct uwb_pal pal;
+
+ u32 accept;
+ struct list_head rsvs;
+
+ struct dentry *root_d;
+ struct dentry *command_f;
+ struct dentry *reservations_f;
+ struct dentry *accept_f;
+ struct dentry *drp_avail_f;
+};
+
+static struct dentry *root_dir;
+
+static void uwb_dbg_rsv_cb(struct uwb_rsv *rsv)
+{
+ struct uwb_rc *rc = rsv->rc;
+ struct device *dev = &rc->uwb_dev.dev;
+ struct uwb_dev_addr devaddr;
+ char owner[UWB_ADDR_STRSIZE], target[UWB_ADDR_STRSIZE];
+
+ uwb_dev_addr_print(owner, sizeof(owner), &rsv->owner->dev_addr);
+ if (rsv->target.type == UWB_RSV_TARGET_DEV)
+ devaddr = rsv->target.dev->dev_addr;
+ else
+ devaddr = rsv->target.devaddr;
+ uwb_dev_addr_print(target, sizeof(target), &devaddr);
+
+ dev_dbg(dev, "debug: rsv %s -> %s: %s\n",
+ owner, target, uwb_rsv_state_str(rsv->state));
+}
+
+static int cmd_rsv_establish(struct uwb_rc *rc,
+ struct uwb_dbg_cmd_rsv_establish *cmd)
+{
+ struct uwb_mac_addr macaddr;
+ struct uwb_rsv *rsv;
+ struct uwb_dev *target;
+ int ret;
+
+ memcpy(&macaddr, cmd->target, sizeof(macaddr));
+ target = uwb_dev_get_by_macaddr(rc, &macaddr);
+ if (target == NULL)
+ return -ENODEV;
+
+ rsv = uwb_rsv_create(rc, uwb_dbg_rsv_cb, NULL);
+ if (rsv == NULL) {
+ uwb_dev_put(target);
+ return -ENOMEM;
+ }
+
+ rsv->owner = &rc->uwb_dev;
+ rsv->target.type = UWB_RSV_TARGET_DEV;
+ rsv->target.dev = target;
+ rsv->type = cmd->type;
+ rsv->max_mas = cmd->max_mas;
+ rsv->min_mas = cmd->min_mas;
+ rsv->sparsity = cmd->sparsity;
+
+ ret = uwb_rsv_establish(rsv);
+ if (ret)
+ uwb_rsv_destroy(rsv);
+ else
+ list_add_tail(&rsv->pal_node, &rc->dbg->rsvs);
+
+ return ret;
+}
+
+static int cmd_rsv_terminate(struct uwb_rc *rc,
+ struct uwb_dbg_cmd_rsv_terminate *cmd)
+{
+ struct uwb_rsv *rsv, *found = NULL;
+ int i = 0;
+
+ list_for_each_entry(rsv, &rc->dbg->rsvs, pal_node) {
+ if (i == cmd->index) {
+ found = rsv;
+ break;
+ }
+ }
+ if (!found)
+ return -EINVAL;
+
+ list_del(&found->pal_node);
+ uwb_rsv_terminate(found);
+
+ return 0;
+}
+
+static int command_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+
+ return 0;
+}
+
+static ssize_t command_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *off)
+{
+ struct uwb_rc *rc = file->private_data;
+ struct uwb_dbg_cmd cmd;
+ int ret;
+
+ if (len != sizeof(struct uwb_dbg_cmd))
+ return -EINVAL;
+
+ if (copy_from_user(&cmd, buf, len) != 0)
+ return -EFAULT;
+
+ switch (cmd.type) {
+ case UWB_DBG_CMD_RSV_ESTABLISH:
+ ret = cmd_rsv_establish(rc, &cmd.rsv_establish);
+ break;
+ case UWB_DBG_CMD_RSV_TERMINATE:
+ ret = cmd_rsv_terminate(rc, &cmd.rsv_terminate);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret < 0 ? ret : len;
+}
+
+static struct file_operations command_fops = {
+ .open = command_open,
+ .write = command_write,
+ .read = NULL,
+ .llseek = no_llseek,
+ .owner = THIS_MODULE,
+};
+
+static int reservations_print(struct seq_file *s, void *p)
+{
+ struct uwb_rc *rc = s->private;
+ struct uwb_rsv *rsv;
+
+ mutex_lock(&rc->rsvs_mutex);
+
+ list_for_each_entry(rsv, &rc->reservations, rc_node) {
+ struct uwb_dev_addr devaddr;
+ char owner[UWB_ADDR_STRSIZE], target[UWB_ADDR_STRSIZE];
+ bool is_owner;
+ char buf[72];
+
+ uwb_dev_addr_print(owner, sizeof(owner), &rsv->owner->dev_addr);
+ if (rsv->target.type == UWB_RSV_TARGET_DEV) {
+ devaddr = rsv->target.dev->dev_addr;
+ is_owner = &rc->uwb_dev == rsv->owner;
+ } else {
+ devaddr = rsv->target.devaddr;
+ is_owner = true;
+ }
+ uwb_dev_addr_print(target, sizeof(target), &devaddr);
+
+ seq_printf(s, "%c %s -> %s: %s\n",
+ is_owner ? 'O' : 'T',
+ owner, target, uwb_rsv_state_str(rsv->state));
+ seq_printf(s, " stream: %d type: %s\n",
+ rsv->stream, uwb_rsv_type_str(rsv->type));
+ bitmap_scnprintf(buf, sizeof(buf), rsv->mas.bm, UWB_NUM_MAS);
+ seq_printf(s, " %s\n", buf);
+ }
+
+ mutex_unlock(&rc->rsvs_mutex);
+
+ return 0;
+}
+
+static int reservations_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, reservations_print, inode->i_private);
+}
+
+static struct file_operations reservations_fops = {
+ .open = reservations_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int drp_avail_print(struct seq_file *s, void *p)
+{
+ struct uwb_rc *rc = s->private;
+ char buf[72];
+
+ bitmap_scnprintf(buf, sizeof(buf), rc->drp_avail.global, UWB_NUM_MAS);
+ seq_printf(s, "global: %s\n", buf);
+ bitmap_scnprintf(buf, sizeof(buf), rc->drp_avail.local, UWB_NUM_MAS);
+ seq_printf(s, "local: %s\n", buf);
+ bitmap_scnprintf(buf, sizeof(buf), rc->drp_avail.pending, UWB_NUM_MAS);
+ seq_printf(s, "pending: %s\n", buf);
+
+ return 0;
+}
+
+static int drp_avail_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, drp_avail_print, inode->i_private);
+}
+
+static struct file_operations drp_avail_fops = {
+ .open = drp_avail_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static void uwb_dbg_new_rsv(struct uwb_rsv *rsv)
+{
+ struct uwb_rc *rc = rsv->rc;
+
+ if (rc->dbg->accept)
+ uwb_rsv_accept(rsv, uwb_dbg_rsv_cb, NULL);
+}
+
+/**
+ * uwb_dbg_add_rc - add a debug interface for a radio controller
+ * @rc: the radio controller
+ */
+void uwb_dbg_add_rc(struct uwb_rc *rc)
+{
+ rc->dbg = kzalloc(sizeof(struct uwb_dbg), GFP_KERNEL);
+ if (rc->dbg == NULL)
+ return;
+
+ INIT_LIST_HEAD(&rc->dbg->rsvs);
+
+ uwb_pal_init(&rc->dbg->pal);
+ rc->dbg->pal.new_rsv = uwb_dbg_new_rsv;
+ uwb_pal_register(rc, &rc->dbg->pal);
+ if (root_dir) {
+ rc->dbg->root_d = debugfs_create_dir(dev_name(&rc->uwb_dev.dev),
+ root_dir);
+ rc->dbg->command_f = debugfs_create_file("command", 0200,
+ rc->dbg->root_d, rc,
+ &command_fops);
+ rc->dbg->reservations_f = debugfs_create_file("reservations", 0444,
+ rc->dbg->root_d, rc,
+ &reservations_fops);
+ rc->dbg->accept_f = debugfs_create_bool("accept", 0644,
+ rc->dbg->root_d,
+ &rc->dbg->accept);
+ rc->dbg->drp_avail_f = debugfs_create_file("drp_avail", 0444,
+ rc->dbg->root_d, rc,
+ &drp_avail_fops);
+ }
+}
+
+/**
+ * uwb_dbg_add_rc - remove a radio controller's debug interface
+ * @rc: the radio controller
+ */
+void uwb_dbg_del_rc(struct uwb_rc *rc)
+{
+ struct uwb_rsv *rsv, *t;
+
+ if (rc->dbg == NULL)
+ return;
+
+ list_for_each_entry_safe(rsv, t, &rc->dbg->rsvs, pal_node) {
+ uwb_rsv_destroy(rsv);
+ }
+
+ uwb_pal_unregister(rc, &rc->dbg->pal);
+
+ if (root_dir) {
+ debugfs_remove(rc->dbg->drp_avail_f);
+ debugfs_remove(rc->dbg->accept_f);
+ debugfs_remove(rc->dbg->reservations_f);
+ debugfs_remove(rc->dbg->command_f);
+ debugfs_remove(rc->dbg->root_d);
+ }
+}
+
+/**
+ * uwb_dbg_exit - initialize the debug interface sub-module
+ */
+void uwb_dbg_init(void)
+{
+ root_dir = debugfs_create_dir("uwb", NULL);
+}
+
+/**
+ * uwb_dbg_exit - clean-up the debug interface sub-module
+ */
+void uwb_dbg_exit(void)
+{
+ debugfs_remove(root_dir);
+}
diff --git a/drivers/uwb/uwb-internal.h b/drivers/uwb/uwb-internal.h
new file mode 100644
index 000000000000..2ad307d12961
--- /dev/null
+++ b/drivers/uwb/uwb-internal.h
@@ -0,0 +1,305 @@
+/*
+ * Ultra Wide Band
+ * UWB internal API
+ *
+ * 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 contains most of the internal API for UWB. This is stuff used
+ * across the stack that of course, is of no interest to the rest.
+ *
+ * Some parts might end up going public (like uwb_rc_*())...
+ */
+
+#ifndef __UWB_INTERNAL_H__
+#define __UWB_INTERNAL_H__
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/uwb.h>
+#include <linux/mutex.h>
+
+struct uwb_beca_e;
+
+/* General device API */
+extern void uwb_dev_init(struct uwb_dev *uwb_dev);
+extern int __uwb_dev_offair(struct uwb_dev *, struct uwb_rc *);
+extern int uwb_dev_add(struct uwb_dev *uwb_dev, struct device *parent_dev,
+ struct uwb_rc *parent_rc);
+extern void uwb_dev_rm(struct uwb_dev *uwb_dev);
+extern void uwbd_dev_onair(struct uwb_rc *, struct uwb_beca_e *);
+extern void uwbd_dev_offair(struct uwb_beca_e *);
+void uwb_notify(struct uwb_rc *rc, struct uwb_dev *uwb_dev, enum uwb_notifs event);
+
+/* General UWB Radio Controller Internal API */
+extern struct uwb_rc *__uwb_rc_try_get(struct uwb_rc *);
+static inline struct uwb_rc *__uwb_rc_get(struct uwb_rc *rc)
+{
+ uwb_dev_get(&rc->uwb_dev);
+ return rc;
+}
+
+static inline void __uwb_rc_put(struct uwb_rc *rc)
+{
+ uwb_dev_put(&rc->uwb_dev);
+}
+
+extern int uwb_rc_reset(struct uwb_rc *rc);
+extern int uwb_rc_beacon(struct uwb_rc *rc,
+ int channel, unsigned bpst_offset);
+extern int uwb_rc_scan(struct uwb_rc *rc,
+ unsigned channel, enum uwb_scan_type type,
+ unsigned bpst_offset);
+extern int uwb_rc_send_all_drp_ie(struct uwb_rc *rc);
+extern ssize_t uwb_rc_print_IEs(struct uwb_rc *rc, char *, size_t);
+extern void uwb_rc_ie_init(struct uwb_rc *);
+extern void uwb_rc_ie_init(struct uwb_rc *);
+extern ssize_t uwb_rc_ie_setup(struct uwb_rc *);
+extern void uwb_rc_ie_release(struct uwb_rc *);
+extern int uwb_rc_ie_add(struct uwb_rc *,
+ const struct uwb_ie_hdr *, size_t);
+extern int uwb_rc_ie_rm(struct uwb_rc *, enum uwb_ie);
+
+extern const char *uwb_rc_strerror(unsigned code);
+
+/*
+ * Time to wait for a response to an RC command.
+ *
+ * Some commands can take a long time to response. e.g., START_BEACON
+ * may scan for several superframes before joining an existing beacon
+ * group and this can take around 600 ms.
+ */
+#define UWB_RC_CMD_TIMEOUT_MS 1000 /* ms */
+
+/*
+ * Notification/Event Handlers
+ */
+
+struct uwb_rc_neh;
+
+void uwb_rc_neh_create(struct uwb_rc *rc);
+void uwb_rc_neh_destroy(struct uwb_rc *rc);
+
+struct uwb_rc_neh *uwb_rc_neh_add(struct uwb_rc *rc, struct uwb_rccb *cmd,
+ u8 expected_type, u16 expected_event,
+ uwb_rc_cmd_cb_f cb, void *arg);
+void uwb_rc_neh_rm(struct uwb_rc *rc, struct uwb_rc_neh *neh);
+void uwb_rc_neh_arm(struct uwb_rc *rc, struct uwb_rc_neh *neh);
+void uwb_rc_neh_put(struct uwb_rc_neh *neh);
+
+/* Event size tables */
+extern int uwb_est_create(void);
+extern void uwb_est_destroy(void);
+
+
+/*
+ * UWB Events & management daemon
+ */
+
+/**
+ * enum uwb_event_type - types of UWB management daemon events
+ *
+ * The UWB management daemon (uwbd) can receive two types of events:
+ * UWB_EVT_TYPE_NOTIF - notification from the radio controller.
+ * UWB_EVT_TYPE_MSG - a simple message.
+ */
+enum uwb_event_type {
+ UWB_EVT_TYPE_NOTIF,
+ UWB_EVT_TYPE_MSG,
+};
+
+/**
+ * struct uwb_event_notif - an event for a radio controller notification
+ * @size: Size of the buffer (ie: Guaranteed to contain at least
+ * a full 'struct uwb_rceb')
+ * @rceb: Pointer to a kmalloced() event payload
+ */
+struct uwb_event_notif {
+ size_t size;
+ struct uwb_rceb *rceb;
+};
+
+/**
+ * enum uwb_event_message - an event for a message for asynchronous processing
+ *
+ * UWB_EVT_MSG_RESET - reset the radio controller and all PAL hardware.
+ */
+enum uwb_event_message {
+ UWB_EVT_MSG_RESET,
+};
+
+/**
+ * UWB Event
+ * @rc: Radio controller that emitted the event (referenced)
+ * @ts_jiffies: Timestamp, when was it received
+ * @type: This event's type.
+ */
+struct uwb_event {
+ struct list_head list_node;
+ struct uwb_rc *rc;
+ unsigned long ts_jiffies;
+ enum uwb_event_type type;
+ union {
+ struct uwb_event_notif notif;
+ enum uwb_event_message message;
+ };
+};
+
+extern void uwbd_start(void);
+extern void uwbd_stop(void);
+extern struct uwb_event *uwb_event_alloc(size_t, gfp_t gfp_mask);
+extern void uwbd_event_queue(struct uwb_event *);
+void uwbd_flush(struct uwb_rc *rc);
+
+/* UWB event handlers */
+extern int uwbd_evt_handle_rc_beacon(struct uwb_event *);
+extern int uwbd_evt_handle_rc_beacon_size(struct uwb_event *);
+extern int uwbd_evt_handle_rc_bpoie_change(struct uwb_event *);
+extern int uwbd_evt_handle_rc_bp_slot_change(struct uwb_event *);
+extern int uwbd_evt_handle_rc_drp(struct uwb_event *);
+extern int uwbd_evt_handle_rc_drp_avail(struct uwb_event *);
+
+int uwbd_msg_handle_reset(struct uwb_event *evt);
+
+
+/*
+ * Address management
+ */
+int uwb_rc_dev_addr_assign(struct uwb_rc *rc);
+int uwbd_evt_handle_rc_dev_addr_conflict(struct uwb_event *evt);
+
+/*
+ * UWB Beacon Cache
+ *
+ * Each beacon we received is kept in a cache--when we receive that
+ * beacon consistently, that means there is a new device that we have
+ * to add to the system.
+ */
+
+extern unsigned long beacon_timeout_ms;
+
+/** Beacon cache list */
+struct uwb_beca {
+ struct list_head list;
+ size_t entries;
+ struct mutex mutex;
+};
+
+extern struct uwb_beca uwb_beca;
+
+/**
+ * Beacon cache entry
+ *
+ * @jiffies_refresh: last time a beacon was received that refreshed
+ * this cache entry.
+ * @uwb_dev: device connected to this beacon. This pointer is not
+ * safe, you need to get it with uwb_dev_try_get()
+ *
+ * @hits: how many time we have seen this beacon since last time we
+ * cleared it
+ */
+struct uwb_beca_e {
+ struct mutex mutex;
+ struct kref refcnt;
+ struct list_head node;
+ struct uwb_mac_addr *mac_addr;
+ struct uwb_dev_addr dev_addr;
+ u8 hits;
+ unsigned long ts_jiffies;
+ struct uwb_dev *uwb_dev;
+ struct uwb_rc_evt_beacon *be;
+ struct stats lqe_stats, rssi_stats; /* radio statistics */
+};
+struct uwb_beacon_frame;
+extern ssize_t uwb_bce_print_IEs(struct uwb_dev *, struct uwb_beca_e *,
+ char *, size_t);
+extern struct uwb_beca_e *__uwb_beca_add(struct uwb_rc_evt_beacon *,
+ struct uwb_beacon_frame *,
+ unsigned long);
+
+extern void uwb_bce_kfree(struct kref *_bce);
+static inline void uwb_bce_get(struct uwb_beca_e *bce)
+{
+ kref_get(&bce->refcnt);
+}
+static inline void uwb_bce_put(struct uwb_beca_e *bce)
+{
+ kref_put(&bce->refcnt, uwb_bce_kfree);
+}
+extern void uwb_beca_purge(void);
+extern void uwb_beca_release(void);
+
+struct uwb_dev *uwb_dev_get_by_devaddr(struct uwb_rc *rc,
+ const struct uwb_dev_addr *devaddr);
+struct uwb_dev *uwb_dev_get_by_macaddr(struct uwb_rc *rc,
+ const struct uwb_mac_addr *macaddr);
+
+/* -- UWB Sysfs representation */
+extern struct class uwb_rc_class;
+extern struct device_attribute dev_attr_mac_address;
+extern struct device_attribute dev_attr_beacon;
+extern struct device_attribute dev_attr_scan;
+
+/* -- DRP Bandwidth allocator: bandwidth allocations, reservations, DRP */
+void uwb_rsv_init(struct uwb_rc *rc);
+int uwb_rsv_setup(struct uwb_rc *rc);
+void uwb_rsv_cleanup(struct uwb_rc *rc);
+
+void uwb_rsv_set_state(struct uwb_rsv *rsv, enum uwb_rsv_state new_state);
+void uwb_rsv_remove(struct uwb_rsv *rsv);
+struct uwb_rsv *uwb_rsv_find(struct uwb_rc *rc, struct uwb_dev *src,
+ struct uwb_ie_drp *drp_ie);
+void uwb_rsv_sched_update(struct uwb_rc *rc);
+
+void uwb_drp_handle_timeout(struct uwb_rsv *rsv);
+int uwb_drp_ie_update(struct uwb_rsv *rsv);
+void uwb_drp_ie_to_bm(struct uwb_mas_bm *bm, const struct uwb_ie_drp *drp_ie);
+
+void uwb_drp_avail_init(struct uwb_rc *rc);
+int uwb_drp_avail_reserve_pending(struct uwb_rc *rc, struct uwb_mas_bm *mas);
+void uwb_drp_avail_reserve(struct uwb_rc *rc, struct uwb_mas_bm *mas);
+void uwb_drp_avail_release(struct uwb_rc *rc, struct uwb_mas_bm *mas);
+void uwb_drp_avail_ie_update(struct uwb_rc *rc);
+
+/* -- PAL support */
+void uwb_rc_pal_init(struct uwb_rc *rc);
+
+/* -- Misc */
+
+extern ssize_t uwb_mac_frame_hdr_print(char *, size_t,
+ const struct uwb_mac_frame_hdr *);
+
+/* -- Debug interface */
+void uwb_dbg_init(void);
+void uwb_dbg_exit(void);
+void uwb_dbg_add_rc(struct uwb_rc *rc);
+void uwb_dbg_del_rc(struct uwb_rc *rc);
+
+/* Workarounds for version specific stuff */
+
+static inline void uwb_dev_lock(struct uwb_dev *uwb_dev)
+{
+ down(&uwb_dev->dev.sem);
+}
+
+static inline void uwb_dev_unlock(struct uwb_dev *uwb_dev)
+{
+ up(&uwb_dev->dev.sem);
+}
+
+#endif /* #ifndef __UWB_INTERNAL_H__ */
diff --git a/drivers/uwb/uwbd.c b/drivers/uwb/uwbd.c
new file mode 100644
index 000000000000..b3673d614adb
--- /dev/null
+++ b/drivers/uwb/uwbd.c
@@ -0,0 +1,427 @@
+/*
+ * Ultra Wide Band
+ * Neighborhood Management Daemon
+ *
+ * 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 daemon takes care of maintaing information that describes the
+ * UWB neighborhood that the radios in this machine can see. It also
+ * keeps a tab of which devices are visible, makes sure each HC sits
+ * on a different channel to avoid interfering, etc.
+ *
+ * Different drivers (radio controller, device, any API in general)
+ * communicate with this daemon through an event queue. Daemon wakes
+ * up, takes a list of events and handles them one by one; handling
+ * function is extracted from a table based on the event's type and
+ * subtype. Events are freed only if the handling function says so.
+ *
+ * . Lock protecting the event list has to be an spinlock and locked
+ * with IRQSAVE because it might be called from an interrupt
+ * context (ie: when events arrive and the notification drops
+ * down from the ISR).
+ *
+ * . UWB radio controller drivers queue events to the daemon using
+ * uwbd_event_queue(). They just get the event, chew it to make it
+ * look like UWBD likes it and pass it in a buffer allocated with
+ * uwb_event_alloc().
+ *
+ * EVENTS
+ *
+ * Events have a type, a subtype, a lenght, some other stuff and the
+ * data blob, which depends on the event. The header is 'struct
+ * uwb_event'; for payloads, see 'struct uwbd_evt_*'.
+ *
+ * EVENT HANDLER TABLES
+ *
+ * To find a handling function for an event, the type is used to index
+ * a subtype-table in the type-table. The subtype-table is indexed
+ * with the subtype to get the function that handles the event. Start
+ * with the main type-table 'uwbd_evt_type_handler'.
+ *
+ * DEVICES
+ *
+ * Devices are created when a bunch of beacons have been received and
+ * it is stablished that the device has stable radio presence. CREATED
+ * only, not configured. Devices are ONLY configured when an
+ * Application-Specific IE Probe is receieved, in which the device
+ * declares which Protocol ID it groks. Then the device is CONFIGURED
+ * (and the driver->probe() stuff of the device model is invoked).
+ *
+ * Devices are considered disconnected when a certain number of
+ * beacons are not received in an amount of time.
+ *
+ * Handler functions are called normally uwbd_evt_handle_*().
+ */
+
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/freezer.h>
+#include "uwb-internal.h"
+
+#define D_LOCAL 1
+#include <linux/uwb/debug.h>
+
+
+/**
+ * UWBD Event handler function signature
+ *
+ * Return !0 if the event needs not to be freed (ie the handler
+ * takes/took care of it). 0 means the daemon code will free the
+ * event.
+ *
+ * @evt->rc is already referenced and guaranteed to exist. See
+ * uwb_evt_handle().
+ */
+typedef int (*uwbd_evt_handler_f)(struct uwb_event *);
+
+/**
+ * Properties of a UWBD event
+ *
+ * @handler: the function that will handle this event
+ * @name: text name of event
+ */
+struct uwbd_event {
+ uwbd_evt_handler_f handler;
+ const char *name;
+};
+
+/** Table of handlers for and properties of the UWBD Radio Control Events */
+static
+struct uwbd_event uwbd_events[] = {
+ [UWB_RC_EVT_BEACON] = {
+ .handler = uwbd_evt_handle_rc_beacon,
+ .name = "BEACON_RECEIVED"
+ },
+ [UWB_RC_EVT_BEACON_SIZE] = {
+ .handler = uwbd_evt_handle_rc_beacon_size,
+ .name = "BEACON_SIZE_CHANGE"
+ },
+ [UWB_RC_EVT_BPOIE_CHANGE] = {
+ .handler = uwbd_evt_handle_rc_bpoie_change,
+ .name = "BPOIE_CHANGE"
+ },
+ [UWB_RC_EVT_BP_SLOT_CHANGE] = {
+ .handler = uwbd_evt_handle_rc_bp_slot_change,
+ .name = "BP_SLOT_CHANGE"
+ },
+ [UWB_RC_EVT_DRP_AVAIL] = {
+ .handler = uwbd_evt_handle_rc_drp_avail,
+ .name = "DRP_AVAILABILITY_CHANGE"
+ },
+ [UWB_RC_EVT_DRP] = {
+ .handler = uwbd_evt_handle_rc_drp,
+ .name = "DRP"
+ },
+ [UWB_RC_EVT_DEV_ADDR_CONFLICT] = {
+ .handler = uwbd_evt_handle_rc_dev_addr_conflict,
+ .name = "DEV_ADDR_CONFLICT",
+ },
+};
+
+
+
+struct uwbd_evt_type_handler {
+ const char *name;
+ struct uwbd_event *uwbd_events;
+ size_t size;
+};
+
+#define UWBD_EVT_TYPE_HANDLER(n,a) { \
+ .name = (n), \
+ .uwbd_events = (a), \
+ .size = sizeof(a)/sizeof((a)[0]) \
+}
+
+
+/** Table of handlers for each UWBD Event type. */
+static
+struct uwbd_evt_type_handler uwbd_evt_type_handlers[] = {
+ [UWB_RC_CET_GENERAL] = UWBD_EVT_TYPE_HANDLER("RC", uwbd_events)
+};
+
+static const
+size_t uwbd_evt_type_handlers_len =
+ sizeof(uwbd_evt_type_handlers) / sizeof(uwbd_evt_type_handlers[0]);
+
+static const struct uwbd_event uwbd_message_handlers[] = {
+ [UWB_EVT_MSG_RESET] = {
+ .handler = uwbd_msg_handle_reset,
+ .name = "reset",
+ },
+};
+
+static DEFINE_MUTEX(uwbd_event_mutex);
+
+/**
+ * Handle an URC event passed to the UWB Daemon
+ *
+ * @evt: the event to handle
+ * @returns: 0 if the event can be kfreed, !0 on the contrary
+ * (somebody else took ownership) [coincidentally, returning
+ * a <0 errno code will free it :)].
+ *
+ * Looks up the two indirection tables (one for the type, one for the
+ * subtype) to decide which function handles it and then calls the
+ * handler.
+ *
+ * The event structure passed to the event handler has the radio
+ * controller in @evt->rc referenced. The reference will be dropped
+ * once the handler returns, so if it needs it for longer (async),
+ * it'll need to take another one.
+ */
+static
+int uwbd_event_handle_urc(struct uwb_event *evt)
+{
+ int result;
+ struct uwbd_evt_type_handler *type_table;
+ uwbd_evt_handler_f handler;
+ u8 type, context;
+ u16 event;
+
+ type = evt->notif.rceb->bEventType;
+ event = le16_to_cpu(evt->notif.rceb->wEvent);
+ context = evt->notif.rceb->bEventContext;
+
+ if (type > uwbd_evt_type_handlers_len) {
+ if (printk_ratelimit())
+ printk(KERN_ERR "UWBD: event type %u: unknown "
+ "(too high)\n", type);
+ return -EINVAL;
+ }
+ type_table = &uwbd_evt_type_handlers[type];
+ if (type_table->uwbd_events == NULL) {
+ if (printk_ratelimit())
+ printk(KERN_ERR "UWBD: event type %u: unknown\n", type);
+ return -EINVAL;
+ }
+ if (event > type_table->size) {
+ if (printk_ratelimit())
+ printk(KERN_ERR "UWBD: event %s[%u]: "
+ "unknown (too high)\n", type_table->name, event);
+ return -EINVAL;
+ }
+ handler = type_table->uwbd_events[event].handler;
+ if (handler == NULL) {
+ if (printk_ratelimit())
+ printk(KERN_ERR "UWBD: event %s[%u]: unknown\n",
+ type_table->name, event);
+ return -EINVAL;
+ }
+ d_printf(3, NULL, "processing 0x%02x/%04x/%02x, %zu bytes\n",
+ type, event, context, evt->notif.size);
+ result = (*handler)(evt);
+ if (result < 0) {
+ if (printk_ratelimit())
+ printk(KERN_ERR "UWBD: event 0x%02x/%04x/%02x, "
+ "table %s[%u]: handling failed: %d\n",
+ type, event, context, type_table->name,
+ event, result);
+ }
+ return result;
+}
+
+static void uwbd_event_handle_message(struct uwb_event *evt)
+{
+ struct uwb_rc *rc;
+ int result;
+
+ rc = evt->rc;
+
+ if (evt->message < 0 || evt->message >= ARRAY_SIZE(uwbd_message_handlers)) {
+ dev_err(&rc->uwb_dev.dev, "UWBD: invalid message type %d\n", evt->message);
+ return;
+ }
+
+ /* If this is a reset event we need to drop the
+ * uwbd_event_mutex or it deadlocks when the reset handler
+ * attempts to flush the uwbd events. */
+ if (evt->message == UWB_EVT_MSG_RESET)
+ mutex_unlock(&uwbd_event_mutex);
+
+ result = uwbd_message_handlers[evt->message].handler(evt);
+ if (result < 0)
+ dev_err(&rc->uwb_dev.dev, "UWBD: '%s' message failed: %d\n",
+ uwbd_message_handlers[evt->message].name, result);
+
+ if (evt->message == UWB_EVT_MSG_RESET)
+ mutex_lock(&uwbd_event_mutex);
+}
+
+static void uwbd_event_handle(struct uwb_event *evt)
+{
+ struct uwb_rc *rc;
+ int should_keep;
+
+ rc = evt->rc;
+
+ if (rc->ready) {
+ switch (evt->type) {
+ case UWB_EVT_TYPE_NOTIF:
+ should_keep = uwbd_event_handle_urc(evt);
+ if (should_keep <= 0)
+ kfree(evt->notif.rceb);
+ break;
+ case UWB_EVT_TYPE_MSG:
+ uwbd_event_handle_message(evt);
+ break;
+ default:
+ dev_err(&rc->uwb_dev.dev, "UWBD: invalid event type %d\n", evt->type);
+ break;
+ }
+ }
+
+ __uwb_rc_put(rc); /* for the __uwb_rc_get() in uwb_rc_notif_cb() */
+}
+/* The UWB Daemon */
+
+
+/** Daemon's PID: used to decide if we can queue or not */
+static int uwbd_pid;
+/** Daemon's task struct for managing the kthread */
+static struct task_struct *uwbd_task;
+/** Daemon's waitqueue for waiting for new events */
+static DECLARE_WAIT_QUEUE_HEAD(uwbd_wq);
+/** Daemon's list of events; we queue/dequeue here */
+static struct list_head uwbd_event_list = LIST_HEAD_INIT(uwbd_event_list);
+/** Daemon's list lock to protect concurent access */
+static DEFINE_SPINLOCK(uwbd_event_list_lock);
+
+
+/**
+ * UWB Daemon
+ *
+ * Listens to all UWB notifications and takes care to track the state
+ * of the UWB neighboorhood for the kernel. When we do a run, we
+ * spinlock, move the list to a private copy and release the
+ * lock. Hold it as little as possible. Not a conflict: it is
+ * guaranteed we own the events in the private list.
+ *
+ * FIXME: should change so we don't have a 1HZ timer all the time, but
+ * only if there are devices.
+ */
+static int uwbd(void *unused)
+{
+ unsigned long flags;
+ struct list_head list = LIST_HEAD_INIT(list);
+ struct uwb_event *evt, *nxt;
+ int should_stop = 0;
+ while (1) {
+ wait_event_interruptible_timeout(
+ uwbd_wq,
+ !list_empty(&uwbd_event_list)
+ || (should_stop = kthread_should_stop()),
+ HZ);
+ if (should_stop)
+ break;
+ try_to_freeze();
+
+ mutex_lock(&uwbd_event_mutex);
+ spin_lock_irqsave(&uwbd_event_list_lock, flags);
+ list_splice_init(&uwbd_event_list, &list);
+ spin_unlock_irqrestore(&uwbd_event_list_lock, flags);
+ list_for_each_entry_safe(evt, nxt, &list, list_node) {
+ list_del(&evt->list_node);
+ uwbd_event_handle(evt);
+ kfree(evt);
+ }
+ mutex_unlock(&uwbd_event_mutex);
+
+ uwb_beca_purge(); /* Purge devices that left */
+ }
+ return 0;
+}
+
+
+/** Start the UWB daemon */
+void uwbd_start(void)
+{
+ uwbd_task = kthread_run(uwbd, NULL, "uwbd");
+ if (uwbd_task == NULL)
+ printk(KERN_ERR "UWB: Cannot start management daemon; "
+ "UWB won't work\n");
+ else
+ uwbd_pid = uwbd_task->pid;
+}
+
+/* Stop the UWB daemon and free any unprocessed events */
+void uwbd_stop(void)
+{
+ unsigned long flags;
+ struct uwb_event *evt, *nxt;
+ kthread_stop(uwbd_task);
+ spin_lock_irqsave(&uwbd_event_list_lock, flags);
+ uwbd_pid = 0;
+ list_for_each_entry_safe(evt, nxt, &uwbd_event_list, list_node) {
+ if (evt->type == UWB_EVT_TYPE_NOTIF)
+ kfree(evt->notif.rceb);
+ kfree(evt);
+ }
+ spin_unlock_irqrestore(&uwbd_event_list_lock, flags);
+ uwb_beca_release();
+}
+
+/*
+ * Queue an event for the management daemon
+ *
+ * When some lower layer receives an event, it uses this function to
+ * push it forward to the UWB daemon.
+ *
+ * Once you pass the event, you don't own it any more, but the daemon
+ * does. It will uwb_event_free() it when done, so make sure you
+ * uwb_event_alloc()ed it or bad things will happen.
+ *
+ * If the daemon is not running, we just free the event.
+ */
+void uwbd_event_queue(struct uwb_event *evt)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&uwbd_event_list_lock, flags);
+ if (uwbd_pid != 0) {
+ list_add(&evt->list_node, &uwbd_event_list);
+ wake_up_all(&uwbd_wq);
+ } else {
+ __uwb_rc_put(evt->rc);
+ if (evt->type == UWB_EVT_TYPE_NOTIF)
+ kfree(evt->notif.rceb);
+ kfree(evt);
+ }
+ spin_unlock_irqrestore(&uwbd_event_list_lock, flags);
+ return;
+}
+
+void uwbd_flush(struct uwb_rc *rc)
+{
+ struct uwb_event *evt, *nxt;
+
+ mutex_lock(&uwbd_event_mutex);
+
+ spin_lock_irq(&uwbd_event_list_lock);
+ list_for_each_entry_safe(evt, nxt, &uwbd_event_list, list_node) {
+ if (evt->rc == rc) {
+ __uwb_rc_put(rc);
+ list_del(&evt->list_node);
+ if (evt->type == UWB_EVT_TYPE_NOTIF)
+ kfree(evt->notif.rceb);
+ kfree(evt);
+ }
+ }
+ spin_unlock_irq(&uwbd_event_list_lock);
+
+ mutex_unlock(&uwbd_event_mutex);
+}
diff --git a/drivers/uwb/whc-rc.c b/drivers/uwb/whc-rc.c
new file mode 100644
index 000000000000..5a93abea6d23
--- /dev/null
+++ b/drivers/uwb/whc-rc.c
@@ -0,0 +1,528 @@
+/*
+ * Wireless Host Controller: Radio Control Interface (WHCI v0.95[2.3])
+ * Radio Control command/event transport to the UWB stack
+ *
+ * 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.
+ *
+ *
+ * Initialize and hook up the Radio Control interface.
+ *
+ * For each device probed, creates an 'struct whcrc' which contains
+ * just the representation of the UWB Radio Controller, and the logic
+ * for reading notifications and passing them to the UWB Core.
+ *
+ * So we initialize all of those, register the UWB Radio Controller
+ * and setup the notification/event handle to pipe the notifications
+ * to the UWB management Daemon.
+ *
+ * Once uwb_rc_add() is called, the UWB stack takes control, resets
+ * the radio and readies the device to take commands the UWB
+ * API/user-space.
+ *
+ * Note this driver is just a transport driver; the commands are
+ * formed at the UWB stack and given to this driver who will deliver
+ * them to the hw and transfer the replies/notifications back to the
+ * UWB stack through the UWB daemon (UWBD).
+ */
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/uwb.h>
+#include <linux/uwb/whci.h>
+#include <linux/uwb/umc.h>
+#include "uwb-internal.h"
+
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+/**
+ * Descriptor for an instance of the UWB Radio Control Driver that
+ * attaches to the URC interface of the WHCI PCI card.
+ *
+ * Unless there is a lock specific to the 'data members', all access
+ * is protected by uwb_rc->mutex.
+ */
+struct whcrc {
+ struct umc_dev *umc_dev;
+ struct uwb_rc *uwb_rc; /* UWB host controller */
+
+ unsigned long area;
+ void __iomem *rc_base;
+ size_t rc_len;
+ spinlock_t irq_lock;
+
+ void *evt_buf, *cmd_buf;
+ dma_addr_t evt_dma_buf, cmd_dma_buf;
+ wait_queue_head_t cmd_wq;
+ struct work_struct event_work;
+};
+
+/**
+ * Execute an UWB RC command on WHCI/RC
+ *
+ * @rc: Instance of a Radio Controller that is a whcrc
+ * @cmd: Buffer containing the RCCB and payload to execute
+ * @cmd_size: Size of the command buffer.
+ *
+ * We copy the command into whcrc->cmd_buf (as it is pretty and
+ * aligned`and physically contiguous) and then press the right keys in
+ * the controller's URCCMD register to get it to read it. We might
+ * have to wait for the cmd_sem to be open to us.
+ *
+ * NOTE: rc's mutex has to be locked
+ */
+static int whcrc_cmd(struct uwb_rc *uwb_rc,
+ const struct uwb_rccb *cmd, size_t cmd_size)
+{
+ int result = 0;
+ struct whcrc *whcrc = uwb_rc->priv;
+ struct device *dev = &whcrc->umc_dev->dev;
+ u32 urccmd;
+
+ d_fnstart(3, dev, "(%p, %p, %zu)\n", uwb_rc, cmd, cmd_size);
+ might_sleep();
+
+ if (cmd_size >= 4096) {
+ result = -E2BIG;
+ goto error;
+ }
+
+ /*
+ * If the URC is halted, then the hardware has reset itself.
+ * Attempt to recover by restarting the device and then return
+ * an error as it's likely that the current command isn't
+ * valid for a newly started RC.
+ */
+ if (le_readl(whcrc->rc_base + URCSTS) & URCSTS_HALTED) {
+ dev_err(dev, "requesting reset of halted radio controller\n");
+ uwb_rc_reset_all(uwb_rc);
+ result = -EIO;
+ goto error;
+ }
+
+ result = wait_event_timeout(whcrc->cmd_wq,
+ !(le_readl(whcrc->rc_base + URCCMD) & URCCMD_ACTIVE), HZ/2);
+ if (result == 0) {
+ dev_err(dev, "device is not ready to execute commands\n");
+ result = -ETIMEDOUT;
+ goto error;
+ }
+
+ memmove(whcrc->cmd_buf, cmd, cmd_size);
+ le_writeq(whcrc->cmd_dma_buf, whcrc->rc_base + URCCMDADDR);
+
+ spin_lock(&whcrc->irq_lock);
+ urccmd = le_readl(whcrc->rc_base + URCCMD);
+ urccmd &= ~(URCCMD_EARV | URCCMD_SIZE_MASK);
+ le_writel(urccmd | URCCMD_ACTIVE | URCCMD_IWR | cmd_size,
+ whcrc->rc_base + URCCMD);
+ spin_unlock(&whcrc->irq_lock);
+
+error:
+ d_fnend(3, dev, "(%p, %p, %zu) = %d\n",
+ uwb_rc, cmd, cmd_size, result);
+ return result;
+}
+
+static int whcrc_reset(struct uwb_rc *rc)
+{
+ struct whcrc *whcrc = rc->priv;
+
+ return umc_controller_reset(whcrc->umc_dev);
+}
+
+/**
+ * Reset event reception mechanism and tell hw we are ready to get more
+ *
+ * We have read all the events in the event buffer, so we are ready to
+ * reset it to the beginning.
+ *
+ * This is only called during initialization or after an event buffer
+ * has been retired. This means we can be sure that event processing
+ * is disabled and it's safe to update the URCEVTADDR register.
+ *
+ * There's no need to wait for the event processing to start as the
+ * URC will not clear URCCMD_ACTIVE until (internal) event buffer
+ * space is available.
+ */
+static
+void whcrc_enable_events(struct whcrc *whcrc)
+{
+ struct device *dev = &whcrc->umc_dev->dev;
+ u32 urccmd;
+
+ d_fnstart(4, dev, "(whcrc %p)\n", whcrc);
+
+ le_writeq(whcrc->evt_dma_buf, whcrc->rc_base + URCEVTADDR);
+
+ spin_lock(&whcrc->irq_lock);
+ urccmd = le_readl(whcrc->rc_base + URCCMD) & ~URCCMD_ACTIVE;
+ le_writel(urccmd | URCCMD_EARV, whcrc->rc_base + URCCMD);
+ spin_unlock(&whcrc->irq_lock);
+
+ d_fnend(4, dev, "(whcrc %p) = void\n", whcrc);
+}
+
+static void whcrc_event_work(struct work_struct *work)
+{
+ struct whcrc *whcrc = container_of(work, struct whcrc, event_work);
+ struct device *dev = &whcrc->umc_dev->dev;
+ size_t size;
+ u64 urcevtaddr;
+
+ urcevtaddr = le_readq(whcrc->rc_base + URCEVTADDR);
+ size = urcevtaddr & URCEVTADDR_OFFSET_MASK;
+
+ d_printf(3, dev, "received %zu octet event\n", size);
+ d_dump(4, dev, whcrc->evt_buf, size > 32 ? 32 : size);
+
+ uwb_rc_neh_grok(whcrc->uwb_rc, whcrc->evt_buf, size);
+ whcrc_enable_events(whcrc);
+}
+
+/**
+ * Catch interrupts?
+ *
+ * We ack inmediately (and expect the hw to do the right thing and
+ * raise another IRQ if things have changed :)
+ */
+static
+irqreturn_t whcrc_irq_cb(int irq, void *_whcrc)
+{
+ struct whcrc *whcrc = _whcrc;
+ struct device *dev = &whcrc->umc_dev->dev;
+ u32 urcsts;
+
+ d_fnstart(4, dev, "irq %d _whcrc %p)\n", irq, _whcrc);
+ urcsts = le_readl(whcrc->rc_base + URCSTS);
+ if (!(urcsts & URCSTS_INT_MASK))
+ return IRQ_NONE;
+ le_writel(urcsts & URCSTS_INT_MASK, whcrc->rc_base + URCSTS);
+
+ d_printf(4, dev, "acked 0x%08x, urcsts 0x%08x\n",
+ le_readl(whcrc->rc_base + URCSTS), urcsts);
+
+ if (whcrc->uwb_rc == NULL) {
+ if (printk_ratelimit())
+ dev_dbg(dev, "Received interrupt when not yet "
+ "ready!\n");
+ goto out;
+ }
+
+ if (urcsts & URCSTS_HSE) {
+ dev_err(dev, "host system error -- hardware halted\n");
+ /* FIXME: do something sensible here */
+ goto out;
+ }
+ if (urcsts & URCSTS_ER) {
+ d_printf(3, dev, "ER: event ready\n");
+ schedule_work(&whcrc->event_work);
+ }
+ if (urcsts & URCSTS_RCI) {
+ d_printf(3, dev, "RCI: ready to execute another command\n");
+ wake_up_all(&whcrc->cmd_wq);
+ }
+out:
+ return IRQ_HANDLED;
+}
+
+
+/**
+ * Initialize a UMC RC interface: map regions, get (shared) IRQ
+ */
+static
+int whcrc_setup_rc_umc(struct whcrc *whcrc)
+{
+ int result = 0;
+ struct device *dev = &whcrc->umc_dev->dev;
+ struct umc_dev *umc_dev = whcrc->umc_dev;
+
+ whcrc->area = umc_dev->resource.start;
+ whcrc->rc_len = umc_dev->resource.end - umc_dev->resource.start + 1;
+ result = -EBUSY;
+ if (request_mem_region(whcrc->area, whcrc->rc_len, KBUILD_MODNAME)
+ == NULL) {
+ dev_err(dev, "can't request URC region (%zu bytes @ 0x%lx): %d\n",
+ whcrc->rc_len, whcrc->area, result);
+ goto error_request_region;
+ }
+
+ whcrc->rc_base = ioremap_nocache(whcrc->area, whcrc->rc_len);
+ if (whcrc->rc_base == NULL) {
+ dev_err(dev, "can't ioremap registers (%zu bytes @ 0x%lx): %d\n",
+ whcrc->rc_len, whcrc->area, result);
+ goto error_ioremap_nocache;
+ }
+
+ result = request_irq(umc_dev->irq, whcrc_irq_cb, IRQF_SHARED,
+ KBUILD_MODNAME, whcrc);
+ if (result < 0) {
+ dev_err(dev, "can't allocate IRQ %d: %d\n",
+ umc_dev->irq, result);
+ goto error_request_irq;
+ }
+
+ result = -ENOMEM;
+ whcrc->cmd_buf = dma_alloc_coherent(&umc_dev->dev, PAGE_SIZE,
+ &whcrc->cmd_dma_buf, GFP_KERNEL);
+ if (whcrc->cmd_buf == NULL) {
+ dev_err(dev, "Can't allocate cmd transfer buffer\n");
+ goto error_cmd_buffer;
+ }
+
+ whcrc->evt_buf = dma_alloc_coherent(&umc_dev->dev, PAGE_SIZE,
+ &whcrc->evt_dma_buf, GFP_KERNEL);
+ if (whcrc->evt_buf == NULL) {
+ dev_err(dev, "Can't allocate evt transfer buffer\n");
+ goto error_evt_buffer;
+ }
+ d_printf(3, dev, "UWB RC Interface: %zu bytes at 0x%p, irq %u\n",
+ whcrc->rc_len, whcrc->rc_base, umc_dev->irq);
+ return 0;
+
+error_evt_buffer:
+ dma_free_coherent(&umc_dev->dev, PAGE_SIZE, whcrc->cmd_buf,
+ whcrc->cmd_dma_buf);
+error_cmd_buffer:
+ free_irq(umc_dev->irq, whcrc);
+error_request_irq:
+ iounmap(whcrc->rc_base);
+error_ioremap_nocache:
+ release_mem_region(whcrc->area, whcrc->rc_len);
+error_request_region:
+ return result;
+}
+
+
+/**
+ * Release RC's UMC resources
+ */
+static
+void whcrc_release_rc_umc(struct whcrc *whcrc)
+{
+ struct umc_dev *umc_dev = whcrc->umc_dev;
+
+ dma_free_coherent(&umc_dev->dev, PAGE_SIZE, whcrc->evt_buf,
+ whcrc->evt_dma_buf);
+ dma_free_coherent(&umc_dev->dev, PAGE_SIZE, whcrc->cmd_buf,
+ whcrc->cmd_dma_buf);
+ free_irq(umc_dev->irq, whcrc);
+ iounmap(whcrc->rc_base);
+ release_mem_region(whcrc->area, whcrc->rc_len);
+}
+
+
+/**
+ * whcrc_start_rc - start a WHCI radio controller
+ * @whcrc: the radio controller to start
+ *
+ * Reset the UMC device, start the radio controller, enable events and
+ * finally enable interrupts.
+ */
+static int whcrc_start_rc(struct uwb_rc *rc)
+{
+ struct whcrc *whcrc = rc->priv;
+ int result = 0;
+ struct device *dev = &whcrc->umc_dev->dev;
+ unsigned long start, duration;
+
+ /* Reset the thing */
+ le_writel(URCCMD_RESET, whcrc->rc_base + URCCMD);
+ if (d_test(3))
+ start = jiffies;
+ if (whci_wait_for(dev, whcrc->rc_base + URCCMD, URCCMD_RESET, 0,
+ 5000, "device to reset at init") < 0) {
+ result = -EBUSY;
+ goto error;
+ } else if (d_test(3)) {
+ duration = jiffies - start;
+ if (duration > msecs_to_jiffies(40))
+ dev_err(dev, "Device took %ums to "
+ "reset. MAX expected: 40ms\n",
+ jiffies_to_msecs(duration));
+ }
+
+ /* Set the event buffer, start the controller (enable IRQs later) */
+ le_writel(0, whcrc->rc_base + URCINTR);
+ le_writel(URCCMD_RS, whcrc->rc_base + URCCMD);
+ result = -ETIMEDOUT;
+ if (d_test(3))
+ start = jiffies;
+ if (whci_wait_for(dev, whcrc->rc_base + URCSTS, URCSTS_HALTED, 0,
+ 5000, "device to start") < 0)
+ goto error;
+ if (d_test(3)) {
+ duration = jiffies - start;
+ if (duration > msecs_to_jiffies(40))
+ dev_err(dev, "Device took %ums to start. "
+ "MAX expected: 40ms\n",
+ jiffies_to_msecs(duration));
+ }
+ whcrc_enable_events(whcrc);
+ result = 0;
+ le_writel(URCINTR_EN_ALL, whcrc->rc_base + URCINTR);
+error:
+ return result;
+}
+
+
+/**
+ * whcrc_stop_rc - stop a WHCI radio controller
+ * @whcrc: the radio controller to stop
+ *
+ * Disable interrupts and cancel any pending event processing work
+ * before clearing the Run/Stop bit.
+ */
+static
+void whcrc_stop_rc(struct uwb_rc *rc)
+{
+ struct whcrc *whcrc = rc->priv;
+ struct umc_dev *umc_dev = whcrc->umc_dev;
+
+ le_writel(0, whcrc->rc_base + URCINTR);
+ cancel_work_sync(&whcrc->event_work);
+
+ le_writel(0, whcrc->rc_base + URCCMD);
+ whci_wait_for(&umc_dev->dev, whcrc->rc_base + URCSTS,
+ URCSTS_HALTED, 0, 40, "URCSTS.HALTED");
+}
+
+static void whcrc_init(struct whcrc *whcrc)
+{
+ spin_lock_init(&whcrc->irq_lock);
+ init_waitqueue_head(&whcrc->cmd_wq);
+ INIT_WORK(&whcrc->event_work, whcrc_event_work);
+}
+
+/**
+ * Initialize the radio controller.
+ *
+ * NOTE: we setup whcrc->uwb_rc before calling uwb_rc_add(); in the
+ * IRQ handler we use that to determine if the hw is ready to
+ * handle events. Looks like a race condition, but it really is
+ * not.
+ */
+static
+int whcrc_probe(struct umc_dev *umc_dev)
+{
+ int result;
+ struct uwb_rc *uwb_rc;
+ struct whcrc *whcrc;
+ struct device *dev = &umc_dev->dev;
+
+ d_fnstart(3, dev, "(umc_dev %p)\n", umc_dev);
+ result = -ENOMEM;
+ uwb_rc = uwb_rc_alloc();
+ if (uwb_rc == NULL) {
+ dev_err(dev, "unable to allocate RC instance\n");
+ goto error_rc_alloc;
+ }
+ whcrc = kzalloc(sizeof(*whcrc), GFP_KERNEL);
+ if (whcrc == NULL) {
+ dev_err(dev, "unable to allocate WHC-RC instance\n");
+ goto error_alloc;
+ }
+ whcrc_init(whcrc);
+ whcrc->umc_dev = umc_dev;
+
+ result = whcrc_setup_rc_umc(whcrc);
+ if (result < 0) {
+ dev_err(dev, "Can't setup RC UMC interface: %d\n", result);
+ goto error_setup_rc_umc;
+ }
+ whcrc->uwb_rc = uwb_rc;
+
+ uwb_rc->owner = THIS_MODULE;
+ uwb_rc->cmd = whcrc_cmd;
+ uwb_rc->reset = whcrc_reset;
+ uwb_rc->start = whcrc_start_rc;
+ uwb_rc->stop = whcrc_stop_rc;
+
+ result = uwb_rc_add(uwb_rc, dev, whcrc);
+ if (result < 0)
+ goto error_rc_add;
+ umc_set_drvdata(umc_dev, whcrc);
+ d_fnend(3, dev, "(umc_dev %p) = 0\n", umc_dev);
+ return 0;
+
+error_rc_add:
+ whcrc_release_rc_umc(whcrc);
+error_setup_rc_umc:
+ kfree(whcrc);
+error_alloc:
+ uwb_rc_put(uwb_rc);
+error_rc_alloc:
+ d_fnend(3, dev, "(umc_dev %p) = %d\n", umc_dev, result);
+ return result;
+}
+
+/**
+ * Clean up the radio control resources
+ *
+ * When we up the command semaphore, everybody possibly held trying to
+ * execute a command should be granted entry and then they'll see the
+ * host is quiescing and up it (so it will chain to the next waiter).
+ * This should not happen (in any case), as we can only remove when
+ * there are no handles open...
+ */
+static void whcrc_remove(struct umc_dev *umc_dev)
+{
+ struct whcrc *whcrc = umc_get_drvdata(umc_dev);
+ struct uwb_rc *uwb_rc = whcrc->uwb_rc;
+
+ umc_set_drvdata(umc_dev, NULL);
+ uwb_rc_rm(uwb_rc);
+ whcrc_release_rc_umc(whcrc);
+ kfree(whcrc);
+ uwb_rc_put(uwb_rc);
+ d_printf(1, &umc_dev->dev, "freed whcrc %p\n", whcrc);
+}
+
+/* PCI device ID's that we handle [so it gets loaded] */
+static struct pci_device_id whcrc_id_table[] = {
+ { PCI_DEVICE_CLASS(PCI_CLASS_WIRELESS_WHCI, ~0) },
+ { /* empty last entry */ }
+};
+MODULE_DEVICE_TABLE(pci, whcrc_id_table);
+
+static struct umc_driver whcrc_driver = {
+ .name = "whc-rc",
+ .cap_id = UMC_CAP_ID_WHCI_RC,
+ .probe = whcrc_probe,
+ .remove = whcrc_remove,
+};
+
+static int __init whcrc_driver_init(void)
+{
+ return umc_driver_register(&whcrc_driver);
+}
+module_init(whcrc_driver_init);
+
+static void __exit whcrc_driver_exit(void)
+{
+ umc_driver_unregister(&whcrc_driver);
+}
+module_exit(whcrc_driver_exit);
+
+MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
+MODULE_DESCRIPTION("Wireless Host Controller Radio Control Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/uwb/whci.c b/drivers/uwb/whci.c
new file mode 100644
index 000000000000..3df2388f908f
--- /dev/null
+++ b/drivers/uwb/whci.c
@@ -0,0 +1,269 @@
+/*
+ * WHCI UWB Multi-interface Controller enumerator.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
+ *
+ * This file is released under the GNU GPL v2.
+ */
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/uwb/whci.h>
+#include <linux/uwb/umc.h>
+
+struct whci_card {
+ struct pci_dev *pci;
+ void __iomem *uwbbase;
+ u8 n_caps;
+ struct umc_dev *devs[0];
+};
+
+
+/* Fix faulty HW :( */
+static
+u64 whci_capdata_quirks(struct whci_card *card, u64 capdata)
+{
+ u64 capdata_orig = capdata;
+ struct pci_dev *pci_dev = card->pci;
+ if (pci_dev->vendor == PCI_VENDOR_ID_INTEL
+ && (pci_dev->device == 0x0c3b || pci_dev->device == 0004)
+ && pci_dev->class == 0x0d1010) {
+ switch (UWBCAPDATA_TO_CAP_ID(capdata)) {
+ /* WLP capability has 0x100 bytes of aperture */
+ case 0x80:
+ capdata |= 0x40 << 8; break;
+ /* WUSB capability has 0x80 bytes of aperture
+ * and ID is 1 */
+ case 0x02:
+ capdata &= ~0xffff;
+ capdata |= 0x2001;
+ break;
+ }
+ }
+ if (capdata_orig != capdata)
+ dev_warn(&pci_dev->dev,
+ "PCI v%04x d%04x c%06x#%02x: "
+ "corrected capdata from %016Lx to %016Lx\n",
+ pci_dev->vendor, pci_dev->device, pci_dev->class,
+ (unsigned)UWBCAPDATA_TO_CAP_ID(capdata),
+ (unsigned long long)capdata_orig,
+ (unsigned long long)capdata);
+ return capdata;
+}
+
+
+/**
+ * whci_wait_for - wait for a WHCI register to be set
+ *
+ * Polls (for at most @max_ms ms) until '*@reg & @mask == @result'.
+ */
+int whci_wait_for(struct device *dev, u32 __iomem *reg, u32 mask, u32 result,
+ unsigned long max_ms, const char *tag)
+{
+ unsigned t = 0;
+ u32 val;
+ for (;;) {
+ val = le_readl(reg);
+ if ((val & mask) == result)
+ break;
+ msleep(10);
+ if (t >= max_ms) {
+ dev_err(dev, "timed out waiting for %s ", tag);
+ return -ETIMEDOUT;
+ }
+ t += 10;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(whci_wait_for);
+
+
+/*
+ * NOTE: the capinfo and capdata registers are slightly different
+ * (size and cap-id fields). So for cap #0, we need to fill
+ * in. Size comes from the size of the register block
+ * (statically calculated); cap_id comes from nowhere, we use
+ * zero, that is reserved, for the radio controller, because
+ * none was defined at the spec level.
+ */
+static int whci_add_cap(struct whci_card *card, int n)
+{
+ struct umc_dev *umc;
+ u64 capdata;
+ int bar, err;
+
+ umc = umc_device_create(&card->pci->dev, n);
+ if (umc == NULL)
+ return -ENOMEM;
+
+ capdata = le_readq(card->uwbbase + UWBCAPDATA(n));
+
+ bar = UWBCAPDATA_TO_BAR(capdata) << 1;
+
+ capdata = whci_capdata_quirks(card, capdata);
+ /* Capability 0 is the radio controller. It's size is 32
+ * bytes (WHCI0.95[2.3, T2-9]). */
+ umc->version = UWBCAPDATA_TO_VERSION(capdata);
+ umc->cap_id = n == 0 ? 0 : UWBCAPDATA_TO_CAP_ID(capdata);
+ umc->bar = bar;
+ umc->resource.start = pci_resource_start(card->pci, bar)
+ + UWBCAPDATA_TO_OFFSET(capdata);
+ umc->resource.end = umc->resource.start
+ + (n == 0 ? 0x20 : UWBCAPDATA_TO_SIZE(capdata)) - 1;
+ umc->resource.name = umc->dev.bus_id;
+ umc->resource.flags = card->pci->resource[bar].flags;
+ umc->resource.parent = &card->pci->resource[bar];
+ umc->irq = card->pci->irq;
+
+ err = umc_device_register(umc);
+ if (err < 0)
+ goto error;
+ card->devs[n] = umc;
+ return 0;
+
+error:
+ kfree(umc);
+ return err;
+}
+
+static void whci_del_cap(struct whci_card *card, int n)
+{
+ struct umc_dev *umc = card->devs[n];
+
+ if (umc != NULL)
+ umc_device_unregister(umc);
+}
+
+static int whci_n_caps(struct pci_dev *pci)
+{
+ void __iomem *uwbbase;
+ u64 capinfo;
+
+ uwbbase = pci_iomap(pci, 0, 8);
+ if (!uwbbase)
+ return -ENOMEM;
+ capinfo = le_readq(uwbbase + UWBCAPINFO);
+ pci_iounmap(pci, uwbbase);
+
+ return UWBCAPINFO_TO_N_CAPS(capinfo);
+}
+
+static int whci_probe(struct pci_dev *pci, const struct pci_device_id *id)
+{
+ struct whci_card *card;
+ int err, n_caps, n;
+
+ err = pci_enable_device(pci);
+ if (err < 0)
+ goto error;
+ pci_enable_msi(pci);
+ pci_set_master(pci);
+ err = -ENXIO;
+ if (!pci_set_dma_mask(pci, DMA_64BIT_MASK))
+ pci_set_consistent_dma_mask(pci, DMA_64BIT_MASK);
+ else if (!pci_set_dma_mask(pci, DMA_32BIT_MASK))
+ pci_set_consistent_dma_mask(pci, DMA_32BIT_MASK);
+ else
+ goto error_dma;
+
+ err = n_caps = whci_n_caps(pci);
+ if (n_caps < 0)
+ goto error_ncaps;
+
+ err = -ENOMEM;
+ card = kzalloc(sizeof(struct whci_card)
+ + sizeof(struct whci_dev *) * (n_caps + 1),
+ GFP_KERNEL);
+ if (card == NULL)
+ goto error_kzalloc;
+ card->pci = pci;
+ card->n_caps = n_caps;
+
+ err = -EBUSY;
+ if (!request_mem_region(pci_resource_start(pci, 0),
+ UWBCAPDATA_SIZE(card->n_caps),
+ "whci (capability data)"))
+ goto error_request_memregion;
+ err = -ENOMEM;
+ card->uwbbase = pci_iomap(pci, 0, UWBCAPDATA_SIZE(card->n_caps));
+ if (!card->uwbbase)
+ goto error_iomap;
+
+ /* Add each capability. */
+ for (n = 0; n <= card->n_caps; n++) {
+ err = whci_add_cap(card, n);
+ if (err < 0 && n == 0) {
+ dev_err(&pci->dev, "cannot bind UWB radio controller:"
+ " %d\n", err);
+ goto error_bind;
+ }
+ if (err < 0)
+ dev_warn(&pci->dev, "warning: cannot bind capability "
+ "#%u: %d\n", n, err);
+ }
+ pci_set_drvdata(pci, card);
+ return 0;
+
+error_bind:
+ pci_iounmap(pci, card->uwbbase);
+error_iomap:
+ release_mem_region(pci_resource_start(pci, 0), UWBCAPDATA_SIZE(card->n_caps));
+error_request_memregion:
+ kfree(card);
+error_kzalloc:
+error_ncaps:
+error_dma:
+ pci_disable_msi(pci);
+ pci_disable_device(pci);
+error:
+ return err;
+}
+
+static void whci_remove(struct pci_dev *pci)
+{
+ struct whci_card *card = pci_get_drvdata(pci);
+ int n;
+
+ pci_set_drvdata(pci, NULL);
+ /* Unregister each capability in reverse (so the master device
+ * is unregistered last). */
+ for (n = card->n_caps; n >= 0 ; n--)
+ whci_del_cap(card, n);
+ pci_iounmap(pci, card->uwbbase);
+ release_mem_region(pci_resource_start(pci, 0), UWBCAPDATA_SIZE(card->n_caps));
+ kfree(card);
+ pci_disable_msi(pci);
+ pci_disable_device(pci);
+}
+
+static struct pci_device_id whci_id_table[] = {
+ { PCI_DEVICE_CLASS(PCI_CLASS_WIRELESS_WHCI, ~0) },
+ { 0 },
+};
+MODULE_DEVICE_TABLE(pci, whci_id_table);
+
+
+static struct pci_driver whci_driver = {
+ .name = "whci",
+ .id_table = whci_id_table,
+ .probe = whci_probe,
+ .remove = whci_remove,
+};
+
+static int __init whci_init(void)
+{
+ return pci_register_driver(&whci_driver);
+}
+
+static void __exit whci_exit(void)
+{
+ pci_unregister_driver(&whci_driver);
+}
+
+module_init(whci_init);
+module_exit(whci_exit);
+
+MODULE_DESCRIPTION("WHCI UWB Multi-interface Controller enumerator");
+MODULE_AUTHOR("Cambridge Silicon Radio Ltd.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/uwb/wlp/Makefile b/drivers/uwb/wlp/Makefile
new file mode 100644
index 000000000000..c72c11db5b1b
--- /dev/null
+++ b/drivers/uwb/wlp/Makefile
@@ -0,0 +1,10 @@
+obj-$(CONFIG_UWB_WLP) := wlp.o
+
+wlp-objs := \
+ driver.o \
+ eda.o \
+ messages.o \
+ sysfs.o \
+ txrx.o \
+ wlp-lc.o \
+ wss-lc.o
diff --git a/drivers/uwb/wlp/driver.c b/drivers/uwb/wlp/driver.c
new file mode 100644
index 000000000000..cb8d699b6a67
--- /dev/null
+++ b/drivers/uwb/wlp/driver.c
@@ -0,0 +1,43 @@
+/*
+ * WiMedia Logical Link Control Protocol (WLP)
+ *
+ * Copyright (C) 2007 Intel Corporation
+ * Reinette Chatre <reinette.chatre@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.
+ *
+ *
+ * Life cycle of WLP substack
+ *
+ * FIXME: Docs
+ */
+
+#include <linux/module.h>
+
+static int __init wlp_subsys_init(void)
+{
+ return 0;
+}
+module_init(wlp_subsys_init);
+
+static void __exit wlp_subsys_exit(void)
+{
+ return;
+}
+module_exit(wlp_subsys_exit);
+
+MODULE_AUTHOR("Reinette Chatre <reinette.chatre@intel.com>");
+MODULE_DESCRIPTION("WiMedia Logical Link Control Protocol (WLP)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/uwb/wlp/eda.c b/drivers/uwb/wlp/eda.c
new file mode 100644
index 000000000000..cdfe8dfc4340
--- /dev/null
+++ b/drivers/uwb/wlp/eda.c
@@ -0,0 +1,449 @@
+/*
+ * WUSB Wire Adapter: WLP interface
+ * Ethernet to device address cache
+ *
+ * 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.
+ *
+ *
+ * We need to be able to map ethernet addresses to device addresses
+ * and back because there is not explicit relationship between the eth
+ * addresses used in the ETH frames and the device addresses (no, it
+ * would not have been simpler to force as ETH address the MBOA MAC
+ * address...no, not at all :).
+ *
+ * A device has one MBOA MAC address and one device address. It is possible
+ * for a device to have more than one virtual MAC address (although a
+ * virtual address can be the same as the MBOA MAC address). The device
+ * address is guaranteed to be unique among the devices in the extended
+ * beacon group (see ECMA 17.1.1). We thus use the device address as index
+ * to this cache. We do allow searching based on virtual address as this
+ * is how Ethernet frames will be addressed.
+ *
+ * We need to support virtual EUI-48. Although, right now the virtual
+ * EUI-48 will always be the same as the MAC SAP address. The EDA cache
+ * entry thus contains a MAC SAP address as well as the virtual address
+ * (used to map the network stack address to a neighbor). When we move
+ * to support more than one virtual MAC on a host then this organization
+ * will have to change. Perhaps a neighbor has a list of WSSs, each with a
+ * tag and virtual EUI-48.
+ *
+ * On data transmission
+ * it is used to determine if the neighbor is connected and what WSS it
+ * belongs to. With this we know what tag to add to the WLP frame. Storing
+ * the WSS in the EDA cache may be overkill because we only support one
+ * WSS. Hopefully we will support more than one WSS at some point.
+ * On data reception it is used to determine the WSS based on
+ * the tag and address of the transmitting neighbor.
+ */
+
+#define D_LOCAL 5
+#include <linux/netdevice.h>
+#include <linux/uwb/debug.h>
+#include <linux/etherdevice.h>
+#include <linux/wlp.h>
+#include "wlp-internal.h"
+
+
+/* FIXME: cache is not purged, only on device close */
+
+/* FIXME: does not scale, change to dynamic array */
+
+/*
+ * Initialize the EDA cache
+ *
+ * @returns 0 if ok, < 0 errno code on error
+ *
+ * Call when the interface is being brought up
+ *
+ * NOTE: Keep it as a separate function as the implementation will
+ * change and be more complex.
+ */
+void wlp_eda_init(struct wlp_eda *eda)
+{
+ INIT_LIST_HEAD(&eda->cache);
+ spin_lock_init(&eda->lock);
+}
+
+/*
+ * Release the EDA cache
+ *
+ * @returns 0 if ok, < 0 errno code on error
+ *
+ * Called when the interface is brought down
+ */
+void wlp_eda_release(struct wlp_eda *eda)
+{
+ unsigned long flags;
+ struct wlp_eda_node *itr, *next;
+
+ spin_lock_irqsave(&eda->lock, flags);
+ list_for_each_entry_safe(itr, next, &eda->cache, list_node) {
+ list_del(&itr->list_node);
+ kfree(itr);
+ }
+ spin_unlock_irqrestore(&eda->lock, flags);
+}
+
+/*
+ * Add an address mapping
+ *
+ * @returns 0 if ok, < 0 errno code on error
+ *
+ * An address mapping is initially created when the neighbor device is seen
+ * for the first time (it is "onair"). At this time the neighbor is not
+ * connected or associated with a WSS so we only populate the Ethernet and
+ * Device address fields.
+ *
+ */
+int wlp_eda_create_node(struct wlp_eda *eda,
+ const unsigned char eth_addr[ETH_ALEN],
+ const struct uwb_dev_addr *dev_addr)
+{
+ int result = 0;
+ struct wlp_eda_node *itr;
+ unsigned long flags;
+
+ BUG_ON(dev_addr == NULL || eth_addr == NULL);
+ spin_lock_irqsave(&eda->lock, flags);
+ list_for_each_entry(itr, &eda->cache, list_node) {
+ if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
+ printk(KERN_ERR "EDA cache already contains entry "
+ "for neighbor %02x:%02x\n",
+ dev_addr->data[1], dev_addr->data[0]);
+ result = -EEXIST;
+ goto out_unlock;
+ }
+ }
+ itr = kzalloc(sizeof(*itr), GFP_ATOMIC);
+ if (itr != NULL) {
+ memcpy(itr->eth_addr, eth_addr, sizeof(itr->eth_addr));
+ itr->dev_addr = *dev_addr;
+ list_add(&itr->list_node, &eda->cache);
+ } else
+ result = -ENOMEM;
+out_unlock:
+ spin_unlock_irqrestore(&eda->lock, flags);
+ return result;
+}
+
+/*
+ * Remove entry from EDA cache
+ *
+ * This is done when the device goes off air.
+ */
+void wlp_eda_rm_node(struct wlp_eda *eda, const struct uwb_dev_addr *dev_addr)
+{
+ struct wlp_eda_node *itr, *next;
+ unsigned long flags;
+
+ spin_lock_irqsave(&eda->lock, flags);
+ list_for_each_entry_safe(itr, next, &eda->cache, list_node) {
+ if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
+ list_del(&itr->list_node);
+ kfree(itr);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&eda->lock, flags);
+}
+
+/*
+ * Update an address mapping
+ *
+ * @returns 0 if ok, < 0 errno code on error
+ */
+int wlp_eda_update_node(struct wlp_eda *eda,
+ const struct uwb_dev_addr *dev_addr,
+ struct wlp_wss *wss,
+ const unsigned char virt_addr[ETH_ALEN],
+ const u8 tag, const enum wlp_wss_connect state)
+{
+ int result = -ENOENT;
+ struct wlp_eda_node *itr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&eda->lock, flags);
+ list_for_each_entry(itr, &eda->cache, list_node) {
+ if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
+ /* Found it, update it */
+ itr->wss = wss;
+ memcpy(itr->virt_addr, virt_addr,
+ sizeof(itr->virt_addr));
+ itr->tag = tag;
+ itr->state = state;
+ result = 0;
+ goto out_unlock;
+ }
+ }
+ /* Not found */
+out_unlock:
+ spin_unlock_irqrestore(&eda->lock, flags);
+ return result;
+}
+
+/*
+ * Update only state field of an address mapping
+ *
+ * @returns 0 if ok, < 0 errno code on error
+ */
+int wlp_eda_update_node_state(struct wlp_eda *eda,
+ const struct uwb_dev_addr *dev_addr,
+ const enum wlp_wss_connect state)
+{
+ int result = -ENOENT;
+ struct wlp_eda_node *itr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&eda->lock, flags);
+ list_for_each_entry(itr, &eda->cache, list_node) {
+ if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
+ /* Found it, update it */
+ itr->state = state;
+ result = 0;
+ goto out_unlock;
+ }
+ }
+ /* Not found */
+out_unlock:
+ spin_unlock_irqrestore(&eda->lock, flags);
+ return result;
+}
+
+/*
+ * Return contents of EDA cache entry
+ *
+ * @dev_addr: index to EDA cache
+ * @eda_entry: pointer to where contents of EDA cache will be copied
+ */
+int wlp_copy_eda_node(struct wlp_eda *eda, struct uwb_dev_addr *dev_addr,
+ struct wlp_eda_node *eda_entry)
+{
+ int result = -ENOENT;
+ struct wlp_eda_node *itr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&eda->lock, flags);
+ list_for_each_entry(itr, &eda->cache, list_node) {
+ if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
+ *eda_entry = *itr;
+ result = 0;
+ goto out_unlock;
+ }
+ }
+ /* Not found */
+out_unlock:
+ spin_unlock_irqrestore(&eda->lock, flags);
+ return result;
+}
+
+/*
+ * Execute function for every element in the cache
+ *
+ * @function: function to execute on element of cache (must be atomic)
+ * @priv: private data of function
+ * @returns: result of first function that failed, or last function
+ * executed if no function failed.
+ *
+ * Stop executing when function returns error for any element in cache.
+ *
+ * IMPORTANT: We are using a spinlock here: the function executed on each
+ * element has to be atomic.
+ */
+int wlp_eda_for_each(struct wlp_eda *eda, wlp_eda_for_each_f function,
+ void *priv)
+{
+ int result = 0;
+ struct wlp *wlp = container_of(eda, struct wlp, eda);
+ struct wlp_eda_node *entry;
+ unsigned long flags;
+
+ spin_lock_irqsave(&eda->lock, flags);
+ list_for_each_entry(entry, &eda->cache, list_node) {
+ result = (*function)(wlp, entry, priv);
+ if (result < 0)
+ break;
+ }
+ spin_unlock_irqrestore(&eda->lock, flags);
+ return result;
+}
+
+/*
+ * Execute function for single element in the cache (return dev addr)
+ *
+ * @virt_addr: index into EDA cache used to determine which element to
+ * execute the function on
+ * @dev_addr: device address of element in cache will be returned using
+ * @dev_addr
+ * @function: function to execute on element of cache (must be atomic)
+ * @priv: private data of function
+ * @returns: result of function
+ *
+ * IMPORTANT: We are using a spinlock here: the function executed on the
+ * element has to be atomic.
+ */
+int wlp_eda_for_virtual(struct wlp_eda *eda,
+ const unsigned char virt_addr[ETH_ALEN],
+ struct uwb_dev_addr *dev_addr,
+ wlp_eda_for_each_f function,
+ void *priv)
+{
+ int result = 0;
+ struct wlp *wlp = container_of(eda, struct wlp, eda);
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct wlp_eda_node *itr;
+ unsigned long flags;
+ int found = 0;
+
+ spin_lock_irqsave(&eda->lock, flags);
+ list_for_each_entry(itr, &eda->cache, list_node) {
+ if (!memcmp(itr->virt_addr, virt_addr,
+ sizeof(itr->virt_addr))) {
+ d_printf(6, dev, "EDA: looking for "
+ "%02x:%02x:%02x:%02x:%02x:%02x hit %02x:%02x "
+ "wss %p tag 0x%02x state %u\n",
+ virt_addr[0], virt_addr[1],
+ virt_addr[2], virt_addr[3],
+ virt_addr[4], virt_addr[5],
+ itr->dev_addr.data[1],
+ itr->dev_addr.data[0], itr->wss,
+ itr->tag, itr->state);
+ result = (*function)(wlp, itr, priv);
+ *dev_addr = itr->dev_addr;
+ found = 1;
+ break;
+ } else
+ d_printf(6, dev, "EDA: looking for "
+ "%02x:%02x:%02x:%02x:%02x:%02x "
+ "against "
+ "%02x:%02x:%02x:%02x:%02x:%02x miss\n",
+ virt_addr[0], virt_addr[1],
+ virt_addr[2], virt_addr[3],
+ virt_addr[4], virt_addr[5],
+ itr->virt_addr[0], itr->virt_addr[1],
+ itr->virt_addr[2], itr->virt_addr[3],
+ itr->virt_addr[4], itr->virt_addr[5]);
+ }
+ if (!found) {
+ if (printk_ratelimit())
+ dev_err(dev, "EDA: Eth addr %02x:%02x:%02x"
+ ":%02x:%02x:%02x not found.\n",
+ virt_addr[0], virt_addr[1],
+ virt_addr[2], virt_addr[3],
+ virt_addr[4], virt_addr[5]);
+ result = -ENODEV;
+ }
+ spin_unlock_irqrestore(&eda->lock, flags);
+ return result;
+}
+
+static const char *__wlp_wss_connect_state[] = { "WLP_WSS_UNCONNECTED",
+ "WLP_WSS_CONNECTED",
+ "WLP_WSS_CONNECT_FAILED",
+};
+
+static const char *wlp_wss_connect_state_str(unsigned id)
+{
+ if (id >= ARRAY_SIZE(__wlp_wss_connect_state))
+ return "unknown WSS connection state";
+ return __wlp_wss_connect_state[id];
+}
+
+/*
+ * View EDA cache from user space
+ *
+ * A debugging feature to give user visibility into the EDA cache. Also
+ * used to display members of WSS to user (called from wlp_wss_members_show())
+ */
+ssize_t wlp_eda_show(struct wlp *wlp, char *buf)
+{
+ ssize_t result = 0;
+ struct wlp_eda_node *entry;
+ unsigned long flags;
+ struct wlp_eda *eda = &wlp->eda;
+ spin_lock_irqsave(&eda->lock, flags);
+ result = scnprintf(buf, PAGE_SIZE, "#eth_addr dev_addr wss_ptr "
+ "tag state virt_addr\n");
+ list_for_each_entry(entry, &eda->cache, list_node) {
+ result += scnprintf(buf + result, PAGE_SIZE - result,
+ "%02x:%02x:%02x:%02x:%02x:%02x %02x:%02x "
+ "%p 0x%02x %s "
+ "%02x:%02x:%02x:%02x:%02x:%02x\n",
+ entry->eth_addr[0], entry->eth_addr[1],
+ entry->eth_addr[2], entry->eth_addr[3],
+ entry->eth_addr[4], entry->eth_addr[5],
+ entry->dev_addr.data[1],
+ entry->dev_addr.data[0], entry->wss,
+ entry->tag,
+ wlp_wss_connect_state_str(entry->state),
+ entry->virt_addr[0], entry->virt_addr[1],
+ entry->virt_addr[2], entry->virt_addr[3],
+ entry->virt_addr[4], entry->virt_addr[5]);
+ if (result >= PAGE_SIZE)
+ break;
+ }
+ spin_unlock_irqrestore(&eda->lock, flags);
+ return result;
+}
+EXPORT_SYMBOL_GPL(wlp_eda_show);
+
+/*
+ * Add new EDA cache entry based on user input in sysfs
+ *
+ * Should only be used for debugging.
+ *
+ * The WSS is assumed to be the only WSS supported. This needs to be
+ * redesigned when we support more than one WSS.
+ */
+ssize_t wlp_eda_store(struct wlp *wlp, const char *buf, size_t size)
+{
+ ssize_t result;
+ struct wlp_eda *eda = &wlp->eda;
+ u8 eth_addr[6];
+ struct uwb_dev_addr dev_addr;
+ u8 tag;
+ unsigned state;
+
+ result = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx "
+ "%02hhx:%02hhx %02hhx %u\n",
+ &eth_addr[0], &eth_addr[1],
+ &eth_addr[2], &eth_addr[3],
+ &eth_addr[4], &eth_addr[5],
+ &dev_addr.data[1], &dev_addr.data[0], &tag, &state);
+ switch (result) {
+ case 6: /* no dev addr specified -- remove entry NOT IMPLEMENTED */
+ /*result = wlp_eda_rm(eda, eth_addr, &dev_addr);*/
+ result = -ENOSYS;
+ break;
+ case 10:
+ state = state >= 1 ? 1 : 0;
+ result = wlp_eda_create_node(eda, eth_addr, &dev_addr);
+ if (result < 0 && result != -EEXIST)
+ goto error;
+ /* Set virtual addr to be same as MAC */
+ result = wlp_eda_update_node(eda, &dev_addr, &wlp->wss,
+ eth_addr, tag, state);
+ if (result < 0)
+ goto error;
+ break;
+ default: /* bad format */
+ result = -EINVAL;
+ }
+error:
+ return result < 0 ? result : size;
+}
+EXPORT_SYMBOL_GPL(wlp_eda_store);
diff --git a/drivers/uwb/wlp/messages.c b/drivers/uwb/wlp/messages.c
new file mode 100644
index 000000000000..a64cb8241713
--- /dev/null
+++ b/drivers/uwb/wlp/messages.c
@@ -0,0 +1,1946 @@
+/*
+ * WiMedia Logical Link Control Protocol (WLP)
+ * Message construction and parsing
+ *
+ * Copyright (C) 2007 Intel Corporation
+ * Reinette Chatre <reinette.chatre@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/wlp.h>
+#define D_LOCAL 6
+#include <linux/uwb/debug.h>
+#include "wlp-internal.h"
+
+static
+const char *__wlp_assoc_frame[] = {
+ [WLP_ASSOC_D1] = "WLP_ASSOC_D1",
+ [WLP_ASSOC_D2] = "WLP_ASSOC_D2",
+ [WLP_ASSOC_M1] = "WLP_ASSOC_M1",
+ [WLP_ASSOC_M2] = "WLP_ASSOC_M2",
+ [WLP_ASSOC_M3] = "WLP_ASSOC_M3",
+ [WLP_ASSOC_M4] = "WLP_ASSOC_M4",
+ [WLP_ASSOC_M5] = "WLP_ASSOC_M5",
+ [WLP_ASSOC_M6] = "WLP_ASSOC_M6",
+ [WLP_ASSOC_M7] = "WLP_ASSOC_M7",
+ [WLP_ASSOC_M8] = "WLP_ASSOC_M8",
+ [WLP_ASSOC_F0] = "WLP_ASSOC_F0",
+ [WLP_ASSOC_E1] = "WLP_ASSOC_E1",
+ [WLP_ASSOC_E2] = "WLP_ASSOC_E2",
+ [WLP_ASSOC_C1] = "WLP_ASSOC_C1",
+ [WLP_ASSOC_C2] = "WLP_ASSOC_C2",
+ [WLP_ASSOC_C3] = "WLP_ASSOC_C3",
+ [WLP_ASSOC_C4] = "WLP_ASSOC_C4",
+};
+
+static const char *wlp_assoc_frame_str(unsigned id)
+{
+ if (id >= ARRAY_SIZE(__wlp_assoc_frame))
+ return "unknown association frame";
+ return __wlp_assoc_frame[id];
+}
+
+static const char *__wlp_assc_error[] = {
+ "none",
+ "Authenticator Failure",
+ "Rogue activity suspected",
+ "Device busy",
+ "Setup Locked",
+ "Registrar not ready",
+ "Invalid WSS selection",
+ "Message timeout",
+ "Enrollment session timeout",
+ "Device password invalid",
+ "Unsupported version",
+ "Internal error",
+ "Undefined error",
+ "Numeric comparison failure",
+ "Waiting for user input",
+};
+
+static const char *wlp_assc_error_str(unsigned id)
+{
+ if (id >= ARRAY_SIZE(__wlp_assc_error))
+ return "unknown WLP association error";
+ return __wlp_assc_error[id];
+}
+
+static inline void wlp_set_attr_hdr(struct wlp_attr_hdr *hdr, unsigned type,
+ size_t len)
+{
+ hdr->type = cpu_to_le16(type);
+ hdr->length = cpu_to_le16(len);
+}
+
+/*
+ * Populate fields of a constant sized attribute
+ *
+ * @returns: total size of attribute including size of new value
+ *
+ * We have two instances of this function (wlp_pset and wlp_set): one takes
+ * the value as a parameter, the other takes a pointer to the value as
+ * parameter. They thus only differ in how the value is assigned to the
+ * attribute.
+ *
+ * We use sizeof(*attr) - sizeof(struct wlp_attr_hdr) instead of
+ * sizeof(type) to be able to use this same code for the structures that
+ * contain 8bit enum values and be able to deal with pointer types.
+ */
+#define wlp_set(type, type_code, name) \
+static size_t wlp_set_##name(struct wlp_attr_##name *attr, type value) \
+{ \
+ d_fnstart(6, NULL, "(attribute %p)\n", attr); \
+ wlp_set_attr_hdr(&attr->hdr, type_code, \
+ sizeof(*attr) - sizeof(struct wlp_attr_hdr)); \
+ attr->name = value; \
+ d_dump(6, NULL, attr, sizeof(*attr)); \
+ d_fnend(6, NULL, "(attribute %p)\n", attr); \
+ return sizeof(*attr); \
+}
+
+#define wlp_pset(type, type_code, name) \
+static size_t wlp_set_##name(struct wlp_attr_##name *attr, type value) \
+{ \
+ d_fnstart(6, NULL, "(attribute %p)\n", attr); \
+ wlp_set_attr_hdr(&attr->hdr, type_code, \
+ sizeof(*attr) - sizeof(struct wlp_attr_hdr)); \
+ attr->name = *value; \
+ d_dump(6, NULL, attr, sizeof(*attr)); \
+ d_fnend(6, NULL, "(attribute %p)\n", attr); \
+ return sizeof(*attr); \
+}
+
+/**
+ * Populate fields of a variable attribute
+ *
+ * @returns: total size of attribute including size of new value
+ *
+ * Provided with a pointer to the memory area reserved for the
+ * attribute structure, the field is populated with the value. The
+ * reserved memory has to contain enough space for the value.
+ */
+#define wlp_vset(type, type_code, name) \
+static size_t wlp_set_##name(struct wlp_attr_##name *attr, type value, \
+ size_t len) \
+{ \
+ d_fnstart(6, NULL, "(attribute %p)\n", attr); \
+ wlp_set_attr_hdr(&attr->hdr, type_code, len); \
+ memcpy(attr->name, value, len); \
+ d_dump(6, NULL, attr, sizeof(*attr) + len); \
+ d_fnend(6, NULL, "(attribute %p)\n", attr); \
+ return sizeof(*attr) + len; \
+}
+
+wlp_vset(char *, WLP_ATTR_DEV_NAME, dev_name)
+wlp_vset(char *, WLP_ATTR_MANUF, manufacturer)
+wlp_set(enum wlp_assoc_type, WLP_ATTR_MSG_TYPE, msg_type)
+wlp_vset(char *, WLP_ATTR_MODEL_NAME, model_name)
+wlp_vset(char *, WLP_ATTR_MODEL_NR, model_nr)
+wlp_vset(char *, WLP_ATTR_SERIAL, serial)
+wlp_vset(char *, WLP_ATTR_WSS_NAME, wss_name)
+wlp_pset(struct wlp_uuid *, WLP_ATTR_UUID_E, uuid_e)
+wlp_pset(struct wlp_uuid *, WLP_ATTR_UUID_R, uuid_r)
+wlp_pset(struct wlp_uuid *, WLP_ATTR_WSSID, wssid)
+wlp_pset(struct wlp_dev_type *, WLP_ATTR_PRI_DEV_TYPE, prim_dev_type)
+/*wlp_pset(struct wlp_dev_type *, WLP_ATTR_SEC_DEV_TYPE, sec_dev_type)*/
+wlp_set(u8, WLP_ATTR_WLP_VER, version)
+wlp_set(enum wlp_assc_error, WLP_ATTR_WLP_ASSC_ERR, wlp_assc_err)
+wlp_set(enum wlp_wss_sel_mthd, WLP_ATTR_WSS_SEL_MTHD, wss_sel_mthd)
+wlp_set(u8, WLP_ATTR_ACC_ENRL, accept_enrl)
+wlp_set(u8, WLP_ATTR_WSS_SEC_STAT, wss_sec_status)
+wlp_pset(struct uwb_mac_addr *, WLP_ATTR_WSS_BCAST, wss_bcast)
+wlp_pset(struct wlp_nonce *, WLP_ATTR_ENRL_NONCE, enonce)
+wlp_pset(struct wlp_nonce *, WLP_ATTR_REG_NONCE, rnonce)
+wlp_set(u8, WLP_ATTR_WSS_TAG, wss_tag)
+wlp_pset(struct uwb_mac_addr *, WLP_ATTR_WSS_VIRT, wss_virt)
+
+/**
+ * Fill in the WSS information attributes
+ *
+ * We currently only support one WSS, and this is assumed in this function
+ * that can populate only one WSS information attribute.
+ */
+static size_t wlp_set_wss_info(struct wlp_attr_wss_info *attr,
+ struct wlp_wss *wss)
+{
+ size_t datalen;
+ void *ptr = attr->wss_info;
+ size_t used = sizeof(*attr);
+ d_fnstart(6, NULL, "(attribute %p)\n", attr);
+ datalen = sizeof(struct wlp_wss_info) + strlen(wss->name);
+ wlp_set_attr_hdr(&attr->hdr, WLP_ATTR_WSS_INFO, datalen);
+ used = wlp_set_wssid(ptr, &wss->wssid);
+ used += wlp_set_wss_name(ptr + used, wss->name, strlen(wss->name));
+ used += wlp_set_accept_enrl(ptr + used, wss->accept_enroll);
+ used += wlp_set_wss_sec_status(ptr + used, wss->secure_status);
+ used += wlp_set_wss_bcast(ptr + used, &wss->bcast);
+ d_dump(6, NULL, attr, sizeof(*attr) + datalen);
+ d_fnend(6, NULL, "(attribute %p, used %d)\n",
+ attr, (int)(sizeof(*attr) + used));
+ return sizeof(*attr) + used;
+}
+
+/**
+ * Verify attribute header
+ *
+ * @hdr: Pointer to attribute header that will be verified.
+ * @type: Expected attribute type.
+ * @len: Expected length of attribute value (excluding header).
+ *
+ * Most attribute values have a known length even when they do have a
+ * length field. This knowledge can be used via this function to verify
+ * that the length field matches the expected value.
+ */
+static int wlp_check_attr_hdr(struct wlp *wlp, struct wlp_attr_hdr *hdr,
+ enum wlp_attr_type type, unsigned len)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+
+ if (le16_to_cpu(hdr->type) != type) {
+ dev_err(dev, "WLP: unexpected header type. Expected "
+ "%u, got %u.\n", type, le16_to_cpu(hdr->type));
+ return -EINVAL;
+ }
+ if (le16_to_cpu(hdr->length) != len) {
+ dev_err(dev, "WLP: unexpected length in header. Expected "
+ "%u, got %u.\n", len, le16_to_cpu(hdr->length));
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * Check if header of WSS information attribute valid
+ *
+ * @returns: length of WSS attributes (value of length attribute field) if
+ * valid WSS information attribute found
+ * -ENODATA if no WSS information attribute found
+ * -EIO other error occured
+ *
+ * The WSS information attribute is optional. The function will be provided
+ * with a pointer to data that could _potentially_ be a WSS information
+ * attribute. If a valid WSS information attribute is found it will return
+ * 0, if no WSS information attribute is found it will return -ENODATA, and
+ * another error will be returned if it is a WSS information attribute, but
+ * some parsing failure occured.
+ */
+static int wlp_check_wss_info_attr_hdr(struct wlp *wlp,
+ struct wlp_attr_hdr *hdr, size_t buflen)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ size_t len;
+ int result = 0;
+
+ if (buflen < sizeof(*hdr)) {
+ dev_err(dev, "WLP: Not enough space in buffer to parse"
+ " WSS information attribute header.\n");
+ result = -EIO;
+ goto out;
+ }
+ if (le16_to_cpu(hdr->type) != WLP_ATTR_WSS_INFO) {
+ /* WSS information is optional */
+ result = -ENODATA;
+ goto out;
+ }
+ len = le16_to_cpu(hdr->length);
+ if (buflen < sizeof(*hdr) + len) {
+ dev_err(dev, "WLP: Not enough space in buffer to parse "
+ "variable data. Got %d, expected %d.\n",
+ (int)buflen, (int)(sizeof(*hdr) + len));
+ result = -EIO;
+ goto out;
+ }
+ result = len;
+out:
+ return result;
+}
+
+
+/**
+ * Get value of attribute from fixed size attribute field.
+ *
+ * @attr: Pointer to attribute field.
+ * @value: Pointer to variable in which attribute value will be placed.
+ * @buflen: Size of buffer in which attribute field (including header)
+ * can be found.
+ * @returns: Amount of given buffer consumed by parsing for this attribute.
+ *
+ * The size and type of the value is known by the type of the attribute.
+ */
+#define wlp_get(type, type_code, name) \
+ssize_t wlp_get_##name(struct wlp *wlp, struct wlp_attr_##name *attr, \
+ type *value, ssize_t buflen) \
+{ \
+ struct device *dev = &wlp->rc->uwb_dev.dev; \
+ if (buflen < 0) \
+ return -EINVAL; \
+ if (buflen < sizeof(*attr)) { \
+ dev_err(dev, "WLP: Not enough space in buffer to parse" \
+ " attribute field. Need %d, received %zu\n", \
+ (int)sizeof(*attr), buflen); \
+ return -EIO; \
+ } \
+ if (wlp_check_attr_hdr(wlp, &attr->hdr, type_code, \
+ sizeof(attr->name)) < 0) { \
+ dev_err(dev, "WLP: Header verification failed. \n"); \
+ return -EINVAL; \
+ } \
+ *value = attr->name; \
+ return sizeof(*attr); \
+}
+
+#define wlp_get_sparse(type, type_code, name) \
+ static wlp_get(type, type_code, name)
+
+/**
+ * Get value of attribute from variable sized attribute field.
+ *
+ * @max: The maximum size of this attribute. This value is dictated by
+ * the maximum value from the WLP specification.
+ *
+ * @attr: Pointer to attribute field.
+ * @value: Pointer to variable that will contain the value. The memory
+ * must already have been allocated for this value.
+ * @buflen: Size of buffer in which attribute field (including header)
+ * can be found.
+ * @returns: Amount of given bufferconsumed by parsing for this attribute.
+ */
+#define wlp_vget(type_val, type_code, name, max) \
+static ssize_t wlp_get_##name(struct wlp *wlp, \
+ struct wlp_attr_##name *attr, \
+ type_val *value, ssize_t buflen) \
+{ \
+ struct device *dev = &wlp->rc->uwb_dev.dev; \
+ size_t len; \
+ if (buflen < 0) \
+ return -EINVAL; \
+ if (buflen < sizeof(*attr)) { \
+ dev_err(dev, "WLP: Not enough space in buffer to parse" \
+ " header.\n"); \
+ return -EIO; \
+ } \
+ if (le16_to_cpu(attr->hdr.type) != type_code) { \
+ dev_err(dev, "WLP: Unexpected attribute type. Got %u, " \
+ "expected %u.\n", le16_to_cpu(attr->hdr.type), \
+ type_code); \
+ return -EINVAL; \
+ } \
+ len = le16_to_cpu(attr->hdr.length); \
+ if (len > max) { \
+ dev_err(dev, "WLP: Attribute larger than maximum " \
+ "allowed. Received %zu, max is %d.\n", len, \
+ (int)max); \
+ return -EFBIG; \
+ } \
+ if (buflen < sizeof(*attr) + len) { \
+ dev_err(dev, "WLP: Not enough space in buffer to parse "\
+ "variable data.\n"); \
+ return -EIO; \
+ } \
+ memcpy(value, (void *) attr + sizeof(*attr), len); \
+ return sizeof(*attr) + len; \
+}
+
+wlp_get(u8, WLP_ATTR_WLP_VER, version)
+wlp_get_sparse(enum wlp_wss_sel_mthd, WLP_ATTR_WSS_SEL_MTHD, wss_sel_mthd)
+wlp_get_sparse(struct wlp_dev_type, WLP_ATTR_PRI_DEV_TYPE, prim_dev_type)
+wlp_get_sparse(enum wlp_assc_error, WLP_ATTR_WLP_ASSC_ERR, wlp_assc_err)
+wlp_get_sparse(struct wlp_uuid, WLP_ATTR_UUID_E, uuid_e)
+wlp_get_sparse(struct wlp_uuid, WLP_ATTR_UUID_R, uuid_r)
+wlp_get(struct wlp_uuid, WLP_ATTR_WSSID, wssid)
+wlp_get_sparse(u8, WLP_ATTR_ACC_ENRL, accept_enrl)
+wlp_get_sparse(u8, WLP_ATTR_WSS_SEC_STAT, wss_sec_status)
+wlp_get_sparse(struct uwb_mac_addr, WLP_ATTR_WSS_BCAST, wss_bcast)
+wlp_get_sparse(u8, WLP_ATTR_WSS_TAG, wss_tag)
+wlp_get_sparse(struct uwb_mac_addr, WLP_ATTR_WSS_VIRT, wss_virt)
+wlp_get_sparse(struct wlp_nonce, WLP_ATTR_ENRL_NONCE, enonce)
+wlp_get_sparse(struct wlp_nonce, WLP_ATTR_REG_NONCE, rnonce)
+
+/* The buffers for the device info attributes can be found in the
+ * wlp_device_info struct. These buffers contain one byte more than the
+ * max allowed by the spec - this is done to be able to add the
+ * terminating \0 for user display. This terminating byte is not required
+ * in the actual attribute field (because it has a length field) so the
+ * maximum allowed for this value is one less than its size in the
+ * structure.
+ */
+wlp_vget(char, WLP_ATTR_WSS_NAME, wss_name,
+ FIELD_SIZEOF(struct wlp_wss, name) - 1)
+wlp_vget(char, WLP_ATTR_DEV_NAME, dev_name,
+ FIELD_SIZEOF(struct wlp_device_info, name) - 1)
+wlp_vget(char, WLP_ATTR_MANUF, manufacturer,
+ FIELD_SIZEOF(struct wlp_device_info, manufacturer) - 1)
+wlp_vget(char, WLP_ATTR_MODEL_NAME, model_name,
+ FIELD_SIZEOF(struct wlp_device_info, model_name) - 1)
+wlp_vget(char, WLP_ATTR_MODEL_NR, model_nr,
+ FIELD_SIZEOF(struct wlp_device_info, model_nr) - 1)
+wlp_vget(char, WLP_ATTR_SERIAL, serial,
+ FIELD_SIZEOF(struct wlp_device_info, serial) - 1)
+
+/**
+ * Retrieve WSS Name, Accept enroll, Secure status, Broadcast from WSS info
+ *
+ * @attr: pointer to WSS name attribute in WSS information attribute field
+ * @info: structure that will be populated with data from WSS information
+ * field (WSS name, Accept enroll, secure status, broadcast address)
+ * @buflen: size of buffer
+ *
+ * Although the WSSID attribute forms part of the WSS info attribute it is
+ * retrieved separately and stored in a different location.
+ */
+static ssize_t wlp_get_wss_info_attrs(struct wlp *wlp,
+ struct wlp_attr_hdr *attr,
+ struct wlp_wss_tmp_info *info,
+ ssize_t buflen)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ void *ptr = attr;
+ size_t used = 0;
+ ssize_t result = -EINVAL;
+
+ d_printf(6, dev, "WLP: WSS info: Retrieving WSS name\n");
+ result = wlp_get_wss_name(wlp, ptr, info->name, buflen);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WSS name from "
+ "WSS info in D2 message.\n");
+ goto error_parse;
+ }
+ used += result;
+ d_printf(6, dev, "WLP: WSS info: Retrieving accept enroll\n");
+ result = wlp_get_accept_enrl(wlp, ptr + used, &info->accept_enroll,
+ buflen - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain accepting "
+ "enrollment from WSS info in D2 message.\n");
+ goto error_parse;
+ }
+ if (info->accept_enroll != 0 && info->accept_enroll != 1) {
+ dev_err(dev, "WLP: invalid value for accepting "
+ "enrollment in D2 message.\n");
+ result = -EINVAL;
+ goto error_parse;
+ }
+ used += result;
+ d_printf(6, dev, "WLP: WSS info: Retrieving secure status\n");
+ result = wlp_get_wss_sec_status(wlp, ptr + used, &info->sec_status,
+ buflen - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain secure "
+ "status from WSS info in D2 message.\n");
+ goto error_parse;
+ }
+ if (info->sec_status != 0 && info->sec_status != 1) {
+ dev_err(dev, "WLP: invalid value for secure "
+ "status in D2 message.\n");
+ result = -EINVAL;
+ goto error_parse;
+ }
+ used += result;
+ d_printf(6, dev, "WLP: WSS info: Retrieving broadcast\n");
+ result = wlp_get_wss_bcast(wlp, ptr + used, &info->bcast,
+ buflen - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain broadcast "
+ "address from WSS info in D2 message.\n");
+ goto error_parse;
+ }
+ used += result;
+ result = used;
+error_parse:
+ return result;
+}
+
+/**
+ * Create a new WSSID entry for the neighbor, allocate temporary storage
+ *
+ * Each neighbor can have many WSS active. We maintain a list of WSSIDs
+ * advertised by neighbor. During discovery we also cache information about
+ * these WSS in temporary storage.
+ *
+ * The temporary storage will be removed after it has been used (eg.
+ * displayed to user), the wssid element will be removed from the list when
+ * the neighbor is rediscovered or when it disappears.
+ */
+static struct wlp_wssid_e *wlp_create_wssid_e(struct wlp *wlp,
+ struct wlp_neighbor_e *neighbor)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct wlp_wssid_e *wssid_e;
+
+ wssid_e = kzalloc(sizeof(*wssid_e), GFP_KERNEL);
+ if (wssid_e == NULL) {
+ dev_err(dev, "WLP: unable to allocate memory "
+ "for WSS information.\n");
+ goto error_alloc;
+ }
+ wssid_e->info = kzalloc(sizeof(struct wlp_wss_tmp_info), GFP_KERNEL);
+ if (wssid_e->info == NULL) {
+ dev_err(dev, "WLP: unable to allocate memory "
+ "for temporary WSS information.\n");
+ kfree(wssid_e);
+ wssid_e = NULL;
+ goto error_alloc;
+ }
+ list_add(&wssid_e->node, &neighbor->wssid);
+error_alloc:
+ return wssid_e;
+}
+
+/**
+ * Parse WSS information attribute
+ *
+ * @attr: pointer to WSS information attribute header
+ * @buflen: size of buffer in which WSS information attribute appears
+ * @wssid: will place wssid from WSS info attribute in this location
+ * @wss_info: will place other information from WSS information attribute
+ * in this location
+ *
+ * memory for @wssid and @wss_info must be allocated when calling this
+ */
+static ssize_t wlp_get_wss_info(struct wlp *wlp, struct wlp_attr_wss_info *attr,
+ size_t buflen, struct wlp_uuid *wssid,
+ struct wlp_wss_tmp_info *wss_info)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ ssize_t result;
+ size_t len;
+ size_t used = 0;
+ void *ptr;
+
+ result = wlp_check_wss_info_attr_hdr(wlp, (struct wlp_attr_hdr *)attr,
+ buflen);
+ if (result < 0)
+ goto out;
+ len = result;
+ used = sizeof(*attr);
+ ptr = attr;
+ d_printf(6, dev, "WLP: WSS info: Retrieving WSSID\n");
+ result = wlp_get_wssid(wlp, ptr + used, wssid, buflen - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WSSID from WSS info.\n");
+ goto out;
+ }
+ used += result;
+ result = wlp_get_wss_info_attrs(wlp, ptr + used, wss_info,
+ buflen - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WSS information "
+ "from WSS information attributes. \n");
+ goto out;
+ }
+ used += result;
+ if (len + sizeof(*attr) != used) {
+ dev_err(dev, "WLP: Amount of data parsed does not "
+ "match length field. Parsed %zu, length "
+ "field %zu. \n", used, len);
+ result = -EINVAL;
+ goto out;
+ }
+ result = used;
+ d_printf(6, dev, "WLP: Successfully parsed WLP information "
+ "attribute. used %zu bytes\n", used);
+out:
+ return result;
+}
+
+/**
+ * Retrieve WSS info from association frame
+ *
+ * @attr: pointer to WSS information attribute
+ * @neighbor: ptr to neighbor being discovered, NULL if enrollment in
+ * progress
+ * @wss: ptr to WSS being enrolled in, NULL if discovery in progress
+ * @buflen: size of buffer in which WSS information appears
+ *
+ * The WSS information attribute appears in the D2 association message.
+ * This message is used in two ways: to discover all neighbors or to enroll
+ * into a WSS activated by a neighbor. During discovery we only want to
+ * store the WSS info in a cache, to be deleted right after it has been
+ * used (eg. displayed to the user). During enrollment we store the WSS
+ * information for the lifetime of enrollment.
+ *
+ * During discovery we are interested in all WSS information, during
+ * enrollment we are only interested in the WSS being enrolled in. Even so,
+ * when in enrollment we keep parsing the message after finding the WSS of
+ * interest, this simplifies the calling routine in that it can be sure
+ * that all WSS information attributes have been parsed out of the message.
+ *
+ * Association frame is process with nbmutex held. The list access is safe.
+ */
+static ssize_t wlp_get_all_wss_info(struct wlp *wlp,
+ struct wlp_attr_wss_info *attr,
+ struct wlp_neighbor_e *neighbor,
+ struct wlp_wss *wss, ssize_t buflen)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ size_t used = 0;
+ ssize_t result = -EINVAL;
+ struct wlp_attr_wss_info *cur;
+ struct wlp_uuid wssid;
+ struct wlp_wss_tmp_info wss_info;
+ unsigned enroll; /* 0 - discovery to cache, 1 - enrollment */
+ struct wlp_wssid_e *wssid_e;
+ char buf[WLP_WSS_UUID_STRSIZE];
+
+ d_fnstart(6, dev, "wlp %p, attr %p, neighbor %p, wss %p, buflen %d \n",
+ wlp, attr, neighbor, wss, (int)buflen);
+ if (buflen < 0)
+ goto out;
+
+ if (neighbor != NULL && wss == NULL)
+ enroll = 0; /* discovery */
+ else if (wss != NULL && neighbor == NULL)
+ enroll = 1; /* enrollment */
+ else
+ goto out;
+
+ cur = attr;
+ while (buflen - used > 0) {
+ memset(&wss_info, 0, sizeof(wss_info));
+ cur = (void *)cur + used;
+ result = wlp_get_wss_info(wlp, cur, buflen - used, &wssid,
+ &wss_info);
+ if (result == -ENODATA) {
+ result = used;
+ goto out;
+ } else if (result < 0) {
+ dev_err(dev, "WLP: Unable to parse WSS information "
+ "from WSS information attribute. \n");
+ result = -EINVAL;
+ goto error_parse;
+ }
+ if (enroll && !memcmp(&wssid, &wss->wssid, sizeof(wssid))) {
+ if (wss_info.accept_enroll != 1) {
+ dev_err(dev, "WLP: Requested WSS does "
+ "not accept enrollment.\n");
+ result = -EINVAL;
+ goto out;
+ }
+ memcpy(wss->name, wss_info.name, sizeof(wss->name));
+ wss->bcast = wss_info.bcast;
+ wss->secure_status = wss_info.sec_status;
+ wss->accept_enroll = wss_info.accept_enroll;
+ wss->state = WLP_WSS_STATE_PART_ENROLLED;
+ wlp_wss_uuid_print(buf, sizeof(buf), &wssid);
+ d_printf(2, dev, "WLP: Found WSS %s. Enrolling.\n",
+ buf);
+ } else {
+ wssid_e = wlp_create_wssid_e(wlp, neighbor);
+ if (wssid_e == NULL) {
+ dev_err(dev, "WLP: Cannot create new WSSID "
+ "entry for neighbor %02x:%02x.\n",
+ neighbor->uwb_dev->dev_addr.data[1],
+ neighbor->uwb_dev->dev_addr.data[0]);
+ result = -ENOMEM;
+ goto out;
+ }
+ wssid_e->wssid = wssid;
+ *wssid_e->info = wss_info;
+ }
+ used += result;
+ }
+ result = used;
+error_parse:
+ if (result < 0 && !enroll) /* this was a discovery */
+ wlp_remove_neighbor_tmp_info(neighbor);
+out:
+ d_fnend(6, dev, "wlp %p, attr %p, neighbor %p, wss %p, buflen %d, "
+ "result %d \n", wlp, attr, neighbor, wss, (int)buflen,
+ (int)result);
+ return result;
+
+}
+
+/**
+ * Parse WSS information attributes into cache for discovery
+ *
+ * @attr: the first WSS information attribute in message
+ * @neighbor: the neighbor whose cache will be populated
+ * @buflen: size of the input buffer
+ */
+static ssize_t wlp_get_wss_info_to_cache(struct wlp *wlp,
+ struct wlp_attr_wss_info *attr,
+ struct wlp_neighbor_e *neighbor,
+ ssize_t buflen)
+{
+ return wlp_get_all_wss_info(wlp, attr, neighbor, NULL, buflen);
+}
+
+/**
+ * Parse WSS information attributes into WSS struct for enrollment
+ *
+ * @attr: the first WSS information attribute in message
+ * @wss: the WSS that will be enrolled
+ * @buflen: size of the input buffer
+ */
+static ssize_t wlp_get_wss_info_to_enroll(struct wlp *wlp,
+ struct wlp_attr_wss_info *attr,
+ struct wlp_wss *wss, ssize_t buflen)
+{
+ return wlp_get_all_wss_info(wlp, attr, NULL, wss, buflen);
+}
+
+/**
+ * Construct a D1 association frame
+ *
+ * We use the radio control functions to determine the values of the device
+ * properties. These are of variable length and the total space needed is
+ * tallied first before we start constructing the message. The radio
+ * control functions return strings that are terminated with \0. This
+ * character should not be included in the message (there is a length field
+ * accompanying it in the attribute).
+ */
+static int wlp_build_assoc_d1(struct wlp *wlp, struct wlp_wss *wss,
+ struct sk_buff **skb)
+{
+
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ int result = 0;
+ struct wlp_device_info *info;
+ size_t used = 0;
+ struct wlp_frame_assoc *_d1;
+ struct sk_buff *_skb;
+ void *d1_itr;
+
+ d_fnstart(6, dev, "wlp %p\n", wlp);
+ if (wlp->dev_info == NULL) {
+ result = __wlp_setup_device_info(wlp);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to setup device "
+ "information for D1 message.\n");
+ goto error;
+ }
+ }
+ info = wlp->dev_info;
+ d_printf(6, dev, "Local properties:\n"
+ "Device name (%d bytes): %s\n"
+ "Model name (%d bytes): %s\n"
+ "Manufacturer (%d bytes): %s\n"
+ "Model number (%d bytes): %s\n"
+ "Serial number (%d bytes): %s\n"
+ "Primary device type: \n"
+ " Category: %d \n"
+ " OUI: %02x:%02x:%02x \n"
+ " OUI Subdivision: %u \n",
+ (int)strlen(info->name), info->name,
+ (int)strlen(info->model_name), info->model_name,
+ (int)strlen(info->manufacturer), info->manufacturer,
+ (int)strlen(info->model_nr), info->model_nr,
+ (int)strlen(info->serial), info->serial,
+ info->prim_dev_type.category,
+ info->prim_dev_type.OUI[0], info->prim_dev_type.OUI[1],
+ info->prim_dev_type.OUI[2], info->prim_dev_type.OUIsubdiv);
+ _skb = dev_alloc_skb(sizeof(*_d1)
+ + sizeof(struct wlp_attr_uuid_e)
+ + sizeof(struct wlp_attr_wss_sel_mthd)
+ + sizeof(struct wlp_attr_dev_name)
+ + strlen(info->name)
+ + sizeof(struct wlp_attr_manufacturer)
+ + strlen(info->manufacturer)
+ + sizeof(struct wlp_attr_model_name)
+ + strlen(info->model_name)
+ + sizeof(struct wlp_attr_model_nr)
+ + strlen(info->model_nr)
+ + sizeof(struct wlp_attr_serial)
+ + strlen(info->serial)
+ + sizeof(struct wlp_attr_prim_dev_type)
+ + sizeof(struct wlp_attr_wlp_assc_err));
+ if (_skb == NULL) {
+ dev_err(dev, "WLP: Cannot allocate memory for association "
+ "message.\n");
+ result = -ENOMEM;
+ goto error;
+ }
+ _d1 = (void *) _skb->data;
+ d_printf(6, dev, "D1 starts at %p \n", _d1);
+ _d1->hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID);
+ _d1->hdr.type = WLP_FRAME_ASSOCIATION;
+ _d1->type = WLP_ASSOC_D1;
+
+ wlp_set_version(&_d1->version, WLP_VERSION);
+ wlp_set_msg_type(&_d1->msg_type, WLP_ASSOC_D1);
+ d1_itr = _d1->attr;
+ used = wlp_set_uuid_e(d1_itr, &wlp->uuid);
+ used += wlp_set_wss_sel_mthd(d1_itr + used, WLP_WSS_REG_SELECT);
+ used += wlp_set_dev_name(d1_itr + used, info->name,
+ strlen(info->name));
+ used += wlp_set_manufacturer(d1_itr + used, info->manufacturer,
+ strlen(info->manufacturer));
+ used += wlp_set_model_name(d1_itr + used, info->model_name,
+ strlen(info->model_name));
+ used += wlp_set_model_nr(d1_itr + used, info->model_nr,
+ strlen(info->model_nr));
+ used += wlp_set_serial(d1_itr + used, info->serial,
+ strlen(info->serial));
+ used += wlp_set_prim_dev_type(d1_itr + used, &info->prim_dev_type);
+ used += wlp_set_wlp_assc_err(d1_itr + used, WLP_ASSOC_ERROR_NONE);
+ skb_put(_skb, sizeof(*_d1) + used);
+ d_printf(6, dev, "D1 message:\n");
+ d_dump(6, dev, _d1, sizeof(*_d1)
+ + sizeof(struct wlp_attr_uuid_e)
+ + sizeof(struct wlp_attr_wss_sel_mthd)
+ + sizeof(struct wlp_attr_dev_name)
+ + strlen(info->name)
+ + sizeof(struct wlp_attr_manufacturer)
+ + strlen(info->manufacturer)
+ + sizeof(struct wlp_attr_model_name)
+ + strlen(info->model_name)
+ + sizeof(struct wlp_attr_model_nr)
+ + strlen(info->model_nr)
+ + sizeof(struct wlp_attr_serial)
+ + strlen(info->serial)
+ + sizeof(struct wlp_attr_prim_dev_type)
+ + sizeof(struct wlp_attr_wlp_assc_err));
+ *skb = _skb;
+error:
+ d_fnend(6, dev, "wlp %p, result = %d\n", wlp, result);
+ return result;
+}
+
+/**
+ * Construct a D2 association frame
+ *
+ * We use the radio control functions to determine the values of the device
+ * properties. These are of variable length and the total space needed is
+ * tallied first before we start constructing the message. The radio
+ * control functions return strings that are terminated with \0. This
+ * character should not be included in the message (there is a length field
+ * accompanying it in the attribute).
+ */
+static
+int wlp_build_assoc_d2(struct wlp *wlp, struct wlp_wss *wss,
+ struct sk_buff **skb, struct wlp_uuid *uuid_e)
+{
+
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ int result = 0;
+ struct wlp_device_info *info;
+ size_t used = 0;
+ struct wlp_frame_assoc *_d2;
+ struct sk_buff *_skb;
+ void *d2_itr;
+ size_t mem_needed;
+
+ d_fnstart(6, dev, "wlp %p\n", wlp);
+ if (wlp->dev_info == NULL) {
+ result = __wlp_setup_device_info(wlp);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to setup device "
+ "information for D2 message.\n");
+ goto error;
+ }
+ }
+ info = wlp->dev_info;
+ d_printf(6, dev, "Local properties:\n"
+ "Device name (%d bytes): %s\n"
+ "Model name (%d bytes): %s\n"
+ "Manufacturer (%d bytes): %s\n"
+ "Model number (%d bytes): %s\n"
+ "Serial number (%d bytes): %s\n"
+ "Primary device type: \n"
+ " Category: %d \n"
+ " OUI: %02x:%02x:%02x \n"
+ " OUI Subdivision: %u \n",
+ (int)strlen(info->name), info->name,
+ (int)strlen(info->model_name), info->model_name,
+ (int)strlen(info->manufacturer), info->manufacturer,
+ (int)strlen(info->model_nr), info->model_nr,
+ (int)strlen(info->serial), info->serial,
+ info->prim_dev_type.category,
+ info->prim_dev_type.OUI[0], info->prim_dev_type.OUI[1],
+ info->prim_dev_type.OUI[2], info->prim_dev_type.OUIsubdiv);
+ mem_needed = sizeof(*_d2)
+ + sizeof(struct wlp_attr_uuid_e)
+ + sizeof(struct wlp_attr_uuid_r)
+ + sizeof(struct wlp_attr_dev_name)
+ + strlen(info->name)
+ + sizeof(struct wlp_attr_manufacturer)
+ + strlen(info->manufacturer)
+ + sizeof(struct wlp_attr_model_name)
+ + strlen(info->model_name)
+ + sizeof(struct wlp_attr_model_nr)
+ + strlen(info->model_nr)
+ + sizeof(struct wlp_attr_serial)
+ + strlen(info->serial)
+ + sizeof(struct wlp_attr_prim_dev_type)
+ + sizeof(struct wlp_attr_wlp_assc_err);
+ if (wlp->wss.state >= WLP_WSS_STATE_ACTIVE)
+ mem_needed += sizeof(struct wlp_attr_wss_info)
+ + sizeof(struct wlp_wss_info)
+ + strlen(wlp->wss.name);
+ _skb = dev_alloc_skb(mem_needed);
+ if (_skb == NULL) {
+ dev_err(dev, "WLP: Cannot allocate memory for association "
+ "message.\n");
+ result = -ENOMEM;
+ goto error;
+ }
+ _d2 = (void *) _skb->data;
+ d_printf(6, dev, "D2 starts at %p \n", _d2);
+ _d2->hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID);
+ _d2->hdr.type = WLP_FRAME_ASSOCIATION;
+ _d2->type = WLP_ASSOC_D2;
+
+ wlp_set_version(&_d2->version, WLP_VERSION);
+ wlp_set_msg_type(&_d2->msg_type, WLP_ASSOC_D2);
+ d2_itr = _d2->attr;
+ used = wlp_set_uuid_e(d2_itr, uuid_e);
+ used += wlp_set_uuid_r(d2_itr + used, &wlp->uuid);
+ if (wlp->wss.state >= WLP_WSS_STATE_ACTIVE)
+ used += wlp_set_wss_info(d2_itr + used, &wlp->wss);
+ used += wlp_set_dev_name(d2_itr + used, info->name,
+ strlen(info->name));
+ used += wlp_set_manufacturer(d2_itr + used, info->manufacturer,
+ strlen(info->manufacturer));
+ used += wlp_set_model_name(d2_itr + used, info->model_name,
+ strlen(info->model_name));
+ used += wlp_set_model_nr(d2_itr + used, info->model_nr,
+ strlen(info->model_nr));
+ used += wlp_set_serial(d2_itr + used, info->serial,
+ strlen(info->serial));
+ used += wlp_set_prim_dev_type(d2_itr + used, &info->prim_dev_type);
+ used += wlp_set_wlp_assc_err(d2_itr + used, WLP_ASSOC_ERROR_NONE);
+ skb_put(_skb, sizeof(*_d2) + used);
+ d_printf(6, dev, "D2 message:\n");
+ d_dump(6, dev, _d2, mem_needed);
+ *skb = _skb;
+error:
+ d_fnend(6, dev, "wlp %p, result = %d\n", wlp, result);
+ return result;
+}
+
+/**
+ * Allocate memory for and populate fields of F0 association frame
+ *
+ * Currently (while focusing on unsecure enrollment) we ignore the
+ * nonce's that could be placed in the message. Only the error field is
+ * populated by the value provided by the caller.
+ */
+static
+int wlp_build_assoc_f0(struct wlp *wlp, struct sk_buff **skb,
+ enum wlp_assc_error error)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ int result = -ENOMEM;
+ struct {
+ struct wlp_frame_assoc f0_hdr;
+ struct wlp_attr_enonce enonce;
+ struct wlp_attr_rnonce rnonce;
+ struct wlp_attr_wlp_assc_err assc_err;
+ } *f0;
+ struct sk_buff *_skb;
+ struct wlp_nonce tmp;
+
+ d_fnstart(6, dev, "wlp %p\n", wlp);
+ _skb = dev_alloc_skb(sizeof(*f0));
+ if (_skb == NULL) {
+ dev_err(dev, "WLP: Unable to allocate memory for F0 "
+ "association frame. \n");
+ goto error_alloc;
+ }
+ f0 = (void *) _skb->data;
+ d_printf(6, dev, "F0 starts at %p \n", f0);
+ f0->f0_hdr.hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID);
+ f0->f0_hdr.hdr.type = WLP_FRAME_ASSOCIATION;
+ f0->f0_hdr.type = WLP_ASSOC_F0;
+ wlp_set_version(&f0->f0_hdr.version, WLP_VERSION);
+ wlp_set_msg_type(&f0->f0_hdr.msg_type, WLP_ASSOC_F0);
+ memset(&tmp, 0, sizeof(tmp));
+ wlp_set_enonce(&f0->enonce, &tmp);
+ wlp_set_rnonce(&f0->rnonce, &tmp);
+ wlp_set_wlp_assc_err(&f0->assc_err, error);
+ skb_put(_skb, sizeof(*f0));
+ *skb = _skb;
+ result = 0;
+error_alloc:
+ d_fnend(6, dev, "wlp %p, result %d \n", wlp, result);
+ return result;
+}
+
+/**
+ * Parse F0 frame
+ *
+ * We just retrieve the values and print it as an error to the user.
+ * Calling function already knows an error occured (F0 indicates error), so
+ * we just parse the content as debug for higher layers.
+ */
+int wlp_parse_f0(struct wlp *wlp, struct sk_buff *skb)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct wlp_frame_assoc *f0 = (void *) skb->data;
+ void *ptr = skb->data;
+ size_t len = skb->len;
+ size_t used;
+ ssize_t result;
+ struct wlp_nonce enonce, rnonce;
+ enum wlp_assc_error assc_err;
+ char enonce_buf[WLP_WSS_NONCE_STRSIZE];
+ char rnonce_buf[WLP_WSS_NONCE_STRSIZE];
+
+ used = sizeof(*f0);
+ result = wlp_get_enonce(wlp, ptr + used, &enonce, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain Enrollee nonce "
+ "attribute from F0 message.\n");
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_rnonce(wlp, ptr + used, &rnonce, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain Registrar nonce "
+ "attribute from F0 message.\n");
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_wlp_assc_err(wlp, ptr + used, &assc_err, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WLP Association error "
+ "attribute from F0 message.\n");
+ goto error_parse;
+ }
+ wlp_wss_nonce_print(enonce_buf, sizeof(enonce_buf), &enonce);
+ wlp_wss_nonce_print(rnonce_buf, sizeof(rnonce_buf), &rnonce);
+ dev_err(dev, "WLP: Received F0 error frame from neighbor. Enrollee "
+ "nonce: %s, Registrar nonce: %s, WLP Association error: %s.\n",
+ enonce_buf, rnonce_buf, wlp_assc_error_str(assc_err));
+ result = 0;
+error_parse:
+ return result;
+}
+
+/**
+ * Retrieve variable device information from association message
+ *
+ * The device information parsed is not required in any message. This
+ * routine will thus not fail if an attribute is not present.
+ * The attributes are expected in a certain order, even if all are not
+ * present. The "attribute type" value is used to ensure the attributes
+ * are parsed in the correct order.
+ *
+ * If an error is encountered during parsing the function will return an
+ * error code, when this happens the given device_info structure may be
+ * partially filled.
+ */
+static
+int wlp_get_variable_info(struct wlp *wlp, void *data,
+ struct wlp_device_info *dev_info, ssize_t len)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ size_t used = 0;
+ struct wlp_attr_hdr *hdr;
+ ssize_t result = 0;
+ unsigned last = 0;
+
+ while (len - used > 0) {
+ if (len - used < sizeof(*hdr)) {
+ dev_err(dev, "WLP: Partial data in frame, cannot "
+ "parse. \n");
+ goto error_parse;
+ }
+ hdr = data + used;
+ switch (le16_to_cpu(hdr->type)) {
+ case WLP_ATTR_MANUF:
+ if (last >= WLP_ATTR_MANUF) {
+ dev_err(dev, "WLP: Incorrect order of "
+ "attribute values in D1 msg.\n");
+ goto error_parse;
+ }
+ result = wlp_get_manufacturer(wlp, data + used,
+ dev_info->manufacturer,
+ len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to obtain "
+ "Manufacturer attribute from D1 "
+ "message.\n");
+ goto error_parse;
+ }
+ last = WLP_ATTR_MANUF;
+ used += result;
+ break;
+ case WLP_ATTR_MODEL_NAME:
+ if (last >= WLP_ATTR_MODEL_NAME) {
+ dev_err(dev, "WLP: Incorrect order of "
+ "attribute values in D1 msg.\n");
+ goto error_parse;
+ }
+ result = wlp_get_model_name(wlp, data + used,
+ dev_info->model_name,
+ len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to obtain Model "
+ "name attribute from D1 message.\n");
+ goto error_parse;
+ }
+ last = WLP_ATTR_MODEL_NAME;
+ used += result;
+ break;
+ case WLP_ATTR_MODEL_NR:
+ if (last >= WLP_ATTR_MODEL_NR) {
+ dev_err(dev, "WLP: Incorrect order of "
+ "attribute values in D1 msg.\n");
+ goto error_parse;
+ }
+ result = wlp_get_model_nr(wlp, data + used,
+ dev_info->model_nr,
+ len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to obtain Model "
+ "number attribute from D1 message.\n");
+ goto error_parse;
+ }
+ last = WLP_ATTR_MODEL_NR;
+ used += result;
+ break;
+ case WLP_ATTR_SERIAL:
+ if (last >= WLP_ATTR_SERIAL) {
+ dev_err(dev, "WLP: Incorrect order of "
+ "attribute values in D1 msg.\n");
+ goto error_parse;
+ }
+ result = wlp_get_serial(wlp, data + used,
+ dev_info->serial, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to obtain Serial "
+ "number attribute from D1 message.\n");
+ goto error_parse;
+ }
+ last = WLP_ATTR_SERIAL;
+ used += result;
+ break;
+ case WLP_ATTR_PRI_DEV_TYPE:
+ if (last >= WLP_ATTR_PRI_DEV_TYPE) {
+ dev_err(dev, "WLP: Incorrect order of "
+ "attribute values in D1 msg.\n");
+ goto error_parse;
+ }
+ result = wlp_get_prim_dev_type(wlp, data + used,
+ &dev_info->prim_dev_type,
+ len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to obtain Primary "
+ "device type attribute from D1 "
+ "message.\n");
+ goto error_parse;
+ }
+ dev_info->prim_dev_type.category =
+ le16_to_cpu(dev_info->prim_dev_type.category);
+ dev_info->prim_dev_type.subID =
+ le16_to_cpu(dev_info->prim_dev_type.subID);
+ last = WLP_ATTR_PRI_DEV_TYPE;
+ used += result;
+ break;
+ default:
+ /* This is not variable device information. */
+ goto out;
+ break;
+ }
+ }
+out:
+ return used;
+error_parse:
+ return -EINVAL;
+}
+
+/**
+ * Parse incoming D1 frame, populate attribute values
+ *
+ * Caller provides pointers to memory already allocated for attributes
+ * expected in the D1 frame. These variables will be populated.
+ */
+static
+int wlp_parse_d1_frame(struct wlp *wlp, struct sk_buff *skb,
+ struct wlp_uuid *uuid_e,
+ enum wlp_wss_sel_mthd *sel_mthd,
+ struct wlp_device_info *dev_info,
+ enum wlp_assc_error *assc_err)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct wlp_frame_assoc *d1 = (void *) skb->data;
+ void *ptr = skb->data;
+ size_t len = skb->len;
+ size_t used;
+ ssize_t result;
+
+ used = sizeof(*d1);
+ result = wlp_get_uuid_e(wlp, ptr + used, uuid_e, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain UUID-E attribute from D1 "
+ "message.\n");
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_wss_sel_mthd(wlp, ptr + used, sel_mthd, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WSS selection method "
+ "from D1 message.\n");
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_dev_name(wlp, ptr + used, dev_info->name,
+ len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain Device Name from D1 "
+ "message.\n");
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_variable_info(wlp, ptr + used, dev_info, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain Device Information from "
+ "D1 message.\n");
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_wlp_assc_err(wlp, ptr + used, assc_err, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WLP Association Error "
+ "Information from D1 message.\n");
+ goto error_parse;
+ }
+ result = 0;
+error_parse:
+ return result;
+}
+/**
+ * Handle incoming D1 frame
+ *
+ * The frame has already been verified to contain an Association header with
+ * the correct version number. Parse the incoming frame, construct and send
+ * a D2 frame in response.
+ *
+ * It is not clear what to do with most fields in the incoming D1 frame. We
+ * retrieve and discard the information here for now.
+ */
+void wlp_handle_d1_frame(struct work_struct *ws)
+{
+ struct wlp_assoc_frame_ctx *frame_ctx = container_of(ws,
+ struct wlp_assoc_frame_ctx,
+ ws);
+ struct wlp *wlp = frame_ctx->wlp;
+ struct wlp_wss *wss = &wlp->wss;
+ struct sk_buff *skb = frame_ctx->skb;
+ struct uwb_dev_addr *src = &frame_ctx->src;
+ int result;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct wlp_uuid uuid_e;
+ enum wlp_wss_sel_mthd sel_mthd = 0;
+ struct wlp_device_info dev_info;
+ enum wlp_assc_error assc_err;
+ char uuid[WLP_WSS_UUID_STRSIZE];
+ struct sk_buff *resp = NULL;
+
+ /* Parse D1 frame */
+ d_fnstart(6, dev, "WLP: handle D1 frame. wlp = %p, skb = %p\n",
+ wlp, skb);
+ mutex_lock(&wss->mutex);
+ mutex_lock(&wlp->mutex); /* to access wlp->uuid */
+ memset(&dev_info, 0, sizeof(dev_info));
+ result = wlp_parse_d1_frame(wlp, skb, &uuid_e, &sel_mthd, &dev_info,
+ &assc_err);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to parse incoming D1 frame.\n");
+ kfree_skb(skb);
+ goto out;
+ }
+ wlp_wss_uuid_print(uuid, sizeof(uuid), &uuid_e);
+ d_printf(6, dev, "From D1 frame:\n"
+ "UUID-E: %s\n"
+ "Selection method: %d\n"
+ "Device name (%d bytes): %s\n"
+ "Model name (%d bytes): %s\n"
+ "Manufacturer (%d bytes): %s\n"
+ "Model number (%d bytes): %s\n"
+ "Serial number (%d bytes): %s\n"
+ "Primary device type: \n"
+ " Category: %d \n"
+ " OUI: %02x:%02x:%02x \n"
+ " OUI Subdivision: %u \n",
+ uuid, sel_mthd,
+ (int)strlen(dev_info.name), dev_info.name,
+ (int)strlen(dev_info.model_name), dev_info.model_name,
+ (int)strlen(dev_info.manufacturer), dev_info.manufacturer,
+ (int)strlen(dev_info.model_nr), dev_info.model_nr,
+ (int)strlen(dev_info.serial), dev_info.serial,
+ dev_info.prim_dev_type.category,
+ dev_info.prim_dev_type.OUI[0],
+ dev_info.prim_dev_type.OUI[1],
+ dev_info.prim_dev_type.OUI[2],
+ dev_info.prim_dev_type.OUIsubdiv);
+
+ kfree_skb(skb);
+ if (!wlp_uuid_is_set(&wlp->uuid)) {
+ dev_err(dev, "WLP: UUID is not set. Set via sysfs to "
+ "proceed. Respong to D1 message with error F0.\n");
+ result = wlp_build_assoc_f0(wlp, &resp,
+ WLP_ASSOC_ERROR_NOT_READY);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to construct F0 message.\n");
+ goto out;
+ }
+ } else {
+ /* Construct D2 frame */
+ result = wlp_build_assoc_d2(wlp, wss, &resp, &uuid_e);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to construct D2 message.\n");
+ goto out;
+ }
+ }
+ /* Send D2 frame */
+ BUG_ON(wlp->xmit_frame == NULL);
+ result = wlp->xmit_frame(wlp, resp, src);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to transmit D2 association "
+ "message: %d\n", result);
+ if (result == -ENXIO)
+ dev_err(dev, "WLP: Is network interface up? \n");
+ /* We could try again ... */
+ dev_kfree_skb_any(resp); /* we need to free if tx fails */
+ }
+out:
+ kfree(frame_ctx);
+ mutex_unlock(&wlp->mutex);
+ mutex_unlock(&wss->mutex);
+ d_fnend(6, dev, "WLP: handle D1 frame. wlp = %p\n", wlp);
+}
+
+/**
+ * Parse incoming D2 frame, create and populate temporary cache
+ *
+ * @skb: socket buffer in which D2 frame can be found
+ * @neighbor: the neighbor that sent the D2 frame
+ *
+ * Will allocate memory for temporary storage of information learned during
+ * discovery.
+ */
+int wlp_parse_d2_frame_to_cache(struct wlp *wlp, struct sk_buff *skb,
+ struct wlp_neighbor_e *neighbor)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct wlp_frame_assoc *d2 = (void *) skb->data;
+ void *ptr = skb->data;
+ size_t len = skb->len;
+ size_t used;
+ ssize_t result;
+ struct wlp_uuid uuid_e;
+ struct wlp_device_info *nb_info;
+ enum wlp_assc_error assc_err;
+
+ used = sizeof(*d2);
+ result = wlp_get_uuid_e(wlp, ptr + used, &uuid_e, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain UUID-E attribute from D2 "
+ "message.\n");
+ goto error_parse;
+ }
+ if (memcmp(&uuid_e, &wlp->uuid, sizeof(uuid_e))) {
+ dev_err(dev, "WLP: UUID-E in incoming D2 does not match "
+ "local UUID sent in D1. \n");
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_uuid_r(wlp, ptr + used, &neighbor->uuid, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain UUID-R attribute from D2 "
+ "message.\n");
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_wss_info_to_cache(wlp, ptr + used, neighbor,
+ len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WSS information "
+ "from D2 message.\n");
+ goto error_parse;
+ }
+ used += result;
+ neighbor->info = kzalloc(sizeof(struct wlp_device_info), GFP_KERNEL);
+ if (neighbor->info == NULL) {
+ dev_err(dev, "WLP: cannot allocate memory to store device "
+ "info.\n");
+ result = -ENOMEM;
+ goto error_parse;
+ }
+ nb_info = neighbor->info;
+ result = wlp_get_dev_name(wlp, ptr + used, nb_info->name,
+ len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain Device Name from D2 "
+ "message.\n");
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_variable_info(wlp, ptr + used, nb_info, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain Device Information from "
+ "D2 message.\n");
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_wlp_assc_err(wlp, ptr + used, &assc_err, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WLP Association Error "
+ "Information from D2 message.\n");
+ goto error_parse;
+ }
+ if (assc_err != WLP_ASSOC_ERROR_NONE) {
+ dev_err(dev, "WLP: neighbor device returned association "
+ "error %d\n", assc_err);
+ result = -EINVAL;
+ goto error_parse;
+ }
+ result = 0;
+error_parse:
+ if (result < 0)
+ wlp_remove_neighbor_tmp_info(neighbor);
+ return result;
+}
+
+/**
+ * Parse incoming D2 frame, populate attribute values of WSS bein enrolled in
+ *
+ * @wss: our WSS that will be enrolled
+ * @skb: socket buffer in which D2 frame can be found
+ * @neighbor: the neighbor that sent the D2 frame
+ * @wssid: the wssid of the WSS in which we want to enroll
+ *
+ * Forms part of enrollment sequence. We are trying to enroll in WSS with
+ * @wssid by using @neighbor as registrar. A D1 message was sent to
+ * @neighbor and now we need to parse the D2 response. The neighbor's
+ * response is searched for the requested WSS and if found (and it accepts
+ * enrollment), we store the information.
+ */
+int wlp_parse_d2_frame_to_enroll(struct wlp_wss *wss, struct sk_buff *skb,
+ struct wlp_neighbor_e *neighbor,
+ struct wlp_uuid *wssid)
+{
+ struct wlp *wlp = container_of(wss, struct wlp, wss);
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ void *ptr = skb->data;
+ size_t len = skb->len;
+ size_t used;
+ ssize_t result;
+ struct wlp_uuid uuid_e;
+ struct wlp_uuid uuid_r;
+ struct wlp_device_info nb_info;
+ enum wlp_assc_error assc_err;
+ char uuid_bufA[WLP_WSS_UUID_STRSIZE];
+ char uuid_bufB[WLP_WSS_UUID_STRSIZE];
+
+ used = sizeof(struct wlp_frame_assoc);
+ result = wlp_get_uuid_e(wlp, ptr + used, &uuid_e, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain UUID-E attribute from D2 "
+ "message.\n");
+ goto error_parse;
+ }
+ if (memcmp(&uuid_e, &wlp->uuid, sizeof(uuid_e))) {
+ dev_err(dev, "WLP: UUID-E in incoming D2 does not match "
+ "local UUID sent in D1. \n");
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_uuid_r(wlp, ptr + used, &uuid_r, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain UUID-R attribute from D2 "
+ "message.\n");
+ goto error_parse;
+ }
+ if (memcmp(&uuid_r, &neighbor->uuid, sizeof(uuid_r))) {
+ wlp_wss_uuid_print(uuid_bufA, sizeof(uuid_bufA),
+ &neighbor->uuid);
+ wlp_wss_uuid_print(uuid_bufB, sizeof(uuid_bufB), &uuid_r);
+ dev_err(dev, "WLP: UUID of neighbor does not match UUID "
+ "learned during discovery. Originally discovered: %s, "
+ "now from D2 message: %s\n", uuid_bufA, uuid_bufB);
+ result = -EINVAL;
+ goto error_parse;
+ }
+ used += result;
+ wss->wssid = *wssid;
+ result = wlp_get_wss_info_to_enroll(wlp, ptr + used, wss, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WSS information "
+ "from D2 message.\n");
+ goto error_parse;
+ }
+ if (wss->state != WLP_WSS_STATE_PART_ENROLLED) {
+ dev_err(dev, "WLP: D2 message did not contain information "
+ "for successful enrollment. \n");
+ result = -EINVAL;
+ goto error_parse;
+ }
+ used += result;
+ /* Place device information on stack to continue parsing of message */
+ result = wlp_get_dev_name(wlp, ptr + used, nb_info.name,
+ len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain Device Name from D2 "
+ "message.\n");
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_variable_info(wlp, ptr + used, &nb_info, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain Device Information from "
+ "D2 message.\n");
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_wlp_assc_err(wlp, ptr + used, &assc_err, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WLP Association Error "
+ "Information from D2 message.\n");
+ goto error_parse;
+ }
+ if (assc_err != WLP_ASSOC_ERROR_NONE) {
+ dev_err(dev, "WLP: neighbor device returned association "
+ "error %d\n", assc_err);
+ if (wss->state == WLP_WSS_STATE_PART_ENROLLED) {
+ dev_err(dev, "WLP: Enrolled in WSS (should not "
+ "happen according to spec). Undoing. \n");
+ wlp_wss_reset(wss);
+ }
+ result = -EINVAL;
+ goto error_parse;
+ }
+ result = 0;
+error_parse:
+ return result;
+}
+
+/**
+ * Parse C3/C4 frame into provided variables
+ *
+ * @wssid: will point to copy of wssid retrieved from C3/C4 frame
+ * @tag: will point to copy of tag retrieved from C3/C4 frame
+ * @virt_addr: will point to copy of virtual address retrieved from C3/C4
+ * frame.
+ *
+ * Calling function has to allocate memory for these values.
+ *
+ * skb contains a valid C3/C4 frame, return the individual fields of this
+ * frame in the provided variables.
+ */
+int wlp_parse_c3c4_frame(struct wlp *wlp, struct sk_buff *skb,
+ struct wlp_uuid *wssid, u8 *tag,
+ struct uwb_mac_addr *virt_addr)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ int result;
+ void *ptr = skb->data;
+ size_t len = skb->len;
+ size_t used;
+ char buf[WLP_WSS_UUID_STRSIZE];
+ struct wlp_frame_assoc *assoc = ptr;
+
+ d_fnstart(6, dev, "wlp %p, skb %p \n", wlp, skb);
+ used = sizeof(*assoc);
+ result = wlp_get_wssid(wlp, ptr + used, wssid, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WSSID attribute from "
+ "%s message.\n", wlp_assoc_frame_str(assoc->type));
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_wss_tag(wlp, ptr + used, tag, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WSS tag attribute from "
+ "%s message.\n", wlp_assoc_frame_str(assoc->type));
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_wss_virt(wlp, ptr + used, virt_addr, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WSS virtual address "
+ "attribute from %s message.\n",
+ wlp_assoc_frame_str(assoc->type));
+ goto error_parse;
+ }
+ wlp_wss_uuid_print(buf, sizeof(buf), wssid);
+ d_printf(6, dev, "WLP: parsed: WSSID %s, tag 0x%02x, virt "
+ "%02x:%02x:%02x:%02x:%02x:%02x \n", buf, *tag,
+ virt_addr->data[0], virt_addr->data[1], virt_addr->data[2],
+ virt_addr->data[3], virt_addr->data[4], virt_addr->data[5]);
+
+error_parse:
+ d_fnend(6, dev, "wlp %p, skb %p, result = %d \n", wlp, skb, result);
+ return result;
+}
+
+/**
+ * Allocate memory for and populate fields of C1 or C2 association frame
+ *
+ * The C1 and C2 association frames appear identical - except for the type.
+ */
+static
+int wlp_build_assoc_c1c2(struct wlp *wlp, struct wlp_wss *wss,
+ struct sk_buff **skb, enum wlp_assoc_type type)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ int result = -ENOMEM;
+ struct {
+ struct wlp_frame_assoc c_hdr;
+ struct wlp_attr_wssid wssid;
+ } *c;
+ struct sk_buff *_skb;
+
+ d_fnstart(6, dev, "wlp %p, wss %p \n", wlp, wss);
+ _skb = dev_alloc_skb(sizeof(*c));
+ if (_skb == NULL) {
+ dev_err(dev, "WLP: Unable to allocate memory for C1/C2 "
+ "association frame. \n");
+ goto error_alloc;
+ }
+ c = (void *) _skb->data;
+ d_printf(6, dev, "C1/C2 starts at %p \n", c);
+ c->c_hdr.hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID);
+ c->c_hdr.hdr.type = WLP_FRAME_ASSOCIATION;
+ c->c_hdr.type = type;
+ wlp_set_version(&c->c_hdr.version, WLP_VERSION);
+ wlp_set_msg_type(&c->c_hdr.msg_type, type);
+ wlp_set_wssid(&c->wssid, &wss->wssid);
+ skb_put(_skb, sizeof(*c));
+ d_printf(6, dev, "C1/C2 message:\n");
+ d_dump(6, dev, c, sizeof(*c));
+ *skb = _skb;
+ result = 0;
+error_alloc:
+ d_fnend(6, dev, "wlp %p, wss %p, result %d \n", wlp, wss, result);
+ return result;
+}
+
+
+static
+int wlp_build_assoc_c1(struct wlp *wlp, struct wlp_wss *wss,
+ struct sk_buff **skb)
+{
+ return wlp_build_assoc_c1c2(wlp, wss, skb, WLP_ASSOC_C1);
+}
+
+static
+int wlp_build_assoc_c2(struct wlp *wlp, struct wlp_wss *wss,
+ struct sk_buff **skb)
+{
+ return wlp_build_assoc_c1c2(wlp, wss, skb, WLP_ASSOC_C2);
+}
+
+
+/**
+ * Allocate memory for and populate fields of C3 or C4 association frame
+ *
+ * The C3 and C4 association frames appear identical - except for the type.
+ */
+static
+int wlp_build_assoc_c3c4(struct wlp *wlp, struct wlp_wss *wss,
+ struct sk_buff **skb, enum wlp_assoc_type type)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ int result = -ENOMEM;
+ struct {
+ struct wlp_frame_assoc c_hdr;
+ struct wlp_attr_wssid wssid;
+ struct wlp_attr_wss_tag wss_tag;
+ struct wlp_attr_wss_virt wss_virt;
+ } *c;
+ struct sk_buff *_skb;
+
+ d_fnstart(6, dev, "wlp %p, wss %p \n", wlp, wss);
+ _skb = dev_alloc_skb(sizeof(*c));
+ if (_skb == NULL) {
+ dev_err(dev, "WLP: Unable to allocate memory for C3/C4 "
+ "association frame. \n");
+ goto error_alloc;
+ }
+ c = (void *) _skb->data;
+ d_printf(6, dev, "C3/C4 starts at %p \n", c);
+ c->c_hdr.hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID);
+ c->c_hdr.hdr.type = WLP_FRAME_ASSOCIATION;
+ c->c_hdr.type = type;
+ wlp_set_version(&c->c_hdr.version, WLP_VERSION);
+ wlp_set_msg_type(&c->c_hdr.msg_type, type);
+ wlp_set_wssid(&c->wssid, &wss->wssid);
+ wlp_set_wss_tag(&c->wss_tag, wss->tag);
+ wlp_set_wss_virt(&c->wss_virt, &wss->virtual_addr);
+ skb_put(_skb, sizeof(*c));
+ d_printf(6, dev, "C3/C4 message:\n");
+ d_dump(6, dev, c, sizeof(*c));
+ *skb = _skb;
+ result = 0;
+error_alloc:
+ d_fnend(6, dev, "wlp %p, wss %p, result %d \n", wlp, wss, result);
+ return result;
+}
+
+static
+int wlp_build_assoc_c3(struct wlp *wlp, struct wlp_wss *wss,
+ struct sk_buff **skb)
+{
+ return wlp_build_assoc_c3c4(wlp, wss, skb, WLP_ASSOC_C3);
+}
+
+static
+int wlp_build_assoc_c4(struct wlp *wlp, struct wlp_wss *wss,
+ struct sk_buff **skb)
+{
+ return wlp_build_assoc_c3c4(wlp, wss, skb, WLP_ASSOC_C4);
+}
+
+
+#define wlp_send_assoc(type, id) \
+static int wlp_send_assoc_##type(struct wlp *wlp, struct wlp_wss *wss, \
+ struct uwb_dev_addr *dev_addr) \
+{ \
+ struct device *dev = &wlp->rc->uwb_dev.dev; \
+ int result; \
+ struct sk_buff *skb = NULL; \
+ d_fnstart(6, dev, "wlp %p, wss %p, neighbor: %02x:%02x\n", \
+ wlp, wss, dev_addr->data[1], dev_addr->data[0]); \
+ d_printf(6, dev, "WLP: Constructing %s frame. \n", \
+ wlp_assoc_frame_str(id)); \
+ /* Build the frame */ \
+ result = wlp_build_assoc_##type(wlp, wss, &skb); \
+ if (result < 0) { \
+ dev_err(dev, "WLP: Unable to construct %s association " \
+ "frame: %d\n", wlp_assoc_frame_str(id), result);\
+ goto error_build_assoc; \
+ } \
+ /* Send the frame */ \
+ d_printf(6, dev, "Transmitting %s frame to %02x:%02x \n", \
+ wlp_assoc_frame_str(id), \
+ dev_addr->data[1], dev_addr->data[0]); \
+ BUG_ON(wlp->xmit_frame == NULL); \
+ result = wlp->xmit_frame(wlp, skb, dev_addr); \
+ if (result < 0) { \
+ dev_err(dev, "WLP: Unable to transmit %s association " \
+ "message: %d\n", wlp_assoc_frame_str(id), \
+ result); \
+ if (result == -ENXIO) \
+ dev_err(dev, "WLP: Is network interface " \
+ "up? \n"); \
+ goto error_xmit; \
+ } \
+ return 0; \
+error_xmit: \
+ /* We could try again ... */ \
+ dev_kfree_skb_any(skb);/*we need to free if tx fails*/ \
+error_build_assoc: \
+ d_fnend(6, dev, "wlp %p, wss %p, neighbor: %02x:%02x\n", \
+ wlp, wss, dev_addr->data[1], dev_addr->data[0]); \
+ return result; \
+}
+
+wlp_send_assoc(d1, WLP_ASSOC_D1)
+wlp_send_assoc(c1, WLP_ASSOC_C1)
+wlp_send_assoc(c3, WLP_ASSOC_C3)
+
+int wlp_send_assoc_frame(struct wlp *wlp, struct wlp_wss *wss,
+ struct uwb_dev_addr *dev_addr,
+ enum wlp_assoc_type type)
+{
+ int result = 0;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ switch (type) {
+ case WLP_ASSOC_D1:
+ result = wlp_send_assoc_d1(wlp, wss, dev_addr);
+ break;
+ case WLP_ASSOC_C1:
+ result = wlp_send_assoc_c1(wlp, wss, dev_addr);
+ break;
+ case WLP_ASSOC_C3:
+ result = wlp_send_assoc_c3(wlp, wss, dev_addr);
+ break;
+ default:
+ dev_err(dev, "WLP: Received request to send unknown "
+ "association message.\n");
+ result = -EINVAL;
+ break;
+ }
+ return result;
+}
+
+/**
+ * Handle incoming C1 frame
+ *
+ * The frame has already been verified to contain an Association header with
+ * the correct version number. Parse the incoming frame, construct and send
+ * a C2 frame in response.
+ */
+void wlp_handle_c1_frame(struct work_struct *ws)
+{
+ struct wlp_assoc_frame_ctx *frame_ctx = container_of(ws,
+ struct wlp_assoc_frame_ctx,
+ ws);
+ struct wlp *wlp = frame_ctx->wlp;
+ struct wlp_wss *wss = &wlp->wss;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct wlp_frame_assoc *c1 = (void *) frame_ctx->skb->data;
+ unsigned int len = frame_ctx->skb->len;
+ struct uwb_dev_addr *src = &frame_ctx->src;
+ int result;
+ struct wlp_uuid wssid;
+ char buf[WLP_WSS_UUID_STRSIZE];
+ struct sk_buff *resp = NULL;
+
+ /* Parse C1 frame */
+ d_fnstart(6, dev, "WLP: handle C1 frame. wlp = %p, c1 = %p\n",
+ wlp, c1);
+ mutex_lock(&wss->mutex);
+ result = wlp_get_wssid(wlp, (void *)c1 + sizeof(*c1), &wssid,
+ len - sizeof(*c1));
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WSSID from C1 frame.\n");
+ goto out;
+ }
+ wlp_wss_uuid_print(buf, sizeof(buf), &wssid);
+ d_printf(6, dev, "Received C1 frame with WSSID %s \n", buf);
+ if (!memcmp(&wssid, &wss->wssid, sizeof(wssid))
+ && wss->state == WLP_WSS_STATE_ACTIVE) {
+ d_printf(6, dev, "WSSID from C1 frame is known locally "
+ "and is active\n");
+ /* Construct C2 frame */
+ result = wlp_build_assoc_c2(wlp, wss, &resp);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to construct C2 message.\n");
+ goto out;
+ }
+ } else {
+ d_printf(6, dev, "WSSID from C1 frame is not known locally "
+ "or is not active\n");
+ /* Construct F0 frame */
+ result = wlp_build_assoc_f0(wlp, &resp, WLP_ASSOC_ERROR_INV);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to construct F0 message.\n");
+ goto out;
+ }
+ }
+ /* Send C2 frame */
+ d_printf(6, dev, "Transmitting response (C2/F0) frame to %02x:%02x \n",
+ src->data[1], src->data[0]);
+ BUG_ON(wlp->xmit_frame == NULL);
+ result = wlp->xmit_frame(wlp, resp, src);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to transmit response association "
+ "message: %d\n", result);
+ if (result == -ENXIO)
+ dev_err(dev, "WLP: Is network interface up? \n");
+ /* We could try again ... */
+ dev_kfree_skb_any(resp); /* we need to free if tx fails */
+ }
+out:
+ kfree_skb(frame_ctx->skb);
+ kfree(frame_ctx);
+ mutex_unlock(&wss->mutex);
+ d_fnend(6, dev, "WLP: handle C1 frame. wlp = %p\n", wlp);
+}
+
+/**
+ * Handle incoming C3 frame
+ *
+ * The frame has already been verified to contain an Association header with
+ * the correct version number. Parse the incoming frame, construct and send
+ * a C4 frame in response. If the C3 frame identifies a WSS that is locally
+ * active then we connect to this neighbor (add it to our EDA cache).
+ */
+void wlp_handle_c3_frame(struct work_struct *ws)
+{
+ struct wlp_assoc_frame_ctx *frame_ctx = container_of(ws,
+ struct wlp_assoc_frame_ctx,
+ ws);
+ struct wlp *wlp = frame_ctx->wlp;
+ struct wlp_wss *wss = &wlp->wss;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct sk_buff *skb = frame_ctx->skb;
+ struct uwb_dev_addr *src = &frame_ctx->src;
+ int result;
+ char buf[WLP_WSS_UUID_STRSIZE];
+ struct sk_buff *resp = NULL;
+ struct wlp_uuid wssid;
+ u8 tag;
+ struct uwb_mac_addr virt_addr;
+
+ /* Parse C3 frame */
+ d_fnstart(6, dev, "WLP: handle C3 frame. wlp = %p, skb = %p\n",
+ wlp, skb);
+ mutex_lock(&wss->mutex);
+ result = wlp_parse_c3c4_frame(wlp, skb, &wssid, &tag, &virt_addr);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain values from C3 frame.\n");
+ goto out;
+ }
+ wlp_wss_uuid_print(buf, sizeof(buf), &wssid);
+ d_printf(6, dev, "Received C3 frame with WSSID %s \n", buf);
+ if (!memcmp(&wssid, &wss->wssid, sizeof(wssid))
+ && wss->state >= WLP_WSS_STATE_ACTIVE) {
+ d_printf(6, dev, "WSSID from C3 frame is known locally "
+ "and is active\n");
+ result = wlp_eda_update_node(&wlp->eda, src, wss,
+ (void *) virt_addr.data, tag,
+ WLP_WSS_CONNECTED);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to update EDA cache "
+ "with new connected neighbor information.\n");
+ result = wlp_build_assoc_f0(wlp, &resp,
+ WLP_ASSOC_ERROR_INT);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to construct F0 "
+ "message.\n");
+ goto out;
+ }
+ } else {
+ wss->state = WLP_WSS_STATE_CONNECTED;
+ /* Construct C4 frame */
+ result = wlp_build_assoc_c4(wlp, wss, &resp);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to construct C4 "
+ "message.\n");
+ goto out;
+ }
+ }
+ } else {
+ d_printf(6, dev, "WSSID from C3 frame is not known locally "
+ "or is not active\n");
+ /* Construct F0 frame */
+ result = wlp_build_assoc_f0(wlp, &resp, WLP_ASSOC_ERROR_INV);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to construct F0 message.\n");
+ goto out;
+ }
+ }
+ /* Send C4 frame */
+ d_printf(6, dev, "Transmitting response (C4/F0) frame to %02x:%02x \n",
+ src->data[1], src->data[0]);
+ BUG_ON(wlp->xmit_frame == NULL);
+ result = wlp->xmit_frame(wlp, resp, src);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to transmit response association "
+ "message: %d\n", result);
+ if (result == -ENXIO)
+ dev_err(dev, "WLP: Is network interface up? \n");
+ /* We could try again ... */
+ dev_kfree_skb_any(resp); /* we need to free if tx fails */
+ }
+out:
+ kfree_skb(frame_ctx->skb);
+ kfree(frame_ctx);
+ mutex_unlock(&wss->mutex);
+ d_fnend(6, dev, "WLP: handle C3 frame. wlp = %p, skb = %p\n",
+ wlp, skb);
+}
+
+
diff --git a/drivers/uwb/wlp/sysfs.c b/drivers/uwb/wlp/sysfs.c
new file mode 100644
index 000000000000..1bb9b1f97d47
--- /dev/null
+++ b/drivers/uwb/wlp/sysfs.c
@@ -0,0 +1,709 @@
+/*
+ * WiMedia Logical Link Control Protocol (WLP)
+ * sysfs functions
+ *
+ * Copyright (C) 2007 Intel Corporation
+ * Reinette Chatre <reinette.chatre@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/wlp.h>
+#include "wlp-internal.h"
+
+static
+size_t wlp_wss_wssid_e_print(char *buf, size_t bufsize,
+ struct wlp_wssid_e *wssid_e)
+{
+ size_t used = 0;
+ used += scnprintf(buf, bufsize, " WSS: ");
+ used += wlp_wss_uuid_print(buf + used, bufsize - used,
+ &wssid_e->wssid);
+
+ if (wssid_e->info != NULL) {
+ used += scnprintf(buf + used, bufsize - used, " ");
+ used += uwb_mac_addr_print(buf + used, bufsize - used,
+ &wssid_e->info->bcast);
+ used += scnprintf(buf + used, bufsize - used, " %u %u %s\n",
+ wssid_e->info->accept_enroll,
+ wssid_e->info->sec_status,
+ wssid_e->info->name);
+ }
+ return used;
+}
+
+/**
+ * Print out information learned from neighbor discovery
+ *
+ * Some fields being printed may not be included in the device discovery
+ * information (it is not mandatory). We are thus careful how the
+ * information is printed to ensure it is clear to the user what field is
+ * being referenced.
+ * The information being printed is for one time use - temporary storage is
+ * cleaned after it is printed.
+ *
+ * Ideally sysfs output should be on one line. The information printed here
+ * contain a few strings so it will be hard to parse if they are all
+ * printed on the same line - without agreeing on a standard field
+ * separator.
+ */
+static
+ssize_t wlp_wss_neighborhood_print_remove(struct wlp *wlp, char *buf,
+ size_t bufsize)
+{
+ size_t used = 0;
+ struct wlp_neighbor_e *neighb;
+ struct wlp_wssid_e *wssid_e;
+
+ mutex_lock(&wlp->nbmutex);
+ used = scnprintf(buf, bufsize, "#Neighbor information\n"
+ "#uuid dev_addr\n"
+ "# Device Name:\n# Model Name:\n# Manufacturer:\n"
+ "# Model Nr:\n# Serial:\n"
+ "# Pri Dev type: CategoryID OUI OUISubdiv "
+ "SubcategoryID\n"
+ "# WSS: WSSID WSS_name accept_enroll sec_status "
+ "bcast\n"
+ "# WSS: WSSID WSS_name accept_enroll sec_status "
+ "bcast\n\n");
+ list_for_each_entry(neighb, &wlp->neighbors, node) {
+ if (bufsize - used <= 0)
+ goto out;
+ used += wlp_wss_uuid_print(buf + used, bufsize - used,
+ &neighb->uuid);
+ buf[used++] = ' ';
+ used += uwb_dev_addr_print(buf + used, bufsize - used,
+ &neighb->uwb_dev->dev_addr);
+ if (neighb->info != NULL)
+ used += scnprintf(buf + used, bufsize - used,
+ "\n Device Name: %s\n"
+ " Model Name: %s\n"
+ " Manufacturer:%s \n"
+ " Model Nr: %s\n"
+ " Serial: %s\n"
+ " Pri Dev type: "
+ "%u %02x:%02x:%02x %u %u\n",
+ neighb->info->name,
+ neighb->info->model_name,
+ neighb->info->manufacturer,
+ neighb->info->model_nr,
+ neighb->info->serial,
+ neighb->info->prim_dev_type.category,
+ neighb->info->prim_dev_type.OUI[0],
+ neighb->info->prim_dev_type.OUI[1],
+ neighb->info->prim_dev_type.OUI[2],
+ neighb->info->prim_dev_type.OUIsubdiv,
+ neighb->info->prim_dev_type.subID);
+ list_for_each_entry(wssid_e, &neighb->wssid, node) {
+ used += wlp_wss_wssid_e_print(buf + used,
+ bufsize - used,
+ wssid_e);
+ }
+ buf[used++] = '\n';
+ wlp_remove_neighbor_tmp_info(neighb);
+ }
+
+
+out:
+ mutex_unlock(&wlp->nbmutex);
+ return used;
+}
+
+
+/**
+ * Show properties of all WSS in neighborhood.
+ *
+ * Will trigger a complete discovery of WSS activated by this device and
+ * its neighbors.
+ */
+ssize_t wlp_neighborhood_show(struct wlp *wlp, char *buf)
+{
+ wlp_discover(wlp);
+ return wlp_wss_neighborhood_print_remove(wlp, buf, PAGE_SIZE);
+}
+EXPORT_SYMBOL_GPL(wlp_neighborhood_show);
+
+static
+ssize_t __wlp_wss_properties_show(struct wlp_wss *wss, char *buf,
+ size_t bufsize)
+{
+ ssize_t result;
+
+ result = wlp_wss_uuid_print(buf, bufsize, &wss->wssid);
+ result += scnprintf(buf + result, bufsize - result, " ");
+ result += uwb_mac_addr_print(buf + result, bufsize - result,
+ &wss->bcast);
+ result += scnprintf(buf + result, bufsize - result,
+ " 0x%02x %u ", wss->hash, wss->secure_status);
+ result += wlp_wss_key_print(buf + result, bufsize - result,
+ wss->master_key);
+ result += scnprintf(buf + result, bufsize - result, " 0x%02x ",
+ wss->tag);
+ result += uwb_mac_addr_print(buf + result, bufsize - result,
+ &wss->virtual_addr);
+ result += scnprintf(buf + result, bufsize - result, " %s", wss->name);
+ result += scnprintf(buf + result, bufsize - result,
+ "\n\n#WSSID\n#WSS broadcast address\n"
+ "#WSS hash\n#WSS secure status\n"
+ "#WSS master key\n#WSS local tag\n"
+ "#WSS local virtual EUI-48\n#WSS name\n");
+ return result;
+}
+
+/**
+ * Show which WSS is activated.
+ */
+ssize_t wlp_wss_activate_show(struct wlp_wss *wss, char *buf)
+{
+ int result = 0;
+
+ if (mutex_lock_interruptible(&wss->mutex))
+ goto out;
+ if (wss->state >= WLP_WSS_STATE_ACTIVE)
+ result = __wlp_wss_properties_show(wss, buf, PAGE_SIZE);
+ else
+ result = scnprintf(buf, PAGE_SIZE, "No local WSS active.\n");
+ result += scnprintf(buf + result, PAGE_SIZE - result,
+ "\n\n"
+ "# echo WSSID SECURE_STATUS ACCEPT_ENROLLMENT "
+ "NAME #create new WSS\n"
+ "# echo WSSID [DEV ADDR] #enroll in and activate "
+ "existing WSS, can request registrar\n"
+ "#\n"
+ "# WSSID is a 16 byte hex array. Eg. 12 A3 3B ... \n"
+ "# SECURE_STATUS 0 - unsecure, 1 - secure (default)\n"
+ "# ACCEPT_ENROLLMENT 0 - no, 1 - yes (default)\n"
+ "# NAME is the text string identifying the WSS\n"
+ "# DEV ADDR is the device address of neighbor "
+ "that should be registrar. Eg. 32:AB\n");
+
+ mutex_unlock(&wss->mutex);
+out:
+ return result;
+
+}
+EXPORT_SYMBOL_GPL(wlp_wss_activate_show);
+
+/**
+ * Create/activate a new WSS or enroll/activate in neighboring WSS
+ *
+ * The user can provide the WSSID of a WSS in which it wants to enroll.
+ * Only the WSSID is necessary if the WSS have been discovered before. If
+ * the WSS has not been discovered before, or the user wants to use a
+ * particular neighbor as its registrar, then the user can also provide a
+ * device address or the neighbor that will be used as registrar.
+ *
+ * A new WSS is created when the user provides a WSSID, secure status, and
+ * WSS name.
+ */
+ssize_t wlp_wss_activate_store(struct wlp_wss *wss,
+ const char *buf, size_t size)
+{
+ ssize_t result = -EINVAL;
+ struct wlp_uuid wssid;
+ struct uwb_dev_addr dev;
+ struct uwb_dev_addr bcast = {.data = {0xff, 0xff} };
+ char name[65];
+ unsigned sec_status, accept;
+ memset(name, 0, sizeof(name));
+ result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx:%02hhx",
+ &wssid.data[0] , &wssid.data[1],
+ &wssid.data[2] , &wssid.data[3],
+ &wssid.data[4] , &wssid.data[5],
+ &wssid.data[6] , &wssid.data[7],
+ &wssid.data[8] , &wssid.data[9],
+ &wssid.data[10], &wssid.data[11],
+ &wssid.data[12], &wssid.data[13],
+ &wssid.data[14], &wssid.data[15],
+ &dev.data[1], &dev.data[0]);
+ if (result == 16 || result == 17) {
+ result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%u %u %64c",
+ &wssid.data[0] , &wssid.data[1],
+ &wssid.data[2] , &wssid.data[3],
+ &wssid.data[4] , &wssid.data[5],
+ &wssid.data[6] , &wssid.data[7],
+ &wssid.data[8] , &wssid.data[9],
+ &wssid.data[10], &wssid.data[11],
+ &wssid.data[12], &wssid.data[13],
+ &wssid.data[14], &wssid.data[15],
+ &sec_status, &accept, name);
+ if (result == 16)
+ result = wlp_wss_enroll_activate(wss, &wssid, &bcast);
+ else if (result == 19) {
+ sec_status = sec_status == 0 ? 0 : 1;
+ accept = accept == 0 ? 0 : 1;
+ /* We read name using %c, so the newline needs to be
+ * removed */
+ if (strlen(name) != sizeof(name) - 1)
+ name[strlen(name) - 1] = '\0';
+ result = wlp_wss_create_activate(wss, &wssid, name,
+ sec_status, accept);
+ } else
+ result = -EINVAL;
+ } else if (result == 18)
+ result = wlp_wss_enroll_activate(wss, &wssid, &dev);
+ else
+ result = -EINVAL;
+ return result < 0 ? result : size;
+}
+EXPORT_SYMBOL_GPL(wlp_wss_activate_store);
+
+/**
+ * Show the UUID of this host
+ */
+ssize_t wlp_uuid_show(struct wlp *wlp, char *buf)
+{
+ ssize_t result = 0;
+
+ mutex_lock(&wlp->mutex);
+ result = wlp_wss_uuid_print(buf, PAGE_SIZE, &wlp->uuid);
+ buf[result++] = '\n';
+ mutex_unlock(&wlp->mutex);
+ return result;
+}
+EXPORT_SYMBOL_GPL(wlp_uuid_show);
+
+/**
+ * Store a new UUID for this host
+ *
+ * According to the spec this should be encoded as an octet string in the
+ * order the octets are shown in string representation in RFC 4122 (WLP
+ * 0.99 [Table 6])
+ *
+ * We do not check value provided by user.
+ */
+ssize_t wlp_uuid_store(struct wlp *wlp, const char *buf, size_t size)
+{
+ ssize_t result;
+ struct wlp_uuid uuid;
+
+ mutex_lock(&wlp->mutex);
+ result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx ",
+ &uuid.data[0] , &uuid.data[1],
+ &uuid.data[2] , &uuid.data[3],
+ &uuid.data[4] , &uuid.data[5],
+ &uuid.data[6] , &uuid.data[7],
+ &uuid.data[8] , &uuid.data[9],
+ &uuid.data[10], &uuid.data[11],
+ &uuid.data[12], &uuid.data[13],
+ &uuid.data[14], &uuid.data[15]);
+ if (result != 16) {
+ result = -EINVAL;
+ goto error;
+ }
+ wlp->uuid = uuid;
+error:
+ mutex_unlock(&wlp->mutex);
+ return result < 0 ? result : size;
+}
+EXPORT_SYMBOL_GPL(wlp_uuid_store);
+
+/**
+ * Show contents of members of device information structure
+ */
+#define wlp_dev_info_show(type) \
+ssize_t wlp_dev_##type##_show(struct wlp *wlp, char *buf) \
+{ \
+ ssize_t result = 0; \
+ mutex_lock(&wlp->mutex); \
+ if (wlp->dev_info == NULL) { \
+ result = __wlp_setup_device_info(wlp); \
+ if (result < 0) \
+ goto out; \
+ } \
+ result = scnprintf(buf, PAGE_SIZE, "%s\n", wlp->dev_info->type);\
+out: \
+ mutex_unlock(&wlp->mutex); \
+ return result; \
+} \
+EXPORT_SYMBOL_GPL(wlp_dev_##type##_show);
+
+wlp_dev_info_show(name)
+wlp_dev_info_show(model_name)
+wlp_dev_info_show(model_nr)
+wlp_dev_info_show(manufacturer)
+wlp_dev_info_show(serial)
+
+/**
+ * Store contents of members of device information structure
+ */
+#define wlp_dev_info_store(type, len) \
+ssize_t wlp_dev_##type##_store(struct wlp *wlp, const char *buf, size_t size)\
+{ \
+ ssize_t result; \
+ char format[10]; \
+ mutex_lock(&wlp->mutex); \
+ if (wlp->dev_info == NULL) { \
+ result = __wlp_alloc_device_info(wlp); \
+ if (result < 0) \
+ goto out; \
+ } \
+ memset(wlp->dev_info->type, 0, sizeof(wlp->dev_info->type)); \
+ sprintf(format, "%%%uc", len); \
+ result = sscanf(buf, format, wlp->dev_info->type); \
+out: \
+ mutex_unlock(&wlp->mutex); \
+ return result < 0 ? result : size; \
+} \
+EXPORT_SYMBOL_GPL(wlp_dev_##type##_store);
+
+wlp_dev_info_store(name, 32)
+wlp_dev_info_store(manufacturer, 64)
+wlp_dev_info_store(model_name, 32)
+wlp_dev_info_store(model_nr, 32)
+wlp_dev_info_store(serial, 32)
+
+static
+const char *__wlp_dev_category[] = {
+ [WLP_DEV_CAT_COMPUTER] = "Computer",
+ [WLP_DEV_CAT_INPUT] = "Input device",
+ [WLP_DEV_CAT_PRINT_SCAN_FAX_COPIER] = "Printer, scanner, FAX, or "
+ "Copier",
+ [WLP_DEV_CAT_CAMERA] = "Camera",
+ [WLP_DEV_CAT_STORAGE] = "Storage Network",
+ [WLP_DEV_CAT_INFRASTRUCTURE] = "Infrastructure",
+ [WLP_DEV_CAT_DISPLAY] = "Display",
+ [WLP_DEV_CAT_MULTIM] = "Multimedia device",
+ [WLP_DEV_CAT_GAMING] = "Gaming device",
+ [WLP_DEV_CAT_TELEPHONE] = "Telephone",
+ [WLP_DEV_CAT_OTHER] = "Other",
+};
+
+static
+const char *wlp_dev_category_str(unsigned cat)
+{
+ if ((cat >= WLP_DEV_CAT_COMPUTER && cat <= WLP_DEV_CAT_TELEPHONE)
+ || cat == WLP_DEV_CAT_OTHER)
+ return __wlp_dev_category[cat];
+ return "unknown category";
+}
+
+ssize_t wlp_dev_prim_category_show(struct wlp *wlp, char *buf)
+{
+ ssize_t result = 0;
+ mutex_lock(&wlp->mutex);
+ if (wlp->dev_info == NULL) {
+ result = __wlp_setup_device_info(wlp);
+ if (result < 0)
+ goto out;
+ }
+ result = scnprintf(buf, PAGE_SIZE, "%s\n",
+ wlp_dev_category_str(wlp->dev_info->prim_dev_type.category));
+out:
+ mutex_unlock(&wlp->mutex);
+ return result;
+}
+EXPORT_SYMBOL_GPL(wlp_dev_prim_category_show);
+
+ssize_t wlp_dev_prim_category_store(struct wlp *wlp, const char *buf,
+ size_t size)
+{
+ ssize_t result;
+ u16 cat;
+ mutex_lock(&wlp->mutex);
+ if (wlp->dev_info == NULL) {
+ result = __wlp_alloc_device_info(wlp);
+ if (result < 0)
+ goto out;
+ }
+ result = sscanf(buf, "%hu", &cat);
+ if ((cat >= WLP_DEV_CAT_COMPUTER && cat <= WLP_DEV_CAT_TELEPHONE)
+ || cat == WLP_DEV_CAT_OTHER)
+ wlp->dev_info->prim_dev_type.category = cat;
+ else
+ result = -EINVAL;
+out:
+ mutex_unlock(&wlp->mutex);
+ return result < 0 ? result : size;
+}
+EXPORT_SYMBOL_GPL(wlp_dev_prim_category_store);
+
+ssize_t wlp_dev_prim_OUI_show(struct wlp *wlp, char *buf)
+{
+ ssize_t result = 0;
+ mutex_lock(&wlp->mutex);
+ if (wlp->dev_info == NULL) {
+ result = __wlp_setup_device_info(wlp);
+ if (result < 0)
+ goto out;
+ }
+ result = scnprintf(buf, PAGE_SIZE, "%02x:%02x:%02x\n",
+ wlp->dev_info->prim_dev_type.OUI[0],
+ wlp->dev_info->prim_dev_type.OUI[1],
+ wlp->dev_info->prim_dev_type.OUI[2]);
+out:
+ mutex_unlock(&wlp->mutex);
+ return result;
+}
+EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_show);
+
+ssize_t wlp_dev_prim_OUI_store(struct wlp *wlp, const char *buf, size_t size)
+{
+ ssize_t result;
+ u8 OUI[3];
+ mutex_lock(&wlp->mutex);
+ if (wlp->dev_info == NULL) {
+ result = __wlp_alloc_device_info(wlp);
+ if (result < 0)
+ goto out;
+ }
+ result = sscanf(buf, "%hhx:%hhx:%hhx",
+ &OUI[0], &OUI[1], &OUI[2]);
+ if (result != 3) {
+ result = -EINVAL;
+ goto out;
+ } else
+ memcpy(wlp->dev_info->prim_dev_type.OUI, OUI, sizeof(OUI));
+out:
+ mutex_unlock(&wlp->mutex);
+ return result < 0 ? result : size;
+}
+EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_store);
+
+
+ssize_t wlp_dev_prim_OUI_sub_show(struct wlp *wlp, char *buf)
+{
+ ssize_t result = 0;
+ mutex_lock(&wlp->mutex);
+ if (wlp->dev_info == NULL) {
+ result = __wlp_setup_device_info(wlp);
+ if (result < 0)
+ goto out;
+ }
+ result = scnprintf(buf, PAGE_SIZE, "%u\n",
+ wlp->dev_info->prim_dev_type.OUIsubdiv);
+out:
+ mutex_unlock(&wlp->mutex);
+ return result;
+}
+EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_sub_show);
+
+ssize_t wlp_dev_prim_OUI_sub_store(struct wlp *wlp, const char *buf,
+ size_t size)
+{
+ ssize_t result;
+ unsigned sub;
+ u8 max_sub = ~0;
+ mutex_lock(&wlp->mutex);
+ if (wlp->dev_info == NULL) {
+ result = __wlp_alloc_device_info(wlp);
+ if (result < 0)
+ goto out;
+ }
+ result = sscanf(buf, "%u", &sub);
+ if (sub <= max_sub)
+ wlp->dev_info->prim_dev_type.OUIsubdiv = sub;
+ else
+ result = -EINVAL;
+out:
+ mutex_unlock(&wlp->mutex);
+ return result < 0 ? result : size;
+}
+EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_sub_store);
+
+ssize_t wlp_dev_prim_subcat_show(struct wlp *wlp, char *buf)
+{
+ ssize_t result = 0;
+ mutex_lock(&wlp->mutex);
+ if (wlp->dev_info == NULL) {
+ result = __wlp_setup_device_info(wlp);
+ if (result < 0)
+ goto out;
+ }
+ result = scnprintf(buf, PAGE_SIZE, "%u\n",
+ wlp->dev_info->prim_dev_type.subID);
+out:
+ mutex_unlock(&wlp->mutex);
+ return result;
+}
+EXPORT_SYMBOL_GPL(wlp_dev_prim_subcat_show);
+
+ssize_t wlp_dev_prim_subcat_store(struct wlp *wlp, const char *buf,
+ size_t size)
+{
+ ssize_t result;
+ unsigned sub;
+ __le16 max_sub = ~0;
+ mutex_lock(&wlp->mutex);
+ if (wlp->dev_info == NULL) {
+ result = __wlp_alloc_device_info(wlp);
+ if (result < 0)
+ goto out;
+ }
+ result = sscanf(buf, "%u", &sub);
+ if (sub <= max_sub)
+ wlp->dev_info->prim_dev_type.subID = sub;
+ else
+ result = -EINVAL;
+out:
+ mutex_unlock(&wlp->mutex);
+ return result < 0 ? result : size;
+}
+EXPORT_SYMBOL_GPL(wlp_dev_prim_subcat_store);
+
+/**
+ * Subsystem implementation for interaction with individual WSS via sysfs
+ *
+ * Followed instructions for subsystem in Documentation/filesystems/sysfs.txt
+ */
+
+#define kobj_to_wlp_wss(obj) container_of(obj, struct wlp_wss, kobj)
+#define attr_to_wlp_wss_attr(_attr) \
+ container_of(_attr, struct wlp_wss_attribute, attr)
+
+/**
+ * Sysfs subsystem: forward read calls
+ *
+ * Sysfs operation for forwarding read call to the show method of the
+ * attribute owner
+ */
+static
+ssize_t wlp_wss_attr_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct wlp_wss_attribute *wss_attr = attr_to_wlp_wss_attr(attr);
+ struct wlp_wss *wss = kobj_to_wlp_wss(kobj);
+ ssize_t ret = -EIO;
+
+ if (wss_attr->show)
+ ret = wss_attr->show(wss, buf);
+ return ret;
+}
+/**
+ * Sysfs subsystem: forward write calls
+ *
+ * Sysfs operation for forwarding write call to the store method of the
+ * attribute owner
+ */
+static
+ssize_t wlp_wss_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count)
+{
+ struct wlp_wss_attribute *wss_attr = attr_to_wlp_wss_attr(attr);
+ struct wlp_wss *wss = kobj_to_wlp_wss(kobj);
+ ssize_t ret = -EIO;
+
+ if (wss_attr->store)
+ ret = wss_attr->store(wss, buf, count);
+ return ret;
+}
+
+static
+struct sysfs_ops wss_sysfs_ops = {
+ .show = wlp_wss_attr_show,
+ .store = wlp_wss_attr_store,
+};
+
+struct kobj_type wss_ktype = {
+ .release = wlp_wss_release,
+ .sysfs_ops = &wss_sysfs_ops,
+};
+
+
+/**
+ * Sysfs files for individual WSS
+ */
+
+/**
+ * Print static properties of this WSS
+ *
+ * The name of a WSS may not be null teminated. It's max size is 64 bytes
+ * so we copy it to a larger array just to make sure we print sane data.
+ */
+static ssize_t wlp_wss_properties_show(struct wlp_wss *wss, char *buf)
+{
+ int result = 0;
+
+ if (mutex_lock_interruptible(&wss->mutex))
+ goto out;
+ result = __wlp_wss_properties_show(wss, buf, PAGE_SIZE);
+ mutex_unlock(&wss->mutex);
+out:
+ return result;
+}
+WSS_ATTR(properties, S_IRUGO, wlp_wss_properties_show, NULL);
+
+/**
+ * Print all connected members of this WSS
+ * The EDA cache contains all members of WSS neighborhood.
+ */
+static ssize_t wlp_wss_members_show(struct wlp_wss *wss, char *buf)
+{
+ struct wlp *wlp = container_of(wss, struct wlp, wss);
+ return wlp_eda_show(wlp, buf);
+}
+WSS_ATTR(members, S_IRUGO, wlp_wss_members_show, NULL);
+
+static
+const char *__wlp_strstate[] = {
+ "none",
+ "partially enrolled",
+ "enrolled",
+ "active",
+ "connected",
+};
+
+static const char *wlp_wss_strstate(unsigned state)
+{
+ if (state >= ARRAY_SIZE(__wlp_strstate))
+ return "unknown state";
+ return __wlp_strstate[state];
+}
+
+/*
+ * Print current state of this WSS
+ */
+static ssize_t wlp_wss_state_show(struct wlp_wss *wss, char *buf)
+{
+ int result = 0;
+
+ if (mutex_lock_interruptible(&wss->mutex))
+ goto out;
+ result = scnprintf(buf, PAGE_SIZE, "%s\n",
+ wlp_wss_strstate(wss->state));
+ mutex_unlock(&wss->mutex);
+out:
+ return result;
+}
+WSS_ATTR(state, S_IRUGO, wlp_wss_state_show, NULL);
+
+
+static
+struct attribute *wss_attrs[] = {
+ &wss_attr_properties.attr,
+ &wss_attr_members.attr,
+ &wss_attr_state.attr,
+ NULL,
+};
+
+struct attribute_group wss_attr_group = {
+ .name = NULL, /* we want them in the same directory */
+ .attrs = wss_attrs,
+};
diff --git a/drivers/uwb/wlp/txrx.c b/drivers/uwb/wlp/txrx.c
new file mode 100644
index 000000000000..c701bd1a2887
--- /dev/null
+++ b/drivers/uwb/wlp/txrx.c
@@ -0,0 +1,374 @@
+/*
+ * WiMedia Logical Link Control Protocol (WLP)
+ * Message exchange infrastructure
+ *
+ * Copyright (C) 2007 Intel Corporation
+ * Reinette Chatre <reinette.chatre@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/etherdevice.h>
+#include <linux/wlp.h>
+#define D_LOCAL 5
+#include <linux/uwb/debug.h>
+#include "wlp-internal.h"
+
+
+/**
+ * Direct incoming association msg to correct parsing routine
+ *
+ * We only expect D1, E1, C1, C3 messages as new. All other incoming
+ * association messages should form part of an established session that is
+ * handled elsewhere.
+ * The handling of these messages often require calling sleeping functions
+ * - this cannot be done in interrupt context. We use the kernel's
+ * workqueue to handle these messages.
+ */
+static
+void wlp_direct_assoc_frame(struct wlp *wlp, struct sk_buff *skb,
+ struct uwb_dev_addr *src)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct wlp_frame_assoc *assoc = (void *) skb->data;
+ struct wlp_assoc_frame_ctx *frame_ctx;
+ d_fnstart(5, dev, "wlp %p, skb %p\n", wlp, skb);
+ frame_ctx = kmalloc(sizeof(*frame_ctx), GFP_ATOMIC);
+ if (frame_ctx == NULL) {
+ dev_err(dev, "WLP: Unable to allocate memory for association "
+ "frame handling.\n");
+ kfree_skb(skb);
+ goto out;
+ }
+ frame_ctx->wlp = wlp;
+ frame_ctx->skb = skb;
+ frame_ctx->src = *src;
+ switch (assoc->type) {
+ case WLP_ASSOC_D1:
+ d_printf(5, dev, "Received a D1 frame.\n");
+ INIT_WORK(&frame_ctx->ws, wlp_handle_d1_frame);
+ schedule_work(&frame_ctx->ws);
+ break;
+ case WLP_ASSOC_E1:
+ d_printf(5, dev, "Received a E1 frame. FIXME?\n");
+ kfree_skb(skb); /* Temporary until we handle it */
+ kfree(frame_ctx); /* Temporary until we handle it */
+ break;
+ case WLP_ASSOC_C1:
+ d_printf(5, dev, "Received a C1 frame.\n");
+ INIT_WORK(&frame_ctx->ws, wlp_handle_c1_frame);
+ schedule_work(&frame_ctx->ws);
+ break;
+ case WLP_ASSOC_C3:
+ d_printf(5, dev, "Received a C3 frame.\n");
+ INIT_WORK(&frame_ctx->ws, wlp_handle_c3_frame);
+ schedule_work(&frame_ctx->ws);
+ break;
+ default:
+ dev_err(dev, "Received unexpected association frame. "
+ "Type = %d \n", assoc->type);
+ kfree_skb(skb);
+ kfree(frame_ctx);
+ break;
+ }
+out:
+ d_fnend(5, dev, "wlp %p\n", wlp);
+}
+
+/**
+ * Process incoming association frame
+ *
+ * Although it could be possible to deal with some incoming association
+ * messages without creating a new session we are keeping things simple. We
+ * do not accept new association messages if there is a session in progress
+ * and the messages do not belong to that session.
+ *
+ * If an association message arrives that causes the creation of a session
+ * (WLP_ASSOC_E1) while we are in the process of creating a session then we
+ * rely on the neighbor mutex to protect the data. That is, the new session
+ * will not be started until the previous is completed.
+ */
+static
+void wlp_receive_assoc_frame(struct wlp *wlp, struct sk_buff *skb,
+ struct uwb_dev_addr *src)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct wlp_frame_assoc *assoc = (void *) skb->data;
+ struct wlp_session *session = wlp->session;
+ u8 version;
+ d_fnstart(5, dev, "wlp %p, skb %p\n", wlp, skb);
+
+ if (wlp_get_version(wlp, &assoc->version, &version,
+ sizeof(assoc->version)) < 0)
+ goto error;
+ if (version != WLP_VERSION) {
+ dev_err(dev, "Unsupported WLP version in association "
+ "message.\n");
+ goto error;
+ }
+ if (session != NULL) {
+ /* Function that created this session is still holding the
+ * &wlp->mutex to protect this session. */
+ if (assoc->type == session->exp_message ||
+ assoc->type == WLP_ASSOC_F0) {
+ if (!memcmp(&session->neighbor_addr, src,
+ sizeof(*src))) {
+ session->data = skb;
+ (session->cb)(wlp);
+ } else {
+ dev_err(dev, "Received expected message from "
+ "unexpected source. Expected message "
+ "%d or F0 from %02x:%02x, but received "
+ "it from %02x:%02x. Dropping.\n",
+ session->exp_message,
+ session->neighbor_addr.data[1],
+ session->neighbor_addr.data[0],
+ src->data[1], src->data[0]);
+ goto error;
+ }
+ } else {
+ dev_err(dev, "Association already in progress. "
+ "Dropping.\n");
+ goto error;
+ }
+ } else {
+ wlp_direct_assoc_frame(wlp, skb, src);
+ }
+ d_fnend(5, dev, "wlp %p\n", wlp);
+ return;
+error:
+ kfree_skb(skb);
+ d_fnend(5, dev, "wlp %p\n", wlp);
+}
+
+/**
+ * Verify incoming frame is from connected neighbor, prep to pass to WLP client
+ *
+ * Verification proceeds according to WLP 0.99 [7.3.1]. The source address
+ * is used to determine which neighbor is sending the frame and the WSS tag
+ * is used to know to which WSS the frame belongs (we only support one WSS
+ * so this test is straight forward).
+ * With the WSS found we need to ensure that we are connected before
+ * allowing the exchange of data frames.
+ */
+static
+int wlp_verify_prep_rx_frame(struct wlp *wlp, struct sk_buff *skb,
+ struct uwb_dev_addr *src)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ int result = -EINVAL;
+ struct wlp_eda_node eda_entry;
+ struct wlp_frame_std_abbrv_hdr *hdr = (void *) skb->data;
+
+ d_fnstart(6, dev, "wlp %p, skb %p \n", wlp, skb);
+ /*verify*/
+ result = wlp_copy_eda_node(&wlp->eda, src, &eda_entry);
+ if (result < 0) {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Incoming frame is from unknown "
+ "neighbor %02x:%02x.\n", src->data[1],
+ src->data[0]);
+ goto out;
+ }
+ if (hdr->tag != eda_entry.tag) {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Tag of incoming frame from "
+ "%02x:%02x does not match expected tag. "
+ "Received 0x%02x, expected 0x%02x. \n",
+ src->data[1], src->data[0], hdr->tag,
+ eda_entry.tag);
+ result = -EINVAL;
+ goto out;
+ }
+ if (eda_entry.state != WLP_WSS_CONNECTED) {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Incoming frame from "
+ "%02x:%02x does is not from connected WSS.\n",
+ src->data[1], src->data[0]);
+ result = -EINVAL;
+ goto out;
+ }
+ /*prep*/
+ skb_pull(skb, sizeof(*hdr));
+out:
+ d_fnend(6, dev, "wlp %p, skb %p, result = %d \n", wlp, skb, result);
+ return result;
+}
+
+/**
+ * Receive a WLP frame from device
+ *
+ * @returns: 1 if calling function should free the skb
+ * 0 if it successfully handled skb and freed it
+ * 0 if error occured, will free skb in this case
+ */
+int wlp_receive_frame(struct device *dev, struct wlp *wlp, struct sk_buff *skb,
+ struct uwb_dev_addr *src)
+{
+ unsigned len = skb->len;
+ void *ptr = skb->data;
+ struct wlp_frame_hdr *hdr;
+ int result = 0;
+
+ d_fnstart(6, dev, "skb (%p), len (%u)\n", skb, len);
+ if (len < sizeof(*hdr)) {
+ dev_err(dev, "Not enough data to parse WLP header.\n");
+ result = -EINVAL;
+ goto out;
+ }
+ hdr = ptr;
+ d_dump(6, dev, hdr, sizeof(*hdr));
+ if (le16_to_cpu(hdr->mux_hdr) != WLP_PROTOCOL_ID) {
+ dev_err(dev, "Not a WLP frame type.\n");
+ result = -EINVAL;
+ goto out;
+ }
+ switch (hdr->type) {
+ case WLP_FRAME_STANDARD:
+ if (len < sizeof(struct wlp_frame_std_abbrv_hdr)) {
+ dev_err(dev, "Not enough data to parse Standard "
+ "WLP header.\n");
+ goto out;
+ }
+ result = wlp_verify_prep_rx_frame(wlp, skb, src);
+ if (result < 0) {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Verification of frame "
+ "from neighbor %02x:%02x failed.\n",
+ src->data[1], src->data[0]);
+ goto out;
+ }
+ result = 1;
+ break;
+ case WLP_FRAME_ABBREVIATED:
+ dev_err(dev, "Abbreviated frame received. FIXME?\n");
+ kfree_skb(skb);
+ break;
+ case WLP_FRAME_CONTROL:
+ dev_err(dev, "Control frame received. FIXME?\n");
+ kfree_skb(skb);
+ break;
+ case WLP_FRAME_ASSOCIATION:
+ if (len < sizeof(struct wlp_frame_assoc)) {
+ dev_err(dev, "Not enough data to parse Association "
+ "WLP header.\n");
+ goto out;
+ }
+ d_printf(5, dev, "Association frame received.\n");
+ wlp_receive_assoc_frame(wlp, skb, src);
+ break;
+ default:
+ dev_err(dev, "Invalid frame received.\n");
+ result = -EINVAL;
+ break;
+ }
+out:
+ if (result < 0) {
+ kfree_skb(skb);
+ result = 0;
+ }
+ d_fnend(6, dev, "skb (%p)\n", skb);
+ return result;
+}
+EXPORT_SYMBOL_GPL(wlp_receive_frame);
+
+
+/**
+ * Verify frame from network stack, prepare for further transmission
+ *
+ * @skb: the socket buffer that needs to be prepared for transmission (it
+ * is in need of a WLP header). If this is a broadcast frame we take
+ * over the entire transmission.
+ * If it is a unicast the WSS connection should already be established
+ * and transmission will be done by the calling function.
+ * @dst: On return this will contain the device address to which the
+ * frame is destined.
+ * @returns: 0 on success no tx : WLP header sucessfully applied to skb buffer,
+ * calling function can proceed with tx
+ * 1 on success with tx : WLP will take over transmission of this
+ * frame
+ * <0 on error
+ *
+ * The network stack (WLP client) is attempting to transmit a frame. We can
+ * only transmit data if a local WSS is at least active (connection will be
+ * done here if this is a broadcast frame and neighbor also has the WSS
+ * active).
+ *
+ * The frame can be either broadcast or unicast. Broadcast in a WSS is
+ * supported via multicast, but we don't support multicast yet (until
+ * devices start to support MAB IEs). If a broadcast frame needs to be
+ * transmitted it is treated as a unicast frame to each neighbor. In this
+ * case the WLP takes over transmission of the skb and returns 1
+ * to the caller to indicate so. Also, in this case, if a neighbor has the
+ * same WSS activated but is not connected then the WSS connection will be
+ * done at this time. The neighbor's virtual address will be learned at
+ * this time.
+ *
+ * The destination address in a unicast frame is the virtual address of the
+ * neighbor. This address only becomes known when a WSS connection is
+ * established. We thus rely on a broadcast frame to trigger the setup of
+ * WSS connections to all neighbors before we are able to send unicast
+ * frames to them. This seems reasonable as IP would usually use ARP first
+ * before any unicast frames are sent.
+ *
+ * If we are already connected to the neighbor (neighbor's virtual address
+ * is known) we just prepare the WLP header and the caller will continue to
+ * send the frame.
+ *
+ * A failure in this function usually indicates something that cannot be
+ * fixed automatically. So, if this function fails (@return < 0) the calling
+ * function should not retry to send the frame as it will very likely keep
+ * failing.
+ *
+ */
+int wlp_prepare_tx_frame(struct device *dev, struct wlp *wlp,
+ struct sk_buff *skb, struct uwb_dev_addr *dst)
+{
+ int result = -EINVAL;
+ struct ethhdr *eth_hdr = (void *) skb->data;
+
+ d_fnstart(6, dev, "wlp (%p), skb (%p) \n", wlp, skb);
+ if (is_broadcast_ether_addr(eth_hdr->h_dest)) {
+ d_printf(6, dev, "WLP: handling broadcast frame. \n");
+ result = wlp_eda_for_each(&wlp->eda, wlp_wss_send_copy, skb);
+ if (result < 0) {
+ if (printk_ratelimit())
+ dev_err(dev, "Unable to handle broadcast "
+ "frame from WLP client.\n");
+ goto out;
+ }
+ dev_kfree_skb_irq(skb);
+ result = 1;
+ /* Frame will be transmitted by WLP. */
+ } else {
+ d_printf(6, dev, "WLP: handling unicast frame. \n");
+ result = wlp_eda_for_virtual(&wlp->eda, eth_hdr->h_dest, dst,
+ wlp_wss_prep_hdr, skb);
+ if (unlikely(result < 0)) {
+ if (printk_ratelimit())
+ dev_err(dev, "Unable to prepare "
+ "skb for transmission. \n");
+ goto out;
+ }
+ }
+out:
+ d_fnend(6, dev, "wlp (%p), skb (%p). result = %d \n", wlp, skb, result);
+ return result;
+}
+EXPORT_SYMBOL_GPL(wlp_prepare_tx_frame);
diff --git a/drivers/uwb/wlp/wlp-internal.h b/drivers/uwb/wlp/wlp-internal.h
new file mode 100644
index 000000000000..1c94fabfb1a7
--- /dev/null
+++ b/drivers/uwb/wlp/wlp-internal.h
@@ -0,0 +1,228 @@
+/*
+ * WiMedia Logical Link Control Protocol (WLP)
+ * Internal API
+ *
+ * Copyright (C) 2007 Intel Corporation
+ * Reinette Chatre <reinette.chatre@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.
+ *
+ */
+
+#ifndef __WLP_INTERNAL_H__
+#define __WLP_INTERNAL_H__
+
+/**
+ * State of WSS connection
+ *
+ * A device needs to connect to a neighbor in an activated WSS before data
+ * can be transmitted. The spec also distinguishes between a new connection
+ * attempt and a connection attempt after previous connection attempts. The
+ * state WLP_WSS_CONNECT_FAILED is used for this scenario. See WLP 0.99
+ * [7.2.6]
+ */
+enum wlp_wss_connect {
+ WLP_WSS_UNCONNECTED = 0,
+ WLP_WSS_CONNECTED,
+ WLP_WSS_CONNECT_FAILED,
+};
+
+extern struct kobj_type wss_ktype;
+extern struct attribute_group wss_attr_group;
+
+extern int uwb_rc_ie_add(struct uwb_rc *, const struct uwb_ie_hdr *, size_t);
+extern int uwb_rc_ie_rm(struct uwb_rc *, enum uwb_ie);
+
+
+/* This should be changed to a dynamic array where entries are sorted
+ * by eth_addr and search is done in a binary form
+ *
+ * Although thinking twice about it: this technologie's maximum reach
+ * is 10 meters...unless you want to pack too much stuff in around
+ * your radio controller/WLP device, the list will probably not be
+ * too big.
+ *
+ * In any case, there is probably some data structure in the kernel
+ * than we could reused for that already.
+ *
+ * The below structure is really just good while we support one WSS per
+ * host.
+ */
+struct wlp_eda_node {
+ struct list_head list_node;
+ unsigned char eth_addr[ETH_ALEN];
+ struct uwb_dev_addr dev_addr;
+ struct wlp_wss *wss;
+ unsigned char virt_addr[ETH_ALEN];
+ u8 tag;
+ enum wlp_wss_connect state;
+};
+
+typedef int (*wlp_eda_for_each_f)(struct wlp *, struct wlp_eda_node *, void *);
+
+extern void wlp_eda_init(struct wlp_eda *);
+extern void wlp_eda_release(struct wlp_eda *);
+extern int wlp_eda_create_node(struct wlp_eda *,
+ const unsigned char eth_addr[ETH_ALEN],
+ const struct uwb_dev_addr *);
+extern void wlp_eda_rm_node(struct wlp_eda *, const struct uwb_dev_addr *);
+extern int wlp_eda_update_node(struct wlp_eda *,
+ const struct uwb_dev_addr *,
+ struct wlp_wss *,
+ const unsigned char virt_addr[ETH_ALEN],
+ const u8, const enum wlp_wss_connect);
+extern int wlp_eda_update_node_state(struct wlp_eda *,
+ const struct uwb_dev_addr *,
+ const enum wlp_wss_connect);
+
+extern int wlp_copy_eda_node(struct wlp_eda *, struct uwb_dev_addr *,
+ struct wlp_eda_node *);
+extern int wlp_eda_for_each(struct wlp_eda *, wlp_eda_for_each_f , void *);
+extern int wlp_eda_for_virtual(struct wlp_eda *,
+ const unsigned char eth_addr[ETH_ALEN],
+ struct uwb_dev_addr *,
+ wlp_eda_for_each_f , void *);
+
+
+extern void wlp_remove_neighbor_tmp_info(struct wlp_neighbor_e *);
+
+extern size_t wlp_wss_key_print(char *, size_t, u8 *);
+
+/* Function called when no more references to WSS exists */
+extern void wlp_wss_release(struct kobject *);
+
+extern void wlp_wss_reset(struct wlp_wss *);
+extern int wlp_wss_create_activate(struct wlp_wss *, struct wlp_uuid *,
+ char *, unsigned, unsigned);
+extern int wlp_wss_enroll_activate(struct wlp_wss *, struct wlp_uuid *,
+ struct uwb_dev_addr *);
+extern ssize_t wlp_discover(struct wlp *);
+
+extern int wlp_enroll_neighbor(struct wlp *, struct wlp_neighbor_e *,
+ struct wlp_wss *, struct wlp_uuid *);
+extern int wlp_wss_is_active(struct wlp *, struct wlp_wss *,
+ struct uwb_dev_addr *);
+
+struct wlp_assoc_conn_ctx {
+ struct work_struct ws;
+ struct wlp *wlp;
+ struct sk_buff *skb;
+ struct wlp_eda_node eda_entry;
+};
+
+
+extern int wlp_wss_connect_prep(struct wlp *, struct wlp_eda_node *, void *);
+extern int wlp_wss_send_copy(struct wlp *, struct wlp_eda_node *, void *);
+
+
+/* Message handling */
+struct wlp_assoc_frame_ctx {
+ struct work_struct ws;
+ struct wlp *wlp;
+ struct sk_buff *skb;
+ struct uwb_dev_addr src;
+};
+
+extern int wlp_wss_prep_hdr(struct wlp *, struct wlp_eda_node *, void *);
+extern void wlp_handle_d1_frame(struct work_struct *);
+extern int wlp_parse_d2_frame_to_cache(struct wlp *, struct sk_buff *,
+ struct wlp_neighbor_e *);
+extern int wlp_parse_d2_frame_to_enroll(struct wlp_wss *, struct sk_buff *,
+ struct wlp_neighbor_e *,
+ struct wlp_uuid *);
+extern void wlp_handle_c1_frame(struct work_struct *);
+extern void wlp_handle_c3_frame(struct work_struct *);
+extern int wlp_parse_c3c4_frame(struct wlp *, struct sk_buff *,
+ struct wlp_uuid *, u8 *,
+ struct uwb_mac_addr *);
+extern int wlp_parse_f0(struct wlp *, struct sk_buff *);
+extern int wlp_send_assoc_frame(struct wlp *, struct wlp_wss *,
+ struct uwb_dev_addr *, enum wlp_assoc_type);
+extern ssize_t wlp_get_version(struct wlp *, struct wlp_attr_version *,
+ u8 *, ssize_t);
+extern ssize_t wlp_get_wssid(struct wlp *, struct wlp_attr_wssid *,
+ struct wlp_uuid *, ssize_t);
+extern int __wlp_alloc_device_info(struct wlp *);
+extern int __wlp_setup_device_info(struct wlp *);
+
+extern struct wlp_wss_attribute wss_attribute_properties;
+extern struct wlp_wss_attribute wss_attribute_members;
+extern struct wlp_wss_attribute wss_attribute_state;
+
+static inline
+size_t wlp_wss_uuid_print(char *buf, size_t bufsize, struct wlp_uuid *uuid)
+{
+ size_t result;
+
+ result = scnprintf(buf, bufsize,
+ "%02x:%02x:%02x:%02x:%02x:%02x:"
+ "%02x:%02x:%02x:%02x:%02x:%02x:"
+ "%02x:%02x:%02x:%02x",
+ uuid->data[0], uuid->data[1],
+ uuid->data[2], uuid->data[3],
+ uuid->data[4], uuid->data[5],
+ uuid->data[6], uuid->data[7],
+ uuid->data[8], uuid->data[9],
+ uuid->data[10], uuid->data[11],
+ uuid->data[12], uuid->data[13],
+ uuid->data[14], uuid->data[15]);
+ return result;
+}
+
+/**
+ * FIXME: How should a nonce be displayed?
+ */
+static inline
+size_t wlp_wss_nonce_print(char *buf, size_t bufsize, struct wlp_nonce *nonce)
+{
+ size_t result;
+
+ result = scnprintf(buf, bufsize,
+ "%02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x",
+ nonce->data[0], nonce->data[1],
+ nonce->data[2], nonce->data[3],
+ nonce->data[4], nonce->data[5],
+ nonce->data[6], nonce->data[7],
+ nonce->data[8], nonce->data[9],
+ nonce->data[10], nonce->data[11],
+ nonce->data[12], nonce->data[13],
+ nonce->data[14], nonce->data[15]);
+ return result;
+}
+
+
+static inline
+void wlp_session_cb(struct wlp *wlp)
+{
+ struct completion *completion = wlp->session->cb_priv;
+ complete(completion);
+}
+
+static inline
+int wlp_uuid_is_set(struct wlp_uuid *uuid)
+{
+ struct wlp_uuid zero_uuid = { .data = { 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00} };
+
+ if (!memcmp(uuid, &zero_uuid, sizeof(*uuid)))
+ return 0;
+ return 1;
+}
+
+#endif /* __WLP_INTERNAL_H__ */
diff --git a/drivers/uwb/wlp/wlp-lc.c b/drivers/uwb/wlp/wlp-lc.c
new file mode 100644
index 000000000000..0799402e73fb
--- /dev/null
+++ b/drivers/uwb/wlp/wlp-lc.c
@@ -0,0 +1,585 @@
+/*
+ * WiMedia Logical Link Control Protocol (WLP)
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Reinette Chatre <reinette.chatre@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/wlp.h>
+#define D_LOCAL 6
+#include <linux/uwb/debug.h>
+#include "wlp-internal.h"
+
+
+static
+void wlp_neighbor_init(struct wlp_neighbor_e *neighbor)
+{
+ INIT_LIST_HEAD(&neighbor->wssid);
+}
+
+/**
+ * Create area for device information storage
+ *
+ * wlp->mutex must be held
+ */
+int __wlp_alloc_device_info(struct wlp *wlp)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ BUG_ON(wlp->dev_info != NULL);
+ wlp->dev_info = kzalloc(sizeof(struct wlp_device_info), GFP_KERNEL);
+ if (wlp->dev_info == NULL) {
+ dev_err(dev, "WLP: Unable to allocate memory for "
+ "device information.\n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+
+/**
+ * Fill in device information using function provided by driver
+ *
+ * wlp->mutex must be held
+ */
+static
+void __wlp_fill_device_info(struct wlp *wlp)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+
+ BUG_ON(wlp->fill_device_info == NULL);
+ d_printf(6, dev, "Retrieving device information "
+ "from device driver.\n");
+ wlp->fill_device_info(wlp, wlp->dev_info);
+}
+
+/**
+ * Setup device information
+ *
+ * Allocate area for device information and populate it.
+ *
+ * wlp->mutex must be held
+ */
+int __wlp_setup_device_info(struct wlp *wlp)
+{
+ int result;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+
+ result = __wlp_alloc_device_info(wlp);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to allocate area for "
+ "device information.\n");
+ return result;
+ }
+ __wlp_fill_device_info(wlp);
+ return 0;
+}
+
+/**
+ * Remove information about neighbor stored temporarily
+ *
+ * Information learned during discovey should only be stored when the
+ * device enrolls in the neighbor's WSS. We do need to store this
+ * information temporarily in order to present it to the user.
+ *
+ * We are only interested in keeping neighbor WSS information if that
+ * neighbor is accepting enrollment.
+ *
+ * should be called with wlp->nbmutex held
+ */
+void wlp_remove_neighbor_tmp_info(struct wlp_neighbor_e *neighbor)
+{
+ struct wlp_wssid_e *wssid_e, *next;
+ u8 keep;
+ if (!list_empty(&neighbor->wssid)) {
+ list_for_each_entry_safe(wssid_e, next, &neighbor->wssid,
+ node) {
+ if (wssid_e->info != NULL) {
+ keep = wssid_e->info->accept_enroll;
+ kfree(wssid_e->info);
+ wssid_e->info = NULL;
+ if (!keep) {
+ list_del(&wssid_e->node);
+ kfree(wssid_e);
+ }
+ }
+ }
+ }
+ if (neighbor->info != NULL) {
+ kfree(neighbor->info);
+ neighbor->info = NULL;
+ }
+}
+
+/**
+ * Populate WLP neighborhood cache with neighbor information
+ *
+ * A new neighbor is found. If it is discoverable then we add it to the
+ * neighborhood cache.
+ *
+ */
+static
+int wlp_add_neighbor(struct wlp *wlp, struct uwb_dev *dev)
+{
+ int result = 0;
+ int discoverable;
+ struct wlp_neighbor_e *neighbor;
+
+ d_fnstart(6, &dev->dev, "uwb %p \n", dev);
+ d_printf(6, &dev->dev, "Found neighbor device %02x:%02x \n",
+ dev->dev_addr.data[1], dev->dev_addr.data[0]);
+ /**
+ * FIXME:
+ * Use contents of WLP IE found in beacon cache to determine if
+ * neighbor is discoverable.
+ * The device does not support WLP IE yet so this still needs to be
+ * done. Until then we assume all devices are discoverable.
+ */
+ discoverable = 1; /* will be changed when FIXME disappears */
+ if (discoverable) {
+ /* Add neighbor to cache for discovery */
+ neighbor = kzalloc(sizeof(*neighbor), GFP_KERNEL);
+ if (neighbor == NULL) {
+ dev_err(&dev->dev, "Unable to create memory for "
+ "new neighbor. \n");
+ result = -ENOMEM;
+ goto error_no_mem;
+ }
+ wlp_neighbor_init(neighbor);
+ uwb_dev_get(dev);
+ neighbor->uwb_dev = dev;
+ list_add(&neighbor->node, &wlp->neighbors);
+ }
+error_no_mem:
+ d_fnend(6, &dev->dev, "uwb %p, result = %d \n", dev, result);
+ return result;
+}
+
+/**
+ * Remove one neighbor from cache
+ */
+static
+void __wlp_neighbor_release(struct wlp_neighbor_e *neighbor)
+{
+ struct wlp_wssid_e *wssid_e, *next_wssid_e;
+
+ list_for_each_entry_safe(wssid_e, next_wssid_e,
+ &neighbor->wssid, node) {
+ list_del(&wssid_e->node);
+ kfree(wssid_e);
+ }
+ uwb_dev_put(neighbor->uwb_dev);
+ list_del(&neighbor->node);
+ kfree(neighbor);
+}
+
+/**
+ * Clear entire neighborhood cache.
+ */
+static
+void __wlp_neighbors_release(struct wlp *wlp)
+{
+ struct wlp_neighbor_e *neighbor, *next;
+ if (list_empty(&wlp->neighbors))
+ return;
+ list_for_each_entry_safe(neighbor, next, &wlp->neighbors, node) {
+ __wlp_neighbor_release(neighbor);
+ }
+}
+
+static
+void wlp_neighbors_release(struct wlp *wlp)
+{
+ mutex_lock(&wlp->nbmutex);
+ __wlp_neighbors_release(wlp);
+ mutex_unlock(&wlp->nbmutex);
+}
+
+
+
+/**
+ * Send D1 message to neighbor, receive D2 message
+ *
+ * @neighbor: neighbor to which D1 message will be sent
+ * @wss: if not NULL, it is an enrollment request for this WSS
+ * @wssid: if wss not NULL, this is the wssid of the WSS in which we
+ * want to enroll
+ *
+ * A D1/D2 exchange is done for one of two reasons: discovery or
+ * enrollment. If done for discovery the D1 message is sent to the neighbor
+ * and the contents of the D2 response is stored in a temporary cache.
+ * If done for enrollment the @wss and @wssid are provided also. In this
+ * case the D1 message is sent to the neighbor, the D2 response is parsed
+ * for enrollment of the WSS with wssid.
+ *
+ * &wss->mutex is held
+ */
+static
+int wlp_d1d2_exchange(struct wlp *wlp, struct wlp_neighbor_e *neighbor,
+ struct wlp_wss *wss, struct wlp_uuid *wssid)
+{
+ int result;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ DECLARE_COMPLETION_ONSTACK(completion);
+ struct wlp_session session;
+ struct sk_buff *skb;
+ struct wlp_frame_assoc *resp;
+ struct uwb_dev_addr *dev_addr = &neighbor->uwb_dev->dev_addr;
+
+ mutex_lock(&wlp->mutex);
+ if (!wlp_uuid_is_set(&wlp->uuid)) {
+ dev_err(dev, "WLP: UUID is not set. Set via sysfs to "
+ "proceed.\n");
+ result = -ENXIO;
+ goto out;
+ }
+ /* Send D1 association frame */
+ result = wlp_send_assoc_frame(wlp, wss, dev_addr, WLP_ASSOC_D1);
+ if (result < 0) {
+ dev_err(dev, "Unable to send D1 frame to neighbor "
+ "%02x:%02x (%d)\n", dev_addr->data[1],
+ dev_addr->data[0], result);
+ d_printf(6, dev, "Add placeholders into buffer next to "
+ "neighbor information we have (dev address).\n");
+ goto out;
+ }
+ /* Create session, wait for response */
+ session.exp_message = WLP_ASSOC_D2;
+ session.cb = wlp_session_cb;
+ session.cb_priv = &completion;
+ session.neighbor_addr = *dev_addr;
+ BUG_ON(wlp->session != NULL);
+ wlp->session = &session;
+ /* Wait for D2/F0 frame */
+ result = wait_for_completion_interruptible_timeout(&completion,
+ WLP_PER_MSG_TIMEOUT * HZ);
+ if (result == 0) {
+ result = -ETIMEDOUT;
+ dev_err(dev, "Timeout while sending D1 to neighbor "
+ "%02x:%02x.\n", dev_addr->data[1],
+ dev_addr->data[0]);
+ goto error_session;
+ }
+ if (result < 0) {
+ dev_err(dev, "Unable to discover/enroll neighbor %02x:%02x.\n",
+ dev_addr->data[1], dev_addr->data[0]);
+ goto error_session;
+ }
+ /* Parse message in session->data: it will be either D2 or F0 */
+ skb = session.data;
+ resp = (void *) skb->data;
+ d_printf(6, dev, "Received response to D1 frame. \n");
+ d_dump(6, dev, skb->data, skb->len > 72 ? 72 : skb->len);
+
+ if (resp->type == WLP_ASSOC_F0) {
+ result = wlp_parse_f0(wlp, skb);
+ if (result < 0)
+ dev_err(dev, "WLP: Unable to parse F0 from neighbor "
+ "%02x:%02x.\n", dev_addr->data[1],
+ dev_addr->data[0]);
+ result = -EINVAL;
+ goto error_resp_parse;
+ }
+ if (wss == NULL) {
+ /* Discovery */
+ result = wlp_parse_d2_frame_to_cache(wlp, skb, neighbor);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to parse D2 message from "
+ "neighbor %02x:%02x for discovery.\n",
+ dev_addr->data[1], dev_addr->data[0]);
+ goto error_resp_parse;
+ }
+ } else {
+ /* Enrollment */
+ result = wlp_parse_d2_frame_to_enroll(wss, skb, neighbor,
+ wssid);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to parse D2 message from "
+ "neighbor %02x:%02x for enrollment.\n",
+ dev_addr->data[1], dev_addr->data[0]);
+ goto error_resp_parse;
+ }
+ }
+error_resp_parse:
+ kfree_skb(skb);
+error_session:
+ wlp->session = NULL;
+out:
+ mutex_unlock(&wlp->mutex);
+ return result;
+}
+
+/**
+ * Enroll into WSS of provided WSSID by using neighbor as registrar
+ *
+ * &wss->mutex is held
+ */
+int wlp_enroll_neighbor(struct wlp *wlp, struct wlp_neighbor_e *neighbor,
+ struct wlp_wss *wss, struct wlp_uuid *wssid)
+{
+ int result = 0;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ char buf[WLP_WSS_UUID_STRSIZE];
+ struct uwb_dev_addr *dev_addr = &neighbor->uwb_dev->dev_addr;
+ wlp_wss_uuid_print(buf, sizeof(buf), wssid);
+ d_fnstart(6, dev, "wlp %p, neighbor %p, wss %p, wssid %p (%s)\n",
+ wlp, neighbor, wss, wssid, buf);
+ d_printf(6, dev, "Complete me.\n");
+ result = wlp_d1d2_exchange(wlp, neighbor, wss, wssid);
+ if (result < 0) {
+ dev_err(dev, "WLP: D1/D2 message exchange for enrollment "
+ "failed. result = %d \n", result);
+ goto out;
+ }
+ if (wss->state != WLP_WSS_STATE_PART_ENROLLED) {
+ dev_err(dev, "WLP: Unable to enroll into WSS %s using "
+ "neighbor %02x:%02x. \n", buf,
+ dev_addr->data[1], dev_addr->data[0]);
+ result = -EINVAL;
+ goto out;
+ }
+ if (wss->secure_status == WLP_WSS_SECURE) {
+ dev_err(dev, "FIXME: need to complete secure enrollment.\n");
+ result = -EINVAL;
+ goto error;
+ } else {
+ wss->state = WLP_WSS_STATE_ENROLLED;
+ d_printf(2, dev, "WLP: Success Enrollment into unsecure WSS "
+ "%s using neighbor %02x:%02x. \n", buf,
+ dev_addr->data[1], dev_addr->data[0]);
+ }
+
+ d_fnend(6, dev, "wlp %p, neighbor %p, wss %p, wssid %p (%s)\n",
+ wlp, neighbor, wss, wssid, buf);
+out:
+ return result;
+error:
+ wlp_wss_reset(wss);
+ return result;
+}
+
+/**
+ * Discover WSS information of neighbor's active WSS
+ */
+static
+int wlp_discover_neighbor(struct wlp *wlp,
+ struct wlp_neighbor_e *neighbor)
+{
+ return wlp_d1d2_exchange(wlp, neighbor, NULL, NULL);
+}
+
+
+/**
+ * Each neighbor in the neighborhood cache is discoverable. Discover it.
+ *
+ * Discovery is done through sending of D1 association frame and parsing
+ * the D2 association frame response. Only wssid from D2 will be included
+ * in neighbor cache, rest is just displayed to user and forgotten.
+ *
+ * The discovery is not done in parallel. This is simple and enables us to
+ * maintain only one association context.
+ *
+ * The discovery of one neighbor does not affect the other, but if the
+ * discovery of a neighbor fails it is removed from the neighborhood cache.
+ */
+static
+int wlp_discover_all_neighbors(struct wlp *wlp)
+{
+ int result = 0;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct wlp_neighbor_e *neighbor, *next;
+
+ list_for_each_entry_safe(neighbor, next, &wlp->neighbors, node) {
+ result = wlp_discover_neighbor(wlp, neighbor);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to discover neighbor "
+ "%02x:%02x, removing from neighborhood. \n",
+ neighbor->uwb_dev->dev_addr.data[1],
+ neighbor->uwb_dev->dev_addr.data[0]);
+ __wlp_neighbor_release(neighbor);
+ }
+ }
+ return result;
+}
+
+static int wlp_add_neighbor_helper(struct device *dev, void *priv)
+{
+ struct wlp *wlp = priv;
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+
+ return wlp_add_neighbor(wlp, uwb_dev);
+}
+
+/**
+ * Discover WLP neighborhood
+ *
+ * Will send D1 association frame to all devices in beacon group that have
+ * discoverable bit set in WLP IE. D2 frames will be received, information
+ * displayed to user in @buf. Partial information (from D2 association
+ * frame) will be cached to assist with future association
+ * requests.
+ *
+ * The discovery of the WLP neighborhood is triggered by the user. This
+ * should occur infrequently and we thus free current cache and re-allocate
+ * memory if needed.
+ *
+ * If one neighbor fails during initial discovery (determining if it is a
+ * neighbor or not), we fail all - note that interaction with neighbor has
+ * not occured at this point so if a failure occurs we know something went wrong
+ * locally. We thus undo everything.
+ */
+ssize_t wlp_discover(struct wlp *wlp)
+{
+ int result = 0;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+
+ d_fnstart(6, dev, "wlp %p \n", wlp);
+ mutex_lock(&wlp->nbmutex);
+ /* Clear current neighborhood cache. */
+ __wlp_neighbors_release(wlp);
+ /* Determine which devices in neighborhood. Repopulate cache. */
+ result = uwb_dev_for_each(wlp->rc, wlp_add_neighbor_helper, wlp);
+ if (result < 0) {
+ /* May have partial neighbor information, release all. */
+ __wlp_neighbors_release(wlp);
+ goto error_dev_for_each;
+ }
+ /* Discover the properties of devices in neighborhood. */
+ result = wlp_discover_all_neighbors(wlp);
+ /* In case of failure we still print our partial results. */
+ if (result < 0) {
+ dev_err(dev, "Unable to fully discover neighborhood. \n");
+ result = 0;
+ }
+error_dev_for_each:
+ mutex_unlock(&wlp->nbmutex);
+ d_fnend(6, dev, "wlp %p \n", wlp);
+ return result;
+}
+
+/**
+ * Handle events from UWB stack
+ *
+ * We handle events conservatively. If a neighbor goes off the air we
+ * remove it from the neighborhood. If an association process is in
+ * progress this function will block waiting for the nbmutex to become
+ * free. The association process will thus be allowed to complete before it
+ * is removed.
+ */
+static
+void wlp_uwb_notifs_cb(void *_wlp, struct uwb_dev *uwb_dev,
+ enum uwb_notifs event)
+{
+ struct wlp *wlp = _wlp;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct wlp_neighbor_e *neighbor, *next;
+ int result;
+ switch (event) {
+ case UWB_NOTIF_ONAIR:
+ d_printf(6, dev, "UWB device %02x:%02x is onair\n",
+ uwb_dev->dev_addr.data[1],
+ uwb_dev->dev_addr.data[0]);
+ result = wlp_eda_create_node(&wlp->eda,
+ uwb_dev->mac_addr.data,
+ &uwb_dev->dev_addr);
+ if (result < 0)
+ dev_err(dev, "WLP: Unable to add new neighbor "
+ "%02x:%02x to EDA cache.\n",
+ uwb_dev->dev_addr.data[1],
+ uwb_dev->dev_addr.data[0]);
+ break;
+ case UWB_NOTIF_OFFAIR:
+ d_printf(6, dev, "UWB device %02x:%02x is offair\n",
+ uwb_dev->dev_addr.data[1],
+ uwb_dev->dev_addr.data[0]);
+ wlp_eda_rm_node(&wlp->eda, &uwb_dev->dev_addr);
+ mutex_lock(&wlp->nbmutex);
+ list_for_each_entry_safe(neighbor, next, &wlp->neighbors,
+ node) {
+ if (neighbor->uwb_dev == uwb_dev) {
+ d_printf(6, dev, "Removing device from "
+ "neighborhood.\n");
+ __wlp_neighbor_release(neighbor);
+ }
+ }
+ mutex_unlock(&wlp->nbmutex);
+ break;
+ default:
+ dev_err(dev, "don't know how to handle event %d from uwb\n",
+ event);
+ }
+}
+
+int wlp_setup(struct wlp *wlp, struct uwb_rc *rc)
+{
+ struct device *dev = &rc->uwb_dev.dev;
+ int result;
+
+ d_fnstart(6, dev, "wlp %p\n", wlp);
+ BUG_ON(wlp->fill_device_info == NULL);
+ BUG_ON(wlp->xmit_frame == NULL);
+ BUG_ON(wlp->stop_queue == NULL);
+ BUG_ON(wlp->start_queue == NULL);
+ wlp->rc = rc;
+ wlp_eda_init(&wlp->eda);/* Set up address cache */
+ wlp->uwb_notifs_handler.cb = wlp_uwb_notifs_cb;
+ wlp->uwb_notifs_handler.data = wlp;
+ uwb_notifs_register(rc, &wlp->uwb_notifs_handler);
+
+ uwb_pal_init(&wlp->pal);
+ result = uwb_pal_register(rc, &wlp->pal);
+ if (result < 0)
+ uwb_notifs_deregister(wlp->rc, &wlp->uwb_notifs_handler);
+
+ d_fnend(6, dev, "wlp %p, result = %d\n", wlp, result);
+ return result;
+}
+EXPORT_SYMBOL_GPL(wlp_setup);
+
+void wlp_remove(struct wlp *wlp)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ d_fnstart(6, dev, "wlp %p\n", wlp);
+ wlp_neighbors_release(wlp);
+ uwb_pal_unregister(wlp->rc, &wlp->pal);
+ uwb_notifs_deregister(wlp->rc, &wlp->uwb_notifs_handler);
+ wlp_eda_release(&wlp->eda);
+ mutex_lock(&wlp->mutex);
+ if (wlp->dev_info != NULL)
+ kfree(wlp->dev_info);
+ mutex_unlock(&wlp->mutex);
+ wlp->rc = NULL;
+ /* We have to use NULL here because this function can be called
+ * when the device disappeared. */
+ d_fnend(6, NULL, "wlp %p\n", wlp);
+}
+EXPORT_SYMBOL_GPL(wlp_remove);
+
+/**
+ * wlp_reset_all - reset the WLP hardware
+ * @wlp: the WLP device to reset.
+ *
+ * This schedules a full hardware reset of the WLP device. The radio
+ * controller and any other PALs will also be reset.
+ */
+void wlp_reset_all(struct wlp *wlp)
+{
+ uwb_rc_reset_all(wlp->rc);
+}
+EXPORT_SYMBOL_GPL(wlp_reset_all);
diff --git a/drivers/uwb/wlp/wss-lc.c b/drivers/uwb/wlp/wss-lc.c
new file mode 100644
index 000000000000..96b18c9bd6e9
--- /dev/null
+++ b/drivers/uwb/wlp/wss-lc.c
@@ -0,0 +1,1055 @@
+/*
+ * WiMedia Logical Link Control Protocol (WLP)
+ *
+ * Copyright (C) 2007 Intel Corporation
+ * Reinette Chatre <reinette.chatre@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 WLP association protocol.
+ *
+ * FIXME: Docs
+ *
+ * A UWB network interface will configure a WSS through wlp_wss_setup() after
+ * the interface has been assigned a MAC address, typically after
+ * "ifconfig" has been called. When the interface goes down it should call
+ * wlp_wss_remove().
+ *
+ * When the WSS is ready for use the user interacts via sysfs to create,
+ * discover, and activate WSS.
+ *
+ * wlp_wss_enroll_activate()
+ *
+ * wlp_wss_create_activate()
+ * wlp_wss_set_wssid_hash()
+ * wlp_wss_comp_wssid_hash()
+ * wlp_wss_sel_bcast_addr()
+ * wlp_wss_sysfs_add()
+ *
+ * Called when no more references to WSS exist:
+ * wlp_wss_release()
+ * wlp_wss_reset()
+ */
+
+#include <linux/etherdevice.h> /* for is_valid_ether_addr */
+#include <linux/skbuff.h>
+#include <linux/wlp.h>
+#define D_LOCAL 5
+#include <linux/uwb/debug.h>
+#include "wlp-internal.h"
+
+
+size_t wlp_wss_key_print(char *buf, size_t bufsize, u8 *key)
+{
+ size_t result;
+
+ result = scnprintf(buf, bufsize,
+ "%02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x",
+ key[0], key[1], key[2], key[3],
+ key[4], key[5], key[6], key[7],
+ key[8], key[9], key[10], key[11],
+ key[12], key[13], key[14], key[15]);
+ return result;
+}
+
+/**
+ * Compute WSSID hash
+ * WLP Draft 0.99 [7.2.1]
+ *
+ * The WSSID hash for a WSSID is the result of an octet-wise exclusive-OR
+ * of all octets in the WSSID.
+ */
+static
+u8 wlp_wss_comp_wssid_hash(struct wlp_uuid *wssid)
+{
+ return wssid->data[0] ^ wssid->data[1] ^ wssid->data[2]
+ ^ wssid->data[3] ^ wssid->data[4] ^ wssid->data[5]
+ ^ wssid->data[6] ^ wssid->data[7] ^ wssid->data[8]
+ ^ wssid->data[9] ^ wssid->data[10] ^ wssid->data[11]
+ ^ wssid->data[12] ^ wssid->data[13] ^ wssid->data[14]
+ ^ wssid->data[15];
+}
+
+/**
+ * Select a multicast EUI-48 for the WSS broadcast address.
+ * WLP Draft 0.99 [7.2.1]
+ *
+ * Selected based on the WiMedia Alliance OUI, 00-13-88, within the WLP
+ * range, [01-13-88-00-01-00, 01-13-88-00-01-FF] inclusive.
+ *
+ * This address is currently hardcoded.
+ * FIXME?
+ */
+static
+struct uwb_mac_addr wlp_wss_sel_bcast_addr(struct wlp_wss *wss)
+{
+ struct uwb_mac_addr bcast = {
+ .data = { 0x01, 0x13, 0x88, 0x00, 0x01, 0x00 }
+ };
+ return bcast;
+}
+
+/**
+ * Clear the contents of the WSS structure - all except kobj, mutex, virtual
+ *
+ * We do not want to reinitialize - the internal kobj should not change as
+ * it still points to the parent received during setup. The mutex should
+ * remain also. We thus just reset values individually.
+ * The virutal address assigned to WSS will remain the same for the
+ * lifetime of the WSS. We only reset the fields that can change during its
+ * lifetime.
+ */
+void wlp_wss_reset(struct wlp_wss *wss)
+{
+ struct wlp *wlp = container_of(wss, struct wlp, wss);
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ d_fnstart(5, dev, "wss (%p) \n", wss);
+ memset(&wss->wssid, 0, sizeof(wss->wssid));
+ wss->hash = 0;
+ memset(&wss->name[0], 0, sizeof(wss->name));
+ memset(&wss->bcast, 0, sizeof(wss->bcast));
+ wss->secure_status = WLP_WSS_UNSECURE;
+ memset(&wss->master_key[0], 0, sizeof(wss->master_key));
+ wss->tag = 0;
+ wss->state = WLP_WSS_STATE_NONE;
+ d_fnend(5, dev, "wss (%p) \n", wss);
+}
+
+/**
+ * Create sysfs infrastructure for WSS
+ *
+ * The WSS is configured to have the interface as parent (see wlp_wss_setup())
+ * a new sysfs directory that includes wssid as its name is created in the
+ * interface's sysfs directory. The group of files interacting with WSS are
+ * created also.
+ */
+static
+int wlp_wss_sysfs_add(struct wlp_wss *wss, char *wssid_str)
+{
+ struct wlp *wlp = container_of(wss, struct wlp, wss);
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ int result;
+
+ d_fnstart(5, dev, "wss (%p), wssid: %s\n", wss, wssid_str);
+ result = kobject_set_name(&wss->kobj, "wss-%s", wssid_str);
+ if (result < 0)
+ return result;
+ wss->kobj.ktype = &wss_ktype;
+ result = kobject_init_and_add(&wss->kobj,
+ &wss_ktype, wss->kobj.parent, "wlp");
+ if (result < 0) {
+ dev_err(dev, "WLP: Cannot register WSS kobject.\n");
+ goto error_kobject_register;
+ }
+ result = sysfs_create_group(&wss->kobj, &wss_attr_group);
+ if (result < 0) {
+ dev_err(dev, "WLP: Cannot register WSS attributes: %d\n",
+ result);
+ goto error_sysfs_create_group;
+ }
+ d_fnend(5, dev, "Completed. result = %d \n", result);
+ return 0;
+error_sysfs_create_group:
+
+ kobject_put(&wss->kobj); /* will free name if needed */
+ return result;
+error_kobject_register:
+ kfree(wss->kobj.name);
+ wss->kobj.name = NULL;
+ wss->kobj.ktype = NULL;
+ return result;
+}
+
+
+/**
+ * Release WSS
+ *
+ * No more references exist to this WSS. We should undo everything that was
+ * done in wlp_wss_create_activate() except removing the group. The group
+ * is not removed because an object can be unregistered before the group is
+ * created. We also undo any additional operations on the WSS after this
+ * (addition of members).
+ *
+ * If memory was allocated for the kobject's name then it will
+ * be freed by the kobject system during this time.
+ *
+ * The EDA cache is removed and reinitilized when the WSS is removed. We
+ * thus loose knowledge of members of this WSS at that time and need not do
+ * it here.
+ */
+void wlp_wss_release(struct kobject *kobj)
+{
+ struct wlp_wss *wss = container_of(kobj, struct wlp_wss, kobj);
+
+ wlp_wss_reset(wss);
+}
+
+/**
+ * Enroll into a WSS using provided neighbor as registrar
+ *
+ * First search the neighborhood information to learn which neighbor is
+ * referred to, next proceed with enrollment.
+ *
+ * &wss->mutex is held
+ */
+static
+int wlp_wss_enroll_target(struct wlp_wss *wss, struct wlp_uuid *wssid,
+ struct uwb_dev_addr *dest)
+{
+ struct wlp *wlp = container_of(wss, struct wlp, wss);
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct wlp_neighbor_e *neighbor;
+ char buf[WLP_WSS_UUID_STRSIZE];
+ int result = -ENXIO;
+ struct uwb_dev_addr *dev_addr;
+
+ wlp_wss_uuid_print(buf, sizeof(buf), wssid);
+ d_fnstart(5, dev, "wss %p, wssid %s, registrar %02x:%02x \n",
+ wss, buf, dest->data[1], dest->data[0]);
+ mutex_lock(&wlp->nbmutex);
+ list_for_each_entry(neighbor, &wlp->neighbors, node) {
+ dev_addr = &neighbor->uwb_dev->dev_addr;
+ if (!memcmp(dest, dev_addr, sizeof(*dest))) {
+ d_printf(5, dev, "Neighbor %02x:%02x is valid, "
+ "enrolling. \n",
+ dev_addr->data[1], dev_addr->data[0]);
+ result = wlp_enroll_neighbor(wlp, neighbor, wss,
+ wssid);
+ break;
+ }
+ }
+ if (result == -ENXIO)
+ dev_err(dev, "WLP: Cannot find neighbor %02x:%02x. \n",
+ dest->data[1], dest->data[0]);
+ mutex_unlock(&wlp->nbmutex);
+ d_fnend(5, dev, "wss %p, wssid %s, registrar %02x:%02x, result %d \n",
+ wss, buf, dest->data[1], dest->data[0], result);
+ return result;
+}
+
+/**
+ * Enroll into a WSS previously discovered
+ *
+ * User provides WSSID of WSS, search for neighbor that has this WSS
+ * activated and attempt to enroll.
+ *
+ * &wss->mutex is held
+ */
+static
+int wlp_wss_enroll_discovered(struct wlp_wss *wss, struct wlp_uuid *wssid)
+{
+ struct wlp *wlp = container_of(wss, struct wlp, wss);
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct wlp_neighbor_e *neighbor;
+ struct wlp_wssid_e *wssid_e;
+ char buf[WLP_WSS_UUID_STRSIZE];
+ int result = -ENXIO;
+
+ wlp_wss_uuid_print(buf, sizeof(buf), wssid);
+ d_fnstart(5, dev, "wss %p, wssid %s \n", wss, buf);
+ mutex_lock(&wlp->nbmutex);
+ list_for_each_entry(neighbor, &wlp->neighbors, node) {
+ list_for_each_entry(wssid_e, &neighbor->wssid, node) {
+ if (!memcmp(wssid, &wssid_e->wssid, sizeof(*wssid))) {
+ d_printf(5, dev, "Found WSSID %s in neighbor "
+ "%02x:%02x cache. \n", buf,
+ neighbor->uwb_dev->dev_addr.data[1],
+ neighbor->uwb_dev->dev_addr.data[0]);
+ result = wlp_enroll_neighbor(wlp, neighbor,
+ wss, wssid);
+ if (result == 0) /* enrollment success */
+ goto out;
+ break;
+ }
+ }
+ }
+out:
+ if (result == -ENXIO)
+ dev_err(dev, "WLP: Cannot find WSSID %s in cache. \n", buf);
+ mutex_unlock(&wlp->nbmutex);
+ d_fnend(5, dev, "wss %p, wssid %s, result %d \n", wss, buf, result);
+ return result;
+}
+
+/**
+ * Enroll into WSS with provided WSSID, registrar may be provided
+ *
+ * @wss: out WSS that will be enrolled
+ * @wssid: wssid of neighboring WSS that we want to enroll in
+ * @devaddr: registrar can be specified, will be broadcast (ff:ff) if any
+ * neighbor can be used as registrar.
+ *
+ * &wss->mutex is held
+ */
+static
+int wlp_wss_enroll(struct wlp_wss *wss, struct wlp_uuid *wssid,
+ struct uwb_dev_addr *devaddr)
+{
+ int result;
+ struct wlp *wlp = container_of(wss, struct wlp, wss);
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ char buf[WLP_WSS_UUID_STRSIZE];
+ struct uwb_dev_addr bcast = {.data = {0xff, 0xff} };
+
+ wlp_wss_uuid_print(buf, sizeof(buf), wssid);
+ if (wss->state != WLP_WSS_STATE_NONE) {
+ dev_err(dev, "WLP: Already enrolled in WSS %s.\n", buf);
+ result = -EEXIST;
+ goto error;
+ }
+ if (!memcmp(&bcast, devaddr, sizeof(bcast))) {
+ d_printf(5, dev, "Request to enroll in discovered WSS "
+ "with WSSID %s \n", buf);
+ result = wlp_wss_enroll_discovered(wss, wssid);
+ } else {
+ d_printf(5, dev, "Request to enroll in WSSID %s with "
+ "registrar %02x:%02x\n", buf, devaddr->data[1],
+ devaddr->data[0]);
+ result = wlp_wss_enroll_target(wss, wssid, devaddr);
+ }
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to enroll into WSS %s, result %d \n",
+ buf, result);
+ goto error;
+ }
+ d_printf(2, dev, "Successfully enrolled into WSS %s \n", buf);
+ result = wlp_wss_sysfs_add(wss, buf);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to set up sysfs for WSS kobject.\n");
+ wlp_wss_reset(wss);
+ }
+error:
+ return result;
+
+}
+
+/**
+ * Activate given WSS
+ *
+ * Prior to activation a WSS must be enrolled. To activate a WSS a device
+ * includes the WSS hash in the WLP IE in its beacon in each superframe.
+ * WLP 0.99 [7.2.5].
+ *
+ * The WSS tag is also computed at this time. We only support one activated
+ * WSS so we can use the hash as a tag - there will never be a conflict.
+ *
+ * We currently only support one activated WSS so only one WSS hash is
+ * included in the WLP IE.
+ */
+static
+int wlp_wss_activate(struct wlp_wss *wss)
+{
+ struct wlp *wlp = container_of(wss, struct wlp, wss);
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct uwb_rc *uwb_rc = wlp->rc;
+ int result;
+ struct {
+ struct wlp_ie wlp_ie;
+ u8 hash; /* only include one hash */
+ } ie_data;
+
+ d_fnstart(5, dev, "Activating WSS %p. \n", wss);
+ BUG_ON(wss->state != WLP_WSS_STATE_ENROLLED);
+ wss->hash = wlp_wss_comp_wssid_hash(&wss->wssid);
+ wss->tag = wss->hash;
+ memset(&ie_data, 0, sizeof(ie_data));
+ ie_data.wlp_ie.hdr.element_id = UWB_IE_WLP;
+ ie_data.wlp_ie.hdr.length = sizeof(ie_data) - sizeof(struct uwb_ie_hdr);
+ wlp_ie_set_hash_length(&ie_data.wlp_ie, sizeof(ie_data.hash));
+ ie_data.hash = wss->hash;
+ result = uwb_rc_ie_add(uwb_rc, &ie_data.wlp_ie.hdr,
+ sizeof(ie_data));
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to add WLP IE to beacon. "
+ "result = %d.\n", result);
+ goto error_wlp_ie;
+ }
+ wss->state = WLP_WSS_STATE_ACTIVE;
+ result = 0;
+error_wlp_ie:
+ d_fnend(5, dev, "Activating WSS %p, result = %d \n", wss, result);
+ return result;
+}
+
+/**
+ * Enroll in and activate WSS identified by provided WSSID
+ *
+ * The neighborhood cache should contain a list of all neighbors and the
+ * WSS they have activated. Based on that cache we search which neighbor we
+ * can perform the association process with. The user also has option to
+ * specify which neighbor it prefers as registrar.
+ * Successful enrollment is followed by activation.
+ * Successful activation will create the sysfs directory containing
+ * specific information regarding this WSS.
+ */
+int wlp_wss_enroll_activate(struct wlp_wss *wss, struct wlp_uuid *wssid,
+ struct uwb_dev_addr *devaddr)
+{
+ struct wlp *wlp = container_of(wss, struct wlp, wss);
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ int result = 0;
+ char buf[WLP_WSS_UUID_STRSIZE];
+
+ d_fnstart(5, dev, "Enrollment and activation requested. \n");
+ mutex_lock(&wss->mutex);
+ result = wlp_wss_enroll(wss, wssid, devaddr);
+ if (result < 0) {
+ wlp_wss_uuid_print(buf, sizeof(buf), &wss->wssid);
+ dev_err(dev, "WLP: Enrollment into WSS %s failed.\n", buf);
+ goto error_enroll;
+ }
+ result = wlp_wss_activate(wss);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to activate WSS. Undoing enrollment "
+ "result = %d \n", result);
+ /* Undo enrollment */
+ wlp_wss_reset(wss);
+ goto error_activate;
+ }
+error_activate:
+error_enroll:
+ mutex_unlock(&wss->mutex);
+ d_fnend(5, dev, "Completed. result = %d \n", result);
+ return result;
+}
+
+/**
+ * Create, enroll, and activate a new WSS
+ *
+ * @wssid: new wssid provided by user
+ * @name: WSS name requested by used.
+ * @sec_status: security status requested by user
+ *
+ * A user requested the creation of a new WSS. All operations are done
+ * locally. The new WSS will be stored locally, the hash will be included
+ * in the WLP IE, and the sysfs infrastructure for this WSS will be
+ * created.
+ */
+int wlp_wss_create_activate(struct wlp_wss *wss, struct wlp_uuid *wssid,
+ char *name, unsigned sec_status, unsigned accept)
+{
+ struct wlp *wlp = container_of(wss, struct wlp, wss);
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ int result = 0;
+ char buf[WLP_WSS_UUID_STRSIZE];
+ d_fnstart(5, dev, "Request to create new WSS.\n");
+ result = wlp_wss_uuid_print(buf, sizeof(buf), wssid);
+ d_printf(5, dev, "Request to create WSS: WSSID=%s, name=%s, "
+ "sec_status=%u, accepting enrollment=%u \n",
+ buf, name, sec_status, accept);
+ if (!mutex_trylock(&wss->mutex)) {
+ dev_err(dev, "WLP: WLP association session in progress.\n");
+ return -EBUSY;
+ }
+ if (wss->state != WLP_WSS_STATE_NONE) {
+ dev_err(dev, "WLP: WSS already exists. Not creating new.\n");
+ result = -EEXIST;
+ goto out;
+ }
+ if (wss->kobj.parent == NULL) {
+ dev_err(dev, "WLP: WSS parent not ready. Is network interface "
+ "up?\n");
+ result = -ENXIO;
+ goto out;
+ }
+ if (sec_status == WLP_WSS_SECURE) {
+ dev_err(dev, "WLP: FIXME Creation of secure WSS not "
+ "supported yet.\n");
+ result = -EINVAL;
+ goto out;
+ }
+ wss->wssid = *wssid;
+ memcpy(wss->name, name, sizeof(wss->name));
+ wss->bcast = wlp_wss_sel_bcast_addr(wss);
+ wss->secure_status = sec_status;
+ wss->accept_enroll = accept;
+ /*wss->virtual_addr is initialized in call to wlp_wss_setup*/
+ /* sysfs infrastructure */
+ result = wlp_wss_sysfs_add(wss, buf);
+ if (result < 0) {
+ dev_err(dev, "Cannot set up sysfs for WSS kobject.\n");
+ wlp_wss_reset(wss);
+ goto out;
+ } else
+ result = 0;
+ wss->state = WLP_WSS_STATE_ENROLLED;
+ result = wlp_wss_activate(wss);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to activate WSS. Undoing "
+ "enrollment\n");
+ wlp_wss_reset(wss);
+ goto out;
+ }
+ result = 0;
+out:
+ mutex_unlock(&wss->mutex);
+ d_fnend(5, dev, "Completed. result = %d \n", result);
+ return result;
+}
+
+/**
+ * Determine if neighbor has WSS activated
+ *
+ * @returns: 1 if neighbor has WSS activated, zero otherwise
+ *
+ * This can be done in two ways:
+ * - send a C1 frame, parse C2/F0 response
+ * - examine the WLP IE sent by the neighbor
+ *
+ * The WLP IE is not fully supported in hardware so we use the C1/C2 frame
+ * exchange to determine if a WSS is activated. Using the WLP IE should be
+ * faster and should be used when it becomes possible.
+ */
+int wlp_wss_is_active(struct wlp *wlp, struct wlp_wss *wss,
+ struct uwb_dev_addr *dev_addr)
+{
+ int result = 0;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ char buf[WLP_WSS_UUID_STRSIZE];
+ DECLARE_COMPLETION_ONSTACK(completion);
+ struct wlp_session session;
+ struct sk_buff *skb;
+ struct wlp_frame_assoc *resp;
+ struct wlp_uuid wssid;
+
+ wlp_wss_uuid_print(buf, sizeof(buf), &wss->wssid);
+ d_fnstart(5, dev, "wlp %p, wss %p (wssid %s), neighbor %02x:%02x \n",
+ wlp, wss, buf, dev_addr->data[1], dev_addr->data[0]);
+ mutex_lock(&wlp->mutex);
+ /* Send C1 association frame */
+ result = wlp_send_assoc_frame(wlp, wss, dev_addr, WLP_ASSOC_C1);
+ if (result < 0) {
+ dev_err(dev, "Unable to send C1 frame to neighbor "
+ "%02x:%02x (%d)\n", dev_addr->data[1],
+ dev_addr->data[0], result);
+ result = 0;
+ goto out;
+ }
+ /* Create session, wait for response */
+ session.exp_message = WLP_ASSOC_C2;
+ session.cb = wlp_session_cb;
+ session.cb_priv = &completion;
+ session.neighbor_addr = *dev_addr;
+ BUG_ON(wlp->session != NULL);
+ wlp->session = &session;
+ /* Wait for C2/F0 frame */
+ result = wait_for_completion_interruptible_timeout(&completion,
+ WLP_PER_MSG_TIMEOUT * HZ);
+ if (result == 0) {
+ dev_err(dev, "Timeout while sending C1 to neighbor "
+ "%02x:%02x.\n", dev_addr->data[1],
+ dev_addr->data[0]);
+ goto out;
+ }
+ if (result < 0) {
+ dev_err(dev, "Unable to send C1 to neighbor %02x:%02x.\n",
+ dev_addr->data[1], dev_addr->data[0]);
+ result = 0;
+ goto out;
+ }
+ /* Parse message in session->data: it will be either C2 or F0 */
+ skb = session.data;
+ resp = (void *) skb->data;
+ d_printf(5, dev, "Received response to C1 frame. \n");
+ d_dump(5, dev, skb->data, skb->len > 72 ? 72 : skb->len);
+ if (resp->type == WLP_ASSOC_F0) {
+ result = wlp_parse_f0(wlp, skb);
+ if (result < 0)
+ dev_err(dev, "WLP: unable to parse incoming F0 "
+ "frame from neighbor %02x:%02x.\n",
+ dev_addr->data[1], dev_addr->data[0]);
+ result = 0;
+ goto error_resp_parse;
+ }
+ /* WLP version and message type fields have already been parsed */
+ result = wlp_get_wssid(wlp, (void *)resp + sizeof(*resp), &wssid,
+ skb->len - sizeof(*resp));
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WSSID from C2 frame.\n");
+ result = 0;
+ goto error_resp_parse;
+ }
+ if (!memcmp(&wssid, &wss->wssid, sizeof(wssid))) {
+ d_printf(5, dev, "WSSID in C2 frame matches local "
+ "active WSS.\n");
+ result = 1;
+ } else {
+ dev_err(dev, "WLP: Received a C2 frame without matching "
+ "WSSID.\n");
+ result = 0;
+ }
+error_resp_parse:
+ kfree_skb(skb);
+out:
+ wlp->session = NULL;
+ mutex_unlock(&wlp->mutex);
+ d_fnend(5, dev, "wlp %p, wss %p (wssid %s), neighbor %02x:%02x \n",
+ wlp, wss, buf, dev_addr->data[1], dev_addr->data[0]);
+ return result;
+}
+
+/**
+ * Activate connection with neighbor by updating EDA cache
+ *
+ * @wss: local WSS to which neighbor wants to connect
+ * @dev_addr: neighbor's address
+ * @wssid: neighbor's WSSID - must be same as our WSS's WSSID
+ * @tag: neighbor's WSS tag used to identify frames transmitted by it
+ * @virt_addr: neighbor's virtual EUI-48
+ */
+static
+int wlp_wss_activate_connection(struct wlp *wlp, struct wlp_wss *wss,
+ struct uwb_dev_addr *dev_addr,
+ struct wlp_uuid *wssid, u8 *tag,
+ struct uwb_mac_addr *virt_addr)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ int result = 0;
+ char buf[WLP_WSS_UUID_STRSIZE];
+ wlp_wss_uuid_print(buf, sizeof(buf), wssid);
+ d_fnstart(5, dev, "wlp %p, wss %p, wssid %s, tag %u, virtual "
+ "%02x:%02x:%02x:%02x:%02x:%02x \n", wlp, wss, buf, *tag,
+ virt_addr->data[0], virt_addr->data[1], virt_addr->data[2],
+ virt_addr->data[3], virt_addr->data[4], virt_addr->data[5]);
+
+ if (!memcmp(wssid, &wss->wssid, sizeof(*wssid))) {
+ d_printf(5, dev, "WSSID from neighbor frame matches local "
+ "active WSS.\n");
+ /* Update EDA cache */
+ result = wlp_eda_update_node(&wlp->eda, dev_addr, wss,
+ (void *) virt_addr->data, *tag,
+ WLP_WSS_CONNECTED);
+ if (result < 0)
+ dev_err(dev, "WLP: Unable to update EDA cache "
+ "with new connected neighbor information.\n");
+ } else {
+ dev_err(dev, "WLP: Neighbor does not have matching "
+ "WSSID.\n");
+ result = -EINVAL;
+ }
+
+ d_fnend(5, dev, "wlp %p, wss %p, wssid %s, tag %u, virtual "
+ "%02x:%02x:%02x:%02x:%02x:%02x, result = %d \n",
+ wlp, wss, buf, *tag,
+ virt_addr->data[0], virt_addr->data[1], virt_addr->data[2],
+ virt_addr->data[3], virt_addr->data[4], virt_addr->data[5],
+ result);
+
+ return result;
+}
+
+/**
+ * Connect to WSS neighbor
+ *
+ * Use C3/C4 exchange to determine if neighbor has WSS activated and
+ * retrieve the WSS tag and virtual EUI-48 of the neighbor.
+ */
+static
+int wlp_wss_connect_neighbor(struct wlp *wlp, struct wlp_wss *wss,
+ struct uwb_dev_addr *dev_addr)
+{
+ int result;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ char buf[WLP_WSS_UUID_STRSIZE];
+ struct wlp_uuid wssid;
+ u8 tag;
+ struct uwb_mac_addr virt_addr;
+ DECLARE_COMPLETION_ONSTACK(completion);
+ struct wlp_session session;
+ struct wlp_frame_assoc *resp;
+ struct sk_buff *skb;
+
+ wlp_wss_uuid_print(buf, sizeof(buf), &wss->wssid);
+ d_fnstart(5, dev, "wlp %p, wss %p (wssid %s), neighbor %02x:%02x \n",
+ wlp, wss, buf, dev_addr->data[1], dev_addr->data[0]);
+ mutex_lock(&wlp->mutex);
+ /* Send C3 association frame */
+ result = wlp_send_assoc_frame(wlp, wss, dev_addr, WLP_ASSOC_C3);
+ if (result < 0) {
+ dev_err(dev, "Unable to send C3 frame to neighbor "
+ "%02x:%02x (%d)\n", dev_addr->data[1],
+ dev_addr->data[0], result);
+ goto out;
+ }
+ /* Create session, wait for response */
+ session.exp_message = WLP_ASSOC_C4;
+ session.cb = wlp_session_cb;
+ session.cb_priv = &completion;
+ session.neighbor_addr = *dev_addr;
+ BUG_ON(wlp->session != NULL);
+ wlp->session = &session;
+ /* Wait for C4/F0 frame */
+ result = wait_for_completion_interruptible_timeout(&completion,
+ WLP_PER_MSG_TIMEOUT * HZ);
+ if (result == 0) {
+ dev_err(dev, "Timeout while sending C3 to neighbor "
+ "%02x:%02x.\n", dev_addr->data[1],
+ dev_addr->data[0]);
+ result = -ETIMEDOUT;
+ goto out;
+ }
+ if (result < 0) {
+ dev_err(dev, "Unable to send C3 to neighbor %02x:%02x.\n",
+ dev_addr->data[1], dev_addr->data[0]);
+ goto out;
+ }
+ /* Parse message in session->data: it will be either C4 or F0 */
+ skb = session.data;
+ resp = (void *) skb->data;
+ d_printf(5, dev, "Received response to C3 frame. \n");
+ d_dump(5, dev, skb->data, skb->len > 72 ? 72 : skb->len);
+ if (resp->type == WLP_ASSOC_F0) {
+ result = wlp_parse_f0(wlp, skb);
+ if (result < 0)
+ dev_err(dev, "WLP: unable to parse incoming F0 "
+ "frame from neighbor %02x:%02x.\n",
+ dev_addr->data[1], dev_addr->data[0]);
+ result = -EINVAL;
+ goto error_resp_parse;
+ }
+ result = wlp_parse_c3c4_frame(wlp, skb, &wssid, &tag, &virt_addr);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to parse C4 frame from neighbor.\n");
+ goto error_resp_parse;
+ }
+ result = wlp_wss_activate_connection(wlp, wss, dev_addr, &wssid, &tag,
+ &virt_addr);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to activate connection to "
+ "neighbor %02x:%02x.\n", dev_addr->data[1],
+ dev_addr->data[0]);
+ goto error_resp_parse;
+ }
+error_resp_parse:
+ kfree_skb(skb);
+out:
+ /* Record that we unsuccessfully tried to connect to this neighbor */
+ if (result < 0)
+ wlp_eda_update_node_state(&wlp->eda, dev_addr,
+ WLP_WSS_CONNECT_FAILED);
+ wlp->session = NULL;
+ mutex_unlock(&wlp->mutex);
+ d_fnend(5, dev, "wlp %p, wss %p (wssid %s), neighbor %02x:%02x \n",
+ wlp, wss, buf, dev_addr->data[1], dev_addr->data[0]);
+ return result;
+}
+
+/**
+ * Connect to neighbor with common WSS, send pending frame
+ *
+ * This function is scheduled when a frame is destined to a neighbor with
+ * which we do not have a connection. A copy of the EDA cache entry is
+ * provided - not the actual cache entry (because it is protected by a
+ * spinlock).
+ *
+ * First determine if neighbor has the same WSS activated, connect if it
+ * does. The C3/C4 exchange is dual purpose to determine if neighbor has
+ * WSS activated and proceed with the connection.
+ *
+ * The frame that triggered the connection setup is sent after connection
+ * setup.
+ *
+ * network queue is stopped - we need to restart when done
+ *
+ */
+static
+void wlp_wss_connect_send(struct work_struct *ws)
+{
+ struct wlp_assoc_conn_ctx *conn_ctx = container_of(ws,
+ struct wlp_assoc_conn_ctx,
+ ws);
+ struct wlp *wlp = conn_ctx->wlp;
+ struct sk_buff *skb = conn_ctx->skb;
+ struct wlp_eda_node *eda_entry = &conn_ctx->eda_entry;
+ struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr;
+ struct wlp_wss *wss = &wlp->wss;
+ int result;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ char buf[WLP_WSS_UUID_STRSIZE];
+
+ mutex_lock(&wss->mutex);
+ wlp_wss_uuid_print(buf, sizeof(buf), &wss->wssid);
+ d_fnstart(5, dev, "wlp %p, wss %p (wssid %s), neighbor %02x:%02x \n",
+ wlp, wss, buf, dev_addr->data[1], dev_addr->data[0]);
+ if (wss->state < WLP_WSS_STATE_ACTIVE) {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Attempting to connect with "
+ "WSS that is not active or connected.\n");
+ dev_kfree_skb(skb);
+ goto out;
+ }
+ /* Establish connection - send C3 rcv C4 */
+ result = wlp_wss_connect_neighbor(wlp, wss, dev_addr);
+ if (result < 0) {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Unable to establish connection "
+ "with neighbor %02x:%02x.\n",
+ dev_addr->data[1], dev_addr->data[0]);
+ dev_kfree_skb(skb);
+ goto out;
+ }
+ /* EDA entry changed, update the local copy being used */
+ result = wlp_copy_eda_node(&wlp->eda, dev_addr, eda_entry);
+ if (result < 0) {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Cannot find EDA entry for "
+ "neighbor %02x:%02x \n",
+ dev_addr->data[1], dev_addr->data[0]);
+ }
+ result = wlp_wss_prep_hdr(wlp, eda_entry, skb);
+ if (result < 0) {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Unable to prepare frame header for "
+ "transmission (neighbor %02x:%02x). \n",
+ dev_addr->data[1], dev_addr->data[0]);
+ dev_kfree_skb(skb);
+ goto out;
+ }
+ BUG_ON(wlp->xmit_frame == NULL);
+ result = wlp->xmit_frame(wlp, skb, dev_addr);
+ if (result < 0) {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Unable to transmit frame: %d\n",
+ result);
+ if (result == -ENXIO)
+ dev_err(dev, "WLP: Is network interface up? \n");
+ /* We could try again ... */
+ dev_kfree_skb(skb);/*we need to free if tx fails */
+ }
+out:
+ kfree(conn_ctx);
+ BUG_ON(wlp->start_queue == NULL);
+ wlp->start_queue(wlp);
+ mutex_unlock(&wss->mutex);
+ d_fnend(5, dev, "wlp %p, wss %p (wssid %s)\n", wlp, wss, buf);
+}
+
+/**
+ * Add WLP header to outgoing skb
+ *
+ * @eda_entry: pointer to neighbor's entry in the EDA cache
+ * @_skb: skb containing data destined to the neighbor
+ */
+int wlp_wss_prep_hdr(struct wlp *wlp, struct wlp_eda_node *eda_entry,
+ void *_skb)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ int result = 0;
+ unsigned char *eth_addr = eda_entry->eth_addr;
+ struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr;
+ struct sk_buff *skb = _skb;
+ struct wlp_frame_std_abbrv_hdr *std_hdr;
+
+ d_fnstart(6, dev, "wlp %p \n", wlp);
+ if (eda_entry->state == WLP_WSS_CONNECTED) {
+ /* Add WLP header */
+ BUG_ON(skb_headroom(skb) < sizeof(*std_hdr));
+ std_hdr = (void *) __skb_push(skb, sizeof(*std_hdr));
+ std_hdr->hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID);
+ std_hdr->hdr.type = WLP_FRAME_STANDARD;
+ std_hdr->tag = eda_entry->wss->tag;
+ } else {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Destination neighbor (Ethernet: "
+ "%02x:%02x:%02x:%02x:%02x:%02x, Dev: "
+ "%02x:%02x) is not connected. \n", eth_addr[0],
+ eth_addr[1], eth_addr[2], eth_addr[3],
+ eth_addr[4], eth_addr[5], dev_addr->data[1],
+ dev_addr->data[0]);
+ result = -EINVAL;
+ }
+ d_fnend(6, dev, "wlp %p \n", wlp);
+ return result;
+}
+
+
+/**
+ * Prepare skb for neighbor: connect if not already and prep WLP header
+ *
+ * This function is called in interrupt context, but it needs to sleep. We
+ * temporarily stop the net queue to establish the WLP connection.
+ * Setup of the WLP connection and restart of queue is scheduled
+ * on the default work queue.
+ *
+ * run with eda->lock held (spinlock)
+ */
+int wlp_wss_connect_prep(struct wlp *wlp, struct wlp_eda_node *eda_entry,
+ void *_skb)
+{
+ int result = 0;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr;
+ unsigned char *eth_addr = eda_entry->eth_addr;
+ struct sk_buff *skb = _skb;
+ struct wlp_assoc_conn_ctx *conn_ctx;
+
+ d_fnstart(5, dev, "wlp %p\n", wlp);
+ d_printf(5, dev, "To neighbor %02x:%02x with eth "
+ "%02x:%02x:%02x:%02x:%02x:%02x\n", dev_addr->data[1],
+ dev_addr->data[0], eth_addr[0], eth_addr[1], eth_addr[2],
+ eth_addr[3], eth_addr[4], eth_addr[5]);
+ if (eda_entry->state == WLP_WSS_UNCONNECTED) {
+ /* We don't want any more packets while we set up connection */
+ BUG_ON(wlp->stop_queue == NULL);
+ wlp->stop_queue(wlp);
+ conn_ctx = kmalloc(sizeof(*conn_ctx), GFP_ATOMIC);
+ if (conn_ctx == NULL) {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Unable to allocate memory "
+ "for connection handling.\n");
+ result = -ENOMEM;
+ goto out;
+ }
+ conn_ctx->wlp = wlp;
+ conn_ctx->skb = skb;
+ conn_ctx->eda_entry = *eda_entry;
+ INIT_WORK(&conn_ctx->ws, wlp_wss_connect_send);
+ schedule_work(&conn_ctx->ws);
+ result = 1;
+ } else if (eda_entry->state == WLP_WSS_CONNECT_FAILED) {
+ /* Previous connection attempts failed, don't retry - see
+ * conditions for connection in WLP 0.99 [7.6.2] */
+ if (printk_ratelimit())
+ dev_err(dev, "Could not connect to neighbor "
+ "previously. Not retrying. \n");
+ result = -ENONET;
+ goto out;
+ } else { /* eda_entry->state == WLP_WSS_CONNECTED */
+ d_printf(5, dev, "Neighbor is connected, preparing frame.\n");
+ result = wlp_wss_prep_hdr(wlp, eda_entry, skb);
+ }
+out:
+ d_fnend(5, dev, "wlp %p, result = %d \n", wlp, result);
+ return result;
+}
+
+/**
+ * Emulate broadcast: copy skb, send copy to neighbor (connect if not already)
+ *
+ * We need to copy skbs in the case where we emulate broadcast through
+ * unicast. We copy instead of clone because we are modifying the data of
+ * the frame after copying ... clones share data so we cannot emulate
+ * broadcast using clones.
+ *
+ * run with eda->lock held (spinlock)
+ */
+int wlp_wss_send_copy(struct wlp *wlp, struct wlp_eda_node *eda_entry,
+ void *_skb)
+{
+ int result = -ENOMEM;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct sk_buff *skb = _skb;
+ struct sk_buff *copy;
+ struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr;
+
+ d_fnstart(5, dev, "to neighbor %02x:%02x, skb (%p) \n",
+ dev_addr->data[1], dev_addr->data[0], skb);
+ copy = skb_copy(skb, GFP_ATOMIC);
+ if (copy == NULL) {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Unable to copy skb for "
+ "transmission.\n");
+ goto out;
+ }
+ result = wlp_wss_connect_prep(wlp, eda_entry, copy);
+ if (result < 0) {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Unable to connect/send skb "
+ "to neighbor.\n");
+ dev_kfree_skb_irq(copy);
+ goto out;
+ } else if (result == 1)
+ /* Frame will be transmitted separately */
+ goto out;
+ BUG_ON(wlp->xmit_frame == NULL);
+ result = wlp->xmit_frame(wlp, copy, dev_addr);
+ if (result < 0) {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Unable to transmit frame: %d\n",
+ result);
+ if ((result == -ENXIO) && printk_ratelimit())
+ dev_err(dev, "WLP: Is network interface up? \n");
+ /* We could try again ... */
+ dev_kfree_skb_irq(copy);/*we need to free if tx fails */
+ }
+out:
+ d_fnend(5, dev, "to neighbor %02x:%02x \n", dev_addr->data[1],
+ dev_addr->data[0]);
+ return result;
+}
+
+
+/**
+ * Setup WSS
+ *
+ * Should be called by network driver after the interface has been given a
+ * MAC address.
+ */
+int wlp_wss_setup(struct net_device *net_dev, struct wlp_wss *wss)
+{
+ struct wlp *wlp = container_of(wss, struct wlp, wss);
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ int result = 0;
+ d_fnstart(5, dev, "wss (%p) \n", wss);
+ mutex_lock(&wss->mutex);
+ wss->kobj.parent = &net_dev->dev.kobj;
+ if (!is_valid_ether_addr(net_dev->dev_addr)) {
+ dev_err(dev, "WLP: Invalid MAC address. Cannot use for"
+ "virtual.\n");
+ result = -EINVAL;
+ goto out;
+ }
+ memcpy(wss->virtual_addr.data, net_dev->dev_addr,
+ sizeof(wss->virtual_addr.data));
+out:
+ mutex_unlock(&wss->mutex);
+ d_fnend(5, dev, "wss (%p) \n", wss);
+ return result;
+}
+EXPORT_SYMBOL_GPL(wlp_wss_setup);
+
+/**
+ * Remove WSS
+ *
+ * Called by client that configured WSS through wlp_wss_setup(). This
+ * function is called when client no longer needs WSS, eg. client shuts
+ * down.
+ *
+ * We remove the WLP IE from the beacon before initiating local cleanup.
+ */
+void wlp_wss_remove(struct wlp_wss *wss)
+{
+ struct wlp *wlp = container_of(wss, struct wlp, wss);
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ d_fnstart(5, dev, "wss (%p) \n", wss);
+ mutex_lock(&wss->mutex);
+ if (wss->state == WLP_WSS_STATE_ACTIVE)
+ uwb_rc_ie_rm(wlp->rc, UWB_IE_WLP);
+ if (wss->state != WLP_WSS_STATE_NONE) {
+ sysfs_remove_group(&wss->kobj, &wss_attr_group);
+ kobject_put(&wss->kobj);
+ }
+ wss->kobj.parent = NULL;
+ memset(&wss->virtual_addr, 0, sizeof(wss->virtual_addr));
+ /* Cleanup EDA cache */
+ wlp_eda_release(&wlp->eda);
+ wlp_eda_init(&wlp->eda);
+ mutex_unlock(&wss->mutex);
+ d_fnend(5, dev, "wss (%p) \n", wss);
+}
+EXPORT_SYMBOL_GPL(wlp_wss_remove);