]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
Merge branch 'master' into for-upstream
authorDavid Vrabel <david.vrabel@csr.com>
Mon, 20 Oct 2008 15:07:19 +0000 (16:07 +0100)
committerDavid Vrabel <david.vrabel@csr.com>
Mon, 20 Oct 2008 15:07:19 +0000 (16:07 +0100)
Conflicts:

Documentation/ABI/testing/sysfs-bus-usb
drivers/Makefile

110 files changed:
CREDITS
Documentation/ABI/testing/sysfs-bus-umc [new file with mode: 0644]
Documentation/ABI/testing/sysfs-bus-usb
Documentation/ABI/testing/sysfs-class-usb_host [new file with mode: 0644]
Documentation/ABI/testing/sysfs-class-uwb_rc [new file with mode: 0644]
Documentation/ABI/testing/sysfs-wusb_cbaf [new file with mode: 0644]
Documentation/usb/WUSB-Design-overview.txt [new file with mode: 0644]
Documentation/usb/wusb-cbaf [new file with mode: 0644]
MAINTAINERS
arch/arm/Kconfig
arch/cris/Kconfig
arch/h8300/Kconfig
drivers/Kconfig
drivers/Makefile
drivers/usb/Kconfig
drivers/usb/Makefile
drivers/usb/host/Kconfig
drivers/usb/host/Makefile
drivers/usb/host/hwa-hc.c [new file with mode: 0644]
drivers/usb/host/whci/Kbuild [new file with mode: 0644]
drivers/usb/host/whci/asl.c [new file with mode: 0644]
drivers/usb/host/whci/hcd.c [new file with mode: 0644]
drivers/usb/host/whci/hw.c [new file with mode: 0644]
drivers/usb/host/whci/init.c [new file with mode: 0644]
drivers/usb/host/whci/int.c [new file with mode: 0644]
drivers/usb/host/whci/pzl.c [new file with mode: 0644]
drivers/usb/host/whci/qset.c [new file with mode: 0644]
drivers/usb/host/whci/whcd.h [new file with mode: 0644]
drivers/usb/host/whci/whci-hc.h [new file with mode: 0644]
drivers/usb/host/whci/wusb.c [new file with mode: 0644]
drivers/usb/wusbcore/Kconfig [new file with mode: 0644]
drivers/usb/wusbcore/Makefile [new file with mode: 0644]
drivers/usb/wusbcore/cbaf.c [new file with mode: 0644]
drivers/usb/wusbcore/crypto.c [new file with mode: 0644]
drivers/usb/wusbcore/dev-sysfs.c [new file with mode: 0644]
drivers/usb/wusbcore/devconnect.c [new file with mode: 0644]
drivers/usb/wusbcore/mmc.c [new file with mode: 0644]
drivers/usb/wusbcore/pal.c [new file with mode: 0644]
drivers/usb/wusbcore/reservation.c [new file with mode: 0644]
drivers/usb/wusbcore/rh.c [new file with mode: 0644]
drivers/usb/wusbcore/security.c [new file with mode: 0644]
drivers/usb/wusbcore/wa-hc.c [new file with mode: 0644]
drivers/usb/wusbcore/wa-hc.h [new file with mode: 0644]
drivers/usb/wusbcore/wa-nep.c [new file with mode: 0644]
drivers/usb/wusbcore/wa-rpipe.c [new file with mode: 0644]
drivers/usb/wusbcore/wa-xfer.c [new file with mode: 0644]
drivers/usb/wusbcore/wusbhc.c [new file with mode: 0644]
drivers/usb/wusbcore/wusbhc.h [new file with mode: 0644]
drivers/uwb/Kconfig [new file with mode: 0644]
drivers/uwb/Makefile [new file with mode: 0644]
drivers/uwb/address.c [new file with mode: 0644]
drivers/uwb/beacon.c [new file with mode: 0644]
drivers/uwb/driver.c [new file with mode: 0644]
drivers/uwb/drp-avail.c [new file with mode: 0644]
drivers/uwb/drp-ie.c [new file with mode: 0644]
drivers/uwb/drp.c [new file with mode: 0644]
drivers/uwb/est.c [new file with mode: 0644]
drivers/uwb/hwa-rc.c [new file with mode: 0644]
drivers/uwb/i1480/Makefile [new file with mode: 0644]
drivers/uwb/i1480/dfu/Makefile [new file with mode: 0644]
drivers/uwb/i1480/dfu/dfu.c [new file with mode: 0644]
drivers/uwb/i1480/dfu/i1480-dfu.h [new file with mode: 0644]
drivers/uwb/i1480/dfu/mac.c [new file with mode: 0644]
drivers/uwb/i1480/dfu/phy.c [new file with mode: 0644]
drivers/uwb/i1480/dfu/usb.c [new file with mode: 0644]
drivers/uwb/i1480/i1480-est.c [new file with mode: 0644]
drivers/uwb/i1480/i1480-wlp.h [new file with mode: 0644]
drivers/uwb/i1480/i1480u-wlp/Makefile [new file with mode: 0644]
drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h [new file with mode: 0644]
drivers/uwb/i1480/i1480u-wlp/lc.c [new file with mode: 0644]
drivers/uwb/i1480/i1480u-wlp/netdev.c [new file with mode: 0644]
drivers/uwb/i1480/i1480u-wlp/rx.c [new file with mode: 0644]
drivers/uwb/i1480/i1480u-wlp/sysfs.c [new file with mode: 0644]
drivers/uwb/i1480/i1480u-wlp/tx.c [new file with mode: 0644]
drivers/uwb/ie.c [new file with mode: 0644]
drivers/uwb/lc-dev.c [new file with mode: 0644]
drivers/uwb/lc-rc.c [new file with mode: 0644]
drivers/uwb/neh.c [new file with mode: 0644]
drivers/uwb/pal.c [new file with mode: 0644]
drivers/uwb/reset.c [new file with mode: 0644]
drivers/uwb/rsv.c [new file with mode: 0644]
drivers/uwb/scan.c [new file with mode: 0644]
drivers/uwb/umc-bus.c [new file with mode: 0644]
drivers/uwb/umc-dev.c [new file with mode: 0644]
drivers/uwb/umc-drv.c [new file with mode: 0644]
drivers/uwb/uwb-debug.c [new file with mode: 0644]
drivers/uwb/uwb-internal.h [new file with mode: 0644]
drivers/uwb/uwbd.c [new file with mode: 0644]
drivers/uwb/whc-rc.c [new file with mode: 0644]
drivers/uwb/whci.c [new file with mode: 0644]
drivers/uwb/wlp/Makefile [new file with mode: 0644]
drivers/uwb/wlp/driver.c [new file with mode: 0644]
drivers/uwb/wlp/eda.c [new file with mode: 0644]
drivers/uwb/wlp/messages.c [new file with mode: 0644]
drivers/uwb/wlp/sysfs.c [new file with mode: 0644]
drivers/uwb/wlp/txrx.c [new file with mode: 0644]
drivers/uwb/wlp/wlp-internal.h [new file with mode: 0644]
drivers/uwb/wlp/wlp-lc.c [new file with mode: 0644]
drivers/uwb/wlp/wss-lc.c [new file with mode: 0644]
include/linux/bitmap.h
include/linux/usb/wusb-wa.h [new file with mode: 0644]
include/linux/usb/wusb.h [new file with mode: 0644]
include/linux/uwb.h [new file with mode: 0644]
include/linux/uwb/debug-cmd.h [new file with mode: 0644]
include/linux/uwb/debug.h [new file with mode: 0644]
include/linux/uwb/spec.h [new file with mode: 0644]
include/linux/uwb/umc.h [new file with mode: 0644]
include/linux/uwb/whci.h [new file with mode: 0644]
include/linux/wlp.h [new file with mode: 0644]
lib/bitmap.c

diff --git a/CREDITS b/CREDITS
index c62dcb3b7e2621d918815a656f2682fda044afcb..4fad6717e56cc69e8628da7c95ff0a889e7b3346 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -598,6 +598,11 @@ S: Tamsui town, Taipei county,
 S: Taiwan 251
 S: Republic of China
 
+N: Reinette Chatre
+E: reinette.chatre@intel.com
+D: WiMedia Link Protocol implementation
+D: UWB stack bits and pieces
+
 N: Michael Elizabeth Chastain
 E: mec@shout.net
 D: Configure, Menuconfig, xconfig
@@ -2695,6 +2700,12 @@ S: Demonstratsii 8-382
 S: Tula 300000
 S: Russia
 
+N: Inaky Perez-Gonzalez
+E: inaky.perez-gonzalez@intel.com
+D: UWB stack, HWA-RC driver and HWA-HC drivers
+D: Wireless USB additions to the USB stack
+D: WiMedia Link Protocol bits and pieces
+
 N: Gordon Peters
 E: GordPeters@smarttech.com
 D: Isochronous receive for IEEE 1394 driver (OHCI module).
diff --git a/Documentation/ABI/testing/sysfs-bus-umc b/Documentation/ABI/testing/sysfs-bus-umc
new file mode 100644 (file)
index 0000000..948fec4
--- /dev/null
@@ -0,0 +1,28 @@
+What:           /sys/bus/umc/
+Date:           July 2008
+KernelVersion:  2.6.27
+Contact:        David Vrabel <david.vrabel@csr.com>
+Description:
+                The Wireless Host Controller Interface (WHCI)
+                specification describes a PCI-based device with
+                multiple capabilities; the UWB Multi-interface
+                Controller (UMC).
+
+                The umc bus presents each of the individual
+                capabilties as a device.
+
+What:           /sys/bus/umc/devices/.../capability_id
+Date:           July 2008
+KernelVersion:  2.6.27
+Contact:        David Vrabel <david.vrabel@csr.com>
+Description:
+                The ID of this capability, with 0 being the radio
+                controller capability.
+
+What:           /sys/bus/umc/devices/.../version
+Date:           July 2008
+KernelVersion:  2.6.27
+Contact:        David Vrabel <david.vrabel@csr.com>
+Description:
+                The specification version this capability's hardware
+                interface complies with.
index df6c8a0159f1c9abeaf54931384fdf36255f60a5..7772928ee48f6edfa61d52921164ed106501515b 100644 (file)
@@ -101,3 +101,46 @@ Description:
 Users:
                USB PM tool
                git://git.moblin.org/users/sarah/usb-pm-tool/
+
+What:          /sys/bus/usb/device/.../authorized
+Date:          July 2008
+KernelVersion: 2.6.26
+Contact:       David Vrabel <david.vrabel@csr.com>
+Description:
+               Authorized devices are available for use by device
+               drivers, non-authorized one are not.  By default, wired
+               USB devices are authorized.
+
+               Certified Wireless USB devices are not authorized
+               initially and should be (by writing 1) after the
+               device has been authenticated.
+
+What:          /sys/bus/usb/device/.../wusb_cdid
+Date:          July 2008
+KernelVersion: 2.6.27
+Contact:       David Vrabel <david.vrabel@csr.com>
+Description:
+               For Certified Wireless USB devices only.
+
+               A devices's CDID, as 16 space-separated hex octets.
+
+What:          /sys/bus/usb/device/.../wusb_ck
+Date:          July 2008
+KernelVersion: 2.6.27
+Contact:       David Vrabel <david.vrabel@csr.com>
+Description:
+               For Certified Wireless USB devices only.
+
+               Write the device's connection key (CK) to start the
+               authentication of the device.  The CK is 16
+               space-separated hex octets.
+
+What:          /sys/bus/usb/device/.../wusb_disconnect
+Date:          July 2008
+KernelVersion: 2.6.27
+Contact:       David Vrabel <david.vrabel@csr.com>
+Description:
+               For Certified Wireless USB devices only.
+
+               Write a 1 to force the device to disconnect
+               (equivalent to unplugging a wired USB device).
diff --git a/Documentation/ABI/testing/sysfs-class-usb_host b/Documentation/ABI/testing/sysfs-class-usb_host
new file mode 100644 (file)
index 0000000..46b66ad
--- /dev/null
@@ -0,0 +1,25 @@
+What:           /sys/class/usb_host/usb_hostN/wusb_chid
+Date:           July 2008
+KernelVersion:  2.6.27
+Contact:        David Vrabel <david.vrabel@csr.com>
+Description:
+                Write the CHID (16 space-separated hex octets) for this host controller.
+                This starts the host controller, allowing it to accept connection from
+                WUSB devices.
+
+                Set an all zero CHID to stop the host controller.
+
+What:           /sys/class/usb_host/usb_hostN/wusb_trust_timeout
+Date:           July 2008
+KernelVersion:  2.6.27
+Contact:        David Vrabel <david.vrabel@csr.com>
+Description:
+                Devices that haven't sent a WUSB packet to the host
+                within 'wusb_trust_timeout' ms are considered to have
+                disconnected and are removed.  The default value of
+                4000 ms is the value required by the WUSB
+                specification.
+
+                Since this relates to security (specifically, the
+                lifetime of PTKs and GTKs) it should not be changed
+                from the default.
diff --git a/Documentation/ABI/testing/sysfs-class-uwb_rc b/Documentation/ABI/testing/sysfs-class-uwb_rc
new file mode 100644 (file)
index 0000000..a0d18db
--- /dev/null
@@ -0,0 +1,144 @@
+What:           /sys/class/uwb_rc
+Date:           July 2008
+KernelVersion:  2.6.27
+Contact:        linux-usb@vger.kernel.org
+Description:
+                Interfaces for WiMedia Ultra Wideband Common Radio
+                Platform (UWB) radio controllers.
+
+                Familiarity with the ECMA-368 'High Rate Ultra
+                Wideband MAC and PHY Specification' is assumed.
+
+What:           /sys/class/uwb_rc/beacon_timeout_ms
+Date:           July 2008
+KernelVersion:  2.6.27
+Description:
+                If no beacons are received from a device for at least
+                this time, the device will be considered to have gone
+                and it will be removed.  The default is 3 superframes
+                (~197 ms) as required by the specification.
+
+What:           /sys/class/uwb_rc/uwbN/
+Date:           July 2008
+KernelVersion:  2.6.27
+Contact:        linux-usb@vger.kernel.org
+Description:
+                An individual UWB radio controller.
+
+What:           /sys/class/uwb_rc/uwbN/beacon
+Date:           July 2008
+KernelVersion:  2.6.27
+Contact:        linux-usb@vger.kernel.org
+Description:
+                Write:
+
+                <channel> [<bpst offset>]
+
+                to start beaconing on a specific channel, or stop
+                beaconing if <channel> is -1.  Valid channels depends
+                on the radio controller's supported band groups.
+
+                <bpst offset> may be used to try and join a specific
+                beacon group if more than one was found during a scan.
+
+What:           /sys/class/uwb_rc/uwbN/scan
+Date:           July 2008
+KernelVersion:  2.6.27
+Contact:        linux-usb@vger.kernel.org
+Description:
+                Write:
+
+                <channel> <type> [<bpst offset>]
+
+                to start (or stop) scanning on a channel.  <type> is one of:
+                    0 - scan
+                    1 - scan outside BP
+                    2 - scan while inactive
+                    3 - scanning disabled
+                    4 - scan (with start time of <bpst offset>)
+
+What:           /sys/class/uwb_rc/uwbN/mac_address
+Date:           July 2008
+KernelVersion:  2.6.27
+Contact:        linux-usb@vger.kernel.org
+Description:
+                The EUI-48, in colon-separated hex octets, for this
+                radio controller.  A write will change the radio
+                controller's EUI-48 but only do so while the device is
+                not beaconing or scanning.
+
+What:           /sys/class/uwb_rc/uwbN/wusbhc
+Date:           July 2008
+KernelVersion:  2.6.27
+Contact:        linux-usb@vger.kernel.org
+Description:
+                A symlink to the device (if any) of the WUSB Host
+                Controller PAL using this radio controller.
+
+What:           /sys/class/uwb_rc/uwbN/<EUI-48>/
+Date:           July 2008
+KernelVersion:  2.6.27
+Contact:        linux-usb@vger.kernel.org
+Description:
+                A neighbour UWB device that has either been detected
+                as part of a scan or is a member of the radio
+                controllers beacon group.
+
+What:           /sys/class/uwb_rc/uwbN/<EUI-48>/BPST
+Date:           July 2008
+KernelVersion:  2.6.27
+Contact:        linux-usb@vger.kernel.org
+Description:
+                The time (using the radio controllers internal 1 ms
+                interval superframe timer) of the last beacon from
+                this device was received.
+
+What:           /sys/class/uwb_rc/uwbN/<EUI-48>/DevAddr
+Date:           July 2008
+KernelVersion:  2.6.27
+Contact:        linux-usb@vger.kernel.org
+Description:
+                The current DevAddr of this device in colon separated
+                hex octets.
+
+What:           /sys/class/uwb_rc/uwbN/<EUI-48>/EUI_48
+Date:           July 2008
+KernelVersion:  2.6.27
+Contact:        linux-usb@vger.kernel.org
+Description:
+
+                The EUI-48 of this device in colon separated hex
+                octets.
+
+What:           /sys/class/uwb_rc/uwbN/<EUI-48>/BPST
+Date:           July 2008
+KernelVersion:  2.6.27
+Contact:        linux-usb@vger.kernel.org
+Description:
+
+What:           /sys/class/uwb_rc/uwbN/<EUI-48>/IEs
+Date:           July 2008
+KernelVersion:  2.6.27
+Contact:        linux-usb@vger.kernel.org
+Description:
+                The latest IEs included in this device's beacon, in
+                space separated hex octets with one IE per line.
+
+What:           /sys/class/uwb_rc/uwbN/<EUI-48>/LQE
+Date:           July 2008
+KernelVersion:  2.6.27
+Contact:        linux-usb@vger.kernel.org
+Description:
+                Link Quality Estimate - the Signal to Noise Ratio
+                (SNR) of all packets received from this device in dB.
+                This gives an estimate on a suitable PHY rate. Refer
+                to [ECMA-368] section 13.3 for more details.
+
+What:           /sys/class/uwb_rc/uwbN/<EUI-48>/RSSI
+Date:           July 2008
+KernelVersion:  2.6.27
+Contact:        linux-usb@vger.kernel.org
+Description:
+                Received Signal Strength Indication - the strength of
+                the received signal in dB.  LQE is a more useful
+                measure of the radio link quality.
diff --git a/Documentation/ABI/testing/sysfs-wusb_cbaf b/Documentation/ABI/testing/sysfs-wusb_cbaf
new file mode 100644 (file)
index 0000000..a99c5f8
--- /dev/null
@@ -0,0 +1,100 @@
+What:           /sys/bus/usb/drivers/wusb_cbaf/.../wusb_*
+Date:           August 2008
+KernelVersion:  2.6.27
+Contact:        David Vrabel <david.vrabel@csr.com>
+Description:
+                Various files for managing Cable Based Association of
+                (wireless) USB devices.
+
+                The sequence of operations should be:
+
+                1. Device is plugged in.
+
+                2. The connection manager (CM) sees a device with CBA capability.
+                   (the wusb_chid etc. files in /sys/devices/blah/OURDEVICE).
+
+                3. The CM writes the host name, supported band groups,
+                   and the CHID (host ID) into the wusb_host_name,
+                   wusb_host_band_groups and wusb_chid files. These
+                   get sent to the device and the CDID (if any) for
+                   this host is requested.
+
+                4. The CM can verify that the device's supported band
+                   groups (wusb_device_band_groups) are compatible
+                   with the host.
+
+                5. The CM reads the wusb_cdid file.
+
+                6. The CM looks it up its database.
+
+                   - If it has a matching CHID,CDID entry, the device
+                     has been authorized before and nothing further
+                     needs to be done.
+
+                   - If the CDID is zero (or the CM doesn't find a
+                     matching CDID in its database), the device is
+                     assumed to be not known.  The CM may associate
+                     the host with device by: writing a randomly
+                     generated CDID to wusb_cdid and then a random CK
+                     to wusb_ck (this uploads the new CC to the
+                     device).
+
+                     CMD may choose to prompt the user before
+                     associating with a new device.
+
+                7. Device is unplugged.
+
+                References:
+                  [WUSB-AM] Association Models Supplement to the
+                            Certified Wireless Universal Serial Bus
+                            Specification, version 1.0.
+
+What:           /sys/bus/usb/drivers/wusb_cbaf/.../wusb_chid
+Date:           August 2008
+KernelVersion:  2.6.27
+Contact:        David Vrabel <david.vrabel@csr.com>
+Description:
+                The CHID of the host formatted as 16 space-separated
+                hex octets.
+
+                Writes fetches device's supported band groups and the
+                the CDID for any existing association with this host.
+
+What:           /sys/bus/usb/drivers/wusb_cbaf/.../wusb_host_name
+Date:           August 2008
+KernelVersion:  2.6.27
+Contact:        David Vrabel <david.vrabel@csr.com>
+Description:
+                A friendly name for the host as a UTF-8 encoded string.
+
+What:           /sys/bus/usb/drivers/wusb_cbaf/.../wusb_host_band_groups
+Date:           August 2008
+KernelVersion:  2.6.27
+Contact:        David Vrabel <david.vrabel@csr.com>
+Description:
+                The band groups supported by the host, in the format
+                defined in [WUSB-AM].
+
+What:           /sys/bus/usb/drivers/wusb_cbaf/.../wusb_device_band_groups
+Date:           August 2008
+KernelVersion:  2.6.27
+Contact:        David Vrabel <david.vrabel@csr.com>
+Description:
+                The band groups supported by the device, in the format
+                defined in [WUSB-AM].
+
+What:           /sys/bus/usb/drivers/wusb_cbaf/.../wusb_cdid
+Date:           August 2008
+KernelVersion:  2.6.27
+Contact:        David Vrabel <david.vrabel@csr.com>
+Description:
+                The device's CDID formatted as 16 space-separated hex
+                octets.
+
+What:           /sys/bus/usb/drivers/wusb_cbaf/.../wusb_ck
+Date:           August 2008
+KernelVersion:  2.6.27
+Contact:        David Vrabel <david.vrabel@csr.com>
+Description:
+                Write 16 space-separated random, hex octets to
+                associate with the device.
diff --git a/Documentation/usb/WUSB-Design-overview.txt b/Documentation/usb/WUSB-Design-overview.txt
new file mode 100644 (file)
index 0000000..4c3d62c
--- /dev/null
@@ -0,0 +1,448 @@
+
+Linux UWB + Wireless USB + WiNET
+
+   (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.
+
+
+Please visit http://bughost.org/thewiki/Design-overview.txt-1.8 for
+updated content.
+
+    * Design-overview.txt-1.8
+
+This code implements a Ultra Wide Band stack for Linux, as well as
+drivers for the the USB based UWB radio controllers defined in the
+Wireless USB 1.0 specification (including Wireless USB host controller
+and an Intel WiNET controller).
+
+   1. Introduction
+         1. HWA: Host Wire adapters, your Wireless USB dongle
+
+         2. DWA: Device Wired Adaptor, a Wireless USB hub for wired
+            devices
+         3. WHCI: Wireless Host Controller Interface, the PCI WUSB host
+            adapter
+   2. The UWB stack
+         1. Devices and hosts: the basic structure
+
+         2. Host Controller life cycle
+
+         3. On the air: beacons and enumerating the radio neighborhood
+
+         4. Device lists
+         5. Bandwidth allocation
+
+   3. Wireless USB Host Controller drivers
+
+   4. Glossary
+
+
+    Introduction
+
+UWB is a wide-band communication protocol that is to serve also as the
+low-level protocol for others (much like TCP sits on IP). Currently
+these others are Wireless USB and TCP/IP, but seems Bluetooth and
+Firewire/1394 are coming along.
+
+UWB uses a band from roughly 3 to 10 GHz, transmitting at a max of
+~-41dB (or 0.074 uW/MHz--geography specific data is still being
+negotiated w/ regulators, so watch for changes). That band is divided in
+a bunch of ~1.5 GHz wide channels (or band groups) composed of three
+subbands/subchannels (528 MHz each). Each channel is independent of each
+other, so you could consider them different "busses". Initially this
+driver considers them all a single one.
+
+Radio time is divided in 65536 us long /superframes/, each one divided
+in 256 256us long /MASs/ (Media Allocation Slots), which are the basic
+time/media allocation units for transferring data. At the beginning of
+each superframe there is a Beacon Period (BP), where every device
+transmit its beacon on a single MAS. The length of the BP depends on how
+many devices are present and the length of their beacons.
+
+Devices have a MAC (fixed, 48 bit address) and a device (changeable, 16
+bit address) and send periodic beacons to advertise themselves and pass
+info on what they are and do. They advertise their capabilities and a
+bunch of other stuff.
+
+The different logical parts of this driver are:
+
+    *
+
+      *UWB*: the Ultra-Wide-Band stack -- manages the radio and
+      associated spectrum to allow for devices sharing it. Allows to
+      control bandwidth assingment, beaconing, scanning, etc
+
+    *
+
+      *WUSB*: the layer that sits on top of UWB to provide Wireless USB.
+      The Wireless USB spec defines means to control a UWB radio and to
+      do the actual WUSB.
+
+
+      HWA: Host Wire adapters, your Wireless USB dongle
+
+WUSB also defines a device called a Host Wire Adaptor (HWA), which in
+mere terms is a USB dongle that enables your PC to have UWB and Wireless
+USB. The Wireless USB Host Controller in a HWA looks to the host like a
+[Wireless] USB controller connected via USB (!)
+
+The HWA itself is broken in two or three main interfaces:
+
+    *
+
+      *RC*: Radio control -- this implements an interface to the
+      Ultra-Wide-Band radio controller. The driver for this implements a
+      USB-based UWB Radio Controller to the UWB stack.
+
+    *
+
+      *HC*: the wireless USB host controller. It looks like a USB host
+      whose root port is the radio and the WUSB devices connect to it.
+      To the system it looks like a separate USB host. The driver (will)
+      implement a USB host controller (similar to UHCI, OHCI or EHCI)
+      for which the root hub is the radio...To reiterate: it is a USB
+      controller that is connected via USB instead of PCI.
+
+    *
+
+      *WINET*: some HW provide a WiNET interface (IP over UWB). This
+      package provides a driver for it (it looks like a network
+      interface, winetX). The driver detects when there is a link up for
+      their type and kick into gear.
+
+
+      DWA: Device Wired Adaptor, a Wireless USB hub for wired devices
+
+These are the complement to HWAs. They are a USB host for connecting
+wired devices, but it is connected to your PC connected via Wireless
+USB. To the system it looks like yet another USB host. To the untrained
+eye, it looks like a hub that connects upstream wirelessly.
+
+We still offer no support for this; however, it should share a lot of
+code with the HWA-RC driver; there is a bunch of factorization work that
+has been done to support that in upcoming releases.
+
+
+      WHCI: Wireless Host Controller Interface, the PCI WUSB host adapter
+
+This is your usual PCI device that implements WHCI. Similar in concept
+to EHCI, it allows your wireless USB devices (including DWAs) to connect
+to your host via a PCI interface. As in the case of the HWA, it has a
+Radio Control interface and the WUSB Host Controller interface per se.
+
+There is still no driver support for this, but will be in upcoming
+releases.
+
+
+    The UWB stack
+
+The main mission of the UWB stack is to keep a tally of which devices
+are in radio proximity to allow drivers to connect to them. As well, it
+provides an API for controlling the local radio controllers (RCs from
+now on), such as to start/stop beaconing, scan, allocate bandwidth, etc.
+
+
+      Devices and hosts: the basic structure
+
+The main building block here is the UWB device (struct uwb_dev). For
+each device that pops up in radio presence (ie: the UWB host receives a
+beacon from it) you get a struct uwb_dev that will show up in
+/sys/class/uwb and in /sys/bus/uwb/devices.
+
+For each RC that is detected, a new struct uwb_rc is created. In turn, a
+RC is also a device, so they also show in /sys/class/uwb and
+/sys/bus/uwb/devices, but at the same time, only radio controllers show
+up in /sys/class/uwb_rc.
+
+    *
+
+      [*] The reason for RCs being also devices is that not only we can
+      see them while enumerating the system device tree, but also on the
+      radio (their beacons and stuff), so the handling has to be
+      likewise to that of a device.
+
+Each RC driver is implemented by a separate driver that plugs into the
+interface that the UWB stack provides through a struct uwb_rc_ops. The
+spec creators have been nice enough to make the message format the same
+for HWA and WHCI RCs, so the driver is really a very thin transport that
+moves the requests from the UWB API to the device [/uwb_rc_ops->cmd()/]
+and sends the replies and notifications back to the API
+[/uwb_rc_neh_grok()/]. Notifications are handled to the UWB daemon, that
+is chartered, among other things, to keep the tab of how the UWB radio
+neighborhood looks, creating and destroying devices as they show up or
+dissapear.
+
+Command execution is very simple: a command block is sent and a event
+block or reply is expected back. For sending/receiving command/events, a
+handle called /neh/ (Notification/Event Handle) is opened with
+/uwb_rc_neh_open()/.
+
+The HWA-RC (USB dongle) driver (drivers/uwb/hwa-rc.c) does this job for
+the USB connected HWA. Eventually, drivers/whci-rc.c will do the same
+for the PCI connected WHCI controller.
+
+
+      Host Controller life cycle
+
+So let's say we connect a dongle to the system: it is detected and
+firmware uploaded if needed [for Intel's i1480
+/drivers/uwb/ptc/usb.c:ptc_usb_probe()/] and then it is reenumerated.
+Now we have a real HWA device connected and
+/drivers/uwb/hwa-rc.c:hwarc_probe()/ picks it up, that will set up the
+Wire-Adaptor environment and then suck it into the UWB stack's vision of
+the world [/drivers/uwb/lc-rc.c:uwb_rc_add()/].
+
+    *
+
+      [*] The stack should put a new RC to scan for devices
+      [/uwb_rc_scan()/] so it finds what's available around and tries to
+      connect to them, but this is policy stuff and should be driven
+      from user space. As of now, the operator is expected to do it
+      manually; see the release notes for documentation on the procedure.
+
+When a dongle is disconnected, /drivers/uwb/hwa-rc.c:hwarc_disconnect()/
+takes time of tearing everything down safely (or not...).
+
+
+      On the air: beacons and enumerating the radio neighborhood
+
+So assuming we have devices and we have agreed for a channel to connect
+on (let's say 9), we put the new RC to beacon:
+
+    *
+
+            $ echo 9 0 > /sys/class/uwb_rc/uwb0/beacon
+
+Now it is visible. If there were other devices in the same radio channel
+and beacon group (that's what the zero is for), the dongle's radio
+control interface will send beacon notifications on its
+notification/event endpoint (NEEP). The beacon notifications are part of
+the event stream that is funneled into the API with
+/drivers/uwb/neh.c:uwb_rc_neh_grok()/ and delivered to the UWBD, the UWB
+daemon through a notification list.
+
+UWBD wakes up and scans the event list; finds a beacon and adds it to
+the BEACON CACHE (/uwb_beca/). If he receives a number of beacons from
+the same device, he considers it to be 'onair' and creates a new device
+[/drivers/uwb/lc-dev.c:uwbd_dev_onair()/]. Similarly, when no beacons
+are received in some time, the device is considered gone and wiped out
+[uwbd calls periodically /uwb/beacon.c:uwb_beca_purge()/ that will purge
+the beacon cache of dead devices].
+
+
+      Device lists
+
+All UWB devices are kept in the list of the struct bus_type uwb_bus.
+
+
+      Bandwidth allocation
+
+The UWB stack maintains a local copy of DRP availability through
+processing of incoming *DRP Availability Change* notifications. This
+local copy is currently used to present the current bandwidth
+availability to the user through the sysfs file
+/sys/class/uwb_rc/uwbx/bw_avail. In the future the bandwidth
+availability information will be used by the bandwidth reservation
+routines.
+
+The bandwidth reservation routines are in progress and are thus not
+present in the current release. When completed they will enable a user
+to initiate DRP reservation requests through interaction with sysfs. DRP
+reservation requests from remote UWB devices will also be handled. The
+bandwidth management done by the UWB stack will include callbacks to the
+higher layers will enable the higher layers to use the reservations upon
+completion. [Note: The bandwidth reservation work is in progress and
+subject to change.]
+
+
+    Wireless USB Host Controller drivers
+
+*WARNING* This section needs a lot of work!
+
+As explained above, there are three different types of HCs in the WUSB
+world: HWA-HC, DWA-HC and WHCI-HC.
+
+HWA-HC and DWA-HC share that they are Wire-Adapters (USB or WUSB
+connected controllers), and their transfer management system is almost
+identical. So is their notification delivery system.
+
+HWA-HC and WHCI-HC share that they are both WUSB host controllers, so
+they have to deal with WUSB device life cycle and maintenance, wireless
+root-hub
+
+HWA exposes a Host Controller interface (HWA-HC 0xe0/02/02). This has
+three endpoints (Notifications, Data Transfer In and Data Transfer
+Out--known as NEP, DTI and DTO in the code).
+
+We reserve UWB bandwidth for our Wireless USB Cluster, create a Cluster
+ID and tell the HC to use all that. Then we start it. This means the HC
+starts sending MMCs.
+
+    *
+
+      The MMCs are blocks of data defined somewhere in the WUSB1.0 spec
+      that define a stream in the UWB channel time allocated for sending
+      WUSB IEs (host to device commands/notifications) and Device
+      Notifications (device initiated to host). Each host defines a
+      unique Wireless USB cluster through MMCs. Devices can connect to a
+      single cluster at the time. The IEs are Information Elements, and
+      among them are the bandwidth allocations that tell each device
+      when can they transmit or receive.
+
+Now it all depends on external stimuli.
+
+*New device connection*
+
+A new device pops up, it scans the radio looking for MMCs that give out
+the existence of Wireless USB channels. Once one (or more) are found,
+selects which one to connect to. Sends a /DN_Connect/ (device
+notification connect) during the DNTS (Device Notification Time
+Slot--announced in the MMCs
+
+HC picks the /DN_Connect/ out (nep module sends to notif.c for delivery
+into /devconnect/). This process starts the authentication process for
+the device. First we allocate a /fake port/ and assign an
+unauthenticated address (128 to 255--what we really do is
+0x80 | fake_port_idx). We fiddle with the fake port status and /khubd/
+sees a new connection, so he moves on to enable the fake port with a reset.
+
+So now we are in the reset path -- we know we have a non-yet enumerated
+device with an unauthorized address; we ask user space to authenticate
+(FIXME: not yet done, similar to bluetooth pairing), then we do the key
+exchange (FIXME: not yet done) and issue a /set address 0/ to bring the
+device to the default state. Device is authenticated.
+
+From here, the USB stack takes control through the usb_hcd ops. khubd
+has seen the port status changes, as we have been toggling them. It will
+start enumerating and doing transfers through usb_hcd->urb_enqueue() to
+read descriptors and move our data.
+
+*Device life cycle and keep alives*
+
+Everytime there is a succesful transfer to/from a device, we update a
+per-device activity timestamp. If not, every now and then we check and
+if the activity timestamp gets old, we ping the device by sending it a
+Keep Alive IE; it responds with a /DN_Alive/ pong during the DNTS (this
+arrives to us as a notification through
+devconnect.c:wusb_handle_dn_alive(). If a device times out, we
+disconnect it from the system (cleaning up internal information and
+toggling the bits in the fake hub port, which kicks khubd into removing
+the rest of the stuff).
+
+This is done through devconnect:__wusb_check_devs(), which will scan the
+device list looking for whom needs refreshing.
+
+If the device wants to disconnect, it will either die (ugly) or send a
+/DN_Disconnect/ that will prompt a disconnection from the system.
+
+*Sending and receiving data*
+
+Data is sent and received through /Remote Pipes/ (rpipes). An rpipe is
+/aimed/ at an endpoint in a WUSB device. This is the same for HWAs and
+DWAs.
+
+Each HC has a number of rpipes and buffers that can be assigned to them;
+when doing a data transfer (xfer), first the rpipe has to be aimed and
+prepared (buffers assigned), then we can start queueing requests for
+data in or out.
+
+Data buffers have to be segmented out before sending--so we send first a
+header (segment request) and then if there is any data, a data buffer
+immediately after to the DTI interface (yep, even the request). If our
+buffer is bigger than the max segment size, then we just do multiple
+requests.
+
+[This sucks, because doing USB scatter gatter in Linux is resource
+intensive, if any...not that the current approach is not. It just has to
+be cleaned up a lot :)].
+
+If reading, we don't send data buffers, just the segment headers saying
+we want to read segments.
+
+When the xfer is executed, we receive a notification that says data is
+ready in the DTI endpoint (handled through
+xfer.c:wa_handle_notif_xfer()). In there we read from the DTI endpoint a
+descriptor that gives us the status of the transfer, its identification
+(given when we issued it) and the segment number. If it was a data read,
+we issue another URB to read into the destination buffer the chunk of
+data coming out of the remote endpoint. Done, wait for the next guy. The
+callbacks for the URBs issued from here are the ones that will declare
+the xfer complete at some point and call it's callback.
+
+Seems simple, but the implementation is not trivial.
+
+    *
+
+      *WARNING* Old!!
+
+The main xfer descriptor, wa_xfer (equivalent to a URB) contains an
+array of segments, tallys on segments and buffers and callback
+information. Buried in there is a lot of URBs for executing the segments
+and buffer transfers.
+
+For OUT xfers, there is an array of segments, one URB for each, another
+one of buffer URB. When submitting, we submit URBs for segment request
+1, buffer 1, segment 2, buffer 2...etc. Then we wait on the DTI for xfer
+result data; when all the segments are complete, we call the callback to
+finalize the transfer.
+
+For IN xfers, we only issue URBs for the segments we want to read and
+then wait for the xfer result data.
+
+*URB mapping into xfers*
+
+This is done by hwahc_op_urb_[en|de]queue(). In enqueue() we aim an
+rpipe to the endpoint where we have to transmit, create a transfer
+context (wa_xfer) and submit it. When the xfer is done, our callback is
+called and we assign the status bits and release the xfer resources.
+
+In dequeue() we are basically cancelling/aborting the transfer. We issue
+a xfer abort request to the HC, cancell all the URBs we had submitted
+and not yet done and when all that is done, the xfer callback will be
+called--this will call the URB callback.
+
+
+    Glossary
+
+*DWA* -- Device Wire Adapter
+
+USB host, wired for downstream devices, upstream connects wirelessly
+with Wireless USB.
+
+*EVENT* -- Response to a command on the NEEP
+
+*HWA* -- Host Wire Adapter / USB dongle for UWB and Wireless USB
+
+*NEH* -- Notification/Event Handle
+
+Handle/file descriptor for receiving notifications or events. The WA
+code requires you to get one of this to listen for notifications or
+events on the NEEP.
+
+*NEEP* -- Notification/Event EndPoint
+
+Stuff related to the management of the first endpoint of a HWA USB
+dongle that is used to deliver an stream of events and notifications to
+the host.
+
+*NOTIFICATION* -- Message coming in the NEEP as response to something.
+
+*RC* -- Radio Control
+
+Design-overview.txt-1.8 (last edited 2006-11-04 12:22:24 by
+InakyPerezGonzalez)
+
diff --git a/Documentation/usb/wusb-cbaf b/Documentation/usb/wusb-cbaf
new file mode 100644 (file)
index 0000000..2e78b70
--- /dev/null
@@ -0,0 +1,139 @@
+#! /bin/bash
+#
+
+set -e
+
+progname=$(basename $0)
+function help
+{
+    cat <<EOF
+Usage: $progname COMMAND DEVICEs [ARGS]
+
+Command for manipulating the pairing/authentication credentials of a
+Wireless USB device that supports wired-mode Cable-Based-Association.
+
+Works in conjunction with the wusb-cba.ko driver from http://linuxuwb.org.
+
+
+DEVICE
+
+ sysfs path to the device to authenticate; for example, both this
+ guys are the same:
+
+ /sys/devices/pci0000:00/0000:00:1d.7/usb1/1-4/1-4.4/1-4.4:1.1
+ /sys/bus/usb/drivers/wusb-cbaf/1-4.4:1.1
+
+COMMAND/ARGS are
+
+ start
+
+   Start a WUSB host controller (by setting up a CHID)
+
+ set-chid DEVICE HOST-CHID HOST-BANDGROUP HOST-NAME
+
+   Sets host information in the device; after this you can call the
+   get-cdid to see how does this device report itself to us.
+
+ get-cdid DEVICE
+
+   Get the device ID associated to the HOST-CHDI we sent with
+   'set-chid'. We might not know about it.
+
+ set-cc DEVICE
+
+   If we allow the device to connect, set a random new CDID and CK
+   (connection key). Device saves them for the next time it wants to
+   connect wireless. We save them for that next time also so we can
+   authenticate the device (when we see the CDID he uses to id
+   itself) and the CK to crypto talk to it.
+
+CHID is always 16 hex bytes in 'XX YY ZZ...' form
+BANDGROUP is almost always 0001
+
+Examples:
+
+  You can default most arguments to '' to get a sane value:
+
+  $ $progname set-chid '' '' '' "My host name"
+
+  A full sequence:
+
+  $ $progname set-chid '' '' '' "My host name"
+  $ $progname get-cdid ''
+  $ $progname set-cc ''
+
+EOF
+}
+
+
+# Defaults
+# FIXME: CHID should come from a database :), band group from the host
+host_CHID="00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff"
+host_band_group="0001"
+host_name=$(hostname)
+
+devs="$(echo /sys/bus/usb/drivers/wusb-cbaf/[0-9]*)"
+hdevs="$(for h in /sys/class/uwb_rc/*/wusbhc; do readlink -f $h; done)"
+
+result=0
+case $1 in
+    start)
+        for dev in ${2:-$hdevs}
+          do
+          uwb_rc=$(readlink -f $dev/uwb_rc)
+          if cat $uwb_rc/beacon | grep -q -- "-1"
+              then
+              echo 13 0 > $uwb_rc/beacon
+              echo I: started beaconing on ch 13 on $(basename $uwb_rc) >&2
+          fi
+          echo $host_CHID > $dev/wusb_chid
+          echo I: started host $(basename $dev) >&2
+        done
+        ;;
+    stop)
+        for dev in ${2:-$hdevs}
+          do
+          echo 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > $dev/wusb_chid
+          echo I: stopped host $(basename $dev) >&2
+          uwb_rc=$(readlink -f $dev/uwb_rc)
+          echo -1 | cat > $uwb_rc/beacon
+          echo I: stopped beaconing on $(basename $uwb_rc) >&2
+        done
+        ;;
+    set-chid)
+        shift
+        for dev in ${2:-$devs}; do
+            echo "${4:-$host_name}" > $dev/wusb_host_name
+            echo "${3:-$host_band_group}" > $dev/wusb_host_band_groups
+            echo ${2:-$host_CHID} > $dev/wusb_chid
+        done
+        ;;
+    get-cdid)
+        for dev in ${2:-$devs}
+          do
+          cat $dev/wusb_cdid
+        done
+        ;;
+    set-cc)
+        for dev in ${2:-$devs}; do
+            shift
+            CDID="$(head --bytes=16 /dev/urandom  | od -tx1 -An)"
+            CK="$(head --bytes=16 /dev/urandom  | od -tx1 -An)"
+            echo "$CDID" > $dev/wusb_cdid
+            echo "$CK" > $dev/wusb_ck
+
+            echo I: CC set >&2
+            echo "CHID: $(cat $dev/wusb_chid)"
+            echo "CDID:$CDID"
+            echo "CK:  $CK"
+        done
+        ;;
+    help|h|--help|-h)
+        help
+        ;;
+    *)
+        echo "E: Unknown usage" 1>&2
+        help 1>&2
+        result=1
+esac
+exit $result
index 355c192d699779f773259ddd647b64d574021cfe..2085286210457ed69a280e1bd21e58b7184b2c4d 100644 (file)
@@ -1053,6 +1053,12 @@ L:       cbe-oss-dev@ozlabs.org
 W:     http://www.ibm.com/developerworks/power/cell/
 S:     Supported
 
+CERTIFIED WIRELESS USB (WUSB) SUBSYSTEM:
+P:     David Vrabel
+M:     david.vrabel@csr.com
+L:     linux-usb@vger.kernel.org
+S:     Supported
+
 CFAG12864B LCD DRIVER
 P:     Miguel Ojeda Sandonis
 M:     miguel.ojeda.sandonis@gmail.com
@@ -4191,6 +4197,12 @@ L:       sparclinux@vger.kernel.org
 T:     git kernel.org:/pub/scm/linux/kernel/git/davem/sparc-2.6.git
 S:     Maintained
 
+ULTRA-WIDEBAND (UWB) SUBSYSTEM:
+P:     David Vrabel
+M:     david.vrabel@csr.com
+L:     linux-usb@vger.kernel.org
+S:     Supported
+
 UNIFORM CDROM DRIVER
 P:     Jens Axboe
 M:     axboe@kernel.dk
@@ -4616,6 +4628,11 @@ M:       zaga@fly.cc.fer.hr
 L:     linux-scsi@vger.kernel.org
 S:     Maintained
 
+WIMEDIA LLC PROTOCOL (WLP) SUBSYSTEM
+P:     David Vrabel
+M:     david.vrabel@csr.com
+S:     Maintained
+
 WISTRON LAPTOP BUTTON DRIVER
 P:     Miloslav Trmac
 M:     mitr@volny.cz
index 4853f9df37bd22a037f122bd7d78a9a6600cc474..e1521f32e554451a9f29c4720f69c61280adee4d 100644 (file)
@@ -1254,6 +1254,8 @@ source "drivers/hid/Kconfig"
 
 source "drivers/usb/Kconfig"
 
+source "drivers/uwb/Kconfig"
+
 source "drivers/mmc/Kconfig"
 
 source "drivers/memstick/Kconfig"
index 9389d38f222f2f749497b106872278a7da5bdd0b..cb66c4da25ca76ff9ef023d6ae4e3dc32915a6bb 100644 (file)
@@ -677,6 +677,8 @@ source "fs/Kconfig"
 
 source "drivers/usb/Kconfig"
 
+source "drivers/uwb/Kconfig"
+
 source "arch/cris/Kconfig.debug"
 
 source "security/Kconfig"
index c7966746fbfec582938b77a37baaed384d1b0ec2..7789c3d82d3c3da990f49ddc1a3e202cfbaa3858 100644 (file)
@@ -214,6 +214,8 @@ source "drivers/hwmon/Kconfig"
 
 source "drivers/usb/Kconfig"
 
+source "drivers/uwb/Kconfig"
+
 endmenu
 
 source "fs/Kconfig"
index d19b6f5a1106b5202d88ecbe0dccad43a129c369..d38f43f593d400e64ab7796c1f90760c66966caf 100644 (file)
@@ -78,6 +78,8 @@ source "drivers/hid/Kconfig"
 
 source "drivers/usb/Kconfig"
 
+source "drivers/uwb/Kconfig"
+
 source "drivers/mmc/Kconfig"
 
 source "drivers/memstick/Kconfig"
index 46c8681a07f48cd89766e97155d2adb9f3b91454..cadc64fe8f68dc60ac5edf9e489b62ef913a4b5b 100644 (file)
@@ -100,3 +100,4 @@ obj-$(CONFIG_SSB)           += ssb/
 obj-$(CONFIG_VIRTIO)           += virtio/
 obj-$(CONFIG_REGULATOR)                += regulator/
 obj-$(CONFIG_STAGING)          += staging/
+obj-$(CONFIG_UWB)              += uwb/
index bcefbddeba5099877981a1717cfd22edfd35e159..c23a9857ee674d988e8aba1e6e1dd74d0a5fe6ce 100644 (file)
@@ -97,6 +97,8 @@ source "drivers/usb/core/Kconfig"
 
 source "drivers/usb/mon/Kconfig"
 
+source "drivers/usb/wusbcore/Kconfig"
+
 source "drivers/usb/host/Kconfig"
 
 source "drivers/usb/musb/Kconfig"
index a419c42e880e208f5eec222fff067a7bbe7424c3..8b7c419b876e228439a597fccfc8a2233eafdc03 100644 (file)
@@ -16,9 +16,12 @@ obj-$(CONFIG_USB_UHCI_HCD)   += host/
 obj-$(CONFIG_USB_SL811_HCD)    += host/
 obj-$(CONFIG_USB_U132_HCD)     += host/
 obj-$(CONFIG_USB_R8A66597_HCD) += host/
+obj-$(CONFIG_USB_HWA_HCD)      += host/
 
 obj-$(CONFIG_USB_C67X00_HCD)   += c67x00/
 
+obj-$(CONFIG_USB_WUSB)         += wusbcore/
+
 obj-$(CONFIG_USB_ACM)          += class/
 obj-$(CONFIG_USB_PRINTER)      += class/
 
index 228797e54f9cc009f4ed293da9ccc7f6626ea97a..72fb655e60338b8854905ebf8f99e1b174be92d4 100644 (file)
@@ -305,3 +305,31 @@ config SUPERH_ON_CHIP_R8A66597
        help
           This driver enables support for the on-chip R8A66597 in the
           SH7366 and SH7723 processors.
+
+config USB_WHCI_HCD
+       tristate "Wireless USB Host Controller Interface (WHCI) driver (EXPERIMENTAL)"
+       depends on EXPERIMENTAL
+       depends on PCI && USB
+       select USB_WUSB
+       select UWB_WHCI
+       help
+         A driver for PCI-based Wireless USB Host Controllers that are
+         compliant with the WHCI specification.
+
+         To compile this driver a module, choose M here: the module
+         will be called "whci-hcd".
+
+config USB_HWA_HCD
+       tristate "Host Wire Adapter (HWA) driver (EXPERIMENTAL)"
+       depends on EXPERIMENTAL
+       depends on USB
+       select USB_WUSB
+       select UWB_HWA
+       help
+         This driver enables you to connect Wireless USB devices to
+         your system using a Host Wire Adaptor USB dongle. This is an
+         UWB Radio Controller and WUSB Host Controller connected to
+         your machine via USB (specified in WUSB1.0).
+
+         To compile this driver a module, choose M here: the module
+         will be called "hwa-hc".
index f1edda2dcfdecdaf49f91f775ad8d11a981d110a..23be222240447bbc60c902617e77a6cc5b4476a4 100644 (file)
@@ -8,6 +8,8 @@ endif
 
 isp1760-objs := isp1760-hcd.o isp1760-if.o
 
+obj-$(CONFIG_USB_WHCI_HCD)     += whci/
+
 obj-$(CONFIG_PCI)              += pci-quirks.o
 
 obj-$(CONFIG_USB_EHCI_HCD)     += ehci-hcd.o
@@ -19,3 +21,4 @@ obj-$(CONFIG_USB_SL811_CS)    += sl811_cs.o
 obj-$(CONFIG_USB_U132_HCD)     += u132-hcd.o
 obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o
 obj-$(CONFIG_USB_ISP1760_HCD)  += isp1760.o
+obj-$(CONFIG_USB_HWA_HCD)      += hwa-hc.o
diff --git a/drivers/usb/host/hwa-hc.c b/drivers/usb/host/hwa-hc.c
new file mode 100644 (file)
index 0000000..64be4d8
--- /dev/null
@@ -0,0 +1,925 @@
+/*
+ * Host Wire Adapter:
+ * Driver glue, HWA-specific functions, bridges to WAHC and WUSBHC
+ *
+ * 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 HWA driver is a simple layer that forwards requests to the WAHC
+ * (Wire Adater Host Controller) or WUSBHC (Wireless USB Host
+ * Controller) layers.
+ *
+ * Host Wire Adapter is the 'WUSB 1.0 standard' name for Wireless-USB
+ * Host Controller that is connected to your system via USB (a USB
+ * dongle that implements a USB host...). There is also a Device Wired
+ * Adaptor, DWA (Wireless USB hub) that uses the same mechanism for
+ * transferring data (it is after all a USB host connected via
+ * Wireless USB), we have a common layer called Wire Adapter Host
+ * Controller that does all the hard work. The WUSBHC (Wireless USB
+ * Host Controller) is the part common to WUSB Host Controllers, the
+ * HWA and the PCI-based one, that is implemented following the WHCI
+ * spec. All these layers are implemented in ../wusbcore.
+ *
+ * The main functions are hwahc_op_urb_{en,de}queue(), that pass the
+ * job of converting a URB to a Wire Adapter
+ *
+ * Entry points:
+ *
+ *   hwahc_driver_*()   Driver initialization, registration and
+ *                      teardown.
+ *
+ *   hwahc_probe()     New device came up, create an instance for
+ *                      it [from device enumeration].
+ *
+ *   hwahc_disconnect()        Remove device instance [from device
+ *                      enumeration].
+ *
+ *   [__]hwahc_op_*()   Host-Wire-Adaptor specific functions for
+ *                      starting/stopping/etc (some might be made also
+ *                      DWA).
+ */
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/wait.h>
+#include <linux/completion.h>
+#include "../wusbcore/wa-hc.h"
+#include "../wusbcore/wusbhc.h"
+
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+struct hwahc {
+       struct wusbhc wusbhc;   /* has to be 1st */
+       struct wahc wa;
+       u8 buffer[16];          /* for misc usb transactions */
+};
+
+/**
+ * FIXME should be wusbhc
+ *
+ * NOTE: we need to cache the Cluster ID because later...there is no
+ *       way to get it :)
+ */
+static int __hwahc_set_cluster_id(struct hwahc *hwahc, u8 cluster_id)
+{
+       int result;
+       struct wusbhc *wusbhc = &hwahc->wusbhc;
+       struct wahc *wa = &hwahc->wa;
+       struct device *dev = &wa->usb_iface->dev;
+
+       result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+                       WUSB_REQ_SET_CLUSTER_ID,
+                       USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+                       cluster_id,
+                       wa->usb_iface->cur_altsetting->desc.bInterfaceNumber,
+                       NULL, 0, 1000 /* FIXME: arbitrary */);
+       if (result < 0)
+               dev_err(dev, "Cannot set WUSB Cluster ID to 0x%02x: %d\n",
+                       cluster_id, result);
+       else
+               wusbhc->cluster_id = cluster_id;
+       dev_info(dev, "Wireless USB Cluster ID set to 0x%02x\n", cluster_id);
+       return result;
+}
+
+static int __hwahc_op_set_num_dnts(struct wusbhc *wusbhc, u8 interval, u8 slots)
+{
+       struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+       struct wahc *wa = &hwahc->wa;
+
+       return usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+                       WUSB_REQ_SET_NUM_DNTS,
+                       USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+                       interval << 8 | slots,
+                       wa->usb_iface->cur_altsetting->desc.bInterfaceNumber,
+                       NULL, 0, 1000 /* FIXME: arbitrary */);
+}
+
+/*
+ * Reset a WUSB host controller and wait for it to complete doing it.
+ *
+ * @usb_hcd:   Pointer to WUSB Host Controller instance.
+ *
+ */
+static int hwahc_op_reset(struct usb_hcd *usb_hcd)
+{
+       int result;
+       struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+       struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+       struct device *dev = &hwahc->wa.usb_iface->dev;
+
+       d_fnstart(4, dev, "(hwahc %p)\n", hwahc);
+       mutex_lock(&wusbhc->mutex);
+       wa_nep_disarm(&hwahc->wa);
+       result = __wa_set_feature(&hwahc->wa, WA_RESET);
+       if (result < 0) {
+               dev_err(dev, "error commanding HC to reset: %d\n", result);
+               goto error_unlock;
+       }
+       d_printf(3, dev, "reset: waiting for device to change state\n");
+       result = __wa_wait_status(&hwahc->wa, WA_STATUS_RESETTING, 0);
+       if (result < 0) {
+               dev_err(dev, "error waiting for HC to reset: %d\n", result);
+               goto error_unlock;
+       }
+error_unlock:
+       mutex_unlock(&wusbhc->mutex);
+       d_fnend(4, dev, "(hwahc %p) = %d\n", hwahc, result);
+       return result;
+}
+
+/*
+ * FIXME: break this function up
+ */
+static int hwahc_op_start(struct usb_hcd *usb_hcd)
+{
+       u8 addr;
+       int result;
+       struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+       struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+       struct device *dev = &hwahc->wa.usb_iface->dev;
+
+       /* Set up a Host Info WUSB Information Element */
+       d_fnstart(4, dev, "(hwahc %p)\n", hwahc);
+       result = -ENOSPC;
+       mutex_lock(&wusbhc->mutex);
+       /* Start the numbering from the top so that the bottom
+        * range of the unauth addr space is used for devices,
+        * the top for HCs; use 0xfe - RC# */
+       addr = wusb_cluster_id_get();
+       if (addr == 0)
+               goto error_cluster_id_get;
+       result = __hwahc_set_cluster_id(hwahc, addr);
+       if (result < 0)
+               goto error_set_cluster_id;
+
+       result = wa_nep_arm(&hwahc->wa, GFP_KERNEL);
+       if (result < 0) {
+               dev_err(dev, "cannot listen to notifications: %d\n", result);
+               goto error_stop;
+       }
+       usb_hcd->uses_new_polling = 1;
+       usb_hcd->poll_rh = 1;
+       usb_hcd->state = HC_STATE_RUNNING;
+       result = 0;
+out:
+       mutex_unlock(&wusbhc->mutex);
+       d_fnend(4, dev, "(hwahc %p) = %d\n", hwahc, result);
+       return result;
+
+error_stop:
+       __wa_stop(&hwahc->wa);
+error_set_cluster_id:
+       wusb_cluster_id_put(wusbhc->cluster_id);
+error_cluster_id_get:
+       goto out;
+
+}
+
+/*
+ * FIXME: break this function up
+ */
+static int __hwahc_op_wusbhc_start(struct wusbhc *wusbhc)
+{
+       int result;
+       struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+       struct device *dev = &hwahc->wa.usb_iface->dev;
+
+       /* Set up a Host Info WUSB Information Element */
+       d_fnstart(4, dev, "(hwahc %p)\n", hwahc);
+       result = -ENOSPC;
+
+       result = __wa_set_feature(&hwahc->wa, WA_ENABLE);
+       if (result < 0) {
+               dev_err(dev, "error commanding HC to start: %d\n", result);
+               goto error_stop;
+       }
+       result = __wa_wait_status(&hwahc->wa, WA_ENABLE, WA_ENABLE);
+       if (result < 0) {
+               dev_err(dev, "error waiting for HC to start: %d\n", result);
+               goto error_stop;
+       }
+       result = 0;
+out:
+       d_fnend(4, dev, "(hwahc %p) = %d\n", hwahc, result);
+       return result;
+
+error_stop:
+       result = __wa_clear_feature(&hwahc->wa, WA_ENABLE);
+       goto out;
+}
+
+static int hwahc_op_suspend(struct usb_hcd *usb_hcd, pm_message_t msg)
+{
+       struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+       struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+       dev_err(wusbhc->dev, "%s (%p [%p], 0x%lx) UNIMPLEMENTED\n", __func__,
+               usb_hcd, hwahc, *(unsigned long *) &msg);
+       return -ENOSYS;
+}
+
+static int hwahc_op_resume(struct usb_hcd *usb_hcd)
+{
+       struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+       struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+
+       dev_err(wusbhc->dev, "%s (%p [%p]) UNIMPLEMENTED\n", __func__,
+               usb_hcd, hwahc);
+       return -ENOSYS;
+}
+
+static void __hwahc_op_wusbhc_stop(struct wusbhc *wusbhc)
+{
+       int result;
+       struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+       struct device *dev = &hwahc->wa.usb_iface->dev;
+
+       d_fnstart(4, dev, "(hwahc %p)\n", hwahc);
+       /* Nothing for now */
+       d_fnend(4, dev, "(hwahc %p) = %d\n", hwahc, result);
+       return;
+}
+
+/*
+ * No need to abort pipes, as when this is called, all the children
+ * has been disconnected and that has done it [through
+ * usb_disable_interface() -> usb_disable_endpoint() ->
+ * hwahc_op_ep_disable() - >rpipe_ep_disable()].
+ */
+static void hwahc_op_stop(struct usb_hcd *usb_hcd)
+{
+       int result;
+       struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+       struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+       struct wahc *wa = &hwahc->wa;
+       struct device *dev = &wa->usb_iface->dev;
+
+       d_fnstart(4, dev, "(hwahc %p)\n", hwahc);
+       mutex_lock(&wusbhc->mutex);
+       wusbhc_stop(wusbhc);
+       wa_nep_disarm(&hwahc->wa);
+       result = __wa_stop(&hwahc->wa);
+       wusb_cluster_id_put(wusbhc->cluster_id);
+       mutex_unlock(&wusbhc->mutex);
+       d_fnend(4, dev, "(hwahc %p) = %d\n", hwahc, result);
+       return;
+}
+
+static int hwahc_op_get_frame_number(struct usb_hcd *usb_hcd)
+{
+       struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+       struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+
+       dev_err(wusbhc->dev, "%s (%p [%p]) UNIMPLEMENTED\n", __func__,
+               usb_hcd, hwahc);
+       return -ENOSYS;
+}
+
+static int hwahc_op_urb_enqueue(struct usb_hcd *usb_hcd, struct urb *urb,
+                               gfp_t gfp)
+{
+       struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+       struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+
+       return wa_urb_enqueue(&hwahc->wa, urb->ep, urb, gfp);
+}
+
+static int hwahc_op_urb_dequeue(struct usb_hcd *usb_hcd, struct urb *urb,
+                               int status)
+{
+       struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+       struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+
+       return wa_urb_dequeue(&hwahc->wa, urb);
+}
+
+/*
+ * Release resources allocated for an endpoint
+ *
+ * If there is an associated rpipe to this endpoint, go ahead and put it.
+ */
+static void hwahc_op_endpoint_disable(struct usb_hcd *usb_hcd,
+                                     struct usb_host_endpoint *ep)
+{
+       struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+       struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+
+       rpipe_ep_disable(&hwahc->wa, ep);
+}
+
+/*
+ * Set the UWB MAS allocation for the WUSB cluster
+ *
+ * @stream_index: stream to use (-1 for cancelling the allocation)
+ * @mas: mas bitmap to use
+ */
+static int __hwahc_op_bwa_set(struct wusbhc *wusbhc, s8 stream_index,
+                             const struct uwb_mas_bm *mas)
+{
+       int result;
+       struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+       struct wahc *wa = &hwahc->wa;
+       struct device *dev = &wa->usb_iface->dev;
+       u8 mas_le[UWB_NUM_MAS/8];
+
+       /* Set the stream index */
+       result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+                       WUSB_REQ_SET_STREAM_IDX,
+                       USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+                       stream_index,
+                       wa->usb_iface->cur_altsetting->desc.bInterfaceNumber,
+                       NULL, 0, 1000 /* FIXME: arbitrary */);
+       if (result < 0) {
+               dev_err(dev, "Cannot set WUSB stream index: %d\n", result);
+               goto out;
+       }
+       uwb_mas_bm_copy_le(mas_le, mas);
+       /* Set the MAS allocation */
+       result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+                       WUSB_REQ_SET_WUSB_MAS,
+                       USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+                       0, wa->usb_iface->cur_altsetting->desc.bInterfaceNumber,
+                       mas_le, 32, 1000 /* FIXME: arbitrary */);
+       if (result < 0)
+               dev_err(dev, "Cannot set WUSB MAS allocation: %d\n", result);
+out:
+       return result;
+}
+
+/*
+ * Add an IE to the host's MMC
+ *
+ * @interval:    See WUSB1.0[8.5.3.1]
+ * @repeat_cnt:  See WUSB1.0[8.5.3.1]
+ * @handle:      See WUSB1.0[8.5.3.1]
+ * @wuie:        Pointer to the header of the WUSB IE data to add.
+ *               MUST BE allocated in a kmalloc buffer (no stack or
+ *               vmalloc).
+ *
+ * NOTE: the format of the WUSB IEs for MMCs are different to the
+ *       normal MBOA MAC IEs (IE Id + Length in MBOA MAC vs. Length +
+ *       Id in WUSB IEs). Standards...you gotta love'em.
+ */
+static int __hwahc_op_mmcie_add(struct wusbhc *wusbhc, u8 interval,
+                               u8 repeat_cnt, u8 handle,
+                               struct wuie_hdr *wuie)
+{
+       struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+       struct wahc *wa = &hwahc->wa;
+       u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber;
+
+       return usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+                       WUSB_REQ_ADD_MMC_IE,
+                       USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+                       interval << 8 | repeat_cnt,
+                       handle << 8 | iface_no,
+                       wuie, wuie->bLength, 1000 /* FIXME: arbitrary */);
+}
+
+/*
+ * Remove an IE to the host's MMC
+ *
+ * @handle:      See WUSB1.0[8.5.3.1]
+ */
+static int __hwahc_op_mmcie_rm(struct wusbhc *wusbhc, u8 handle)
+{
+       struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+       struct wahc *wa = &hwahc->wa;
+       u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber;
+       return usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+                       WUSB_REQ_REMOVE_MMC_IE,
+                       USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+                       0, handle << 8 | iface_no,
+                       NULL, 0, 1000 /* FIXME: arbitrary */);
+}
+
+/*
+ * Update device information for a given fake port
+ *
+ * @port_idx: Fake port to which device is connected (wusbhc index, not
+ *            USB port number).
+ */
+static int __hwahc_op_dev_info_set(struct wusbhc *wusbhc,
+                                  struct wusb_dev *wusb_dev)
+{
+       struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+       struct wahc *wa = &hwahc->wa;
+       u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber;
+       struct hwa_dev_info *dev_info;
+       int ret;
+
+       /* fill out the Device Info buffer and send it */
+       dev_info = kzalloc(sizeof(struct hwa_dev_info), GFP_KERNEL);
+       if (!dev_info)
+               return -ENOMEM;
+       uwb_mas_bm_copy_le(dev_info->bmDeviceAvailability,
+                          &wusb_dev->availability);
+       dev_info->bDeviceAddress = wusb_dev->addr;
+
+       /*
+        * If the descriptors haven't been read yet, use a default PHY
+        * rate of 53.3 Mbit/s only.  The correct value will be used
+        * when this will be called again as part of the
+        * authentication process (which occurs after the descriptors
+        * have been read).
+        */
+       if (wusb_dev->wusb_cap_descr)
+               dev_info->wPHYRates = wusb_dev->wusb_cap_descr->wPHYRates;
+       else
+               dev_info->wPHYRates = cpu_to_le16(USB_WIRELESS_PHY_53);
+
+       ret = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+                       WUSB_REQ_SET_DEV_INFO,
+                       USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+                       0, wusb_dev->port_idx << 8 | iface_no,
+                       dev_info, sizeof(struct hwa_dev_info),
+                       1000 /* FIXME: arbitrary */);
+       kfree(dev_info);
+       return ret;
+}
+
+/*
+ * Set host's idea of which encryption (and key) method to use when
+ * talking to ad evice on a given port.
+ *
+ * If key is NULL, it means disable encryption for that "virtual port"
+ * (used when we disconnect).
+ */
+static int __hwahc_dev_set_key(struct wusbhc *wusbhc, u8 port_idx, u32 tkid,
+                              const void *key, size_t key_size,
+                              u8 key_idx)
+{
+       int result = -ENOMEM;
+       struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+       struct wahc *wa = &hwahc->wa;
+       u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber;
+       struct usb_key_descriptor *keyd;
+       size_t keyd_len;
+
+       keyd_len = sizeof(*keyd) + key_size;
+       keyd = kzalloc(keyd_len, GFP_KERNEL);
+       if (keyd == NULL)
+               return -ENOMEM;
+
+       keyd->bLength = keyd_len;
+       keyd->bDescriptorType = USB_DT_KEY;
+       keyd->tTKID[0] = (tkid >>  0) & 0xff;
+       keyd->tTKID[1] = (tkid >>  8) & 0xff;
+       keyd->tTKID[2] = (tkid >> 16) & 0xff;
+       memcpy(keyd->bKeyData, key, key_size);
+
+       result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+                       USB_REQ_SET_DESCRIPTOR,
+                       USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+                       USB_DT_KEY << 8 | key_idx,
+                       port_idx << 8 | iface_no,
+                       keyd, keyd_len, 1000 /* FIXME: arbitrary */);
+
+       memset(keyd, 0, sizeof(*keyd)); /* clear keys etc. */
+       kfree(keyd);
+       return result;
+}
+
+/*
+ * Set host's idea of which encryption (and key) method to use when
+ * talking to ad evice on a given port.
+ *
+ * If key is NULL, it means disable encryption for that "virtual port"
+ * (used when we disconnect).
+ */
+static int __hwahc_op_set_ptk(struct wusbhc *wusbhc, u8 port_idx, u32 tkid,
+                             const void *key, size_t key_size)
+{
+       int result = -ENOMEM;
+       struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+       struct wahc *wa = &hwahc->wa;
+       u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber;
+       u8 encryption_value;
+
+       /* Tell the host which key to use to talk to the device */
+       if (key) {
+               u8 key_idx = wusb_key_index(0, WUSB_KEY_INDEX_TYPE_PTK,
+                                           WUSB_KEY_INDEX_ORIGINATOR_HOST);
+
+               result = __hwahc_dev_set_key(wusbhc, port_idx, tkid,
+                                            key, key_size, key_idx);
+               if (result < 0)
+                       goto error_set_key;
+               encryption_value = wusbhc->ccm1_etd->bEncryptionValue;
+       } else {
+               /* FIXME: this should come from wusbhc->etd[UNSECURE].value */
+               encryption_value = 0;
+       }
+
+       /* Set the encryption type for commmunicating with the device */
+       result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+                       USB_REQ_SET_ENCRYPTION,
+                       USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+                       encryption_value, port_idx << 8 | iface_no,
+                       NULL, 0, 1000 /* FIXME: arbitrary */);
+       if (result < 0)
+               dev_err(wusbhc->dev, "Can't set host's WUSB encryption for "
+                       "port index %u to %s (value %d): %d\n", port_idx,
+                       wusb_et_name(wusbhc->ccm1_etd->bEncryptionType),
+                       wusbhc->ccm1_etd->bEncryptionValue, result);
+error_set_key:
+       return result;
+}
+
+/*
+ * Set host's GTK key
+ */
+static int __hwahc_op_set_gtk(struct wusbhc *wusbhc, u32 tkid,
+                             const void *key, size_t key_size)
+{
+       u8 key_idx = wusb_key_index(0, WUSB_KEY_INDEX_TYPE_GTK,
+                                   WUSB_KEY_INDEX_ORIGINATOR_HOST);
+
+       return __hwahc_dev_set_key(wusbhc, 0, tkid, key, key_size, key_idx);
+}
+
+/*
+ * Get the Wire Adapter 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 wa_fill_descr(struct wahc *wa)
+{
+       int result;
+       struct device *dev = &wa->usb_iface->dev;
+       char *itr;
+       struct usb_device *usb_dev = wa->usb_dev;
+       struct usb_descriptor_header *hdr;
+       struct usb_wa_descriptor *wa_descr;
+       size_t itr_size, actconfig_idx;
+
+       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_WIRE_ADAPTER)
+                       goto found;
+               itr += hdr->bLength;
+               itr_size -= hdr->bLength;
+       }
+       dev_err(dev, "cannot find Wire Adapter Class descriptor\n");
+       return -ENODEV;
+
+found:
+       result = -EINVAL;
+       if (hdr->bLength > itr_size) {  /* is it available? */
+               dev_err(dev, "incomplete Wire Adapter Class descriptor "
+                       "(%zu bytes left, %u needed)\n",
+                       itr_size, hdr->bLength);
+               goto error;
+       }
+       if (hdr->bLength < sizeof(*wa->wa_descr)) {
+               dev_err(dev, "short Wire Adapter Class descriptor\n");
+               goto error;
+       }
+       wa->wa_descr = wa_descr = (struct usb_wa_descriptor *) hdr;
+       /* Make LE fields CPU order */
+       wa_descr->bcdWAVersion = le16_to_cpu(wa_descr->bcdWAVersion);
+       wa_descr->wNumRPipes = le16_to_cpu(wa_descr->wNumRPipes);
+       wa_descr->wRPipeMaxBlock = le16_to_cpu(wa_descr->wRPipeMaxBlock);
+       if (wa_descr->bcdWAVersion > 0x0100)
+               dev_warn(dev, "Wire Adapter v%d.%d newer than groked v1.0\n",
+                        wa_descr->bcdWAVersion & 0xff00 >> 8,
+                        wa_descr->bcdWAVersion & 0x00ff);
+       result = 0;
+error:
+       return result;
+}
+
+static struct hc_driver hwahc_hc_driver = {
+       .description = "hwa-hcd",
+       .product_desc = "Wireless USB HWA host controller",
+       .hcd_priv_size = sizeof(struct hwahc) - sizeof(struct usb_hcd),
+       .irq = NULL,                    /* FIXME */
+       .flags = HCD_USB2,              /* FIXME */
+       .reset = hwahc_op_reset,
+       .start = hwahc_op_start,
+       .pci_suspend = hwahc_op_suspend,
+       .pci_resume = hwahc_op_resume,
+       .stop = hwahc_op_stop,
+       .get_frame_number = hwahc_op_get_frame_number,
+       .urb_enqueue = hwahc_op_urb_enqueue,
+       .urb_dequeue = hwahc_op_urb_dequeue,
+       .endpoint_disable = hwahc_op_endpoint_disable,
+
+       .hub_status_data = wusbhc_rh_status_data,
+       .hub_control = wusbhc_rh_control,
+       .bus_suspend = wusbhc_rh_suspend,
+       .bus_resume = wusbhc_rh_resume,
+       .start_port_reset = wusbhc_rh_start_port_reset,
+};
+
+static int hwahc_security_create(struct hwahc *hwahc)
+{
+       int result;
+       struct wusbhc *wusbhc = &hwahc->wusbhc;
+       struct usb_device *usb_dev = hwahc->wa.usb_dev;
+       struct device *dev = &usb_dev->dev;
+       struct usb_security_descriptor *secd;
+       struct usb_encryption_descriptor *etd;
+       void *itr, *top;
+       size_t itr_size, needed, bytes;
+       u8 index;
+       char buf[64];
+
+       /* Find the host's security descriptors in the config descr bundle */
+       index = (usb_dev->actconfig - usb_dev->config) /
+               sizeof(usb_dev->config[0]);
+       itr = usb_dev->rawdescriptors[index];
+       itr_size = le16_to_cpu(usb_dev->actconfig->desc.wTotalLength);
+       top = itr + itr_size;
+       result = __usb_get_extra_descriptor(usb_dev->rawdescriptors[index],
+                       le16_to_cpu(usb_dev->actconfig->desc.wTotalLength),
+                       USB_DT_SECURITY, (void **) &secd);
+       if (result == -1) {
+               dev_warn(dev, "BUG? WUSB host has no security descriptors\n");
+               return 0;
+       }
+       needed = sizeof(*secd);
+       if (top - (void *)secd < needed) {
+               dev_err(dev, "BUG? Not enough data to process security "
+                       "descriptor header (%zu bytes left vs %zu needed)\n",
+                       top - (void *) secd, needed);
+               return 0;
+       }
+       needed = le16_to_cpu(secd->wTotalLength);
+       if (top - (void *)secd < needed) {
+               dev_err(dev, "BUG? Not enough data to process security "
+                       "descriptors (%zu bytes left vs %zu needed)\n",
+                       top - (void *) secd, needed);
+               return 0;
+       }
+       /* Walk over the sec descriptors and store CCM1's on wusbhc */
+       itr = (void *) secd + sizeof(*secd);
+       top = (void *) secd + le16_to_cpu(secd->wTotalLength);
+       index = 0;
+       bytes = 0;
+       while (itr < top) {
+               etd = itr;
+               if (top - itr < sizeof(*etd)) {
+                       dev_err(dev, "BUG: bad host security descriptor; "
+                               "not enough data (%zu vs %zu left)\n",
+                               top - itr, sizeof(*etd));
+                       break;
+               }
+               if (etd->bLength < sizeof(*etd)) {
+                       dev_err(dev, "BUG: bad host encryption descriptor; "
+                               "descriptor is too short "
+                               "(%zu vs %zu needed)\n",
+                               (size_t)etd->bLength, sizeof(*etd));
+                       break;
+               }
+               itr += etd->bLength;
+               bytes += snprintf(buf + bytes, sizeof(buf) - bytes,
+                                 "%s (0x%02x) ",
+                                 wusb_et_name(etd->bEncryptionType),
+                                 etd->bEncryptionValue);
+               wusbhc->ccm1_etd = etd;
+       }
+       dev_info(dev, "supported encryption types: %s\n", buf);
+       if (wusbhc->ccm1_etd == NULL) {
+               dev_err(dev, "E: host doesn't support CCM-1 crypto\n");
+               return 0;
+       }
+       /* Pretty print what we support */
+       return 0;
+}
+
+static void hwahc_security_release(struct hwahc *hwahc)
+{
+       /* nothing to do here so far... */
+}
+
+static int hwahc_create(struct hwahc *hwahc, struct usb_interface *iface)
+{
+       int result;
+       struct device *dev = &iface->dev;
+       struct wusbhc *wusbhc = &hwahc->wusbhc;
+       struct wahc *wa = &hwahc->wa;
+       struct usb_device *usb_dev = interface_to_usbdev(iface);
+
+       wa->usb_dev = usb_get_dev(usb_dev);     /* bind the USB device */
+       wa->usb_iface = usb_get_intf(iface);
+       wusbhc->dev = dev;
+       wusbhc->uwb_rc = uwb_rc_get_by_grandpa(iface->dev.parent);
+       if (wusbhc->uwb_rc == NULL) {
+               result = -ENODEV;
+               dev_err(dev, "Cannot get associated UWB Host Controller\n");
+               goto error_rc_get;
+       }
+       result = wa_fill_descr(wa);     /* Get the device descriptor */
+       if (result < 0)
+               goto error_fill_descriptor;
+       if (wa->wa_descr->bNumPorts > USB_MAXCHILDREN) {
+               dev_err(dev, "FIXME: USB_MAXCHILDREN too low for WUSB "
+                       "adapter (%u ports)\n", wa->wa_descr->bNumPorts);
+               wusbhc->ports_max = USB_MAXCHILDREN;
+       } else {
+               wusbhc->ports_max = wa->wa_descr->bNumPorts;
+       }
+       wusbhc->mmcies_max = wa->wa_descr->bNumMMCIEs;
+       wusbhc->start = __hwahc_op_wusbhc_start;
+       wusbhc->stop = __hwahc_op_wusbhc_stop;
+       wusbhc->mmcie_add = __hwahc_op_mmcie_add;
+       wusbhc->mmcie_rm = __hwahc_op_mmcie_rm;
+       wusbhc->dev_info_set = __hwahc_op_dev_info_set;
+       wusbhc->bwa_set = __hwahc_op_bwa_set;
+       wusbhc->set_num_dnts = __hwahc_op_set_num_dnts;
+       wusbhc->set_ptk = __hwahc_op_set_ptk;
+       wusbhc->set_gtk = __hwahc_op_set_gtk;
+       result = hwahc_security_create(hwahc);
+       if (result < 0) {
+               dev_err(dev, "Can't initialize security: %d\n", result);
+               goto error_security_create;
+       }
+       wa->wusb = wusbhc;      /* FIXME: ugly, need to fix */
+       result = wusbhc_create(&hwahc->wusbhc);
+       if (result < 0) {
+               dev_err(dev, "Can't create WUSB HC structures: %d\n", result);
+               goto error_wusbhc_create;
+       }
+       result = wa_create(&hwahc->wa, iface);
+       if (result < 0)
+               goto error_wa_create;
+       return 0;
+
+error_wa_create:
+       wusbhc_destroy(&hwahc->wusbhc);
+error_wusbhc_create:
+       /* WA Descr fill allocs no resources */
+error_security_create:
+error_fill_descriptor:
+       uwb_rc_put(wusbhc->uwb_rc);
+error_rc_get:
+       usb_put_intf(iface);
+       usb_put_dev(usb_dev);
+       return result;
+}
+
+static void hwahc_destroy(struct hwahc *hwahc)
+{
+       struct wusbhc *wusbhc = &hwahc->wusbhc;
+
+       d_fnstart(1, NULL, "(hwahc %p)\n", hwahc);
+       mutex_lock(&wusbhc->mutex);
+       __wa_destroy(&hwahc->wa);
+       wusbhc_destroy(&hwahc->wusbhc);
+       hwahc_security_release(hwahc);
+       hwahc->wusbhc.dev = NULL;
+       uwb_rc_put(wusbhc->uwb_rc);
+       usb_put_intf(hwahc->wa.usb_iface);
+       usb_put_dev(hwahc->wa.usb_dev);
+       mutex_unlock(&wusbhc->mutex);
+       d_fnend(1, NULL, "(hwahc %p) = void\n", hwahc);
+}
+
+static void hwahc_init(struct hwahc *hwahc)
+{
+       wa_init(&hwahc->wa);
+}
+
+static int hwahc_probe(struct usb_interface *usb_iface,
+                      const struct usb_device_id *id)
+{
+       int result;
+       struct usb_hcd *usb_hcd;
+       struct wusbhc *wusbhc;
+       struct hwahc *hwahc;
+       struct device *dev = &usb_iface->dev;
+
+       d_fnstart(4, dev, "(%p, %p)\n", usb_iface, id);
+       result = -ENOMEM;
+       usb_hcd = usb_create_hcd(&hwahc_hc_driver, &usb_iface->dev, "wusb-hwa");
+       if (usb_hcd == NULL) {
+               dev_err(dev, "unable to allocate instance\n");
+               goto error_alloc;
+       }
+       usb_hcd->wireless = 1;
+       usb_hcd->flags |= HCD_FLAG_SAW_IRQ;
+       wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+       hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+       hwahc_init(hwahc);
+       result = hwahc_create(hwahc, usb_iface);
+       if (result < 0) {
+               dev_err(dev, "Cannot initialize internals: %d\n", result);
+               goto error_hwahc_create;
+       }
+       result = usb_add_hcd(usb_hcd, 0, 0);
+       if (result < 0) {
+               dev_err(dev, "Cannot add HCD: %d\n", result);
+               goto error_add_hcd;
+       }
+       result = wusbhc_b_create(&hwahc->wusbhc);
+       if (result < 0) {
+               dev_err(dev, "Cannot setup phase B of WUSBHC: %d\n", result);
+               goto error_wusbhc_b_create;
+       }
+       d_fnend(4, dev, "(%p, %p) = 0\n", usb_iface, id);
+       return 0;
+
+error_wusbhc_b_create:
+       usb_remove_hcd(usb_hcd);
+error_add_hcd:
+       hwahc_destroy(hwahc);
+error_hwahc_create:
+       usb_put_hcd(usb_hcd);
+error_alloc:
+       d_fnend(4, dev, "(%p, %p) = %d\n", usb_iface, id, result);
+       return result;
+}
+
+static void hwahc_disconnect(struct usb_interface *usb_iface)
+{
+       struct usb_hcd *usb_hcd;
+       struct wusbhc *wusbhc;
+       struct hwahc *hwahc;
+
+       usb_hcd = usb_get_intfdata(usb_iface);
+       wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+       hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+
+       d_fnstart(1, NULL, "(hwahc %p [usb_iface %p])\n", hwahc, usb_iface);
+       wusbhc_b_destroy(&hwahc->wusbhc);
+       usb_remove_hcd(usb_hcd);
+       hwahc_destroy(hwahc);
+       usb_put_hcd(usb_hcd);
+       d_fnend(1, NULL, "(hwahc %p [usb_iface %p]) = void\n", hwahc,
+               usb_iface);
+}
+
+/** USB device ID's that we handle */
+static struct usb_device_id hwahc_id_table[] = {
+       /* FIXME: use class labels for this */
+       { USB_INTERFACE_INFO(0xe0, 0x02, 0x01), },
+       {},
+};
+MODULE_DEVICE_TABLE(usb, hwahc_id_table);
+
+static struct usb_driver hwahc_driver = {
+       .name =         "hwa-hc",
+       .probe =        hwahc_probe,
+       .disconnect =   hwahc_disconnect,
+       .id_table =     hwahc_id_table,
+};
+
+static int __init hwahc_driver_init(void)
+{
+       int result;
+       result = usb_register(&hwahc_driver);
+       if (result < 0) {
+               printk(KERN_ERR "WA-CDS: Cannot register USB driver: %d\n",
+                      result);
+               goto error_usb_register;
+       }
+       return 0;
+
+error_usb_register:
+       return result;
+
+}
+module_init(hwahc_driver_init);
+
+static void __exit hwahc_driver_exit(void)
+{
+       usb_deregister(&hwahc_driver);
+}
+module_exit(hwahc_driver_exit);
+
+
+MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
+MODULE_DESCRIPTION("Host Wired Adapter USB Host Control Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/whci/Kbuild b/drivers/usb/host/whci/Kbuild
new file mode 100644 (file)
index 0000000..26a3871
--- /dev/null
@@ -0,0 +1,11 @@
+obj-$(CONFIG_USB_WHCI_HCD) += whci-hcd.o
+
+whci-hcd-y := \
+       asl.o   \
+       hcd.o   \
+       hw.o    \
+       init.o  \
+       int.o   \
+       pzl.o   \
+       qset.o  \
+       wusb.o
diff --git a/drivers/usb/host/whci/asl.c b/drivers/usb/host/whci/asl.c
new file mode 100644 (file)
index 0000000..4d7078e
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ * Wireless Host Controller (WHC) asynchronous schedule management.
+ *
+ * Copyright (C) 2007 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/dma-mapping.h>
+#include <linux/uwb/umc.h>
+#include <linux/usb.h>
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+#include "../../wusbcore/wusbhc.h"
+
+#include "whcd.h"
+
+#if D_LOCAL >= 4
+static void dump_asl(struct whc *whc, const char *tag)
+{
+       struct device *dev = &whc->umc->dev;
+       struct whc_qset *qset;
+
+       d_printf(4, dev, "ASL %s\n", tag);
+
+       list_for_each_entry(qset, &whc->async_list, list_node) {
+               dump_qset(qset, dev);
+       }
+}
+#else
+static inline void dump_asl(struct whc *whc, const char *tag)
+{
+}
+#endif
+
+
+static void qset_get_next_prev(struct whc *whc, struct whc_qset *qset,
+                              struct whc_qset **next, struct whc_qset **prev)
+{
+       struct list_head *n, *p;
+
+       BUG_ON(list_empty(&whc->async_list));
+
+       n = qset->list_node.next;
+       if (n == &whc->async_list)
+               n = n->next;
+       p = qset->list_node.prev;
+       if (p == &whc->async_list)
+               p = p->prev;
+
+       *next = container_of(n, struct whc_qset, list_node);
+       *prev = container_of(p, struct whc_qset, list_node);
+
+}
+
+static void asl_qset_insert_begin(struct whc *whc, struct whc_qset *qset)
+{
+       list_move(&qset->list_node, &whc->async_list);
+       qset->in_sw_list = true;
+}
+
+static void asl_qset_insert(struct whc *whc, struct whc_qset *qset)
+{
+       struct whc_qset *next, *prev;
+
+       qset_clear(whc, qset);
+
+       /* Link into ASL. */
+       qset_get_next_prev(whc, qset, &next, &prev);
+       whc_qset_set_link_ptr(&qset->qh.link, next->qset_dma);
+       whc_qset_set_link_ptr(&prev->qh.link, qset->qset_dma);
+       qset->in_hw_list = true;
+}
+
+static void asl_qset_remove(struct whc *whc, struct whc_qset *qset)
+{
+       struct whc_qset *prev, *next;
+
+       qset_get_next_prev(whc, qset, &next, &prev);
+
+       list_move(&qset->list_node, &whc->async_removed_list);
+       qset->in_sw_list = false;
+
+       /*
+        * No more qsets in the ASL?  The caller must stop the ASL as
+        * it's no longer valid.
+        */
+       if (list_empty(&whc->async_list))
+               return;
+
+       /* Remove from ASL. */
+       whc_qset_set_link_ptr(&prev->qh.link, next->qset_dma);
+       qset->in_hw_list = false;
+}
+
+/**
+ * process_qset - process any recently inactivated or halted qTDs in a
+ * qset.
+ *
+ * After inactive qTDs are removed, new qTDs can be added if the
+ * urb queue still contains URBs.
+ *
+ * Returns any additional WUSBCMD bits for the ASL sync command (i.e.,
+ * WUSBCMD_ASYNC_QSET_RM if a halted qset was removed).
+ */
+static uint32_t process_qset(struct whc *whc, struct whc_qset *qset)
+{
+       enum whc_update update = 0;
+       uint32_t status = 0;
+
+       while (qset->ntds) {
+               struct whc_qtd *td;
+               int t;
+
+               t = qset->td_start;
+               td = &qset->qtd[qset->td_start];
+               status = le32_to_cpu(td->status);
+
+               /*
+                * Nothing to do with a still active qTD.
+                */
+               if (status & QTD_STS_ACTIVE)
+                       break;
+
+               if (status & QTD_STS_HALTED) {
+                       /* Ug, an error. */
+                       process_halted_qtd(whc, qset, td);
+                       goto done;
+               }
+
+               /* Mmm, a completed qTD. */
+               process_inactive_qtd(whc, qset, td);
+       }
+
+       update |= qset_add_qtds(whc, qset);
+
+done:
+       /*
+        * Remove this qset from the ASL if requested, but only if has
+        * no qTDs.
+        */
+       if (qset->remove && qset->ntds == 0) {
+               asl_qset_remove(whc, qset);
+               update |= WHC_UPDATE_REMOVED;
+       }
+       return update;
+}
+
+void asl_start(struct whc *whc)
+{
+       struct whc_qset *qset;
+
+       qset = list_first_entry(&whc->async_list, struct whc_qset, list_node);
+
+       le_writeq(qset->qset_dma | QH_LINK_NTDS(8), whc->base + WUSBASYNCLISTADDR);
+
+       whc_write_wusbcmd(whc, WUSBCMD_ASYNC_EN, WUSBCMD_ASYNC_EN);
+       whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS,
+                     WUSBSTS_ASYNC_SCHED, WUSBSTS_ASYNC_SCHED,
+                     1000, "start ASL");
+}
+
+void asl_stop(struct whc *whc)
+{
+       whc_write_wusbcmd(whc, WUSBCMD_ASYNC_EN, 0);
+       whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS,
+                     WUSBSTS_ASYNC_SCHED, 0,
+                     1000, "stop ASL");
+}
+
+void asl_update(struct whc *whc, uint32_t wusbcmd)
+{
+       whc_write_wusbcmd(whc, wusbcmd, wusbcmd);
+       wait_event(whc->async_list_wq,
+                  (le_readl(whc->base + WUSBCMD) & WUSBCMD_ASYNC_UPDATED) == 0);
+}
+
+/**
+ * scan_async_work - scan the ASL for qsets to process.
+ *
+ * Process each qset in the ASL in turn and then signal the WHC that
+ * the ASL has been updated.
+ *
+ * Then start, stop or update the asynchronous schedule as required.
+ */
+void scan_async_work(struct work_struct *work)
+{
+       struct whc *whc = container_of(work, struct whc, async_work);
+       struct whc_qset *qset, *t;
+       enum whc_update update = 0;
+
+       spin_lock_irq(&whc->lock);
+
+       dump_asl(whc, "before processing");
+
+       /*
+        * Transerve the software list backwards so new qsets can be
+        * safely inserted into the ASL without making it non-circular.
+        */
+       list_for_each_entry_safe_reverse(qset, t, &whc->async_list, list_node) {
+               if (!qset->in_hw_list) {
+                       asl_qset_insert(whc, qset);
+                       update |= WHC_UPDATE_ADDED;
+               }
+
+               update |= process_qset(whc, qset);
+       }
+
+       dump_asl(whc, "after processing");
+
+       spin_unlock_irq(&whc->lock);
+
+       if (update) {
+               uint32_t wusbcmd = WUSBCMD_ASYNC_UPDATED | WUSBCMD_ASYNC_SYNCED_DB;
+               if (update & WHC_UPDATE_REMOVED)
+                       wusbcmd |= WUSBCMD_ASYNC_QSET_RM;
+               asl_update(whc, wusbcmd);
+       }
+
+       /*
+        * Now that the ASL is updated, complete the removal of any
+        * removed qsets.
+        */
+       spin_lock(&whc->lock);
+
+       list_for_each_entry_safe(qset, t, &whc->async_removed_list, list_node) {
+               qset_remove_complete(whc, qset);
+       }
+
+       spin_unlock(&whc->lock);
+}
+
+/**
+ * asl_urb_enqueue - queue an URB onto the asynchronous list (ASL).
+ * @whc: the WHCI host controller
+ * @urb: the URB to enqueue
+ * @mem_flags: flags for any memory allocations
+ *
+ * The qset for the endpoint is obtained and the urb queued on to it.
+ *
+ * Work is scheduled to update the hardware's view of the ASL.
+ */
+int asl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags)
+{
+       struct whc_qset *qset;
+       int err;
+       unsigned long flags;
+
+       spin_lock_irqsave(&whc->lock, flags);
+
+       qset = get_qset(whc, urb, GFP_ATOMIC);
+       if (qset == NULL)
+               err = -ENOMEM;
+       else
+               err = qset_add_urb(whc, qset, urb, GFP_ATOMIC);
+       if (!err) {
+               usb_hcd_link_urb_to_ep(&whc->wusbhc.usb_hcd, urb);
+               if (!qset->in_sw_list)
+                       asl_qset_insert_begin(whc, qset);
+       }
+
+       spin_unlock_irqrestore(&whc->lock, flags);
+
+       if (!err)
+               queue_work(whc->workqueue, &whc->async_work);
+
+       return 0;
+}
+
+/**
+ * asl_urb_dequeue - remove an URB (qset) from the async list.
+ * @whc: the WHCI host controller
+ * @urb: the URB to dequeue
+ * @status: the current status of the URB
+ *
+ * URBs that do yet have qTDs can simply be removed from the software
+ * queue, otherwise the qset must be removed from the ASL so the qTDs
+ * can be removed.
+ */
+int asl_urb_dequeue(struct whc *whc, struct urb *urb, int status)
+{
+       struct whc_urb *wurb = urb->hcpriv;
+       struct whc_qset *qset = wurb->qset;
+       struct whc_std *std, *t;
+       int ret;
+       unsigned long flags;
+
+       spin_lock_irqsave(&whc->lock, flags);
+
+       ret = usb_hcd_check_unlink_urb(&whc->wusbhc.usb_hcd, urb, status);
+       if (ret < 0)
+               goto out;
+
+       list_for_each_entry_safe(std, t, &qset->stds, list_node) {
+               if (std->urb == urb)
+                       qset_free_std(whc, std);
+               else
+                       std->qtd = NULL; /* so this std is re-added when the qset is */
+       }
+
+       asl_qset_remove(whc, qset);
+       wurb->status = status;
+       wurb->is_async = true;
+       queue_work(whc->workqueue, &wurb->dequeue_work);
+
+out:
+       spin_unlock_irqrestore(&whc->lock, flags);
+
+       return ret;
+}
+
+/**
+ * asl_qset_delete - delete a qset from the ASL
+ */
+void asl_qset_delete(struct whc *whc, struct whc_qset *qset)
+{
+       qset->remove = 1;
+       queue_work(whc->workqueue, &whc->async_work);
+       qset_delete(whc, qset);
+}
+
+/**
+ * asl_init - initialize the asynchronous schedule list
+ *
+ * A dummy qset with no qTDs is added to the ASL to simplify removing
+ * qsets (no need to stop the ASL when the last qset is removed).
+ */
+int asl_init(struct whc *whc)
+{
+       struct whc_qset *qset;
+
+       qset = qset_alloc(whc, GFP_KERNEL);
+       if (qset == NULL)
+               return -ENOMEM;
+
+       asl_qset_insert_begin(whc, qset);
+       asl_qset_insert(whc, qset);
+
+       return 0;
+}
+
+/**
+ * asl_clean_up - free ASL resources
+ *
+ * The ASL is stopped and empty except for the dummy qset.
+ */
+void asl_clean_up(struct whc *whc)
+{
+       struct whc_qset *qset;
+
+       if (!list_empty(&whc->async_list)) {
+               qset = list_first_entry(&whc->async_list, struct whc_qset, list_node);
+               list_del(&qset->list_node);
+               qset_free(whc, qset);
+       }
+}
diff --git a/drivers/usb/host/whci/hcd.c b/drivers/usb/host/whci/hcd.c
new file mode 100644 (file)
index 0000000..ef3ad4d
--- /dev/null
@@ -0,0 +1,339 @@
+/*
+ * Wireless Host Controller (WHC) driver.
+ *
+ * Copyright (C) 2007 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/init.h>
+#include <linux/uwb/umc.h>
+
+#include "../../wusbcore/wusbhc.h"
+
+#include "whcd.h"
+
+/*
+ * One time initialization.
+ *
+ * Nothing to do here.
+ */
+static int whc_reset(struct usb_hcd *usb_hcd)
+{
+       return 0;
+}
+
+/*
+ * Start the wireless host controller.
+ *
+ * Start device notification.
+ *
+ * Put hc into run state, set DNTS parameters.
+ */
+static int whc_start(struct usb_hcd *usb_hcd)
+{
+       struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+       struct whc *whc = wusbhc_to_whc(wusbhc);
+       u8 bcid;
+       int ret;
+
+       mutex_lock(&wusbhc->mutex);
+
+       le_writel(WUSBINTR_GEN_CMD_DONE
+                 | WUSBINTR_HOST_ERR
+                 | WUSBINTR_ASYNC_SCHED_SYNCED
+                 | WUSBINTR_DNTS_INT
+                 | WUSBINTR_ERR_INT
+                 | WUSBINTR_INT,
+                 whc->base + WUSBINTR);
+
+       /* set cluster ID */
+       bcid = wusb_cluster_id_get();
+       ret = whc_set_cluster_id(whc, bcid);
+       if (ret < 0)
+               goto out;
+       wusbhc->cluster_id = bcid;
+
+       /* start HC */
+       whc_write_wusbcmd(whc, WUSBCMD_RUN, WUSBCMD_RUN);
+
+       usb_hcd->uses_new_polling = 1;
+       usb_hcd->poll_rh = 1;
+       usb_hcd->state = HC_STATE_RUNNING;
+
+out:
+       mutex_unlock(&wusbhc->mutex);
+       return ret;
+}
+
+
+/*
+ * Stop the wireless host controller.
+ *
+ * Stop device notification.
+ *
+ * Wait for pending transfer to stop? Put hc into stop state?
+ */
+static void whc_stop(struct usb_hcd *usb_hcd)
+{
+       struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+       struct whc *whc = wusbhc_to_whc(wusbhc);
+
+       mutex_lock(&wusbhc->mutex);
+
+       wusbhc_stop(wusbhc);
+
+       /* stop HC */
+       le_writel(0, whc->base + WUSBINTR);
+       whc_write_wusbcmd(whc, WUSBCMD_RUN, 0);
+       whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS,
+                     WUSBSTS_HCHALTED, WUSBSTS_HCHALTED,
+                     100, "HC to halt");
+
+       wusb_cluster_id_put(wusbhc->cluster_id);
+
+       mutex_unlock(&wusbhc->mutex);
+}
+
+static int whc_get_frame_number(struct usb_hcd *usb_hcd)
+{
+       /* Frame numbers are not applicable to WUSB. */
+       return -ENOSYS;
+}
+
+
+/*
+ * Queue an URB to the ASL or PZL
+ */
+static int whc_urb_enqueue(struct usb_hcd *usb_hcd, struct urb *urb,
+                          gfp_t mem_flags)
+{
+       struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+       struct whc *whc = wusbhc_to_whc(wusbhc);
+       int ret;
+
+       switch (usb_pipetype(urb->pipe)) {
+       case PIPE_INTERRUPT:
+               ret = pzl_urb_enqueue(whc, urb, mem_flags);
+               break;
+       case PIPE_ISOCHRONOUS:
+               dev_err(&whc->umc->dev, "isochronous transfers unsupported\n");
+               ret = -ENOTSUPP;
+               break;
+       case PIPE_CONTROL:
+       case PIPE_BULK:
+       default:
+               ret = asl_urb_enqueue(whc, urb, mem_flags);
+               break;
+       };
+
+       return ret;
+}
+
+/*
+ * Remove a queued URB from the ASL or PZL.
+ */
+static int whc_urb_dequeue(struct usb_hcd *usb_hcd, struct urb *urb, int status)
+{
+       struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+       struct whc *whc = wusbhc_to_whc(wusbhc);
+       int ret;
+
+       switch (usb_pipetype(urb->pipe)) {
+       case PIPE_INTERRUPT:
+               ret = pzl_urb_dequeue(whc, urb, status);
+               break;
+       case PIPE_ISOCHRONOUS:
+               ret = -ENOTSUPP;
+               break;
+       case PIPE_CONTROL:
+       case PIPE_BULK:
+       default:
+               ret = asl_urb_dequeue(whc, urb, status);
+               break;
+       };
+
+       return ret;
+}
+
+/*
+ * Wait for all URBs to the endpoint to be completed, then delete the
+ * qset.
+ */
+static void whc_endpoint_disable(struct usb_hcd *usb_hcd,
+                                struct usb_host_endpoint *ep)
+{
+       struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+       struct whc *whc = wusbhc_to_whc(wusbhc);
+       struct whc_qset *qset;
+
+       qset = ep->hcpriv;
+       if (qset) {
+               ep->hcpriv = NULL;
+               if (usb_endpoint_xfer_bulk(&ep->desc)
+                   || usb_endpoint_xfer_control(&ep->desc))
+                       asl_qset_delete(whc, qset);
+               else
+                       pzl_qset_delete(whc, qset);
+       }
+}
+
+static struct hc_driver whc_hc_driver = {
+       .description = "whci-hcd",
+       .product_desc = "Wireless host controller",
+       .hcd_priv_size = sizeof(struct whc) - sizeof(struct usb_hcd),
+       .irq = whc_int_handler,
+       .flags = HCD_USB2,
+
+       .reset = whc_reset,
+       .start = whc_start,
+       .stop = whc_stop,
+       .get_frame_number = whc_get_frame_number,
+       .urb_enqueue = whc_urb_enqueue,
+       .urb_dequeue = whc_urb_dequeue,
+       .endpoint_disable = whc_endpoint_disable,
+
+       .hub_status_data = wusbhc_rh_status_data,
+       .hub_control = wusbhc_rh_control,
+       .bus_suspend = wusbhc_rh_suspend,
+       .bus_resume = wusbhc_rh_resume,
+       .start_port_reset = wusbhc_rh_start_port_reset,
+};
+
+static int whc_probe(struct umc_dev *umc)
+{
+       int ret = -ENOMEM;
+       struct usb_hcd *usb_hcd;
+       struct wusbhc *wusbhc = NULL;
+       struct whc *whc = NULL;
+       struct device *dev = &umc->dev;
+
+       usb_hcd = usb_create_hcd(&whc_hc_driver, dev, "whci");
+       if (usb_hcd == NULL) {
+               dev_err(dev, "unable to create hcd\n");
+               goto error;
+       }
+
+       usb_hcd->wireless = 1;
+
+       wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+       whc = wusbhc_to_whc(wusbhc);
+       whc->umc = umc;
+
+       ret = whc_init(whc);
+       if (ret)
+               goto error;
+
+       wusbhc->dev = dev;
+       wusbhc->uwb_rc = uwb_rc_get_by_grandpa(umc->dev.parent);
+       if (!wusbhc->uwb_rc) {
+               ret = -ENODEV;
+               dev_err(dev, "cannot get radio controller\n");
+               goto error;
+       }
+
+       if (whc->n_devices > USB_MAXCHILDREN) {
+               dev_warn(dev, "USB_MAXCHILDREN too low for WUSB adapter (%u ports)\n",
+                        whc->n_devices);
+               wusbhc->ports_max = USB_MAXCHILDREN;
+       } else
+               wusbhc->ports_max = whc->n_devices;
+       wusbhc->mmcies_max      = whc->n_mmc_ies;
+       wusbhc->start           = whc_wusbhc_start;
+       wusbhc->stop            = whc_wusbhc_stop;
+       wusbhc->mmcie_add       = whc_mmcie_add;
+       wusbhc->mmcie_rm        = whc_mmcie_rm;
+       wusbhc->dev_info_set    = whc_dev_info_set;
+       wusbhc->bwa_set         = whc_bwa_set;
+       wusbhc->set_num_dnts    = whc_set_num_dnts;
+       wusbhc->set_ptk         = whc_set_ptk;
+       wusbhc->set_gtk         = whc_set_gtk;
+
+       ret = wusbhc_create(wusbhc);
+       if (ret)
+               goto error_wusbhc_create;
+
+       ret = usb_add_hcd(usb_hcd, whc->umc->irq, IRQF_SHARED);
+       if (ret) {
+               dev_err(dev, "cannot add HCD: %d\n", ret);
+               goto error_usb_add_hcd;
+       }
+
+       ret = wusbhc_b_create(wusbhc);
+       if (ret) {
+               dev_err(dev, "WUSBHC phase B setup failed: %d\n", ret);
+               goto error_wusbhc_b_create;
+       }
+
+       return 0;
+
+error_wusbhc_b_create:
+       usb_remove_hcd(usb_hcd);
+error_usb_add_hcd:
+       wusbhc_destroy(wusbhc);
+error_wusbhc_create:
+       uwb_rc_put(wusbhc->uwb_rc);
+error:
+       whc_clean_up(whc);
+       if (usb_hcd)
+               usb_put_hcd(usb_hcd);
+       return ret;
+}
+
+
+static void whc_remove(struct umc_dev *umc)
+{
+       struct usb_hcd *usb_hcd = dev_get_drvdata(&umc->dev);
+       struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+       struct whc *whc = wusbhc_to_whc(wusbhc);
+
+       if (usb_hcd) {
+               wusbhc_b_destroy(wusbhc);
+               usb_remove_hcd(usb_hcd);
+               wusbhc_destroy(wusbhc);
+               uwb_rc_put(wusbhc->uwb_rc);
+               whc_clean_up(whc);
+               usb_put_hcd(usb_hcd);
+       }
+}
+
+static struct umc_driver whci_hc_driver = {
+       .name =         "whci-hcd",
+       .cap_id =       UMC_CAP_ID_WHCI_WUSB_HC,
+       .probe =        whc_probe,
+       .remove =       whc_remove,
+};
+
+static int __init whci_hc_driver_init(void)
+{
+       return umc_driver_register(&whci_hc_driver);
+}
+module_init(whci_hc_driver_init);
+
+static void __exit whci_hc_driver_exit(void)
+{
+       umc_driver_unregister(&whci_hc_driver);
+}
+module_exit(whci_hc_driver_exit);
+
+/* PCI device ID's that we handle (so it gets loaded) */
+static struct pci_device_id whci_hcd_id_table[] = {
+       { PCI_DEVICE_CLASS(PCI_CLASS_WIRELESS_WHCI, ~0) },
+       { /* empty last entry */ }
+};
+MODULE_DEVICE_TABLE(pci, whci_hcd_id_table);
+
+MODULE_DESCRIPTION("WHCI Wireless USB host controller driver");
+MODULE_AUTHOR("Cambridge Silicon Radio Ltd.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/whci/hw.c b/drivers/usb/host/whci/hw.c
new file mode 100644 (file)
index 0000000..ac86e59
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Wireless Host Controller (WHC) hardware access helpers.
+ *
+ * Copyright (C) 2007 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/dma-mapping.h>
+#include <linux/uwb/umc.h>
+
+#include "../../wusbcore/wusbhc.h"
+
+#include "whcd.h"
+
+void whc_write_wusbcmd(struct whc *whc, u32 mask, u32 val)
+{
+       unsigned long flags;
+       u32 cmd;
+
+       spin_lock_irqsave(&whc->lock, flags);
+
+       cmd = le_readl(whc->base + WUSBCMD);
+       cmd = (cmd & ~mask) | val;
+       le_writel(cmd, whc->base + WUSBCMD);
+
+       spin_unlock_irqrestore(&whc->lock, flags);
+}
+
+/**
+ * whc_do_gencmd - start a generic command via the WUSBGENCMDSTS register
+ * @whc:    the WHCI HC
+ * @cmd:    command to start.
+ * @params: parameters for the command (the WUSBGENCMDPARAMS register value).
+ * @addr:   pointer to any data for the command (may be NULL).
+ * @len:    length of the data (if any).
+ */
+int whc_do_gencmd(struct whc *whc, u32 cmd, u32 params, void *addr, size_t len)
+{
+       unsigned long flags;
+       dma_addr_t dma_addr;
+       int t;
+
+       mutex_lock(&whc->mutex);
+
+       /* Wait for previous command to complete. */
+       t = wait_event_timeout(whc->cmd_wq,
+                              (le_readl(whc->base + WUSBGENCMDSTS) & WUSBGENCMDSTS_ACTIVE) == 0,
+                              WHC_GENCMD_TIMEOUT_MS);
+       if (t == 0) {
+               dev_err(&whc->umc->dev, "generic command timeout (%04x/%04x)\n",
+                       le_readl(whc->base + WUSBGENCMDSTS),
+                       le_readl(whc->base + WUSBGENCMDPARAMS));
+               return -ETIMEDOUT;
+       }
+
+       if (addr) {
+               memcpy(whc->gen_cmd_buf, addr, len);
+               dma_addr = whc->gen_cmd_buf_dma;
+       } else
+               dma_addr = 0;
+
+       /* Poke registers to start cmd. */
+       spin_lock_irqsave(&whc->lock, flags);
+
+       le_writel(params, whc->base + WUSBGENCMDPARAMS);
+       le_writeq(dma_addr, whc->base + WUSBGENADDR);
+
+       le_writel(WUSBGENCMDSTS_ACTIVE | WUSBGENCMDSTS_IOC | cmd,
+                 whc->base + WUSBGENCMDSTS);
+
+       spin_unlock_irqrestore(&whc->lock, flags);
+
+       mutex_unlock(&whc->mutex);
+
+       return 0;
+}
diff --git a/drivers/usb/host/whci/init.c b/drivers/usb/host/whci/init.c
new file mode 100644 (file)
index 0000000..34a783c
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * Wireless Host Controller (WHC) initialization.
+ *
+ * Copyright (C) 2007 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/dma-mapping.h>
+#include <linux/uwb/umc.h>
+
+#include "../../wusbcore/wusbhc.h"
+
+#include "whcd.h"
+
+/*
+ * Reset the host controller.
+ */
+static void whc_hw_reset(struct whc *whc)
+{
+       le_writel(WUSBCMD_WHCRESET, whc->base + WUSBCMD);
+       whci_wait_for(&whc->umc->dev, whc->base + WUSBCMD, WUSBCMD_WHCRESET, 0,
+                     100, "reset");
+}
+
+static void whc_hw_init_di_buf(struct whc *whc)
+{
+       int d;
+
+       /* Disable all entries in the Device Information buffer. */
+       for (d = 0; d < whc->n_devices; d++)
+               whc->di_buf[d].addr_sec_info = WHC_DI_DISABLE;
+
+       le_writeq(whc->di_buf_dma, whc->base + WUSBDEVICEINFOADDR);
+}
+
+static void whc_hw_init_dn_buf(struct whc *whc)
+{
+       /* Clear the Device Notification buffer to ensure the V (valid)
+        * bits are clear.  */
+       memset(whc->dn_buf, 0, 4096);
+
+       le_writeq(whc->dn_buf_dma, whc->base + WUSBDNTSBUFADDR);
+}
+
+int whc_init(struct whc *whc)
+{
+       u32 whcsparams;
+       int ret, i;
+       resource_size_t start, len;
+
+       spin_lock_init(&whc->lock);
+       mutex_init(&whc->mutex);
+       init_waitqueue_head(&whc->cmd_wq);
+       init_waitqueue_head(&whc->async_list_wq);
+       init_waitqueue_head(&whc->periodic_list_wq);
+       whc->workqueue = create_singlethread_workqueue(dev_name(&whc->umc->dev));
+       if (whc->workqueue == NULL) {
+               ret = -ENOMEM;
+               goto error;
+       }
+       INIT_WORK(&whc->dn_work, whc_dn_work);
+
+       INIT_WORK(&whc->async_work, scan_async_work);
+       INIT_LIST_HEAD(&whc->async_list);
+       INIT_LIST_HEAD(&whc->async_removed_list);
+
+       INIT_WORK(&whc->periodic_work, scan_periodic_work);
+       for (i = 0; i < 5; i++)
+               INIT_LIST_HEAD(&whc->periodic_list[i]);
+       INIT_LIST_HEAD(&whc->periodic_removed_list);
+
+       /* Map HC registers. */
+       start = whc->umc->resource.start;
+       len   = whc->umc->resource.end - start + 1;
+       if (!request_mem_region(start, len, "whci-hc")) {
+               dev_err(&whc->umc->dev, "can't request HC region\n");
+               ret = -EBUSY;
+               goto error;
+       }
+       whc->base_phys = start;
+       whc->base = ioremap(start, len);
+       if (!whc->base) {
+               dev_err(&whc->umc->dev, "ioremap\n");
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       whc_hw_reset(whc);
+
+       /* Read maximum number of devices, keys and MMC IEs. */
+       whcsparams = le_readl(whc->base + WHCSPARAMS);
+       whc->n_devices = WHCSPARAMS_TO_N_DEVICES(whcsparams);
+       whc->n_keys    = WHCSPARAMS_TO_N_KEYS(whcsparams);
+       whc->n_mmc_ies = WHCSPARAMS_TO_N_MMC_IES(whcsparams);
+
+       dev_dbg(&whc->umc->dev, "N_DEVICES = %d, N_KEYS = %d, N_MMC_IES = %d\n",
+               whc->n_devices, whc->n_keys, whc->n_mmc_ies);
+
+       whc->qset_pool = dma_pool_create("qset", &whc->umc->dev,
+                                        sizeof(struct whc_qset), 64, 0);
+       if (whc->qset_pool == NULL) {
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       ret = asl_init(whc);
+       if (ret < 0)
+               goto error;
+       ret = pzl_init(whc);
+       if (ret < 0)
+               goto error;
+
+       /* Allocate and initialize a buffer for generic commands, the
+          Device Information buffer, and the Device Notification
+          buffer. */
+
+       whc->gen_cmd_buf = dma_alloc_coherent(&whc->umc->dev, WHC_GEN_CMD_DATA_LEN,
+                                             &whc->gen_cmd_buf_dma, GFP_KERNEL);
+       if (whc->gen_cmd_buf == NULL) {
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       whc->dn_buf = dma_alloc_coherent(&whc->umc->dev,
+                                        sizeof(struct dn_buf_entry) * WHC_N_DN_ENTRIES,
+                                        &whc->dn_buf_dma, GFP_KERNEL);
+       if (!whc->dn_buf) {
+               ret = -ENOMEM;
+               goto error;
+       }
+       whc_hw_init_dn_buf(whc);
+
+       whc->di_buf = dma_alloc_coherent(&whc->umc->dev,
+                                        sizeof(struct di_buf_entry) * whc->n_devices,
+                                        &whc->di_buf_dma, GFP_KERNEL);
+       if (!whc->di_buf) {
+               ret = -ENOMEM;
+               goto error;
+       }
+       whc_hw_init_di_buf(whc);
+
+       return 0;
+
+error:
+       whc_clean_up(whc);
+       return ret;
+}
+
+void whc_clean_up(struct whc *whc)
+{
+       resource_size_t len;
+
+       if (whc->di_buf)
+               dma_free_coherent(&whc->umc->dev, sizeof(struct di_buf_entry) * whc->n_devices,
+                                 whc->di_buf, whc->di_buf_dma);
+       if (whc->dn_buf)
+               dma_free_coherent(&whc->umc->dev, sizeof(struct dn_buf_entry) * WHC_N_DN_ENTRIES,
+                                 whc->dn_buf, whc->dn_buf_dma);
+       if (whc->gen_cmd_buf)
+               dma_free_coherent(&whc->umc->dev, WHC_GEN_CMD_DATA_LEN,
+                                 whc->gen_cmd_buf, whc->gen_cmd_buf_dma);
+
+       pzl_clean_up(whc);
+       asl_clean_up(whc);
+
+       if (whc->qset_pool)
+               dma_pool_destroy(whc->qset_pool);
+
+       len   = whc->umc->resource.end - whc->umc->resource.start + 1;
+       if (whc->base)
+               iounmap(whc->base);
+       if (whc->base_phys)
+               release_mem_region(whc->base_phys, len);
+
+       if (whc->workqueue)
+               destroy_workqueue(whc->workqueue);
+}
diff --git a/drivers/usb/host/whci/int.c b/drivers/usb/host/whci/int.c
new file mode 100644 (file)
index 0000000..fce0117
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Wireless Host Controller (WHC) interrupt handling.
+ *
+ * Copyright (C) 2007 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/init.h>
+#include <linux/uwb/umc.h>
+
+#include "../../wusbcore/wusbhc.h"
+
+#include "whcd.h"
+
+static void transfer_done(struct whc *whc)
+{
+       queue_work(whc->workqueue, &whc->async_work);
+       queue_work(whc->workqueue, &whc->periodic_work);
+}
+
+irqreturn_t whc_int_handler(struct usb_hcd *hcd)
+{
+       struct wusbhc *wusbhc = usb_hcd_to_wusbhc(hcd);
+       struct whc *whc = wusbhc_to_whc(wusbhc);
+       u32 sts;
+
+       sts = le_readl(whc->base + WUSBSTS);
+       if (!(sts & WUSBSTS_INT_MASK))
+               return IRQ_NONE;
+       le_writel(sts & WUSBSTS_INT_MASK, whc->base + WUSBSTS);
+
+       if (sts & WUSBSTS_GEN_CMD_DONE)
+               wake_up(&whc->cmd_wq);
+
+       if (sts & WUSBSTS_HOST_ERR)
+               dev_err(&whc->umc->dev, "FIXME: host system error\n");
+
+       if (sts & WUSBSTS_ASYNC_SCHED_SYNCED)
+               wake_up(&whc->async_list_wq);
+
+       if (sts & WUSBSTS_PERIODIC_SCHED_SYNCED)
+               wake_up(&whc->periodic_list_wq);
+
+       if (sts & WUSBSTS_DNTS_INT)
+               queue_work(whc->workqueue, &whc->dn_work);
+
+       /*
+        * A transfer completed (see [WHCI] section 4.7.1.2 for when
+        * this occurs).
+        */
+       if (sts & (WUSBSTS_INT | WUSBSTS_ERR_INT))
+               transfer_done(whc);
+
+       return IRQ_HANDLED;
+}
+
+static int process_dn_buf(struct whc *whc)
+{
+       struct wusbhc *wusbhc = &whc->wusbhc;
+       struct dn_buf_entry *dn;
+       int processed = 0;
+
+       for (dn = whc->dn_buf; dn < whc->dn_buf + WHC_N_DN_ENTRIES; dn++) {
+               if (dn->status & WHC_DN_STATUS_VALID) {
+                       wusbhc_handle_dn(wusbhc, dn->src_addr,
+                                        (struct wusb_dn_hdr *)dn->dn_data,
+                                        dn->msg_size);
+                       dn->status &= ~WHC_DN_STATUS_VALID;
+                       processed++;
+               }
+       }
+       return processed;
+}
+
+void whc_dn_work(struct work_struct *work)
+{
+       struct whc *whc = container_of(work, struct whc, dn_work);
+       int processed;
+
+       do {
+               processed = process_dn_buf(whc);
+       } while (processed);
+}
diff --git a/drivers/usb/host/whci/pzl.c b/drivers/usb/host/whci/pzl.c
new file mode 100644 (file)
index 0000000..8d62df0
--- /dev/null
@@ -0,0 +1,398 @@
+/*
+ * Wireless Host Controller (WHC) periodic schedule management.
+ *
+ * Copyright (C) 2007 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/dma-mapping.h>
+#include <linux/uwb/umc.h>
+#include <linux/usb.h>
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+#include "../../wusbcore/wusbhc.h"
+
+#include "whcd.h"
+
+#if D_LOCAL >= 4
+static void dump_pzl(struct whc *whc, const char *tag)
+{
+       struct device *dev = &whc->umc->dev;
+       struct whc_qset *qset;
+       int period = 0;
+
+       d_printf(4, dev, "PZL %s\n", tag);
+
+       for (period = 0; period < 5; period++) {
+               d_printf(4, dev, "Period %d\n", period);
+               list_for_each_entry(qset, &whc->periodic_list[period], list_node) {
+                       dump_qset(qset, dev);
+               }
+       }
+}
+#else
+static inline void dump_pzl(struct whc *whc, const char *tag)
+{
+}
+#endif
+
+static void update_pzl_pointers(struct whc *whc, int period, u64 addr)
+{
+       switch (period) {
+       case 0:
+               whc_qset_set_link_ptr(&whc->pz_list[0], addr);
+               whc_qset_set_link_ptr(&whc->pz_list[2], addr);
+               whc_qset_set_link_ptr(&whc->pz_list[4], addr);
+               whc_qset_set_link_ptr(&whc->pz_list[6], addr);
+               whc_qset_set_link_ptr(&whc->pz_list[8], addr);
+               whc_qset_set_link_ptr(&whc->pz_list[10], addr);
+               whc_qset_set_link_ptr(&whc->pz_list[12], addr);
+               whc_qset_set_link_ptr(&whc->pz_list[14], addr);
+               break;
+       case 1:
+               whc_qset_set_link_ptr(&whc->pz_list[1], addr);
+               whc_qset_set_link_ptr(&whc->pz_list[5], addr);
+               whc_qset_set_link_ptr(&whc->pz_list[9], addr);
+               whc_qset_set_link_ptr(&whc->pz_list[13], addr);
+               break;
+       case 2:
+               whc_qset_set_link_ptr(&whc->pz_list[3], addr);
+               whc_qset_set_link_ptr(&whc->pz_list[11], addr);
+               break;
+       case 3:
+               whc_qset_set_link_ptr(&whc->pz_list[7], addr);
+               break;
+       case 4:
+               whc_qset_set_link_ptr(&whc->pz_list[15], addr);
+               break;
+       }
+}
+
+/*
+ * Return the 'period' to use for this qset.  The minimum interval for
+ * the endpoint is used so whatever urbs are submitted the device is
+ * polled often enough.
+ */
+static int qset_get_period(struct whc *whc, struct whc_qset *qset)
+{
+       uint8_t bInterval = qset->ep->desc.bInterval;
+
+       if (bInterval < 6)
+               bInterval = 6;
+       if (bInterval > 10)
+               bInterval = 10;
+       return bInterval - 6;
+}
+
+static void qset_insert_in_sw_list(struct whc *whc, struct whc_qset *qset)
+{
+       int period;
+
+       period = qset_get_period(whc, qset);
+
+       qset_clear(whc, qset);
+       list_move(&qset->list_node, &whc->periodic_list[period]);
+       qset->in_sw_list = true;
+}
+
+static void pzl_qset_remove(struct whc *whc, struct whc_qset *qset)
+{
+       list_move(&qset->list_node, &whc->periodic_removed_list);
+       qset->in_hw_list = false;
+       qset->in_sw_list = false;
+}
+
+/**
+ * pzl_process_qset - process any recently inactivated or halted qTDs
+ * in a qset.
+ *
+ * After inactive qTDs are removed, new qTDs can be added if the
+ * urb queue still contains URBs.
+ *
+ * Returns the schedule updates required.
+ */
+static enum whc_update pzl_process_qset(struct whc *whc, struct whc_qset *qset)
+{
+       enum whc_update update = 0;
+       uint32_t status = 0;
+
+       while (qset->ntds) {
+               struct whc_qtd *td;
+               int t;
+
+               t = qset->td_start;
+               td = &qset->qtd[qset->td_start];
+               status = le32_to_cpu(td->status);
+
+               /*
+                * Nothing to do with a still active qTD.
+                */
+               if (status & QTD_STS_ACTIVE)
+                       break;
+
+               if (status & QTD_STS_HALTED) {
+                       /* Ug, an error. */
+                       process_halted_qtd(whc, qset, td);
+                       goto done;
+               }
+
+               /* Mmm, a completed qTD. */
+               process_inactive_qtd(whc, qset, td);
+       }
+
+       update |= qset_add_qtds(whc, qset);
+
+done:
+       /*
+        * If there are no qTDs in this qset, remove it from the PZL.
+        */
+       if (qset->remove && qset->ntds == 0) {
+               pzl_qset_remove(whc, qset);
+               update |= WHC_UPDATE_REMOVED;
+       }
+
+       return update;
+}
+
+/**
+ * pzl_start - start the periodic schedule
+ * @whc: the WHCI host controller
+ *
+ * The PZL must be valid (e.g., all entries in the list should have
+ * the T bit set).
+ */
+void pzl_start(struct whc *whc)
+{
+       le_writeq(whc->pz_list_dma, whc->base + WUSBPERIODICLISTBASE);
+
+       whc_write_wusbcmd(whc, WUSBCMD_PERIODIC_EN, WUSBCMD_PERIODIC_EN);
+       whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS,
+                     WUSBSTS_PERIODIC_SCHED, WUSBSTS_PERIODIC_SCHED,
+                     1000, "start PZL");
+}
+
+/**
+ * pzl_stop - stop the periodic schedule
+ * @whc: the WHCI host controller
+ */
+void pzl_stop(struct whc *whc)
+{
+       whc_write_wusbcmd(whc, WUSBCMD_PERIODIC_EN, 0);
+       whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS,
+                     WUSBSTS_PERIODIC_SCHED, 0,
+                     1000, "stop PZL");
+}
+
+void pzl_update(struct whc *whc, uint32_t wusbcmd)
+{
+       whc_write_wusbcmd(whc, wusbcmd, wusbcmd);
+       wait_event(whc->periodic_list_wq,
+                  (le_readl(whc->base + WUSBCMD) & WUSBCMD_PERIODIC_UPDATED) == 0);
+}
+
+static void update_pzl_hw_view(struct whc *whc)
+{
+       struct whc_qset *qset, *t;
+       int period;
+       u64 tmp_qh = 0;
+
+       for (period = 0; period < 5; period++) {
+               list_for_each_entry_safe(qset, t, &whc->periodic_list[period], list_node) {
+                       whc_qset_set_link_ptr(&qset->qh.link, tmp_qh);
+                       tmp_qh = qset->qset_dma;
+                       qset->in_hw_list = true;
+               }
+               update_pzl_pointers(whc, period, tmp_qh);
+       }
+}
+
+/**
+ * scan_periodic_work - scan the PZL for qsets to process.
+ *
+ * Process each qset in the PZL in turn and then signal the WHC that
+ * the PZL has been updated.
+ *
+ * Then start, stop or update the periodic schedule as required.
+ */
+void scan_periodic_work(struct work_struct *work)
+{
+       struct whc *whc = container_of(work, struct whc, periodic_work);
+       struct whc_qset *qset, *t;
+       enum whc_update update = 0;
+       int period;
+
+       spin_lock_irq(&whc->lock);
+
+       dump_pzl(whc, "before processing");
+
+       for (period = 4; period >= 0; period--) {
+               list_for_each_entry_safe(qset, t, &whc->periodic_list[period], list_node) {
+                       if (!qset->in_hw_list)
+                               update |= WHC_UPDATE_ADDED;
+                       update |= pzl_process_qset(whc, qset);
+               }
+       }
+
+       if (update & (WHC_UPDATE_ADDED | WHC_UPDATE_REMOVED))
+               update_pzl_hw_view(whc);
+
+       dump_pzl(whc, "after processing");
+
+       spin_unlock_irq(&whc->lock);
+
+       if (update) {
+               uint32_t wusbcmd = WUSBCMD_PERIODIC_UPDATED | WUSBCMD_PERIODIC_SYNCED_DB;
+               if (update & WHC_UPDATE_REMOVED)
+                       wusbcmd |= WUSBCMD_PERIODIC_QSET_RM;
+               pzl_update(whc, wusbcmd);
+       }
+
+       /*
+        * Now that the PZL is updated, complete the removal of any
+        * removed qsets.
+        */
+       spin_lock(&whc->lock);
+
+       list_for_each_entry_safe(qset, t, &whc->periodic_removed_list, list_node) {
+               qset_remove_complete(whc, qset);
+       }
+
+       spin_unlock(&whc->lock);
+}
+
+/**
+ * pzl_urb_enqueue - queue an URB onto the periodic list (PZL)
+ * @whc: the WHCI host controller
+ * @urb: the URB to enqueue
+ * @mem_flags: flags for any memory allocations
+ *
+ * The qset for the endpoint is obtained and the urb queued on to it.
+ *
+ * Work is scheduled to update the hardware's view of the PZL.
+ */
+int pzl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags)
+{
+       struct whc_qset *qset;
+       int err;
+       unsigned long flags;
+
+       spin_lock_irqsave(&whc->lock, flags);
+
+       qset = get_qset(whc, urb, GFP_ATOMIC);
+       if (qset == NULL)
+               err = -ENOMEM;
+       else
+               err = qset_add_urb(whc, qset, urb, GFP_ATOMIC);
+       if (!err) {
+               usb_hcd_link_urb_to_ep(&whc->wusbhc.usb_hcd, urb);
+               if (!qset->in_sw_list)
+                       qset_insert_in_sw_list(whc, qset);
+       }
+
+       spin_unlock_irqrestore(&whc->lock, flags);
+
+       if (!err)
+               queue_work(whc->workqueue, &whc->periodic_work);
+
+       return 0;
+}
+
+/**
+ * pzl_urb_dequeue - remove an URB (qset) from the periodic list
+ * @whc: the WHCI host controller
+ * @urb: the URB to dequeue
+ * @status: the current status of the URB
+ *
+ * URBs that do yet have qTDs can simply be removed from the software
+ * queue, otherwise the qset must be removed so the qTDs can be safely
+ * removed.
+ */
+int pzl_urb_dequeue(struct whc *whc, struct urb *urb, int status)
+{
+       struct whc_urb *wurb = urb->hcpriv;
+       struct whc_qset *qset = wurb->qset;
+       struct whc_std *std, *t;
+       int ret;
+       unsigned long flags;
+
+       spin_lock_irqsave(&whc->lock, flags);
+
+       ret = usb_hcd_check_unlink_urb(&whc->wusbhc.usb_hcd, urb, status);
+       if (ret < 0)
+               goto out;
+
+       list_for_each_entry_safe(std, t, &qset->stds, list_node) {
+               if (std->urb == urb)
+                       qset_free_std(whc, std);
+               else
+                       std->qtd = NULL; /* so this std is re-added when the qset is */
+       }
+
+       pzl_qset_remove(whc, qset);
+       wurb->status = status;
+       wurb->is_async = false;
+       queue_work(whc->workqueue, &wurb->dequeue_work);
+
+out:
+       spin_unlock_irqrestore(&whc->lock, flags);
+
+       return ret;
+}
+
+/**
+ * pzl_qset_delete - delete a qset from the PZL
+ */
+void pzl_qset_delete(struct whc *whc, struct whc_qset *qset)
+{
+       qset->remove = 1;
+       queue_work(whc->workqueue, &whc->periodic_work);
+       qset_delete(whc, qset);
+}
+
+
+/**
+ * pzl_init - initialize the periodic zone list
+ * @whc: the WHCI host controller
+ */
+int pzl_init(struct whc *whc)
+{
+       int i;
+
+       whc->pz_list = dma_alloc_coherent(&whc->umc->dev, sizeof(u64) * 16,
+                                         &whc->pz_list_dma, GFP_KERNEL);
+       if (whc->pz_list == NULL)
+               return -ENOMEM;
+
+       /* Set T bit on all elements in PZL. */
+       for (i = 0; i < 16; i++)
+               whc->pz_list[i] = cpu_to_le64(QH_LINK_NTDS(8) | QH_LINK_T);
+
+       le_writeq(whc->pz_list_dma, whc->base + WUSBPERIODICLISTBASE);
+
+       return 0;
+}
+
+/**
+ * pzl_clean_up - free PZL resources
+ * @whc: the WHCI host controller
+ *
+ * The PZL is stopped and empty.
+ */
+void pzl_clean_up(struct whc *whc)
+{
+       if (whc->pz_list)
+               dma_free_coherent(&whc->umc->dev,  sizeof(u64) * 16, whc->pz_list,
+                                 whc->pz_list_dma);
+}
diff --git a/drivers/usb/host/whci/qset.c b/drivers/usb/host/whci/qset.c
new file mode 100644 (file)
index 0000000..0420037
--- /dev/null
@@ -0,0 +1,567 @@
+/*
+ * Wireless Host Controller (WHC) qset management.
+ *
+ * Copyright (C) 2007 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/dma-mapping.h>
+#include <linux/uwb/umc.h>
+#include <linux/usb.h>
+
+#include "../../wusbcore/wusbhc.h"
+
+#include "whcd.h"
+
+void dump_qset(struct whc_qset *qset, struct device *dev)
+{
+       struct whc_std *std;
+       struct urb *urb = NULL;
+       int i;
+
+       dev_dbg(dev, "qset %08x\n", (u32)qset->qset_dma);
+       dev_dbg(dev, "  -> %08x\n", (u32)qset->qh.link);
+       dev_dbg(dev, "  info: %08x %08x %08x\n",
+               qset->qh.info1, qset->qh.info2,  qset->qh.info3);
+       dev_dbg(dev, "  sts: %04x errs: %d\n", qset->qh.status, qset->qh.err_count);
+       dev_dbg(dev, "  TD: sts: %08x opts: %08x\n",
+               qset->qh.overlay.qtd.status, qset->qh.overlay.qtd.options);
+
+       for (i = 0; i < WHCI_QSET_TD_MAX; i++) {
+               dev_dbg(dev, "  %c%c TD[%d]: sts: %08x opts: %08x ptr: %08x\n",
+                       i == qset->td_start ? 'S' : ' ',
+                       i == qset->td_end ? 'E' : ' ',
+                       i, qset->qtd[i].status, qset->qtd[i].options,
+                       (u32)qset->qtd[i].page_list_ptr);
+       }
+       dev_dbg(dev, "  ntds: %d\n", qset->ntds);
+       list_for_each_entry(std, &qset->stds, list_node) {
+               if (urb != std->urb) {
+                       urb = std->urb;
+                       dev_dbg(dev, "  urb %p transferred: %d bytes\n", urb,
+                               urb->actual_length);
+               }
+               if (std->qtd)
+                       dev_dbg(dev, "    sTD[%td]: %zu bytes @ %08x\n",
+                               std->qtd - &qset->qtd[0],
+                               std->len, std->num_pointers ?
+                               (u32)(std->pl_virt[0].buf_ptr) : (u32)std->dma_addr);
+               else
+                       dev_dbg(dev, "    sTD[-]: %zd bytes @ %08x\n",
+                               std->len, std->num_pointers ?
+                               (u32)(std->pl_virt[0].buf_ptr) : (u32)std->dma_addr);
+       }
+}
+
+struct whc_qset *qset_alloc(struct whc *whc, gfp_t mem_flags)
+{
+       struct whc_qset *qset;
+       dma_addr_t dma;
+
+       qset = dma_pool_alloc(whc->qset_pool, mem_flags, &dma);
+       if (qset == NULL)
+               return NULL;
+       memset(qset, 0, sizeof(struct whc_qset));
+
+       qset->qset_dma = dma;
+       qset->whc = whc;
+
+       INIT_LIST_HEAD(&qset->list_node);
+       INIT_LIST_HEAD(&qset->stds);
+
+       return qset;
+}
+
+/**
+ * qset_fill_qh - fill the static endpoint state in a qset's QHead
+ * @qset: the qset whose QH needs initializing with static endpoint
+ *        state
+ * @urb:  an urb for a transfer to this endpoint
+ */
+static void qset_fill_qh(struct whc_qset *qset, struct urb *urb)
+{
+       struct usb_device *usb_dev = urb->dev;
+       struct usb_wireless_ep_comp_descriptor *epcd;
+       bool is_out;
+
+       is_out = usb_pipeout(urb->pipe);
+
+       epcd = (struct usb_wireless_ep_comp_descriptor *)qset->ep->extra;
+
+       if (epcd) {
+               qset->max_seq = epcd->bMaxSequence;
+               qset->max_burst = epcd->bMaxBurst;
+       } else {
+               qset->max_seq = 2;
+               qset->max_burst = 1;
+       }
+
+       qset->qh.info1 = cpu_to_le32(
+               QH_INFO1_EP(usb_pipeendpoint(urb->pipe))
+               | (is_out ? QH_INFO1_DIR_OUT : QH_INFO1_DIR_IN)
+               | usb_pipe_to_qh_type(urb->pipe)
+               | QH_INFO1_DEV_INFO_IDX(wusb_port_no_to_idx(usb_dev->portnum))
+               | QH_INFO1_MAX_PKT_LEN(usb_maxpacket(urb->dev, urb->pipe, is_out))
+               );
+       qset->qh.info2 = cpu_to_le32(
+               QH_INFO2_BURST(qset->max_burst)
+               | QH_INFO2_DBP(0)
+               | QH_INFO2_MAX_COUNT(3)
+               | QH_INFO2_MAX_RETRY(3)
+               | QH_INFO2_MAX_SEQ(qset->max_seq - 1)
+               );
+       /* FIXME: where can we obtain these Tx parameters from?  Why
+        * doesn't the chip know what Tx power to use? It knows the Rx
+        * strength and can presumably guess the Tx power required
+        * from that? */
+       qset->qh.info3 = cpu_to_le32(
+               QH_INFO3_TX_RATE_53_3
+               | QH_INFO3_TX_PWR(0) /* 0 == max power */
+               );
+}
+
+/**
+ * qset_clear - clear fields in a qset so it may be reinserted into a
+ * schedule
+ */
+void qset_clear(struct whc *whc, struct whc_qset *qset)
+{
+       qset->td_start = qset->td_end = qset->ntds = 0;
+       qset->remove = 0;
+
+       qset->qh.link = cpu_to_le32(QH_LINK_NTDS(8) | QH_LINK_T);
+       qset->qh.status = cpu_to_le16(QH_STATUS_ICUR(qset->td_start));
+       qset->qh.err_count = 0;
+       qset->qh.cur_window = cpu_to_le32((1 << qset->max_burst) - 1);
+       qset->qh.scratch[0] = 0;
+       qset->qh.scratch[1] = 0;
+       qset->qh.scratch[2] = 0;
+
+       memset(&qset->qh.overlay, 0, sizeof(qset->qh.overlay));
+
+       init_completion(&qset->remove_complete);
+}
+
+/**
+ * get_qset - get the qset for an async endpoint
+ *
+ * A new qset is created if one does not already exist.
+ */
+struct whc_qset *get_qset(struct whc *whc, struct urb *urb,
+                                gfp_t mem_flags)
+{
+       struct whc_qset *qset;
+
+       qset = urb->ep->hcpriv;
+       if (qset == NULL) {
+               qset = qset_alloc(whc, mem_flags);
+               if (qset == NULL)
+                       return NULL;
+
+               qset->ep = urb->ep;
+               urb->ep->hcpriv = qset;
+               qset_fill_qh(qset, urb);
+       }
+       return qset;
+}
+
+void qset_remove_complete(struct whc *whc, struct whc_qset *qset)
+{
+       list_del_init(&qset->list_node);
+       complete(&qset->remove_complete);
+}
+
+/**
+ * qset_add_qtds - add qTDs for an URB to a qset
+ *
+ * Returns true if the list (ASL/PZL) must be updated because (for a
+ * WHCI 0.95 controller) an activated qTD was pointed to be iCur.
+ */
+enum whc_update qset_add_qtds(struct whc *whc, struct whc_qset *qset)
+{
+       struct whc_std *std;
+       enum whc_update update = 0;
+
+       list_for_each_entry(std, &qset->stds, list_node) {
+               struct whc_qtd *qtd;
+               uint32_t status;
+
+               if (qset->ntds >= WHCI_QSET_TD_MAX
+                   || (qset->pause_after_urb && std->urb != qset->pause_after_urb))
+                       break;
+
+               if (std->qtd)
+                       continue; /* already has a qTD */
+
+               qtd = std->qtd = &qset->qtd[qset->td_end];
+
+               /* Fill in setup bytes for control transfers. */
+               if (usb_pipecontrol(std->urb->pipe))
+                       memcpy(qtd->setup, std->urb->setup_packet, 8);
+
+               status = QTD_STS_ACTIVE | QTD_STS_LEN(std->len);
+
+               if (whc_std_last(std) && usb_pipeout(std->urb->pipe))
+                       status |= QTD_STS_LAST_PKT;
+
+               /*
+                * For an IN transfer the iAlt field should be set so
+                * the h/w will automatically advance to the next
+                * transfer. However, if there are 8 or more TDs
+                * remaining in this transfer then iAlt cannot be set
+                * as it could point to somewhere in this transfer.
+                */
+               if (std->ntds_remaining < WHCI_QSET_TD_MAX) {
+                       int ialt;
+                       ialt = (qset->td_end + std->ntds_remaining) % WHCI_QSET_TD_MAX;
+                       status |= QTD_STS_IALT(ialt);
+               } else if (usb_pipein(std->urb->pipe))
+                       qset->pause_after_urb = std->urb;
+
+               if (std->num_pointers)
+                       qtd->options = cpu_to_le32(QTD_OPT_IOC);
+               else
+                       qtd->options = cpu_to_le32(QTD_OPT_IOC | QTD_OPT_SMALL);
+               qtd->page_list_ptr = cpu_to_le64(std->dma_addr);
+
+               qtd->status = cpu_to_le32(status);
+
+               if (QH_STATUS_TO_ICUR(qset->qh.status) == qset->td_end)
+                       update = WHC_UPDATE_UPDATED;
+
+               if (++qset->td_end >= WHCI_QSET_TD_MAX)
+                       qset->td_end = 0;
+               qset->ntds++;
+       }
+
+       return update;
+}
+
+/**
+ * qset_remove_qtd - remove the first qTD from a qset.
+ *
+ * The qTD might be still active (if it's part of a IN URB that
+ * resulted in a short read) so ensure it's deactivated.
+ */
+static void qset_remove_qtd(struct whc *whc, struct whc_qset *qset)
+{
+       qset->qtd[qset->td_start].status = 0;
+
+       if (++qset->td_start >= WHCI_QSET_TD_MAX)
+               qset->td_start = 0;
+       qset->ntds--;
+}
+
+/**
+ * qset_free_std - remove an sTD and free it.
+ * @whc: the WHCI host controller
+ * @std: the sTD to remove and free.
+ */
+void qset_free_std(struct whc *whc, struct whc_std *std)
+{
+       list_del(&std->list_node);
+       if (std->num_pointers) {
+               dma_unmap_single(whc->wusbhc.dev, std->dma_addr,
+                                std->num_pointers * sizeof(struct whc_page_list_entry),
+                                DMA_TO_DEVICE);
+               kfree(std->pl_virt);
+       }
+
+       kfree(std);
+}
+
+/**
+ * qset_remove_qtds - remove an URB's qTDs (and sTDs).
+ */
+static void qset_remove_qtds(struct whc *whc, struct whc_qset *qset,
+                            struct urb *urb)
+{
+       struct whc_std *std, *t;
+
+       list_for_each_entry_safe(std, t, &qset->stds, list_node) {
+               if (std->urb != urb)
+                       break;
+               if (std->qtd != NULL)
+                       qset_remove_qtd(whc, qset);
+               qset_free_std(whc, std);
+       }
+}
+
+/**
+ * qset_free_stds - free any remaining sTDs for an URB.
+ */
+static void qset_free_stds(struct whc_qset *qset, struct urb *urb)
+{
+       struct whc_std *std, *t;
+
+       list_for_each_entry_safe(std, t, &qset->stds, list_node) {
+               if (std->urb == urb)
+                       qset_free_std(qset->whc, std);
+       }
+}
+
+static int qset_fill_page_list(struct whc *whc, struct whc_std *std, gfp_t mem_flags)
+{
+       dma_addr_t dma_addr = std->dma_addr;
+       dma_addr_t sp, ep;
+       size_t std_len = std->len;
+       size_t pl_len;
+       int p;
+
+       sp = ALIGN(dma_addr, WHCI_PAGE_SIZE);
+       ep = dma_addr + std_len;
+       std->num_pointers = DIV_ROUND_UP(ep - sp, WHCI_PAGE_SIZE);
+
+       pl_len = std->num_pointers * sizeof(struct whc_page_list_entry);
+       std->pl_virt = kmalloc(pl_len, mem_flags);
+       if (std->pl_virt == NULL)
+               return -ENOMEM;
+       std->dma_addr = dma_map_single(whc->wusbhc.dev, std->pl_virt, pl_len, DMA_TO_DEVICE);
+
+       for (p = 0; p < std->num_pointers; p++) {
+               std->pl_virt[p].buf_ptr = cpu_to_le64(dma_addr);
+               dma_addr = ALIGN(dma_addr + WHCI_PAGE_SIZE, WHCI_PAGE_SIZE);
+       }
+
+       return 0;
+}
+
+/**
+ * urb_dequeue_work - executes asl/pzl update and gives back the urb to the system.
+ */
+static void urb_dequeue_work(struct work_struct *work)
+{
+       struct whc_urb *wurb = container_of(work, struct whc_urb, dequeue_work);
+       struct whc_qset *qset = wurb->qset;
+       struct whc *whc = qset->whc;
+       unsigned long flags;
+
+       if (wurb->is_async == true)
+               asl_update(whc, WUSBCMD_ASYNC_UPDATED
+                          | WUSBCMD_ASYNC_SYNCED_DB
+                          | WUSBCMD_ASYNC_QSET_RM);
+       else
+               pzl_update(whc, WUSBCMD_PERIODIC_UPDATED
+                          | WUSBCMD_PERIODIC_SYNCED_DB
+                          | WUSBCMD_PERIODIC_QSET_RM);
+
+       spin_lock_irqsave(&whc->lock, flags);
+       qset_remove_urb(whc, qset, wurb->urb, wurb->status);
+       spin_unlock_irqrestore(&whc->lock, flags);
+}
+
+/**
+ * qset_add_urb - add an urb to the qset's queue.
+ *
+ * The URB is chopped into sTDs, one for each qTD that will required.
+ * At least one qTD (and sTD) is required even if the transfer has no
+ * data (e.g., for some control transfers).
+ */
+int qset_add_urb(struct whc *whc, struct whc_qset *qset, struct urb *urb,
+       gfp_t mem_flags)
+{
+       struct whc_urb *wurb;
+       int remaining = urb->transfer_buffer_length;
+       u64 transfer_dma = urb->transfer_dma;
+       int ntds_remaining;
+
+       ntds_remaining = DIV_ROUND_UP(remaining, QTD_MAX_XFER_SIZE);
+       if (ntds_remaining == 0)
+               ntds_remaining = 1;
+
+       wurb = kzalloc(sizeof(struct whc_urb), mem_flags);
+       if (wurb == NULL)
+               goto err_no_mem;
+       urb->hcpriv = wurb;
+       wurb->qset = qset;
+       wurb->urb = urb;
+       INIT_WORK(&wurb->dequeue_work, urb_dequeue_work);
+
+       while (ntds_remaining) {
+               struct whc_std *std;
+               size_t std_len;
+
+               std = kmalloc(sizeof(struct whc_std), mem_flags);
+               if (std == NULL)
+                       goto err_no_mem;
+
+               std_len = remaining;
+               if (std_len > QTD_MAX_XFER_SIZE)
+                       std_len = QTD_MAX_XFER_SIZE;
+
+               std->urb = urb;
+               std->dma_addr = transfer_dma;
+               std->len = std_len;
+               std->ntds_remaining = ntds_remaining;
+               std->qtd = NULL;
+
+               INIT_LIST_HEAD(&std->list_node);
+               list_add_tail(&std->list_node, &qset->stds);
+
+               if (std_len > WHCI_PAGE_SIZE) {
+                       if (qset_fill_page_list(whc, std, mem_flags) < 0)
+                               goto err_no_mem;
+               } else
+                       std->num_pointers = 0;
+
+               ntds_remaining--;
+               remaining -= std_len;
+               transfer_dma += std_len;
+       }
+
+       return 0;
+
+err_no_mem:
+       qset_free_stds(qset, urb);
+       return -ENOMEM;
+}
+
+/**
+ * qset_remove_urb - remove an URB from the urb queue.
+ *
+ * The URB is returned to the USB subsystem.
+ */
+void qset_remove_urb(struct whc *whc, struct whc_qset *qset,
+                           struct urb *urb, int status)
+{
+       struct wusbhc *wusbhc = &whc->wusbhc;
+       struct whc_urb *wurb = urb->hcpriv;
+
+       usb_hcd_unlink_urb_from_ep(&wusbhc->usb_hcd, urb);
+       /* Drop the lock as urb->complete() may enqueue another urb. */
+       spin_unlock(&whc->lock);
+       wusbhc_giveback_urb(wusbhc, urb, status);
+       spin_lock(&whc->lock);
+
+       kfree(wurb);
+}
+
+/**
+ * get_urb_status_from_qtd - get the completed urb status from qTD status
+ * @urb:    completed urb
+ * @status: qTD status
+ */
+static int get_urb_status_from_qtd(struct urb *urb, u32 status)
+{
+       if (status & QTD_STS_HALTED) {
+               if (status & QTD_STS_DBE)
+                       return usb_pipein(urb->pipe) ? -ENOSR : -ECOMM;
+               else if (status & QTD_STS_BABBLE)
+                       return -EOVERFLOW;
+               else if (status & QTD_STS_RCE)
+                       return -ETIME;
+               return -EPIPE;
+       }
+       if (usb_pipein(urb->pipe)
+           && (urb->transfer_flags & URB_SHORT_NOT_OK)
+           && urb->actual_length < urb->transfer_buffer_length)
+               return -EREMOTEIO;
+       return 0;
+}
+
+/**
+ * process_inactive_qtd - process an inactive (but not halted) qTD.
+ *
+ * Update the urb with the transfer bytes from the qTD, if the urb is
+ * completely transfered or (in the case of an IN only) the LPF is
+ * set, then the transfer is complete and the urb should be returned
+ * to the system.
+ */
+void process_inactive_qtd(struct whc *whc, struct whc_qset *qset,
+                                struct whc_qtd *qtd)
+{
+       struct whc_std *std = list_first_entry(&qset->stds, struct whc_std, list_node);
+       struct urb *urb = std->urb;
+       uint32_t status;
+       bool complete;
+
+       status = le32_to_cpu(qtd->status);
+
+       urb->actual_length += std->len - QTD_STS_TO_LEN(status);
+
+       if (usb_pipein(urb->pipe) && (status & QTD_STS_LAST_PKT))
+               complete = true;
+       else
+               complete = whc_std_last(std);
+
+       qset_remove_qtd(whc, qset);
+       qset_free_std(whc, std);
+
+       /*
+        * Transfers for this URB are complete?  Then return it to the
+        * USB subsystem.
+        */
+       if (complete) {
+               qset_remove_qtds(whc, qset, urb);
+               qset_remove_urb(whc, qset, urb, get_urb_status_from_qtd(urb, status));
+
+               /*
+                * If iAlt isn't valid then the hardware didn't
+                * advance iCur. Adjust the start and end pointers to
+                * match iCur.
+                */
+               if (!(status & QTD_STS_IALT_VALID))
+                       qset->td_start = qset->td_end
+                               = QH_STATUS_TO_ICUR(le16_to_cpu(qset->qh.status));
+               qset->pause_after_urb = NULL;
+       }
+}
+
+/**
+ * process_halted_qtd - process a qset with a halted qtd
+ *
+ * Remove all the qTDs for the failed URB and return the failed URB to
+ * the USB subsystem.  Then remove all other qTDs so the qset can be
+ * removed.
+ *
+ * FIXME: this is the point where rate adaptation can be done.  If a
+ * transfer failed because it exceeded the maximum number of retries
+ * then it could be reactivated with a slower rate without having to
+ * remove the qset.
+ */
+void process_halted_qtd(struct whc *whc, struct whc_qset *qset,
+                              struct whc_qtd *qtd)
+{
+       struct whc_std *std = list_first_entry(&qset->stds, struct whc_std, list_node);
+       struct urb *urb = std->urb;
+       int urb_status;
+
+       urb_status = get_urb_status_from_qtd(urb, le32_to_cpu(qtd->status));
+
+       qset_remove_qtds(whc, qset, urb);
+       qset_remove_urb(whc, qset, urb, urb_status);
+
+       list_for_each_entry(std, &qset->stds, list_node) {
+               if (qset->ntds == 0)
+                       break;
+               qset_remove_qtd(whc, qset);
+               std->qtd = NULL;
+       }
+
+       qset->remove = 1;
+}
+
+void qset_free(struct whc *whc, struct whc_qset *qset)
+{
+       dma_pool_free(whc->qset_pool, qset, qset->qset_dma);
+}
+
+/**
+ * qset_delete - wait for a qset to be unused, then free it.
+ */
+void qset_delete(struct whc *whc, struct whc_qset *qset)
+{
+       wait_for_completion(&qset->remove_complete);
+       qset_free(whc, qset);
+}
diff --git a/drivers/usb/host/whci/whcd.h b/drivers/usb/host/whci/whcd.h
new file mode 100644 (file)
index 0000000..1d2a53b
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * Wireless Host Controller (WHC) private header.
+ *
+ * Copyright (C) 2007 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+#ifndef __WHCD_H
+#define __WHCD_H
+
+#include <linux/uwb/whci.h>
+#include <linux/workqueue.h>
+
+#include "whci-hc.h"
+
+/* Generic command timeout. */
+#define WHC_GENCMD_TIMEOUT_MS 100
+
+
+struct whc {
+       struct wusbhc wusbhc;
+       struct umc_dev *umc;
+
+       resource_size_t base_phys;
+       void __iomem *base;
+       int irq;
+
+       u8 n_devices;
+       u8 n_keys;
+       u8 n_mmc_ies;
+
+       u64 *pz_list;
+       struct dn_buf_entry *dn_buf;
+       struct di_buf_entry *di_buf;
+       dma_addr_t pz_list_dma;
+       dma_addr_t dn_buf_dma;
+       dma_addr_t di_buf_dma;
+
+       spinlock_t   lock;
+       struct mutex mutex;
+
+       void *            gen_cmd_buf;
+       dma_addr_t        gen_cmd_buf_dma;
+       wait_queue_head_t cmd_wq;
+
+       struct workqueue_struct *workqueue;
+       struct work_struct       dn_work;
+
+       struct dma_pool *qset_pool;
+
+       struct list_head async_list;
+       struct list_head async_removed_list;
+       wait_queue_head_t async_list_wq;
+       struct work_struct async_work;
+
+       struct list_head periodic_list[5];
+       struct list_head periodic_removed_list;
+       wait_queue_head_t periodic_list_wq;
+       struct work_struct periodic_work;
+};
+
+#define wusbhc_to_whc(w) (container_of((w), struct whc, wusbhc))
+
+/**
+ * struct whc_std - a software TD.
+ * @urb: the URB this sTD is for.
+ * @offset: start of the URB's data for this TD.
+ * @len: the length of data in the associated TD.
+ * @ntds_remaining: number of TDs (starting from this one) in this transfer.
+ *
+ * Queued URBs may require more TDs than are available in a qset so we
+ * use a list of these "software TDs" (sTDs) to hold per-TD data.
+ */
+struct whc_std {
+       struct urb *urb;
+       size_t len;
+       int    ntds_remaining;
+       struct whc_qtd *qtd;
+
+       struct list_head list_node;
+       int num_pointers;
+       dma_addr_t dma_addr;
+       struct whc_page_list_entry *pl_virt;
+};
+
+/**
+ * struct whc_urb - per URB host controller structure.
+ * @urb: the URB this struct is for.
+ * @qset: the qset associated to the URB.
+ * @dequeue_work: the work to remove the URB when dequeued.
+ * @is_async: the URB belongs to async sheduler or not.
+ * @status: the status to be returned when calling wusbhc_giveback_urb.
+ */
+struct whc_urb {
+       struct urb *urb;
+       struct whc_qset *qset;
+       struct work_struct dequeue_work;
+       bool is_async;
+       int status;
+};
+
+/**
+ * whc_std_last - is this sTD the URB's last?
+ * @std: the sTD to check.
+ */
+static inline bool whc_std_last(struct whc_std *std)
+{
+       return std->ntds_remaining <= 1;
+}
+
+enum whc_update {
+       WHC_UPDATE_ADDED   = 0x01,
+       WHC_UPDATE_REMOVED = 0x02,
+       WHC_UPDATE_UPDATED = 0x04,
+};
+
+/* init.c */
+int whc_init(struct whc *whc);
+void whc_clean_up(struct whc *whc);
+
+/* hw.c */
+void whc_write_wusbcmd(struct whc *whc, u32 mask, u32 val);
+int whc_do_gencmd(struct whc *whc, u32 cmd, u32 params, void *addr, size_t len);
+
+/* wusb.c */
+int whc_wusbhc_start(struct wusbhc *wusbhc);
+void whc_wusbhc_stop(struct wusbhc *wusbhc);
+int whc_mmcie_add(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt,
+                 u8 handle, struct wuie_hdr *wuie);
+int whc_mmcie_rm(struct wusbhc *wusbhc, u8 handle);
+int whc_bwa_set(struct wusbhc *wusbhc, s8 stream_index, const struct uwb_mas_bm *mas_bm);
+int whc_dev_info_set(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev);
+int whc_set_num_dnts(struct wusbhc *wusbhc, u8 interval, u8 slots);
+int whc_set_ptk(struct wusbhc *wusbhc, u8 port_idx, u32 tkid,
+               const void *ptk, size_t key_size);
+int whc_set_gtk(struct wusbhc *wusbhc, u32 tkid,
+               const void *gtk, size_t key_size);
+int whc_set_cluster_id(struct whc *whc, u8 bcid);
+
+/* int.c */
+irqreturn_t whc_int_handler(struct usb_hcd *hcd);
+void whc_dn_work(struct work_struct *work);
+
+/* asl.c */
+void asl_start(struct whc *whc);
+void asl_stop(struct whc *whc);
+int  asl_init(struct whc *whc);
+void asl_clean_up(struct whc *whc);
+int  asl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags);
+int  asl_urb_dequeue(struct whc *whc, struct urb *urb, int status);
+void asl_qset_delete(struct whc *whc, struct whc_qset *qset);
+void scan_async_work(struct work_struct *work);
+
+/* pzl.c */
+int  pzl_init(struct whc *whc);
+void pzl_clean_up(struct whc *whc);
+void pzl_start(struct whc *whc);
+void pzl_stop(struct whc *whc);
+int  pzl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags);
+int  pzl_urb_dequeue(struct whc *whc, struct urb *urb, int status);
+void pzl_qset_delete(struct whc *whc, struct whc_qset *qset);
+void scan_periodic_work(struct work_struct *work);
+
+/* qset.c */
+struct whc_qset *qset_alloc(struct whc *whc, gfp_t mem_flags);
+void qset_free(struct whc *whc, struct whc_qset *qset);
+struct whc_qset *get_qset(struct whc *whc, struct urb *urb, gfp_t mem_flags);
+void qset_delete(struct whc *whc, struct whc_qset *qset);
+void qset_clear(struct whc *whc, struct whc_qset *qset);
+int qset_add_urb(struct whc *whc, struct whc_qset *qset, struct urb *urb,
+                gfp_t mem_flags);
+void qset_free_std(struct whc *whc, struct whc_std *std);
+void qset_remove_urb(struct whc *whc, struct whc_qset *qset,
+                           struct urb *urb, int status);
+void process_halted_qtd(struct whc *whc, struct whc_qset *qset,
+                              struct whc_qtd *qtd);
+void process_inactive_qtd(struct whc *whc, struct whc_qset *qset,
+                                struct whc_qtd *qtd);
+enum whc_update qset_add_qtds(struct whc *whc, struct whc_qset *qset);
+void qset_remove_complete(struct whc *whc, struct whc_qset *qset);
+void dump_qset(struct whc_qset *qset, struct device *dev);
+void pzl_update(struct whc *whc, uint32_t wusbcmd);
+void asl_update(struct whc *whc, uint32_t wusbcmd);
+
+#endif /* #ifndef __WHCD_H */
diff --git a/drivers/usb/host/whci/whci-hc.h b/drivers/usb/host/whci/whci-hc.h
new file mode 100644 (file)
index 0000000..bff1eb7
--- /dev/null
@@ -0,0 +1,416 @@
+/*
+ * Wireless Host Controller (WHC) data structures.
+ *
+ * Copyright (C) 2007 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+#ifndef _WHCI_WHCI_HC_H
+#define _WHCI_WHCI_HC_H
+
+#include <linux/list.h>
+
+/**
+ * WHCI_PAGE_SIZE - page size use by WHCI
+ *
+ * WHCI assumes that host system uses pages of 4096 octets.
+ */
+#define WHCI_PAGE_SIZE 4096
+
+
+/**
+ * QTD_MAX_TXFER_SIZE - max number of bytes to transfer with a single
+ * qtd.
+ *
+ * This is 2^20 - 1.
+ */
+#define QTD_MAX_XFER_SIZE 1048575
+
+
+/**
+ * struct whc_qtd - Queue Element Transfer Descriptors (qTD)
+ *
+ * This describes the data for a bulk, control or interrupt transfer.
+ *
+ * [WHCI] section 3.2.4
+ */
+struct whc_qtd {
+       __le32 status; /*< remaining transfer len and transfer status */
+       __le32 options;
+       __le64 page_list_ptr; /*< physical pointer to data buffer page list*/
+       __u8   setup[8];      /*< setup data for control transfers */
+} __attribute__((packed));
+
+#define QTD_STS_ACTIVE     (1 << 31)  /* enable execution of transaction */
+#define QTD_STS_HALTED     (1 << 30)  /* transfer halted */
+#define QTD_STS_DBE        (1 << 29)  /* data buffer error */
+#define QTD_STS_BABBLE     (1 << 28)  /* babble detected */
+#define QTD_STS_RCE        (1 << 27)  /* retry count exceeded */
+#define QTD_STS_LAST_PKT   (1 << 26)  /* set Last Packet Flag in WUSB header */
+#define QTD_STS_INACTIVE   (1 << 25)  /* queue set is marked inactive */
+#define QTD_STS_IALT_VALID (1 << 23)                          /* iAlt field is valid */
+#define QTD_STS_IALT(i)    (QTD_STS_IALT_VALID | ((i) << 20)) /* iAlt field */
+#define QTD_STS_LEN(l)     ((l) << 0) /* transfer length */
+#define QTD_STS_TO_LEN(s)  ((s) & 0x000fffff)
+
+#define QTD_OPT_IOC      (1 << 1) /* page_list_ptr points to buffer directly */
+#define QTD_OPT_SMALL    (1 << 0) /* interrupt on complete */
+
+/**
+ * struct whc_itd - Isochronous Queue Element Transfer Descriptors (iTD)
+ *
+ * This describes the data and other parameters for an isochronous
+ * transfer.
+ *
+ * [WHCI] section 3.2.5
+ */
+struct whc_itd {
+       __le16 presentation_time;    /*< presentation time for OUT transfers */
+       __u8   num_segments;         /*< number of data segments in segment list */
+       __u8   status;               /*< command execution status */
+       __le32 options;              /*< misc transfer options */
+       __le64 page_list_ptr;        /*< physical pointer to data buffer page list */
+       __le64 seg_list_ptr;         /*< physical pointer to segment list */
+} __attribute__((packed));
+
+#define ITD_STS_ACTIVE   (1 << 7) /* enable execution of transaction */
+#define ITD_STS_DBE      (1 << 5) /* data buffer error */
+#define ITD_STS_BABBLE   (1 << 4) /* babble detected */
+#define ITD_STS_INACTIVE (1 << 1) /* queue set is marked inactive */
+
+#define ITD_OPT_IOC      (1 << 1) /* interrupt on complete */
+#define ITD_OPT_SMALL    (1 << 0) /* page_list_ptr points to buffer directly */
+
+/**
+ * Page list entry.
+ *
+ * A TD's page list must contain sufficient page list entries for the
+ * total data length in the TD.
+ *
+ * [WHCI] section 3.2.4.3
+ */
+struct whc_page_list_entry {
+       __le64 buf_ptr; /*< physical pointer to buffer */
+} __attribute__((packed));
+
+/**
+ * struct whc_seg_list_entry - Segment list entry.
+ *
+ * Describes a portion of the data buffer described in the containing
+ * qTD's page list.
+ *
+ * seg_ptr = qtd->page_list_ptr[qtd->seg_list_ptr[seg].idx].buf_ptr
+ *           + qtd->seg_list_ptr[seg].offset;
+ *
+ * Segments can't cross page boundries.
+ *
+ * [WHCI] section 3.2.5.5
+ */
+struct whc_seg_list_entry {
+       __le16 len;    /*< segment length */
+       __u8   idx;    /*< index into page list */
+       __u8   status; /*< segment status */
+       __le16 offset; /*< 12 bit offset into page */
+} __attribute__((packed));
+
+/**
+ * struct whc_qhead - endpoint and status information for a qset.
+ *
+ * [WHCI] section 3.2.6
+ */
+struct whc_qhead {
+       __le64 link; /*< next qset in list */
+       __le32 info1;
+       __le32 info2;
+       __le32 info3;
+       __le16 status;
+       __le16 err_count;  /*< transaction error count */
+       __le32 cur_window;
+       __le32 scratch[3]; /*< h/w scratch area */
+       union {
+               struct whc_qtd qtd;
+               struct whc_itd itd;
+       } overlay;
+} __attribute__((packed));
+
+#define QH_LINK_PTR_MASK (~0x03Full)
+#define QH_LINK_PTR(ptr) ((ptr) & QH_LINK_PTR_MASK)
+#define QH_LINK_IQS      (1 << 4) /* isochronous queue set */
+#define QH_LINK_NTDS(n)  (((n) - 1) << 1) /* number of TDs in queue set */
+#define QH_LINK_T        (1 << 0) /* last queue set in periodic schedule list */
+
+#define QH_INFO1_EP(e)           ((e) << 0)  /* endpoint number */
+#define QH_INFO1_DIR_IN          (1 << 4)    /* IN transfer */
+#define QH_INFO1_DIR_OUT         (0 << 4)    /* OUT transfer */
+#define QH_INFO1_TR_TYPE_CTRL    (0x0 << 5)  /* control transfer */
+#define QH_INFO1_TR_TYPE_ISOC    (0x1 << 5)  /* isochronous transfer */
+#define QH_INFO1_TR_TYPE_BULK    (0x2 << 5)  /* bulk transfer */
+#define QH_INFO1_TR_TYPE_INT     (0x3 << 5)  /* interrupt */
+#define QH_INFO1_TR_TYPE_LP_INT  (0x7 << 5)  /* low power interrupt */
+#define QH_INFO1_DEV_INFO_IDX(i) ((i) << 8)  /* index into device info buffer */
+#define QH_INFO1_SET_INACTIVE    (1 << 15)   /* set inactive after transfer */
+#define QH_INFO1_MAX_PKT_LEN(l)  ((l) << 16) /* maximum packet length */
+
+#define QH_INFO2_BURST(b)        ((b) << 0)  /* maximum burst length */
+#define QH_INFO2_DBP(p)          ((p) << 5)  /* data burst policy (see [WUSB] table 5-7) */
+#define QH_INFO2_MAX_COUNT(c)    ((c) << 8)  /* max isoc/int pkts per zone */
+#define QH_INFO2_RQS             (1 << 15)   /* reactivate queue set */
+#define QH_INFO2_MAX_RETRY(r)    ((r) << 16) /* maximum transaction retries */
+#define QH_INFO2_MAX_SEQ(s)      ((s) << 20) /* maximum sequence number */
+#define QH_INFO3_MAX_DELAY(d)    ((d) << 0)  /* maximum stream delay in 125 us units (isoc only) */
+#define QH_INFO3_INTERVAL(i)     ((i) << 16) /* segment interval in 125 us units (isoc only) */
+
+#define QH_INFO3_TX_RATE_53_3    (0 << 24)
+#define QH_INFO3_TX_RATE_80      (1 << 24)
+#define QH_INFO3_TX_RATE_106_7   (2 << 24)
+#define QH_INFO3_TX_RATE_160     (3 << 24)
+#define QH_INFO3_TX_RATE_200     (4 << 24)
+#define QH_INFO3_TX_RATE_320     (5 << 24)
+#define QH_INFO3_TX_RATE_400     (6 << 24)
+#define QH_INFO3_TX_RATE_480     (7 << 24)
+#define QH_INFO3_TX_PWR(p)       ((p) << 29) /* transmit power (see [WUSB] section 5.2.1.2) */
+
+#define QH_STATUS_FLOW_CTRL      (1 << 15)
+#define QH_STATUS_ICUR(i)        ((i) << 5)
+#define QH_STATUS_TO_ICUR(s)     (((s) >> 5) & 0x7)
+
+/**
+ * usb_pipe_to_qh_type - USB core pipe type to QH transfer type
+ *
+ * Returns the QH type field for a USB core pipe type.
+ */
+static inline unsigned usb_pipe_to_qh_type(unsigned pipe)
+{
+       static const unsigned type[] = {
+               [PIPE_ISOCHRONOUS] = QH_INFO1_TR_TYPE_ISOC,
+               [PIPE_INTERRUPT]   = QH_INFO1_TR_TYPE_INT,
+               [PIPE_CONTROL]     = QH_INFO1_TR_TYPE_CTRL,
+               [PIPE_BULK]        = QH_INFO1_TR_TYPE_BULK,
+       };
+       return type[usb_pipetype(pipe)];
+}
+
+/**
+ * Maxiumum number of TDs in a qset.
+ */
+#define WHCI_QSET_TD_MAX 8
+
+/**
+ * struct whc_qset - WUSB data transfers to a specific endpoint
+ * @qh: the QHead of this qset
+ * @qtd: up to 8 qTDs (for qsets for control, bulk and interrupt
+ * transfers)
+ * @itd: up to 8 iTDs (for qsets for isochronous transfers)
+ * @qset_dma: DMA address for this qset
+ * @whc: WHCI HC this qset is for
+ * @ep: endpoint
+ * @stds: list of sTDs queued to this qset
+ * @ntds: number of qTDs queued (not necessarily the same as nTDs
+ * field in the QH)
+ * @td_start: index of the first qTD in the list
+ * @td_end: index of next free qTD in the list (provided
+ *          ntds < WHCI_QSET_TD_MAX)
+ *
+ * Queue Sets (qsets) are added to the asynchronous schedule list
+ * (ASL) or the periodic zone list (PZL).
+ *
+ * qsets may contain up to 8 TDs (either qTDs or iTDs as appropriate).
+ * Each TD may refer to at most 1 MiB of data. If a single transfer
+ * has > 8MiB of data, TDs can be reused as they are completed since
+ * the TD list is used as a circular buffer.  Similarly, several
+ * (smaller) transfers may be queued in a qset.
+ *
+ * WHCI controllers may cache portions of the qsets in the ASL and
+ * PZL, requiring the WHCD to inform the WHC that the lists have been
+ * updated (fields changed or qsets inserted or removed).  For safe
+ * insertion and removal of qsets from the lists the schedule must be
+ * stopped to avoid races in updating the QH link pointers.
+ *
+ * Since the HC is free to execute qsets in any order, all transfers
+ * to an endpoint should use the same qset to ensure transfers are
+ * executed in the order they're submitted.
+ *
+ * [WHCI] section 3.2.3
+ */
+struct whc_qset {
+       struct whc_qhead qh;
+       union {
+               struct whc_qtd qtd[WHCI_QSET_TD_MAX];
+               struct whc_itd itd[WHCI_QSET_TD_MAX];
+       };
+
+       /* private data for WHCD */
+       dma_addr_t qset_dma;
+       struct whc *whc;
+       struct usb_host_endpoint *ep;
+       struct list_head stds;
+       int ntds;
+       int td_start;
+       int td_end;
+       struct list_head list_node;
+       unsigned in_sw_list:1;
+       unsigned in_hw_list:1;
+       unsigned remove:1;
+       struct urb *pause_after_urb;
+       struct completion remove_complete;
+       int max_burst;
+       int max_seq;
+};
+
+static inline void whc_qset_set_link_ptr(u64 *ptr, u64 target)
+{
+       if (target)
+               *ptr = (*ptr & ~(QH_LINK_PTR_MASK | QH_LINK_T)) | QH_LINK_PTR(target);
+       else
+               *ptr = QH_LINK_T;
+}
+
+/**
+ * struct di_buf_entry - Device Information (DI) buffer entry.
+ *
+ * There's one of these per connected device.
+ */
+struct di_buf_entry {
+       __le32 availability_info[8]; /*< MAS availability information, one MAS per bit */
+       __le32 addr_sec_info;        /*< addressing and security info */
+       __le32 reserved[7];
+} __attribute__((packed));
+
+#define WHC_DI_SECURE           (1 << 31)
+#define WHC_DI_DISABLE          (1 << 30)
+#define WHC_DI_KEY_IDX(k)       ((k) << 8)
+#define WHC_DI_KEY_IDX_MASK     0x0000ff00
+#define WHC_DI_DEV_ADDR(a)      ((a) << 0)
+#define WHC_DI_DEV_ADDR_MASK    0x000000ff
+
+/**
+ * struct dn_buf_entry - Device Notification (DN) buffer entry.
+ *
+ * [WHCI] section 3.2.8
+ */
+struct dn_buf_entry {
+       __u8   msg_size;    /*< number of octets of valid DN data */
+       __u8   reserved1;
+       __u8   src_addr;    /*< source address */
+       __u8   status;      /*< buffer entry status */
+       __le32 tkid;        /*< TKID for source device, valid if secure bit is set */
+       __u8   dn_data[56]; /*< up to 56 octets of DN data */
+} __attribute__((packed));
+
+#define WHC_DN_STATUS_VALID  (1 << 7) /* buffer entry is valid */
+#define WHC_DN_STATUS_SECURE (1 << 6) /* notification received using secure frame */
+
+#define WHC_N_DN_ENTRIES (4096 / sizeof(struct dn_buf_entry))
+
+/* The Add MMC IE WUSB Generic Command may take up to 256 bytes of
+   data. [WHCI] section 2.4.7. */
+#define WHC_GEN_CMD_DATA_LEN 256
+
+/*
+ * HC registers.
+ *
+ * [WHCI] section 2.4
+ */
+
+#define WHCIVERSION          0x00
+
+#define WHCSPARAMS           0x04
+#  define WHCSPARAMS_TO_N_MMC_IES(p) (((p) >> 16) & 0xff)
+#  define WHCSPARAMS_TO_N_KEYS(p)    (((p) >> 8) & 0xff)
+#  define WHCSPARAMS_TO_N_DEVICES(p) (((p) >> 0) & 0x7f)
+
+#define WUSBCMD              0x08
+#  define WUSBCMD_BCID(b)            ((b) << 16)
+#  define WUSBCMD_BCID_MASK          (0xff << 16)
+#  define WUSBCMD_ASYNC_QSET_RM      (1 << 12)
+#  define WUSBCMD_PERIODIC_QSET_RM   (1 << 11)
+#  define WUSBCMD_WUSBSI(s)          ((s) << 8)
+#  define WUSBCMD_WUSBSI_MASK        (0x7 << 8)
+#  define WUSBCMD_ASYNC_SYNCED_DB    (1 << 7)
+#  define WUSBCMD_PERIODIC_SYNCED_DB (1 << 6)
+#  define WUSBCMD_ASYNC_UPDATED      (1 << 5)
+#  define WUSBCMD_PERIODIC_UPDATED   (1 << 4)
+#  define WUSBCMD_ASYNC_EN           (1 << 3)
+#  define WUSBCMD_PERIODIC_EN        (1 << 2)
+#  define WUSBCMD_WHCRESET           (1 << 1)
+#  define WUSBCMD_RUN                (1 << 0)
+
+#define WUSBSTS              0x0c
+#  define WUSBSTS_ASYNC_SCHED             (1 << 15)
+#  define WUSBSTS_PERIODIC_SCHED          (1 << 14)
+#  define WUSBSTS_DNTS_SCHED              (1 << 13)
+#  define WUSBSTS_HCHALTED                (1 << 12)
+#  define WUSBSTS_GEN_CMD_DONE            (1 << 9)
+#  define WUSBSTS_CHAN_TIME_ROLLOVER      (1 << 8)
+#  define WUSBSTS_DNTS_OVERFLOW           (1 << 7)
+#  define WUSBSTS_BPST_ADJUSTMENT_CHANGED (1 << 6)
+#  define WUSBSTS_HOST_ERR                (1 << 5)
+#  define WUSBSTS_ASYNC_SCHED_SYNCED      (1 << 4)
+#  define WUSBSTS_PERIODIC_SCHED_SYNCED   (1 << 3)
+#  define WUSBSTS_DNTS_INT                (1 << 2)
+#  define WUSBSTS_ERR_INT                 (1 << 1)
+#  define WUSBSTS_INT                     (1 << 0)
+#  define WUSBSTS_INT_MASK                0x3ff
+
+#define WUSBINTR             0x10
+#  define WUSBINTR_GEN_CMD_DONE             (1 << 9)
+#  define WUSBINTR_CHAN_TIME_ROLLOVER       (1 << 8)
+#  define WUSBINTR_DNTS_OVERFLOW            (1 << 7)
+#  define WUSBINTR_BPST_ADJUSTMENT_CHANGED  (1 << 6)
+#  define WUSBINTR_HOST_ERR                 (1 << 5)
+#  define WUSBINTR_ASYNC_SCHED_SYNCED       (1 << 4)
+#  define WUSBINTR_PERIODIC_SCHED_SYNCED    (1 << 3)
+#  define WUSBINTR_DNTS_INT                 (1 << 2)
+#  define WUSBINTR_ERR_INT                  (1 << 1)
+#  define WUSBINTR_INT                      (1 << 0)
+#  define WUSBINTR_ALL 0x3ff
+
+#define WUSBGENCMDSTS        0x14
+#  define WUSBGENCMDSTS_ACTIVE (1 << 31)
+#  define WUSBGENCMDSTS_ERROR  (1 << 24)
+#  define WUSBGENCMDSTS_IOC    (1 << 23)
+#  define WUSBGENCMDSTS_MMCIE_ADD 0x01
+#  define WUSBGENCMDSTS_MMCIE_RM  0x02
+#  define WUSBGENCMDSTS_SET_MAS   0x03
+#  define WUSBGENCMDSTS_CHAN_STOP 0x04
+#  define WUSBGENCMDSTS_RWP_EN    0x05
+
+#define WUSBGENCMDPARAMS     0x18
+#define WUSBGENADDR          0x20
+#define WUSBASYNCLISTADDR    0x28
+#define WUSBDNTSBUFADDR      0x30
+#define WUSBDEVICEINFOADDR   0x38
+
+#define WUSBSETSECKEYCMD     0x40
+#  define WUSBSETSECKEYCMD_SET    (1 << 31)
+#  define WUSBSETSECKEYCMD_ERASE  (1 << 30)
+#  define WUSBSETSECKEYCMD_GTK    (1 << 8)
+#  define WUSBSETSECKEYCMD_IDX(i) ((i) << 0)
+
+#define WUSBTKID             0x44
+#define WUSBSECKEY           0x48
+#define WUSBPERIODICLISTBASE 0x58
+#define WUSBMASINDEX         0x60
+
+#define WUSBDNTSCTRL         0x64
+#  define WUSBDNTSCTRL_ACTIVE      (1 << 31)
+#  define WUSBDNTSCTRL_INTERVAL(i) ((i) << 8)
+#  define WUSBDNTSCTRL_SLOTS(s)    ((s) << 0)
+
+#define WUSBTIME             0x68
+#define WUSBBPST             0x6c
+#define WUSBDIBUPDATED       0x70
+
+#endif /* #ifndef _WHCI_WHCI_HC_H */
diff --git a/drivers/usb/host/whci/wusb.c b/drivers/usb/host/whci/wusb.c
new file mode 100644 (file)
index 0000000..66e4ddc
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * Wireless Host Controller (WHC) WUSB operations.
+ *
+ * Copyright (C) 2007 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/init.h>
+#include <linux/uwb/umc.h>
+#define D_LOCAL 1
+#include <linux/uwb/debug.h>
+
+#include "../../wusbcore/wusbhc.h"
+
+#include "whcd.h"
+
+#if D_LOCAL >= 1
+static void dump_di(struct whc *whc, int idx)
+{
+       struct di_buf_entry *di = &whc->di_buf[idx];
+       struct device *dev = &whc->umc->dev;
+       char buf[128];
+
+       bitmap_scnprintf(buf, sizeof(buf), (unsigned long *)di->availability_info, UWB_NUM_MAS);
+
+       d_printf(1, dev, "DI[%d]\n", idx);
+       d_printf(1, dev, "  availability: %s\n", buf);
+       d_printf(1, dev, "  %c%c key idx: %d dev addr: %d\n",
+                (di->addr_sec_info & WHC_DI_SECURE) ? 'S' : ' ',
+                (di->addr_sec_info & WHC_DI_DISABLE) ? 'D' : ' ',
+                (di->addr_sec_info & WHC_DI_KEY_IDX_MASK) >> 8,
+                (di->addr_sec_info & WHC_DI_DEV_ADDR_MASK));
+}
+#else
+static inline void dump_di(struct whc *whc, int idx)
+{
+}
+#endif
+
+static int whc_update_di(struct whc *whc, int idx)
+{
+       int offset = idx / 32;
+       u32 bit = 1 << (idx % 32);
+
+       dump_di(whc, idx);
+
+       le_writel(bit, whc->base + WUSBDIBUPDATED + offset);
+
+       return whci_wait_for(&whc->umc->dev,
+                            whc->base + WUSBDIBUPDATED + offset, bit, 0,
+                            100, "DI update");
+}
+
+/*
+ * WHCI starts and stops MMCs based on there being a valid GTK so
+ * these need only start/stop the asynchronous and periodic schedules.
+ */
+
+int whc_wusbhc_start(struct wusbhc *wusbhc)
+{
+       struct whc *whc = wusbhc_to_whc(wusbhc);
+
+       asl_start(whc);
+       pzl_start(whc);
+
+       return 0;
+}
+
+void whc_wusbhc_stop(struct wusbhc *wusbhc)
+{
+       struct whc *whc = wusbhc_to_whc(wusbhc);
+
+       pzl_stop(whc);
+       asl_stop(whc);
+}
+
+int whc_mmcie_add(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt,
+                 u8 handle, struct wuie_hdr *wuie)
+{
+       struct whc *whc = wusbhc_to_whc(wusbhc);
+       u32 params;
+
+       params = (interval << 24)
+               | (repeat_cnt << 16)
+               | (wuie->bLength << 8)
+               | handle;
+
+       return whc_do_gencmd(whc, WUSBGENCMDSTS_MMCIE_ADD, params, wuie, wuie->bLength);
+}
+
+int whc_mmcie_rm(struct wusbhc *wusbhc, u8 handle)
+{
+       struct whc *whc = wusbhc_to_whc(wusbhc);
+       u32 params;
+
+       params = handle;
+
+       return whc_do_gencmd(whc, WUSBGENCMDSTS_MMCIE_RM, params, NULL, 0);
+}
+
+int whc_bwa_set(struct wusbhc *wusbhc, s8 stream_index, const struct uwb_mas_bm *mas_bm)
+{
+       struct whc *whc = wusbhc_to_whc(wusbhc);
+
+       if (stream_index >= 0)
+               whc_write_wusbcmd(whc, WUSBCMD_WUSBSI_MASK, WUSBCMD_WUSBSI(stream_index));
+
+       return whc_do_gencmd(whc, WUSBGENCMDSTS_SET_MAS, 0, (void *)mas_bm, sizeof(*mas_bm));
+}
+
+int whc_dev_info_set(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev)
+{
+       struct whc *whc = wusbhc_to_whc(wusbhc);
+       int idx = wusb_dev->port_idx;
+       struct di_buf_entry *di = &whc->di_buf[idx];
+       int ret;
+
+       mutex_lock(&whc->mutex);
+
+       uwb_mas_bm_copy_le(di->availability_info, &wusb_dev->availability);
+       di->addr_sec_info &= ~(WHC_DI_DISABLE | WHC_DI_DEV_ADDR_MASK);
+       di->addr_sec_info |= WHC_DI_DEV_ADDR(wusb_dev->addr);
+
+       ret = whc_update_di(whc, idx);
+
+       mutex_unlock(&whc->mutex);
+
+       return ret;
+}
+
+/*
+ * Set the number of Device Notification Time Slots (DNTS) and enable
+ * device notifications.
+ */
+int whc_set_num_dnts(struct wusbhc *wusbhc, u8 interval, u8 slots)
+{
+       struct whc *whc = wusbhc_to_whc(wusbhc);
+       u32 dntsctrl;
+
+       dntsctrl = WUSBDNTSCTRL_ACTIVE
+               | WUSBDNTSCTRL_INTERVAL(interval)
+               | WUSBDNTSCTRL_SLOTS(slots);
+
+       le_writel(dntsctrl, whc->base + WUSBDNTSCTRL);
+
+       return 0;
+}
+
+static int whc_set_key(struct whc *whc, u8 key_index, uint32_t tkid,
+                      const void *key, size_t key_size, bool is_gtk)
+{
+       uint32_t setkeycmd;
+       uint32_t seckey[4];
+       int i;
+       int ret;
+
+       memcpy(seckey, key, key_size);
+       setkeycmd = WUSBSETSECKEYCMD_SET | WUSBSETSECKEYCMD_IDX(key_index);
+       if (is_gtk)
+               setkeycmd |= WUSBSETSECKEYCMD_GTK;
+
+       le_writel(tkid, whc->base + WUSBTKID);
+       for (i = 0; i < 4; i++)
+               le_writel(seckey[i], whc->base + WUSBSECKEY + 4*i);
+       le_writel(setkeycmd, whc->base + WUSBSETSECKEYCMD);
+
+       ret = whci_wait_for(&whc->umc->dev, whc->base + WUSBSETSECKEYCMD,
+                           WUSBSETSECKEYCMD_SET, 0, 100, "set key");
+
+       return ret;
+}
+
+/**
+ * whc_set_ptk - set the PTK to use for a device.
+ *
+ * The index into the key table for this PTK is the same as the
+ * device's port index.
+ */
+int whc_set_ptk(struct wusbhc *wusbhc, u8 port_idx, u32 tkid,
+               const void *ptk, size_t key_size)
+{
+       struct whc *whc = wusbhc_to_whc(wusbhc);
+       struct di_buf_entry *di = &whc->di_buf[port_idx];
+       int ret;
+
+       mutex_lock(&whc->mutex);
+
+       if (ptk) {
+               ret = whc_set_key(whc, port_idx, tkid, ptk, key_size, false);
+               if (ret)
+                       goto out;
+
+               di->addr_sec_info &= ~WHC_DI_KEY_IDX_MASK;
+               di->addr_sec_info |= WHC_DI_SECURE | WHC_DI_KEY_IDX(port_idx);
+       } else
+               di->addr_sec_info &= ~WHC_DI_SECURE;
+
+       ret = whc_update_di(whc, port_idx);
+out:
+       mutex_unlock(&whc->mutex);
+       return ret;
+}
+
+/**
+ * whc_set_gtk - set the GTK for subsequent broadcast packets
+ *
+ * The GTK is stored in the last entry in the key table (the previous
+ * N_DEVICES entries are for the per-device PTKs).
+ */
+int whc_set_gtk(struct wusbhc *wusbhc, u32 tkid,
+               const void *gtk, size_t key_size)
+{
+       struct whc *whc = wusbhc_to_whc(wusbhc);
+       int ret;
+
+       mutex_lock(&whc->mutex);
+
+       ret = whc_set_key(whc, whc->n_devices, tkid, gtk, key_size, true);
+
+       mutex_unlock(&whc->mutex);
+
+       return ret;
+}
+
+int whc_set_cluster_id(struct whc *whc, u8 bcid)
+{
+       whc_write_wusbcmd(whc, WUSBCMD_BCID_MASK, WUSBCMD_BCID(bcid));
+       return 0;
+}
diff --git a/drivers/usb/wusbcore/Kconfig b/drivers/usb/wusbcore/Kconfig
new file mode 100644 (file)
index 0000000..eb09a0a
--- /dev/null
@@ -0,0 +1,41 @@
+#
+# Wireless USB Core configuration
+#
+config USB_WUSB
+       tristate "Enable Wireless USB extensions (EXPERIMENTAL)"
+       depends on EXPERIMENTAL
+       depends on USB
+        select UWB
+        select CRYPTO
+        select CRYPTO_BLKCIPHER
+        select CRYPTO_CBC
+        select CRYPTO_MANAGER
+        select CRYPTO_AES
+       help
+         Enable the host-side support for Wireless USB.
+
+          To compile this support select Y (built in). It is safe to
+         select even if you don't have the hardware.
+
+config USB_WUSB_CBAF
+       tristate "Support WUSB Cable Based Association (CBA)"
+       depends on USB
+       help
+         Some WUSB devices support Cable Based Association. It's used to
+         enable the secure communication between the host and the
+         device.
+
+         Enable this option if your WUSB device must to be connected
+         via wired USB before establishing a wireless link.
+
+         It is safe to select even if you don't have a compatible
+         hardware.
+
+config USB_WUSB_CBAF_DEBUG
+       bool "Enable CBA debug messages"
+       depends on USB_WUSB_CBAF
+       help
+         Say Y here if you want the CBA to produce a bunch of debug messages
+         to the system log. Select this if you are having a problem with
+         CBA support and want to see more of what is going on.
+
diff --git a/drivers/usb/wusbcore/Makefile b/drivers/usb/wusbcore/Makefile
new file mode 100644 (file)
index 0000000..75f1ade
--- /dev/null
@@ -0,0 +1,26 @@
+obj-$(CONFIG_USB_WUSB)         += wusbcore.o
+obj-$(CONFIG_USB_HWA_HCD)      += wusb-wa.o
+obj-$(CONFIG_USB_WUSB_CBAF)    += wusb-cbaf.o
+
+
+wusbcore-objs :=       \
+       crypto.o        \
+       devconnect.o    \
+       dev-sysfs.o     \
+       mmc.o           \
+       pal.o           \
+       rh.o            \
+       reservation.o   \
+       security.o      \
+       wusbhc.o
+
+wusb-cbaf-objs := cbaf.o
+
+wusb-wa-objs :=        wa-hc.o         \
+               wa-nep.o        \
+               wa-rpipe.o      \
+               wa-xfer.o
+
+ifeq ($(CONFIG_USB_WUSB_CBAF_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
diff --git a/drivers/usb/wusbcore/cbaf.c b/drivers/usb/wusbcore/cbaf.c
new file mode 100644 (file)
index 0000000..ab4788d
--- /dev/null
@@ -0,0 +1,673 @@
+/*
+ * Wireless USB - Cable Based Association
+ *
+ *
+ * Copyright (C) 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * WUSB devices have to be paired (associated in WUSB lingo) so
+ * that they can connect to the system.
+ *
+ * One way of pairing is using CBA-Cable Based Association. First
+ * time you plug the device with a cable, association is done between
+ * host and device and subsequent times, you can connect wirelessly
+ * without having to associate again. That's the idea.
+ *
+ * This driver does nothing Earth shattering. It just provides an
+ * interface to chat with the wire-connected device so we can get a
+ * CDID (device ID) that might have been previously associated to a
+ * CHID (host ID) and to set up a new <CHID,CDID,CK> triplet
+ * (connection context), with the CK being the secret, or connection
+ * key. This is the pairing data.
+ *
+ * When a device with the CBA capability connects, the probe routine
+ * just creates a bunch of sysfs files that a user space enumeration
+ * manager uses to allow it to connect wirelessly to the system or not.
+ *
+ * The process goes like this:
+ *
+ * 1. Device plugs, cbaf is loaded, notifications happen.
+ *
+ * 2. The connection manager (CM) sees a device with CBAF capability
+ *    (the wusb_chid etc. files in /sys/devices/blah/OURDEVICE).
+ *
+ * 3. The CM writes the host name, supported band groups, and the CHID
+ *    (host ID) into the wusb_host_name, wusb_host_band_groups and
+ *    wusb_chid files. These get sent to the device and the CDID (if
+ *    any) for this host is requested.
+ *
+ * 4. The CM can verify that the device's supported band groups
+ *    (wusb_device_band_groups) are compatible with the host.
+ *
+ * 5. The CM reads the wusb_cdid file.
+ *
+ * 6. The CM looks up its database
+ *
+ * 6.1 If it has a matching CHID,CDID entry, the device has been
+ *     authorized before (paired) and nothing further needs to be
+ *     done.
+ *
+ * 6.2 If the CDID is zero (or the CM doesn't find a matching CDID in
+ *     its database), the device is assumed to be not known.  The CM
+ *     may associate the host with device by: writing a randomly
+ *     generated CDID to wusb_cdid and then a random CK to wusb_ck
+ *     (this uploads the new CC to the device).
+ *
+ *     CMD may choose to prompt the user before associating with a new
+ *     device.
+ *
+ * 7. Device is unplugged.
+ *
+ * When the device tries to connect wirelessly, it will present its
+ * CDID to the WUSB host controller.  The CM will query the
+ * database. If the CHID/CDID pair found, it will (with a 4-way
+ * handshake) challenge the device to demonstrate it has the CK secret
+ * key (from our database) without actually exchanging it. Once
+ * satisfied, crypto keys are derived from the CK, the device is
+ * connected and all communication is encrypted.
+ *
+ * References:
+ *   [WUSB-AM] Association Models Supplement to the Certified Wireless
+ *             Universal Serial Bus Specification, version 1.0.
+ */
+#include <linux/module.h>
+#include <linux/ctype.h>
+#include <linux/version.h>
+#include <linux/usb.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/random.h>
+#include <linux/mutex.h>
+#include <linux/uwb.h>
+#include <linux/usb/wusb.h>
+#include <linux/usb/association.h>
+
+#define CBA_NAME_LEN 0x40 /* [WUSB-AM] table 4-7 */
+
+/* An instance of a Cable-Based-Association-Framework device */
+struct cbaf {
+       struct usb_device *usb_dev;
+       struct usb_interface *usb_iface;
+       void *buffer;
+       size_t buffer_size;
+
+       struct wusb_ckhdid chid;
+       char host_name[CBA_NAME_LEN];
+       u16 host_band_groups;
+
+       struct wusb_ckhdid cdid;
+       char device_name[CBA_NAME_LEN];
+       u16 device_band_groups;
+
+       struct wusb_ckhdid ck;
+};
+
+/*
+ * Verify that a CBAF USB-interface has what we need
+ *
+ * According to [WUSB-AM], CBA devices should provide at least two
+ * interfaces:
+ *  - RETRIEVE_HOST_INFO
+ *  - ASSOCIATE
+ *
+ * If the device doesn't provide these interfaces, we do not know how
+ * to deal with it.
+ */
+static int cbaf_check(struct cbaf *cbaf)
+{
+       int result;
+       struct device *dev = &cbaf->usb_iface->dev;
+       struct wusb_cbaf_assoc_info *assoc_info;
+       struct wusb_cbaf_assoc_request *assoc_request;
+       size_t assoc_size;
+       void *itr, *top;
+       int ar_rhi = 0, ar_assoc = 0;
+
+       result = usb_control_msg(
+               cbaf->usb_dev, usb_rcvctrlpipe(cbaf->usb_dev, 0),
+               CBAF_REQ_GET_ASSOCIATION_INFORMATION,
+               USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+               0, cbaf->usb_iface->cur_altsetting->desc.bInterfaceNumber,
+               cbaf->buffer, cbaf->buffer_size, 1000 /* FIXME: arbitrary */);
+       if (result < 0) {
+               dev_err(dev, "Cannot get available association types: %d\n",
+                       result);
+               return result;
+       }
+
+       assoc_info = cbaf->buffer;
+       if (result < sizeof(*assoc_info)) {
+               dev_err(dev, "Not enough data to decode association info "
+                       "header (%zu vs %zu bytes required)\n",
+                       (size_t)result, sizeof(*assoc_info));
+               return result;
+       }
+
+       assoc_size = le16_to_cpu(assoc_info->Length);
+       if (result < assoc_size) {
+               dev_err(dev, "Not enough data to decode association info "
+                       "(%zu vs %zu bytes required)\n",
+                       (size_t)assoc_size, sizeof(*assoc_info));
+               return result;
+       }
+       /*
+        * From now on, we just verify, but won't error out unless we
+        * don't find the AR_TYPE_WUSB_{RETRIEVE_HOST_INFO,ASSOCIATE}
+        * types.
+        */
+       itr = cbaf->buffer + sizeof(*assoc_info);
+       top = cbaf->buffer + assoc_size;
+       dev_dbg(dev, "Found %u association requests (%zu bytes)\n",
+                assoc_info->NumAssociationRequests, assoc_size);
+
+       while (itr < top) {
+               u16 ar_type, ar_subtype;
+               u32 ar_size;
+               const char *ar_name;
+
+               assoc_request = itr;
+
+               if (top - itr < sizeof(*assoc_request)) {
+                       dev_err(dev, "Not enough data to decode associaton "
+                               "request (%zu vs %zu bytes needed)\n",
+                               top - itr, sizeof(*assoc_request));
+                       break;
+               }
+
+               ar_type = le16_to_cpu(assoc_request->AssociationTypeId);
+               ar_subtype = le16_to_cpu(assoc_request->AssociationSubTypeId);
+               ar_size = le32_to_cpu(assoc_request->AssociationTypeInfoSize);
+               ar_name = "unknown";
+
+               switch (ar_type) {
+               case AR_TYPE_WUSB:
+                       /* Verify we have what is mandated by [WUSB-AM]. */
+                       switch (ar_subtype) {
+                       case AR_TYPE_WUSB_RETRIEVE_HOST_INFO:
+                               ar_name = "RETRIEVE_HOST_INFO";
+                               ar_rhi = 1;
+                               break;
+                       case AR_TYPE_WUSB_ASSOCIATE:
+                               /* send assoc data */
+                               ar_name = "ASSOCIATE";
+                               ar_assoc = 1;
+                               break;
+                       };
+                       break;
+               };
+
+               dev_dbg(dev, "Association request #%02u: 0x%04x/%04x "
+                        "(%zu bytes): %s\n",
+                        assoc_request->AssociationDataIndex, ar_type,
+                        ar_subtype, (size_t)ar_size, ar_name);
+
+               itr += sizeof(*assoc_request);
+       }
+
+       if (!ar_rhi) {
+               dev_err(dev, "Missing RETRIEVE_HOST_INFO association "
+                       "request\n");
+               return -EINVAL;
+       }
+       if (!ar_assoc) {
+               dev_err(dev, "Missing ASSOCIATE association request\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static const struct wusb_cbaf_host_info cbaf_host_info_defaults = {
+       .AssociationTypeId_hdr    = WUSB_AR_AssociationTypeId,
+       .AssociationTypeId        = cpu_to_le16(AR_TYPE_WUSB),
+       .AssociationSubTypeId_hdr = WUSB_AR_AssociationSubTypeId,
+       .AssociationSubTypeId = cpu_to_le16(AR_TYPE_WUSB_RETRIEVE_HOST_INFO),
+       .CHID_hdr                 = WUSB_AR_CHID,
+       .LangID_hdr               = WUSB_AR_LangID,
+       .HostFriendlyName_hdr     = WUSB_AR_HostFriendlyName,
+};
+
+/* Send WUSB host information (CHID and name) to a CBAF device */
+static int cbaf_send_host_info(struct cbaf *cbaf)
+{
+       struct wusb_cbaf_host_info *hi;
+       size_t name_len;
+       size_t hi_size;
+
+       hi = cbaf->buffer;
+       memset(hi, 0, sizeof(*hi));
+       *hi = cbaf_host_info_defaults;
+       hi->CHID = cbaf->chid;
+       hi->LangID = 0; /* FIXME: I guess... */
+       strlcpy(hi->HostFriendlyName, cbaf->host_name, CBA_NAME_LEN);
+       name_len = strlen(cbaf->host_name);
+       hi->HostFriendlyName_hdr.len = cpu_to_le16(name_len);
+       hi_size = sizeof(*hi) + name_len;
+
+       return usb_control_msg(cbaf->usb_dev, usb_sndctrlpipe(cbaf->usb_dev, 0),
+                       CBAF_REQ_SET_ASSOCIATION_RESPONSE,
+                       USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+                       0x0101,
+                       cbaf->usb_iface->cur_altsetting->desc.bInterfaceNumber,
+                       hi, hi_size, 1000 /* FIXME: arbitrary */);
+}
+
+/*
+ * Get device's information (CDID) associated to CHID
+ *
+ * The device will return it's information (CDID, name, bandgroups)
+ * associated to the CHID we have set before, or 0 CDID and default
+ * name and bandgroup if no CHID set or unknown.
+ */
+static int cbaf_cdid_get(struct cbaf *cbaf)
+{
+       int result;
+       struct device *dev = &cbaf->usb_iface->dev;
+       struct wusb_cbaf_device_info *di;
+       size_t needed;
+
+       di = cbaf->buffer;
+       result = usb_control_msg(
+               cbaf->usb_dev, usb_rcvctrlpipe(cbaf->usb_dev, 0),
+               CBAF_REQ_GET_ASSOCIATION_REQUEST,
+               USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+               0x0200, cbaf->usb_iface->cur_altsetting->desc.bInterfaceNumber,
+               di, cbaf->buffer_size, 1000 /* FIXME: arbitrary */);
+       if (result < 0) {
+               dev_err(dev, "Cannot request device information: %d\n", result);
+               return result;
+       }
+
+       needed = result < sizeof(*di) ? sizeof(*di) : le32_to_cpu(di->Length);
+       if (result < needed) {
+               dev_err(dev, "Not enough data in DEVICE_INFO reply (%zu vs "
+                       "%zu bytes needed)\n", (size_t)result, needed);
+               return result;
+       }
+
+       strlcpy(cbaf->device_name, di->DeviceFriendlyName, CBA_NAME_LEN);
+       cbaf->cdid = di->CDID;
+       cbaf->device_band_groups = le16_to_cpu(di->BandGroups);
+
+       return 0;
+}
+
+static ssize_t cbaf_wusb_chid_show(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       struct usb_interface *iface = to_usb_interface(dev);
+       struct cbaf *cbaf = usb_get_intfdata(iface);
+       char pr_chid[WUSB_CKHDID_STRSIZE];
+
+       ckhdid_printf(pr_chid, sizeof(pr_chid), &cbaf->chid);
+       return scnprintf(buf, PAGE_SIZE, "%s\n", pr_chid);
+}
+
+static ssize_t cbaf_wusb_chid_store(struct device *dev,
+                                        struct device_attribute *attr,
+                                        const char *buf, size_t size)
+{
+       ssize_t result;
+       struct usb_interface *iface = to_usb_interface(dev);
+       struct cbaf *cbaf = usb_get_intfdata(iface);
+
+       result = sscanf(buf,
+                       "%02hhx %02hhx %02hhx %02hhx "
+                       "%02hhx %02hhx %02hhx %02hhx "
+                       "%02hhx %02hhx %02hhx %02hhx "
+                       "%02hhx %02hhx %02hhx %02hhx",
+                       &cbaf->chid.data[0] , &cbaf->chid.data[1],
+                       &cbaf->chid.data[2] , &cbaf->chid.data[3],
+                       &cbaf->chid.data[4] , &cbaf->chid.data[5],
+                       &cbaf->chid.data[6] , &cbaf->chid.data[7],
+                       &cbaf->chid.data[8] , &cbaf->chid.data[9],
+                       &cbaf->chid.data[10], &cbaf->chid.data[11],
+                       &cbaf->chid.data[12], &cbaf->chid.data[13],
+                       &cbaf->chid.data[14], &cbaf->chid.data[15]);
+
+       if (result != 16)
+               return -EINVAL;
+
+       result = cbaf_send_host_info(cbaf);
+       if (result < 0)
+               return result;
+       result = cbaf_cdid_get(cbaf);
+       if (result < 0)
+               return -result;
+       return size;
+}
+static DEVICE_ATTR(wusb_chid, 0600, cbaf_wusb_chid_show, cbaf_wusb_chid_store);
+
+static ssize_t cbaf_wusb_host_name_show(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       struct usb_interface *iface = to_usb_interface(dev);
+       struct cbaf *cbaf = usb_get_intfdata(iface);
+
+       return scnprintf(buf, PAGE_SIZE, "%s\n", cbaf->host_name);
+}
+
+static ssize_t cbaf_wusb_host_name_store(struct device *dev,
+                                        struct device_attribute *attr,
+                                        const char *buf, size_t size)
+{
+       ssize_t result;
+       struct usb_interface *iface = to_usb_interface(dev);
+       struct cbaf *cbaf = usb_get_intfdata(iface);
+
+       result = sscanf(buf, "%63s", cbaf->host_name);
+       if (result != 1)
+               return -EINVAL;
+
+       return size;
+}
+static DEVICE_ATTR(wusb_host_name, 0600, cbaf_wusb_host_name_show,
+                                        cbaf_wusb_host_name_store);
+
+static ssize_t cbaf_wusb_host_band_groups_show(struct device *dev,
+                                              struct device_attribute *attr,
+                                              char *buf)
+{
+       struct usb_interface *iface = to_usb_interface(dev);
+       struct cbaf *cbaf = usb_get_intfdata(iface);
+
+       return scnprintf(buf, PAGE_SIZE, "0x%04x\n", cbaf->host_band_groups);
+}
+
+static ssize_t cbaf_wusb_host_band_groups_store(struct device *dev,
+                                               struct device_attribute *attr,
+                                               const char *buf, size_t size)
+{
+       ssize_t result;
+       struct usb_interface *iface = to_usb_interface(dev);
+       struct cbaf *cbaf = usb_get_intfdata(iface);
+       u16 band_groups = 0;
+
+       result = sscanf(buf, "%04hx", &band_groups);
+       if (result != 1)
+               return -EINVAL;
+
+       cbaf->host_band_groups = band_groups;
+
+       return size;
+}
+
+static DEVICE_ATTR(wusb_host_band_groups, 0600,
+                  cbaf_wusb_host_band_groups_show,
+                  cbaf_wusb_host_band_groups_store);
+
+static const struct wusb_cbaf_device_info cbaf_device_info_defaults = {
+       .Length_hdr               = WUSB_AR_Length,
+       .CDID_hdr                 = WUSB_AR_CDID,
+       .BandGroups_hdr           = WUSB_AR_BandGroups,
+       .LangID_hdr               = WUSB_AR_LangID,
+       .DeviceFriendlyName_hdr   = WUSB_AR_DeviceFriendlyName,
+};
+
+static ssize_t cbaf_wusb_cdid_show(struct device *dev,
+                                  struct device_attribute *attr, char *buf)
+{
+       struct usb_interface *iface = to_usb_interface(dev);
+       struct cbaf *cbaf = usb_get_intfdata(iface);
+       char pr_cdid[WUSB_CKHDID_STRSIZE];
+
+       ckhdid_printf(pr_cdid, sizeof(pr_cdid), &cbaf->cdid);
+       return scnprintf(buf, PAGE_SIZE, "%s\n", pr_cdid);
+}
+
+static ssize_t cbaf_wusb_cdid_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t size)
+{
+       ssize_t result;
+       struct usb_interface *iface = to_usb_interface(dev);
+       struct cbaf *cbaf = usb_get_intfdata(iface);
+       struct wusb_ckhdid cdid;
+
+       result = sscanf(buf,
+                       "%02hhx %02hhx %02hhx %02hhx "
+                       "%02hhx %02hhx %02hhx %02hhx "
+                       "%02hhx %02hhx %02hhx %02hhx "
+                       "%02hhx %02hhx %02hhx %02hhx",
+                       &cdid.data[0] , &cdid.data[1],
+                       &cdid.data[2] , &cdid.data[3],
+                       &cdid.data[4] , &cdid.data[5],
+                       &cdid.data[6] , &cdid.data[7],
+                       &cdid.data[8] , &cdid.data[9],
+                       &cdid.data[10], &cdid.data[11],
+                       &cdid.data[12], &cdid.data[13],
+                       &cdid.data[14], &cdid.data[15]);
+       if (result != 16)
+               return -EINVAL;
+
+       cbaf->cdid = cdid;
+
+       return size;
+}
+static DEVICE_ATTR(wusb_cdid, 0600, cbaf_wusb_cdid_show, cbaf_wusb_cdid_store);
+
+static ssize_t cbaf_wusb_device_band_groups_show(struct device *dev,
+                                                struct device_attribute *attr,
+                                                char *buf)
+{
+       struct usb_interface *iface = to_usb_interface(dev);
+       struct cbaf *cbaf = usb_get_intfdata(iface);
+
+       return scnprintf(buf, PAGE_SIZE, "0x%04x\n", cbaf->device_band_groups);
+}
+
+static DEVICE_ATTR(wusb_device_band_groups, 0600,
+                  cbaf_wusb_device_band_groups_show,
+                  NULL);
+
+static ssize_t cbaf_wusb_device_name_show(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       struct usb_interface *iface = to_usb_interface(dev);
+       struct cbaf *cbaf = usb_get_intfdata(iface);
+
+       return scnprintf(buf, PAGE_SIZE, "%s\n", cbaf->device_name);
+}
+static DEVICE_ATTR(wusb_device_name, 0600, cbaf_wusb_device_name_show, NULL);
+
+static const struct wusb_cbaf_cc_data cbaf_cc_data_defaults = {
+       .AssociationTypeId_hdr    = WUSB_AR_AssociationTypeId,
+       .AssociationTypeId        = cpu_to_le16(AR_TYPE_WUSB),
+       .AssociationSubTypeId_hdr = WUSB_AR_AssociationSubTypeId,
+       .AssociationSubTypeId     = cpu_to_le16(AR_TYPE_WUSB_ASSOCIATE),
+       .Length_hdr               = WUSB_AR_Length,
+       .Length                   = cpu_to_le32(sizeof(struct wusb_cbaf_cc_data)),
+       .ConnectionContext_hdr    = WUSB_AR_ConnectionContext,
+       .BandGroups_hdr           = WUSB_AR_BandGroups,
+};
+
+static const struct wusb_cbaf_cc_data_fail cbaf_cc_data_fail_defaults = {
+       .AssociationTypeId_hdr    = WUSB_AR_AssociationTypeId,
+       .AssociationSubTypeId_hdr = WUSB_AR_AssociationSubTypeId,
+       .Length_hdr               = WUSB_AR_Length,
+       .AssociationStatus_hdr    = WUSB_AR_AssociationStatus,
+};
+
+/*
+ * Send a new CC to the device.
+ */
+static int cbaf_cc_upload(struct cbaf *cbaf)
+{
+       int result;
+       struct device *dev = &cbaf->usb_iface->dev;
+       struct wusb_cbaf_cc_data *ccd;
+       char pr_cdid[WUSB_CKHDID_STRSIZE];
+
+       ccd =  cbaf->buffer;
+       *ccd = cbaf_cc_data_defaults;
+       ccd->CHID = cbaf->chid;
+       ccd->CDID = cbaf->cdid;
+       ccd->CK = cbaf->ck;
+       ccd->BandGroups = cpu_to_le16(cbaf->host_band_groups);
+
+       dev_dbg(dev, "Trying to upload CC:\n");
+       ckhdid_printf(pr_cdid, sizeof(pr_cdid), &ccd->CHID);
+       dev_dbg(dev, "  CHID       %s\n", pr_cdid);
+       ckhdid_printf(pr_cdid, sizeof(pr_cdid), &ccd->CDID);
+       dev_dbg(dev, "  CDID       %s\n", pr_cdid);
+       dev_dbg(dev, "  Bandgroups 0x%04x\n", cbaf->host_band_groups);
+
+       result = usb_control_msg(
+               cbaf->usb_dev, usb_sndctrlpipe(cbaf->usb_dev, 0),
+               CBAF_REQ_SET_ASSOCIATION_RESPONSE,
+               USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+               0x0201, cbaf->usb_iface->cur_altsetting->desc.bInterfaceNumber,
+               ccd, sizeof(*ccd), 1000 /* FIXME: arbitrary */);
+
+       return result;
+}
+
+static ssize_t cbaf_wusb_ck_store(struct device *dev,
+                                 struct device_attribute *attr,
+                                 const char *buf, size_t size)
+{
+       ssize_t result;
+       struct usb_interface *iface = to_usb_interface(dev);
+       struct cbaf *cbaf = usb_get_intfdata(iface);
+
+       result = sscanf(buf,
+                       "%02hhx %02hhx %02hhx %02hhx "
+                       "%02hhx %02hhx %02hhx %02hhx "
+                       "%02hhx %02hhx %02hhx %02hhx "
+                       "%02hhx %02hhx %02hhx %02hhx",
+                       &cbaf->ck.data[0] , &cbaf->ck.data[1],
+                       &cbaf->ck.data[2] , &cbaf->ck.data[3],
+                       &cbaf->ck.data[4] , &cbaf->ck.data[5],
+                       &cbaf->ck.data[6] , &cbaf->ck.data[7],
+                       &cbaf->ck.data[8] , &cbaf->ck.data[9],
+                       &cbaf->ck.data[10], &cbaf->ck.data[11],
+                       &cbaf->ck.data[12], &cbaf->ck.data[13],
+                       &cbaf->ck.data[14], &cbaf->ck.data[15]);
+       if (result != 16)
+               return -EINVAL;
+
+       result = cbaf_cc_upload(cbaf);
+       if (result < 0)
+               return result;
+
+       return size;
+}
+static DEVICE_ATTR(wusb_ck, 0600, NULL, cbaf_wusb_ck_store);
+
+static struct attribute *cbaf_dev_attrs[] = {
+       &dev_attr_wusb_host_name.attr,
+       &dev_attr_wusb_host_band_groups.attr,
+       &dev_attr_wusb_chid.attr,
+       &dev_attr_wusb_cdid.attr,
+       &dev_attr_wusb_device_name.attr,
+       &dev_attr_wusb_device_band_groups.attr,
+       &dev_attr_wusb_ck.attr,
+       NULL,
+};
+
+static struct attribute_group cbaf_dev_attr_group = {
+       .name = NULL,   /* we want them in the same directory */
+       .attrs = cbaf_dev_attrs,
+};
+
+static int cbaf_probe(struct usb_interface *iface,
+                     const struct usb_device_id *id)
+{
+       struct cbaf *cbaf;
+       struct device *dev = &iface->dev;
+       int result = -ENOMEM;
+
+       cbaf = kzalloc(sizeof(*cbaf), GFP_KERNEL);
+       if (cbaf == NULL)
+               goto error_kzalloc;
+       cbaf->buffer = kmalloc(512, GFP_KERNEL);
+       if (cbaf->buffer == NULL)
+               goto error_kmalloc_buffer;
+
+       cbaf->buffer_size = 512;
+       cbaf->usb_dev = usb_get_dev(interface_to_usbdev(iface));
+       cbaf->usb_iface = usb_get_intf(iface);
+       result = cbaf_check(cbaf);
+       if (result < 0) {
+               dev_err(dev, "This device is not WUSB-CBAF compliant"
+                       "and is not supported yet.\n");
+               goto error_check;
+       }
+
+       result = sysfs_create_group(&dev->kobj, &cbaf_dev_attr_group);
+       if (result < 0) {
+               dev_err(dev, "Can't register sysfs attr group: %d\n", result);
+               goto error_create_group;
+       }
+       usb_set_intfdata(iface, cbaf);
+       return 0;
+
+error_create_group:
+error_check:
+       kfree(cbaf->buffer);
+error_kmalloc_buffer:
+       kfree(cbaf);
+error_kzalloc:
+       return result;
+}
+
+static void cbaf_disconnect(struct usb_interface *iface)
+{
+       struct cbaf *cbaf = usb_get_intfdata(iface);
+       struct device *dev = &iface->dev;
+       sysfs_remove_group(&dev->kobj, &cbaf_dev_attr_group);
+       usb_set_intfdata(iface, NULL);
+       usb_put_intf(iface);
+       kfree(cbaf->buffer);
+       /* paranoia: clean up crypto keys */
+       memset(cbaf, 0, sizeof(*cbaf));
+       kfree(cbaf);
+}
+
+static struct usb_device_id cbaf_id_table[] = {
+       { USB_INTERFACE_INFO(0xef, 0x03, 0x01), },
+       { },
+};
+MODULE_DEVICE_TABLE(usb, cbaf_id_table);
+
+static struct usb_driver cbaf_driver = {
+       .name =         "wusb-cbaf",
+       .id_table =     cbaf_id_table,
+       .probe =        cbaf_probe,
+       .disconnect =   cbaf_disconnect,
+};
+
+static int __init cbaf_driver_init(void)
+{
+       return usb_register(&cbaf_driver);
+}
+module_init(cbaf_driver_init);
+
+static void __exit cbaf_driver_exit(void)
+{
+       usb_deregister(&cbaf_driver);
+}
+module_exit(cbaf_driver_exit);
+
+MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
+MODULE_DESCRIPTION("Wireless USB Cable Based Association");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/wusbcore/crypto.c b/drivers/usb/wusbcore/crypto.c
new file mode 100644 (file)
index 0000000..c36c438
--- /dev/null
@@ -0,0 +1,538 @@
+/*
+ * Ultra Wide Band
+ * AES-128 CCM Encryption
+ *
+ * Copyright (C) 2007 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 don't do any encryption here; we use the Linux Kernel's AES-128
+ * crypto modules to construct keys and payload blocks in a way
+ * defined by WUSB1.0[6]. Check the erratas, as typos are are patched
+ * there.
+ *
+ * Thanks a zillion to John Keys for his help and clarifications over
+ * the designed-by-a-committee text.
+ *
+ * So the idea is that there is this basic Pseudo-Random-Function
+ * defined in WUSB1.0[6.5] which is the core of everything. It works
+ * by tweaking some blocks, AES crypting them and then xoring
+ * something else with them (this seems to be called CBC(AES) -- can
+ * you tell I know jack about crypto?). So we just funnel it into the
+ * Linux Crypto API.
+ *
+ * We leave a crypto test module so we can verify that vectors match,
+ * every now and then.
+ *
+ * Block size: 16 bytes -- AES seems to do things in 'block sizes'. I
+ *             am learning a lot...
+ *
+ *             Conveniently, some data structures that need to be
+ *             funneled through AES are...16 bytes in size!
+ */
+
+#include <linux/crypto.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/uwb.h>
+#include <linux/usb/wusb.h>
+#include <linux/scatterlist.h>
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+
+/*
+ * Block of data, as understood by AES-CCM
+ *
+ * The code assumes this structure is nothing but a 16 byte array
+ * (packed in a struct to avoid common mess ups that I usually do with
+ * arrays and enforcing type checking).
+ */
+struct aes_ccm_block {
+       u8 data[16];
+} __attribute__((packed));
+
+/*
+ * Counter-mode Blocks (WUSB1.0[6.4])
+ *
+ * According to CCM (or so it seems), for the purpose of calculating
+ * the MIC, the message is broken in N counter-mode blocks, B0, B1,
+ * ... BN.
+ *
+ * B0 contains flags, the CCM nonce and l(m).
+ *
+ * B1 contains l(a), the MAC header, the encryption offset and padding.
+ *
+ * If EO is nonzero, additional blocks are built from payload bytes
+ * until EO is exahusted (FIXME: padding to 16 bytes, I guess). The
+ * padding is not xmitted.
+ */
+
+/* WUSB1.0[T6.4] */
+struct aes_ccm_b0 {
+       u8 flags;       /* 0x59, per CCM spec */
+       struct aes_ccm_nonce ccm_nonce;
+       __be16 lm;
+} __attribute__((packed));
+
+/* WUSB1.0[T6.5] */
+struct aes_ccm_b1 {
+       __be16 la;
+       u8 mac_header[10];
+       __le16 eo;
+       u8 security_reserved;   /* This is always zero */
+       u8 padding;             /* 0 */
+} __attribute__((packed));
+
+/*
+ * Encryption Blocks (WUSB1.0[6.4.4])
+ *
+ * CCM uses Ax blocks to generate a keystream with which the MIC and
+ * the message's payload are encoded. A0 always encrypts/decrypts the
+ * MIC. Ax (x>0) are used for the sucesive payload blocks.
+ *
+ * The x is the counter, and is increased for each block.
+ */
+struct aes_ccm_a {
+       u8 flags;       /* 0x01, per CCM spec */
+       struct aes_ccm_nonce ccm_nonce;
+       __be16 counter; /* Value of x */
+} __attribute__((packed));
+
+static void bytewise_xor(void *_bo, const void *_bi1, const void *_bi2,
+                        size_t size)
+{
+       u8 *bo = _bo;
+       const u8 *bi1 = _bi1, *bi2 = _bi2;
+       size_t itr;
+       for (itr = 0; itr < size; itr++)
+               bo[itr] = bi1[itr] ^ bi2[itr];
+}
+
+/*
+ * CC-MAC function WUSB1.0[6.5]
+ *
+ * Take a data string and produce the encrypted CBC Counter-mode MIC
+ *
+ * Note the names for most function arguments are made to (more or
+ * less) match those used in the pseudo-function definition given in
+ * WUSB1.0[6.5].
+ *
+ * @tfm_cbc: CBC(AES) blkcipher handle (initialized)
+ *
+ * @tfm_aes: AES cipher handle (initialized)
+ *
+ * @mic: buffer for placing the computed MIC (Message Integrity
+ *       Code). This is exactly 8 bytes, and we expect the buffer to
+ *       be at least eight bytes in length.
+ *
+ * @key: 128 bit symmetric key
+ *
+ * @n: CCM nonce
+ *
+ * @a: ASCII string, 14 bytes long (I guess zero padded if needed;
+ *     we use exactly 14 bytes).
+ *
+ * @b: data stream to be processed; cannot be a global or const local
+ *     (will confuse the scatterlists)
+ *
+ * @blen: size of b...
+ *
+ * Still not very clear how this is done, but looks like this: we
+ * create block B0 (as WUSB1.0[6.5] says), then we AES-crypt it with
+ * @key. We bytewise xor B0 with B1 (1) and AES-crypt that. Then we
+ * take the payload and divide it in blocks (16 bytes), xor them with
+ * the previous crypto result (16 bytes) and crypt it, repeat the next
+ * block with the output of the previous one, rinse wash (I guess this
+ * is what AES CBC mode means...but I truly have no idea). So we use
+ * the CBC(AES) blkcipher, that does precisely that. The IV (Initial
+ * Vector) is 16 bytes and is set to zero, so
+ *
+ * See rfc3610. Linux crypto has a CBC implementation, but the
+ * documentation is scarce, to say the least, and the example code is
+ * so intricated that is difficult to understand how things work. Most
+ * of this is guess work -- bite me.
+ *
+ * (1) Created as 6.5 says, again, using as l(a) 'Blen + 14', and
+ *     using the 14 bytes of @a to fill up
+ *     b1.{mac_header,e0,security_reserved,padding}.
+ *
+ * NOTE: The definiton of l(a) in WUSB1.0[6.5] vs the definition of
+ *       l(m) is orthogonal, they bear no relationship, so it is not
+ *       in conflict with the parameter's relation that
+ *       WUSB1.0[6.4.2]) defines.
+ *
+ * NOTE: WUSB1.0[A.1]: Host Nonce is missing a nibble? (1e); fixed in
+ *       first errata released on 2005/07.
+ *
+ * NOTE: we need to clean IV to zero at each invocation to make sure
+ *       we start with a fresh empty Initial Vector, so that the CBC
+ *       works ok.
+ *
+ * NOTE: blen is not aligned to a block size, we'll pad zeros, that's
+ *       what sg[4] is for. Maybe there is a smarter way to do this.
+ */
+static int wusb_ccm_mac(struct crypto_blkcipher *tfm_cbc,
+                       struct crypto_cipher *tfm_aes, void *mic,
+                       const struct aes_ccm_nonce *n,
+                       const struct aes_ccm_label *a, const void *b,
+                       size_t blen)
+{
+       int result = 0;
+       struct blkcipher_desc desc;
+       struct aes_ccm_b0 b0;
+       struct aes_ccm_b1 b1;
+       struct aes_ccm_a ax;
+       struct scatterlist sg[4], sg_dst;
+       void *iv, *dst_buf;
+       size_t ivsize, dst_size;
+       const u8 bzero[16] = { 0 };
+       size_t zero_padding;
+
+       d_fnstart(3, NULL, "(tfm_cbc %p, tfm_aes %p, mic %p, "
+                 "n %p, a %p, b %p, blen %zu)\n",
+                 tfm_cbc, tfm_aes, mic, n, a, b, blen);
+       /*
+        * These checks should be compile time optimized out
+        * ensure @a fills b1's mac_header and following fields
+        */
+       WARN_ON(sizeof(*a) != sizeof(b1) - sizeof(b1.la));
+       WARN_ON(sizeof(b0) != sizeof(struct aes_ccm_block));
+       WARN_ON(sizeof(b1) != sizeof(struct aes_ccm_block));
+       WARN_ON(sizeof(ax) != sizeof(struct aes_ccm_block));
+
+       result = -ENOMEM;
+       zero_padding = sizeof(struct aes_ccm_block)
+               - blen % sizeof(struct aes_ccm_block);
+       zero_padding = blen % sizeof(struct aes_ccm_block);
+       if (zero_padding)
+               zero_padding = sizeof(struct aes_ccm_block) - zero_padding;
+       dst_size = blen + sizeof(b0) + sizeof(b1) + zero_padding;
+       dst_buf = kzalloc(dst_size, GFP_KERNEL);
+       if (dst_buf == NULL) {
+               printk(KERN_ERR "E: can't alloc destination buffer\n");
+               goto error_dst_buf;
+       }
+
+       iv = crypto_blkcipher_crt(tfm_cbc)->iv;
+       ivsize = crypto_blkcipher_ivsize(tfm_cbc);
+       memset(iv, 0, ivsize);
+
+       /* Setup B0 */
+       b0.flags = 0x59;        /* Format B0 */
+       b0.ccm_nonce = *n;
+       b0.lm = cpu_to_be16(0); /* WUSB1.0[6.5] sez l(m) is 0 */
+
+       /* Setup B1
+        *
+        * The WUSB spec is anything but clear! WUSB1.0[6.5]
+        * says that to initialize B1 from A with 'l(a) = blen +
+        * 14'--after clarification, it means to use A's contents
+        * for MAC Header, EO, sec reserved and padding.
+        */
+       b1.la = cpu_to_be16(blen + 14);
+       memcpy(&b1.mac_header, a, sizeof(*a));
+
+       d_printf(4, NULL, "I: B0 (%zu bytes)\n", sizeof(b0));
+       d_dump(4, NULL, &b0, sizeof(b0));
+       d_printf(4, NULL, "I: B1 (%zu bytes)\n", sizeof(b1));
+       d_dump(4, NULL, &b1, sizeof(b1));
+       d_printf(4, NULL, "I: B (%zu bytes)\n", blen);
+       d_dump(4, NULL, b, blen);
+       d_printf(4, NULL, "I: B 0-padding (%zu bytes)\n", zero_padding);
+       d_printf(4, NULL, "D: IV before crypto (%zu)\n", ivsize);
+       d_dump(4, NULL, iv, ivsize);
+
+       sg_init_table(sg, ARRAY_SIZE(sg));
+       sg_set_buf(&sg[0], &b0, sizeof(b0));
+       sg_set_buf(&sg[1], &b1, sizeof(b1));
+       sg_set_buf(&sg[2], b, blen);
+       /* 0 if well behaved :) */
+       sg_set_buf(&sg[3], bzero, zero_padding);
+       sg_init_one(&sg_dst, dst_buf, dst_size);
+
+       desc.tfm = tfm_cbc;
+       desc.flags = 0;
+       result = crypto_blkcipher_encrypt(&desc, &sg_dst, sg, dst_size);
+       if (result < 0) {
+               printk(KERN_ERR "E: can't compute CBC-MAC tag (MIC): %d\n",
+                      result);
+               goto error_cbc_crypt;
+       }
+       d_printf(4, NULL, "D: MIC tag\n");
+       d_dump(4, NULL, iv, ivsize);
+
+       /* Now we crypt the MIC Tag (*iv) with Ax -- values per WUSB1.0[6.5]
+        * The procedure is to AES crypt the A0 block and XOR the MIC
+        * Tag agains it; we only do the first 8 bytes and place it
+        * directly in the destination buffer.
+        *
+        * POS Crypto API: size is assumed to be AES's block size.
+        * Thanks for documenting it -- tip taken from airo.c
+        */
+       ax.flags = 0x01;                /* as per WUSB 1.0 spec */
+       ax.ccm_nonce = *n;
+       ax.counter = 0;
+       crypto_cipher_encrypt_one(tfm_aes, (void *)&ax, (void *)&ax);
+       bytewise_xor(mic, &ax, iv, 8);
+       d_printf(4, NULL, "D: CTR[MIC]\n");
+       d_dump(4, NULL, &ax, 8);
+       d_printf(4, NULL, "D: CCM-MIC tag\n");
+       d_dump(4, NULL, mic, 8);
+       result = 8;
+error_cbc_crypt:
+       kfree(dst_buf);
+error_dst_buf:
+       d_fnend(3, NULL, "(tfm_cbc %p, tfm_aes %p, mic %p, "
+               "n %p, a %p, b %p, blen %zu)\n",
+               tfm_cbc, tfm_aes, mic, n, a, b, blen);
+       return result;
+}
+
+/*
+ * WUSB Pseudo Random Function (WUSB1.0[6.5])
+ *
+ * @b: buffer to the source data; cannot be a global or const local
+ *     (will confuse the scatterlists)
+ */
+ssize_t wusb_prf(void *out, size_t out_size,
+                const u8 key[16], const struct aes_ccm_nonce *_n,
+                const struct aes_ccm_label *a,
+                const void *b, size_t blen, size_t len)
+{
+       ssize_t result, bytes = 0, bitr;
+       struct aes_ccm_nonce n = *_n;
+       struct crypto_blkcipher *tfm_cbc;
+       struct crypto_cipher *tfm_aes;
+       u64 sfn = 0;
+       __le64 sfn_le;
+
+       d_fnstart(3, NULL, "(out %p, out_size %zu, key %p, _n %p, "
+                 "a %p, b %p, blen %zu, len %zu)\n", out, out_size,
+                 key, _n, a, b, blen, len);
+
+       tfm_cbc = crypto_alloc_blkcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC);
+       if (IS_ERR(tfm_cbc)) {
+               result = PTR_ERR(tfm_cbc);
+               printk(KERN_ERR "E: can't load CBC(AES): %d\n", (int)result);
+               goto error_alloc_cbc;
+       }
+       result = crypto_blkcipher_setkey(tfm_cbc, key, 16);
+       if (result < 0) {
+               printk(KERN_ERR "E: can't set CBC key: %d\n", (int)result);
+               goto error_setkey_cbc;
+       }
+
+       tfm_aes = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
+       if (IS_ERR(tfm_aes)) {
+               result = PTR_ERR(tfm_aes);
+               printk(KERN_ERR "E: can't load AES: %d\n", (int)result);
+               goto error_alloc_aes;
+       }
+       result = crypto_cipher_setkey(tfm_aes, key, 16);
+       if (result < 0) {
+               printk(KERN_ERR "E: can't set AES key: %d\n", (int)result);
+               goto error_setkey_aes;
+       }
+
+       for (bitr = 0; bitr < (len + 63) / 64; bitr++) {
+               sfn_le = cpu_to_le64(sfn++);
+               memcpy(&n.sfn, &sfn_le, sizeof(n.sfn)); /* n.sfn++... */
+               result = wusb_ccm_mac(tfm_cbc, tfm_aes, out + bytes,
+                                     &n, a, b, blen);
+               if (result < 0)
+                       goto error_ccm_mac;
+               bytes += result;
+       }
+       result = bytes;
+error_ccm_mac:
+error_setkey_aes:
+       crypto_free_cipher(tfm_aes);
+error_alloc_aes:
+error_setkey_cbc:
+       crypto_free_blkcipher(tfm_cbc);
+error_alloc_cbc:
+       d_fnend(3, NULL, "(out %p, out_size %zu, key %p, _n %p, "
+               "a %p, b %p, blen %zu, len %zu) = %d\n", out, out_size,
+               key, _n, a, b, blen, len, (int)bytes);
+       return result;
+}
+
+/* WUSB1.0[A.2] test vectors */
+static const u8 stv_hsmic_key[16] = {
+       0x4b, 0x79, 0xa3, 0xcf, 0xe5, 0x53, 0x23, 0x9d,
+       0xd7, 0xc1, 0x6d, 0x1c, 0x2d, 0xab, 0x6d, 0x3f
+};
+
+static const struct aes_ccm_nonce stv_hsmic_n = {
+       .sfn = { 0 },
+       .tkid = { 0x76, 0x98, 0x01,  },
+       .dest_addr = { .data = { 0xbe, 0x00 } },
+               .src_addr = { .data = { 0x76, 0x98 } },
+};
+
+/*
+ * Out-of-band MIC Generation verification code
+ *
+ */
+static int wusb_oob_mic_verify(void)
+{
+       int result;
+       u8 mic[8];
+       /* WUSB1.0[A.2] test vectors
+        *
+        * Need to keep it in the local stack as GCC 4.1.3something
+        * messes up and generates noise.
+        */
+       struct usb_handshake stv_hsmic_hs = {
+               .bMessageNumber = 2,
+               .bStatus        = 00,
+               .tTKID          = { 0x76, 0x98, 0x01 },
+               .bReserved      = 00,
+               .CDID           = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
+                                   0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
+                                   0x3c, 0x3d, 0x3e, 0x3f },
+               .nonce          = { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
+                                   0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+                                   0x2c, 0x2d, 0x2e, 0x2f },
+               .MIC            = { 0x75, 0x6a, 0x97, 0x51, 0x0c, 0x8c,
+                                   0x14, 0x7b } ,
+       };
+       size_t hs_size;
+
+       result = wusb_oob_mic(mic, stv_hsmic_key, &stv_hsmic_n, &stv_hsmic_hs);
+       if (result < 0)
+               printk(KERN_ERR "E: WUSB OOB MIC test: failed: %d\n", result);
+       else if (memcmp(stv_hsmic_hs.MIC, mic, sizeof(mic))) {
+               printk(KERN_ERR "E: OOB MIC test: "
+                      "mismatch between MIC result and WUSB1.0[A2]\n");
+               hs_size = sizeof(stv_hsmic_hs) - sizeof(stv_hsmic_hs.MIC);
+               printk(KERN_ERR "E: Handshake2 in: (%zu bytes)\n", hs_size);
+               dump_bytes(NULL, &stv_hsmic_hs, hs_size);
+               printk(KERN_ERR "E: CCM Nonce in: (%zu bytes)\n",
+                      sizeof(stv_hsmic_n));
+               dump_bytes(NULL, &stv_hsmic_n, sizeof(stv_hsmic_n));
+               printk(KERN_ERR "E: MIC out:\n");
+               dump_bytes(NULL, mic, sizeof(mic));
+               printk(KERN_ERR "E: MIC out (from WUSB1.0[A.2]):\n");
+               dump_bytes(NULL, stv_hsmic_hs.MIC, sizeof(stv_hsmic_hs.MIC));
+               result = -EINVAL;
+       } else
+               result = 0;
+       return result;
+}
+
+/*
+ * Test vectors for Key derivation
+ *
+ * These come from WUSB1.0[6.5.1], the vectors in WUSB1.0[A.1]
+ * (errata corrected in 2005/07).
+ */
+static const u8 stv_key_a1[16] __attribute__ ((__aligned__(4))) = {
+       0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87,
+       0x78, 0x69, 0x5a, 0x4b, 0x3c, 0x2d, 0x1e, 0x0f
+};
+
+static const struct aes_ccm_nonce stv_keydvt_n_a1 = {
+       .sfn = { 0 },
+       .tkid = { 0x76, 0x98, 0x01,  },
+       .dest_addr = { .data = { 0xbe, 0x00 } },
+       .src_addr = { .data = { 0x76, 0x98 } },
+};
+
+static const struct wusb_keydvt_out stv_keydvt_out_a1 = {
+       .kck = {
+               0x4b, 0x79, 0xa3, 0xcf, 0xe5, 0x53, 0x23, 0x9d,
+               0xd7, 0xc1, 0x6d, 0x1c, 0x2d, 0xab, 0x6d, 0x3f
+       },
+       .ptk = {
+               0xc8, 0x70, 0x62, 0x82, 0xb6, 0x7c, 0xe9, 0x06,
+               0x7b, 0xc5, 0x25, 0x69, 0xf2, 0x36, 0x61, 0x2d
+       }
+};
+
+/*
+ * Performa a test to make sure we match the vectors defined in
+ * WUSB1.0[A.1](Errata2006/12)
+ */
+static int wusb_key_derive_verify(void)
+{
+       int result = 0;
+       struct wusb_keydvt_out keydvt_out;
+       /* These come from WUSB1.0[A.1] + 2006/12 errata
+        * NOTE: can't make this const or global -- somehow it seems
+        *       the scatterlists for crypto get confused and we get
+        *       bad data. There is no doc on this... */
+       struct wusb_keydvt_in stv_keydvt_in_a1 = {
+               .hnonce = {
+                       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+                       0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
+               },
+               .dnonce = {
+                       0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+                       0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f
+               }
+       };
+
+       result = wusb_key_derive(&keydvt_out, stv_key_a1, &stv_keydvt_n_a1,
+                                &stv_keydvt_in_a1);
+       if (result < 0)
+               printk(KERN_ERR "E: WUSB key derivation test: "
+                      "derivation failed: %d\n", result);
+       if (memcmp(&stv_keydvt_out_a1, &keydvt_out, sizeof(keydvt_out))) {
+               printk(KERN_ERR "E: WUSB key derivation test: "
+                      "mismatch between key derivation result "
+                      "and WUSB1.0[A1] Errata 2006/12\n");
+               printk(KERN_ERR "E: keydvt in: key (%zu bytes)\n",
+                      sizeof(stv_key_a1));
+               dump_bytes(NULL, stv_key_a1, sizeof(stv_key_a1));
+               printk(KERN_ERR "E: keydvt in: nonce (%zu bytes)\n",
+                      sizeof(stv_keydvt_n_a1));
+               dump_bytes(NULL, &stv_keydvt_n_a1, sizeof(stv_keydvt_n_a1));
+               printk(KERN_ERR "E: keydvt in: hnonce & dnonce (%zu bytes)\n",
+                      sizeof(stv_keydvt_in_a1));
+               dump_bytes(NULL, &stv_keydvt_in_a1, sizeof(stv_keydvt_in_a1));
+               printk(KERN_ERR "E: keydvt out: KCK\n");
+               dump_bytes(NULL, &keydvt_out.kck, sizeof(keydvt_out.kck));
+               printk(KERN_ERR "E: keydvt out: PTK\n");
+               dump_bytes(NULL, &keydvt_out.ptk, sizeof(keydvt_out.ptk));
+               result = -EINVAL;
+       } else
+               result = 0;
+       return result;
+}
+
+/*
+ * Initialize crypto system
+ *
+ * FIXME: we do nothing now, other than verifying. Later on we'll
+ * cache the encryption stuff, so that's why we have a separate init.
+ */
+int wusb_crypto_init(void)
+{
+       int result;
+
+       result = wusb_key_derive_verify();
+       if (result < 0)
+               return result;
+       return wusb_oob_mic_verify();
+}
+
+void wusb_crypto_exit(void)
+{
+       /* FIXME: free cached crypto transforms */
+}
diff --git a/drivers/usb/wusbcore/dev-sysfs.c b/drivers/usb/wusbcore/dev-sysfs.c
new file mode 100644 (file)
index 0000000..7897a19
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * WUSB devices
+ * sysfs bindings
+ *
+ * Copyright (C) 2007 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.
+ *
+ *
+ * Get them out of the way...
+ */
+
+#include <linux/jiffies.h>
+#include <linux/ctype.h>
+#include <linux/workqueue.h>
+#include "wusbhc.h"
+
+#undef D_LOCAL
+#define D_LOCAL 4
+#include <linux/uwb/debug.h>
+
+static ssize_t wusb_disconnect_store(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t size)
+{
+       struct usb_device *usb_dev;
+       struct wusbhc *wusbhc;
+       unsigned command;
+       u8 port_idx;
+
+       if (sscanf(buf, "%u", &command) != 1)
+               return -EINVAL;
+       if (command == 0)
+               return size;
+       usb_dev = to_usb_device(dev);
+       wusbhc = wusbhc_get_by_usb_dev(usb_dev);
+       if (wusbhc == NULL)
+               return -ENODEV;
+
+       mutex_lock(&wusbhc->mutex);
+       port_idx = wusb_port_no_to_idx(usb_dev->portnum);
+       __wusbhc_dev_disable(wusbhc, port_idx);
+       mutex_unlock(&wusbhc->mutex);
+       wusbhc_put(wusbhc);
+       return size;
+}
+static DEVICE_ATTR(wusb_disconnect, 0200, NULL, wusb_disconnect_store);
+
+static ssize_t wusb_cdid_show(struct device *dev,
+                             struct device_attribute *attr, char *buf)
+{
+       ssize_t result;
+       struct wusb_dev *wusb_dev;
+
+       wusb_dev = wusb_dev_get_by_usb_dev(to_usb_device(dev));
+       if (wusb_dev == NULL)
+               return -ENODEV;
+       result = ckhdid_printf(buf, PAGE_SIZE, &wusb_dev->cdid);
+       strcat(buf, "\n");
+       wusb_dev_put(wusb_dev);
+       return result + 1;
+}
+static DEVICE_ATTR(wusb_cdid, 0444, wusb_cdid_show, NULL);
+
+static ssize_t wusb_ck_store(struct device *dev,
+                            struct device_attribute *attr,
+                            const char *buf, size_t size)
+{
+       int result;
+       struct usb_device *usb_dev;
+       struct wusbhc *wusbhc;
+       struct wusb_ckhdid ck;
+
+       result = sscanf(buf,
+                       "%02hhx %02hhx %02hhx %02hhx "
+                       "%02hhx %02hhx %02hhx %02hhx "
+                       "%02hhx %02hhx %02hhx %02hhx "
+                       "%02hhx %02hhx %02hhx %02hhx\n",
+                       &ck.data[0] , &ck.data[1],
+                       &ck.data[2] , &ck.data[3],
+                       &ck.data[4] , &ck.data[5],
+                       &ck.data[6] , &ck.data[7],
+                       &ck.data[8] , &ck.data[9],
+                       &ck.data[10], &ck.data[11],
+                       &ck.data[12], &ck.data[13],
+                       &ck.data[14], &ck.data[15]);
+       if (result != 16)
+               return -EINVAL;
+
+       usb_dev = to_usb_device(dev);
+       wusbhc = wusbhc_get_by_usb_dev(usb_dev);
+       if (wusbhc == NULL)
+               return -ENODEV;
+       result = wusb_dev_4way_handshake(wusbhc, usb_dev->wusb_dev, &ck);
+       memset(&ck, 0, sizeof(ck));
+       wusbhc_put(wusbhc);
+       return result < 0 ? result : size;
+}
+static DEVICE_ATTR(wusb_ck, 0200, NULL, wusb_ck_store);
+
+static struct attribute *wusb_dev_attrs[] = {
+               &dev_attr_wusb_disconnect.attr,
+               &dev_attr_wusb_cdid.attr,
+               &dev_attr_wusb_ck.attr,
+               NULL,
+};
+
+static struct attribute_group wusb_dev_attr_group = {
+       .name = NULL,   /* we want them in the same directory */
+       .attrs = wusb_dev_attrs,
+};
+
+int wusb_dev_sysfs_add(struct wusbhc *wusbhc, struct usb_device *usb_dev,
+                      struct wusb_dev *wusb_dev)
+{
+       int result = sysfs_create_group(&usb_dev->dev.kobj,
+                                       &wusb_dev_attr_group);
+       struct device *dev = &usb_dev->dev;
+       if (result < 0)
+               dev_err(dev, "Cannot register WUSB-dev attributes: %d\n",
+                       result);
+       return result;
+}
+
+void wusb_dev_sysfs_rm(struct wusb_dev *wusb_dev)
+{
+       struct usb_device *usb_dev = wusb_dev->usb_dev;
+       if (usb_dev)
+               sysfs_remove_group(&usb_dev->dev.kobj, &wusb_dev_attr_group);
+}
diff --git a/drivers/usb/wusbcore/devconnect.c b/drivers/usb/wusbcore/devconnect.c
new file mode 100644 (file)
index 0000000..f45d777
--- /dev/null
@@ -0,0 +1,1297 @@
+/*
+ * WUSB Wire Adapter: Control/Data Streaming Interface (WUSB[8])
+ * Device Connect handling
+ *
+ * Copyright (C) 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: this file needs to be broken up, it's grown too big
+ *
+ *
+ * WUSB1.0[7.1, 7.5.1, ]
+ *
+ * WUSB device connection is kind of messy. Some background:
+ *
+ *     When a device wants to connect it scans the UWB radio channels
+ *     looking for a WUSB Channel; a WUSB channel is defined by MMCs
+ *     (Micro Managed Commands or something like that) [see
+ *     Design-overview for more on this] .
+ *
+ * So, device scans the radio, finds MMCs and thus a host and checks
+ * when the next DNTS is. It sends a Device Notification Connect
+ * (DN_Connect); the host picks it up (through nep.c and notif.c, ends
+ * up in wusb_devconnect_ack(), which creates a wusb_dev structure in
+ * wusbhc->port[port_number].wusb_dev), assigns an unauth address
+ * to the device (this means from 0x80 to 0xfe) and sends, in the MMC
+ * a Connect Ack Information Element (ConnAck IE).
+ *
+ * So now the device now has a WUSB address. From now on, we use
+ * that to talk to it in the RPipes.
+ *
+ * ASSUMPTIONS:
+ *
+ *  - We use the the as device address the port number where it is
+ *    connected (port 0 doesn't exist). For unauth, it is 128 + that.
+ *
+ * ROADMAP:
+ *
+ *   This file contains the logic for doing that--entry points:
+ *
+ *   wusb_devconnect_ack()      Ack a device until _acked() called.
+ *                              Called by notif.c:wusb_handle_dn_connect()
+ *                              when a DN_Connect is received.
+ *
+ *   wusbhc_devconnect_auth()   Called by rh.c:wusbhc_rh_port_reset() when
+ *                              doing the device connect sequence.
+ *
+ *     wusb_devconnect_acked()  Ack done, release resources.
+ *
+ *   wusb_handle_dn_alive()     Called by notif.c:wusb_handle_dn()
+ *                              for processing a DN_Alive pong from a device.
+ *
+ *   wusb_handle_dn_disconnect()Called by notif.c:wusb_handle_dn() to
+ *                              process a disconenct request from a
+ *                              device.
+ *
+ *   wusb_dev_reset()           Called by rh.c:wusbhc_rh_port_reset() when
+ *                              resetting a device.
+ *
+ *   __wusb_dev_disable()       Called by rh.c:wusbhc_rh_clear_port_feat() when
+ *                              disabling a port.
+ *
+ *   wusb_devconnect_create()   Called when creating the host by
+ *                              lc.c:wusbhc_create().
+ *
+ *   wusb_devconnect_destroy()  Cleanup called removing the host. Called
+ *                              by lc.c:wusbhc_destroy().
+ *
+ *   Each Wireless USB host maintains a list of DN_Connect requests
+ *   (actually we maintain a list of pending Connect Acks, the
+ *   wusbhc->ca_list).
+ *
+ * LIFE CYCLE OF port->wusb_dev
+ *
+ *   Before the @wusbhc structure put()s the reference it owns for
+ *   port->wusb_dev [and clean the wusb_dev pointer], it needs to
+ *   lock @wusbhc->mutex.
+ */
+
+#include <linux/jiffies.h>
+#include <linux/ctype.h>
+#include <linux/workqueue.h>
+#include "wusbhc.h"
+
+#undef D_LOCAL
+#define D_LOCAL 1
+#include <linux/uwb/debug.h>
+
+static void wusbhc_devconnect_acked_work(struct work_struct *work);
+
+static void wusb_dev_free(struct wusb_dev *wusb_dev)
+{
+       if (wusb_dev) {
+               kfree(wusb_dev->set_gtk_req);
+               usb_free_urb(wusb_dev->set_gtk_urb);
+               kfree(wusb_dev);
+       }
+}
+
+static struct wusb_dev *wusb_dev_alloc(struct wusbhc *wusbhc)
+{
+       struct wusb_dev *wusb_dev;
+       struct urb *urb;
+       struct usb_ctrlrequest *req;
+
+       wusb_dev = kzalloc(sizeof(*wusb_dev), GFP_KERNEL);
+       if (wusb_dev == NULL)
+               goto err;
+
+       wusb_dev->wusbhc = wusbhc;
+
+       INIT_WORK(&wusb_dev->devconnect_acked_work, wusbhc_devconnect_acked_work);
+
+       urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (urb == NULL)
+               goto err;
+
+       req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
+       if (req == NULL)
+               goto err;
+
+       req->bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
+       req->bRequest = USB_REQ_SET_DESCRIPTOR;
+       req->wValue = cpu_to_le16(USB_DT_KEY << 8 | wusbhc->gtk_index);
+       req->wIndex = 0;
+       req->wLength = cpu_to_le16(wusbhc->gtk.descr.bLength);
+
+       wusb_dev->set_gtk_urb = urb;
+       wusb_dev->set_gtk_req = req;
+
+       return wusb_dev;
+err:
+       wusb_dev_free(wusb_dev);
+       return NULL;
+}
+
+
+/*
+ * Using the Connect-Ack list, fill out the @wusbhc Connect-Ack WUSB IE
+ * properly so that it can be added to the MMC.
+ *
+ * We just get the @wusbhc->ca_list and fill out the first four ones or
+ * less (per-spec WUSB1.0[7.5, before T7-38). If the ConnectAck WUSB
+ * IE is not allocated, we alloc it.
+ *
+ * @wusbhc->mutex must be taken
+ */
+static void wusbhc_fill_cack_ie(struct wusbhc *wusbhc)
+{
+       unsigned cnt;
+       struct wusb_dev *dev_itr;
+       struct wuie_connect_ack *cack_ie;
+
+       cack_ie = &wusbhc->cack_ie;
+       cnt = 0;
+       list_for_each_entry(dev_itr, &wusbhc->cack_list, cack_node) {
+               cack_ie->blk[cnt].CDID = dev_itr->cdid;
+               cack_ie->blk[cnt].bDeviceAddress = dev_itr->addr;
+               if (++cnt >= WUIE_ELT_MAX)
+                       break;
+       }
+       cack_ie->hdr.bLength = sizeof(cack_ie->hdr)
+               + cnt * sizeof(cack_ie->blk[0]);
+}
+
+/*
+ * Register a new device that wants to connect
+ *
+ * A new device wants to connect, so we add it to the Connect-Ack
+ * list. We give it an address in the unauthorized range (bit 8 set);
+ * user space will have to drive authorization further on.
+ *
+ * @dev_addr: address to use for the device (which is also the port
+ *            number).
+ *
+ * @wusbhc->mutex must be taken
+ */
+static struct wusb_dev *wusbhc_cack_add(struct wusbhc *wusbhc,
+                                       struct wusb_dn_connect *dnc,
+                                       const char *pr_cdid, u8 port_idx)
+{
+       struct device *dev = wusbhc->dev;
+       struct wusb_dev *wusb_dev;
+       int new_connection = wusb_dn_connect_new_connection(dnc);
+       u8 dev_addr;
+       int result;
+
+       /* Is it registered already? */
+       list_for_each_entry(wusb_dev, &wusbhc->cack_list, cack_node)
+               if (!memcmp(&wusb_dev->cdid, &dnc->CDID,
+                           sizeof(wusb_dev->cdid)))
+                       return wusb_dev;
+       /* We don't have it, create an entry, register it */
+       wusb_dev = wusb_dev_alloc(wusbhc);
+       if (wusb_dev == NULL)
+               return NULL;
+       wusb_dev_init(wusb_dev);
+       wusb_dev->cdid = dnc->CDID;
+       wusb_dev->port_idx = port_idx;
+
+       /*
+        * Devices are always available within the cluster reservation
+        * and since the hardware will take the intersection of the
+        * per-device availability and the cluster reservation, the
+        * per-device availability can simply be set to always
+        * available.
+        */
+       bitmap_fill(wusb_dev->availability.bm, UWB_NUM_MAS);
+
+       /* FIXME: handle reconnects instead of assuming connects are
+          always new. */
+       if (1 && new_connection == 0)
+               new_connection = 1;
+       if (new_connection) {
+               dev_addr = (port_idx + 2) | WUSB_DEV_ADDR_UNAUTH;
+
+               dev_info(dev, "Connecting new WUSB device to address %u, "
+                       "port %u\n", dev_addr, port_idx);
+
+               result = wusb_set_dev_addr(wusbhc, wusb_dev, dev_addr);
+               if (result < 0)
+                       return NULL;
+       }
+       wusb_dev->entry_ts = jiffies;
+       list_add_tail(&wusb_dev->cack_node, &wusbhc->cack_list);
+       wusbhc->cack_count++;
+       wusbhc_fill_cack_ie(wusbhc);
+       return wusb_dev;
+}
+
+/*
+ * Remove a Connect-Ack context entry from the HCs view
+ *
+ * @wusbhc->mutex must be taken
+ */
+static void wusbhc_cack_rm(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev)
+{
+       struct device *dev = wusbhc->dev;
+       d_fnstart(3, dev, "(wusbhc %p wusb_dev %p)\n", wusbhc, wusb_dev);
+       list_del_init(&wusb_dev->cack_node);
+       wusbhc->cack_count--;
+       wusbhc_fill_cack_ie(wusbhc);
+       d_fnend(3, dev, "(wusbhc %p wusb_dev %p) = void\n", wusbhc, wusb_dev);
+}
+
+/*
+ * @wusbhc->mutex must be taken */
+static
+void wusbhc_devconnect_acked(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev)
+{
+       struct device *dev = wusbhc->dev;
+       d_fnstart(3, dev, "(wusbhc %p wusb_dev %p)\n", wusbhc, wusb_dev);
+       wusbhc_cack_rm(wusbhc, wusb_dev);
+       if (wusbhc->cack_count)
+               wusbhc_mmcie_set(wusbhc, 0, 0, &wusbhc->cack_ie.hdr);
+       else
+               wusbhc_mmcie_rm(wusbhc, &wusbhc->cack_ie.hdr);
+       d_fnend(3, dev, "(wusbhc %p wusb_dev %p) = void\n", wusbhc, wusb_dev);
+}
+
+static void wusbhc_devconnect_acked_work(struct work_struct *work)
+{
+       struct wusb_dev *wusb_dev = container_of(work, struct wusb_dev,
+                                                devconnect_acked_work);
+       struct wusbhc *wusbhc = wusb_dev->wusbhc;
+
+       mutex_lock(&wusbhc->mutex);
+       wusbhc_devconnect_acked(wusbhc, wusb_dev);
+       mutex_unlock(&wusbhc->mutex);
+}
+
+/*
+ * Ack a device for connection
+ *
+ * FIXME: docs
+ *
+ * @pr_cdid:   Printable CDID...hex Use @dnc->cdid for the real deal.
+ *
+ * So we get the connect ack IE (may have been allocated already),
+ * find an empty connect block, an empty virtual port, create an
+ * address with it (see below), make it an unauth addr [bit 7 set] and
+ * set the MMC.
+ *
+ * Addresses: because WUSB hosts have no downstream hubs, we can do a
+ *            1:1 mapping between 'port number' and device
+ *            address. This simplifies many things, as during this
+ *            initial connect phase the USB stack has no knoledge of
+ *            the device and hasn't assigned an address yet--we know
+ *            USB's choose_address() will use the same euristics we
+ *            use here, so we can assume which address will be assigned.
+ *
+ *            USB stack always assigns address 1 to the root hub, so
+ *            to the port number we add 2 (thus virtual port #0 is
+ *            addr #2).
+ *
+ * @wusbhc shall be referenced
+ */
+static
+void wusbhc_devconnect_ack(struct wusbhc *wusbhc, struct wusb_dn_connect *dnc,
+                          const char *pr_cdid)
+{
+       int result;
+       struct device *dev = wusbhc->dev;
+       struct wusb_dev *wusb_dev;
+       struct wusb_port *port;
+       unsigned idx, devnum;
+
+       d_fnstart(3, dev, "(%p, %p, %s)\n", wusbhc, dnc, pr_cdid);
+       mutex_lock(&wusbhc->mutex);
+
+       /* Check we are not handling it already */
+       for (idx = 0; idx < wusbhc->ports_max; idx++) {
+               port = wusb_port_by_idx(wusbhc, idx);
+               if (port->wusb_dev
+                   && memcmp(&dnc->CDID, &port->wusb_dev->cdid, sizeof(dnc->CDID)) == 0)
+                       goto error_unlock;
+       }
+       /* Look up those fake ports we have for a free one */
+       for (idx = 0; idx < wusbhc->ports_max; idx++) {
+               port = wusb_port_by_idx(wusbhc, idx);
+               if ((port->status & USB_PORT_STAT_POWER)
+                   && !(port->status & USB_PORT_STAT_CONNECTION))
+                       break;
+       }
+       if (idx >= wusbhc->ports_max) {
+               dev_err(dev, "Host controller can't connect more devices "
+                       "(%u already connected); device %s rejected\n",
+                       wusbhc->ports_max, pr_cdid);
+               /* NOTE: we could send a WUIE_Disconnect here, but we haven't
+                *       event acked, so the device will eventually timeout the
+                *       connection, right? */
+               goto error_unlock;
+       }
+
+       devnum = idx + 2;
+
+       /* Make sure we are using no crypto on that "virtual port" */
+       wusbhc->set_ptk(wusbhc, idx, 0, NULL, 0);
+
+       /* Grab a filled in Connect-Ack context, fill out the
+        * Connect-Ack Wireless USB IE, set the MMC */
+       wusb_dev = wusbhc_cack_add(wusbhc, dnc, pr_cdid, idx);
+       if (wusb_dev == NULL)
+               goto error_unlock;
+       result = wusbhc_mmcie_set(wusbhc, 0, 0, &wusbhc->cack_ie.hdr);
+       if (result < 0)
+               goto error_unlock;
+       /* Give the device at least 2ms (WUSB1.0[7.5.1p3]), let's do
+        * three for a good measure */
+       msleep(3);
+       port->wusb_dev = wusb_dev;
+       port->status |= USB_PORT_STAT_CONNECTION;
+       port->change |= USB_PORT_STAT_C_CONNECTION;
+       port->reset_count = 0;
+       /* Now the port status changed to connected; khubd will
+        * pick the change up and try to reset the port to bring it to
+        * the enabled state--so this process returns up to the stack
+        * and it calls back into wusbhc_rh_port_reset() who will call
+        * devconnect_auth().
+        */
+error_unlock:
+       mutex_unlock(&wusbhc->mutex);
+       d_fnend(3, dev, "(%p, %p, %s) = void\n", wusbhc, dnc, pr_cdid);
+       return;
+
+}
+
+/*
+ * Disconnect a Wireless USB device from its fake port
+ *
+ * Marks the port as disconnected so that khubd can pick up the change
+ * and drops our knowledge about the device.
+ *
+ * Assumes there is a device connected
+ *
+ * @port_index: zero based port number
+ *
+ * NOTE: @wusbhc->mutex is locked
+ *
+ * WARNING: From here it is not very safe to access anything hanging off
+ *         wusb_dev
+ */
+static void __wusbhc_dev_disconnect(struct wusbhc *wusbhc,
+                                   struct wusb_port *port)
+{
+       struct device *dev = wusbhc->dev;
+       struct wusb_dev *wusb_dev = port->wusb_dev;
+
+       d_fnstart(3, dev, "(wusbhc %p, port %p)\n", wusbhc, port);
+       port->status &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE
+                         | USB_PORT_STAT_SUSPEND | USB_PORT_STAT_RESET
+                         | USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED);
+       port->change |= USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE;
+       if (wusb_dev) {
+               if (!list_empty(&wusb_dev->cack_node))
+                       list_del_init(&wusb_dev->cack_node);
+               /* For the one in cack_add() */
+               wusb_dev_put(wusb_dev);
+       }
+       port->wusb_dev = NULL;
+       /* don't reset the reset_count to zero or wusbhc_rh_port_reset will get
+        * confused! We only reset to zero when we connect a new device.
+        */
+
+       /* After a device disconnects, change the GTK (see [WUSB]
+        * section 6.2.11.2). */
+       wusbhc_gtk_rekey(wusbhc);
+
+       d_fnend(3, dev, "(wusbhc %p, port %p) = void\n", wusbhc, port);
+       /* The Wireless USB part has forgotten about the device already; now
+        * khubd's timer will pick up the disconnection and remove the USB
+        * device from the system
+        */
+}
+
+/*
+ * Authenticate a device into the WUSB Cluster
+ *
+ * Called from the Root Hub code (rh.c:wusbhc_rh_port_reset()) when
+ * asking for a reset on a port that is not enabled (ie: first connect
+ * on the port).
+ *
+ * Performs the 4way handshake to allow the device to comunicate w/ the
+ * WUSB Cluster securely; once done, issue a request to the device for
+ * it to change to address 0.
+ *
+ * This mimics the reset step of Wired USB that once resetting a
+ * device, leaves the port in enabled state and the dev with the
+ * default address (0).
+ *
+ * WUSB1.0[7.1.2]
+ *
+ * @port_idx: port where the change happened--This is the index into
+ *            the wusbhc port array, not the USB port number.
+ */
+int wusbhc_devconnect_auth(struct wusbhc *wusbhc, u8 port_idx)
+{
+       struct device *dev = wusbhc->dev;
+       struct wusb_port *port = wusb_port_by_idx(wusbhc, port_idx);
+
+       d_fnstart(3, dev, "(%p, %u)\n", wusbhc, port_idx);
+       port->status &= ~USB_PORT_STAT_RESET;
+       port->status |= USB_PORT_STAT_ENABLE;
+       port->change |= USB_PORT_STAT_C_RESET | USB_PORT_STAT_C_ENABLE;
+       d_fnend(3, dev, "(%p, %u) = 0\n", wusbhc, port_idx);
+       return 0;
+}
+
+/*
+ * Refresh the list of keep alives to emit in the MMC
+ *
+ * Some devices don't respond to keep alives unless they've been
+ * authenticated, so skip unauthenticated devices.
+ *
+ * We only publish the first four devices that have a coming timeout
+ * condition. Then when we are done processing those, we go for the
+ * next ones. We ignore the ones that have timed out already (they'll
+ * be purged).
+ *
+ * This might cause the first devices to timeout the last devices in
+ * the port array...FIXME: come up with a better algorithm?
+ *
+ * Note we can't do much about MMC's ops errors; we hope next refresh
+ * will kind of handle it.
+ *
+ * NOTE: @wusbhc->mutex is locked
+ */
+static void __wusbhc_keep_alive(struct wusbhc *wusbhc)
+{
+       struct device *dev = wusbhc->dev;
+       unsigned cnt;
+       struct wusb_dev *wusb_dev;
+       struct wusb_port *wusb_port;
+       struct wuie_keep_alive *ie = &wusbhc->keep_alive_ie;
+       unsigned keep_alives, old_keep_alives;
+
+       old_keep_alives = ie->hdr.bLength - sizeof(ie->hdr);
+       keep_alives = 0;
+       for (cnt = 0;
+            keep_alives <= WUIE_ELT_MAX && cnt < wusbhc->ports_max;
+            cnt++) {
+               unsigned tt = msecs_to_jiffies(wusbhc->trust_timeout);
+
+               wusb_port = wusb_port_by_idx(wusbhc, cnt);
+               wusb_dev = wusb_port->wusb_dev;
+
+               if (wusb_dev == NULL)
+                       continue;
+               if (wusb_dev->usb_dev == NULL || !wusb_dev->usb_dev->authenticated)
+                       continue;
+
+               if (time_after(jiffies, wusb_dev->entry_ts + tt)) {
+                       dev_err(dev, "KEEPALIVE: device %u timed out\n",
+                               wusb_dev->addr);
+                       __wusbhc_dev_disconnect(wusbhc, wusb_port);
+               } else if (time_after(jiffies, wusb_dev->entry_ts + tt/2)) {
+                       /* Approaching timeout cut out, need to refresh */
+                       ie->bDeviceAddress[keep_alives++] = wusb_dev->addr;
+               }
+       }
+       if (keep_alives & 0x1)  /* pad to even number ([WUSB] section 7.5.9) */
+               ie->bDeviceAddress[keep_alives++] = 0x7f;
+       ie->hdr.bLength = sizeof(ie->hdr) +
+               keep_alives*sizeof(ie->bDeviceAddress[0]);
+       if (keep_alives > 0)
+               wusbhc_mmcie_set(wusbhc, 10, 5, &ie->hdr);
+       else if (old_keep_alives != 0)
+               wusbhc_mmcie_rm(wusbhc, &ie->hdr);
+}
+
+/*
+ * Do a run through all devices checking for timeouts
+ */
+static void wusbhc_keep_alive_run(struct work_struct *ws)
+{
+       struct delayed_work *dw =
+               container_of(ws, struct delayed_work, work);
+       struct wusbhc *wusbhc =
+               container_of(dw, struct wusbhc, keep_alive_timer);
+
+       d_fnstart(5, wusbhc->dev, "(wusbhc %p)\n", wusbhc);
+       if (wusbhc->active) {
+               mutex_lock(&wusbhc->mutex);
+               __wusbhc_keep_alive(wusbhc);
+               mutex_unlock(&wusbhc->mutex);
+               queue_delayed_work(wusbd, &wusbhc->keep_alive_timer,
+                                  (wusbhc->trust_timeout * CONFIG_HZ)/1000/2);
+       }
+       d_fnend(5, wusbhc->dev, "(wusbhc %p) = void\n", wusbhc);
+       return;
+}
+
+/*
+ * Find the wusb_dev from its device address.
+ *
+ * The device can be found directly from the address (see
+ * wusb_cack_add() for where the device address is set to port_idx
+ * +2), except when the address is zero.
+ */
+static struct wusb_dev *wusbhc_find_dev_by_addr(struct wusbhc *wusbhc, u8 addr)
+{
+       int p;
+
+       if (addr == 0xff) /* unconnected */
+               return NULL;
+
+       if (addr > 0) {
+               int port = (addr & ~0x80) - 2;
+               if (port < 0 || port >= wusbhc->ports_max)
+                       return NULL;
+               return wusb_port_by_idx(wusbhc, port)->wusb_dev;
+       }
+
+       /* Look for the device with address 0. */
+       for (p = 0; p < wusbhc->ports_max; p++) {
+               struct wusb_dev *wusb_dev = wusb_port_by_idx(wusbhc, p)->wusb_dev;
+               if (wusb_dev && wusb_dev->addr == addr)
+                       return wusb_dev;
+       }
+       return NULL;
+}
+
+/*
+ * Handle a DN_Alive notification (WUSB1.0[7.6.1])
+ *
+ * This just updates the device activity timestamp and then refreshes
+ * the keep alive IE.
+ *
+ * @wusbhc shall be referenced and unlocked
+ */
+static void wusbhc_handle_dn_alive(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev)
+{
+       struct device *dev = wusbhc->dev;
+
+       d_printf(2, dev, "DN ALIVE: device 0x%02x pong\n", wusb_dev->addr);
+
+       mutex_lock(&wusbhc->mutex);
+       wusb_dev->entry_ts = jiffies;
+       __wusbhc_keep_alive(wusbhc);
+       mutex_unlock(&wusbhc->mutex);
+}
+
+/*
+ * Handle a DN_Connect notification (WUSB1.0[7.6.1])
+ *
+ * @wusbhc
+ * @pkt_hdr
+ * @size:    Size of the buffer where the notification resides; if the
+ *           notification data suggests there should be more data than
+ *           available, an error will be signaled and the whole buffer
+ *           consumed.
+ *
+ * @wusbhc->mutex shall be held
+ */
+static void wusbhc_handle_dn_connect(struct wusbhc *wusbhc,
+                                    struct wusb_dn_hdr *dn_hdr,
+                                    size_t size)
+{
+       struct device *dev = wusbhc->dev;
+       struct wusb_dn_connect *dnc;
+       char pr_cdid[WUSB_CKHDID_STRSIZE];
+       static const char *beacon_behaviour[] = {
+               "reserved",
+               "self-beacon",
+               "directed-beacon",
+               "no-beacon"
+       };
+
+       d_fnstart(3, dev, "(%p, %p, %zu)\n", wusbhc, dn_hdr, size);
+       if (size < sizeof(*dnc)) {
+               dev_err(dev, "DN CONNECT: short notification (%zu < %zu)\n",
+                       size, sizeof(*dnc));
+               goto out;
+       }
+
+       dnc = container_of(dn_hdr, struct wusb_dn_connect, hdr);
+       ckhdid_printf(pr_cdid, sizeof(pr_cdid), &dnc->CDID);
+       dev_info(dev, "DN CONNECT: device %s @ %x (%s) wants to %s\n",
+                pr_cdid,
+                wusb_dn_connect_prev_dev_addr(dnc),
+                beacon_behaviour[wusb_dn_connect_beacon_behavior(dnc)],
+                wusb_dn_connect_new_connection(dnc) ? "connect" : "reconnect");
+       /* ACK the connect */
+       wusbhc_devconnect_ack(wusbhc, dnc, pr_cdid);
+out:
+       d_fnend(3, dev, "(%p, %p, %zu) = void\n",
+               wusbhc, dn_hdr, size);
+       return;
+}
+
+/*
+ * Handle a DN_Disconnect notification (WUSB1.0[7.6.1])
+ *
+ * Device is going down -- do the disconnect.
+ *
+ * @wusbhc shall be referenced and unlocked
+ */
+static void wusbhc_handle_dn_disconnect(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev)
+{
+       struct device *dev = wusbhc->dev;
+
+       dev_info(dev, "DN DISCONNECT: device 0x%02x going down\n", wusb_dev->addr);
+
+       mutex_lock(&wusbhc->mutex);
+       __wusbhc_dev_disconnect(wusbhc, wusb_port_by_idx(wusbhc, wusb_dev->port_idx));
+       mutex_unlock(&wusbhc->mutex);
+}
+
+/*
+ * Reset a WUSB device on a HWA
+ *
+ * @wusbhc
+ * @port_idx   Index of the port where the device is
+ *
+ * In Wireless USB, a reset is more or less equivalent to a full
+ * disconnect; so we just do a full disconnect and send the device a
+ * Device Reset IE (WUSB1.0[7.5.11]) giving it a few millisecs (6 MMCs).
+ *
+ * @wusbhc should be refcounted and unlocked
+ */
+int wusbhc_dev_reset(struct wusbhc *wusbhc, u8 port_idx)
+{
+       int result;
+       struct device *dev = wusbhc->dev;
+       struct wusb_dev *wusb_dev;
+       struct wuie_reset *ie;
+
+       d_fnstart(3, dev, "(%p, %u)\n", wusbhc, port_idx);
+       mutex_lock(&wusbhc->mutex);
+       result = 0;
+       wusb_dev = wusb_port_by_idx(wusbhc, port_idx)->wusb_dev;
+       if (wusb_dev == NULL) {
+               /* reset no device? ignore */
+               dev_dbg(dev, "RESET: no device at port %u, ignoring\n",
+                       port_idx);
+               goto error_unlock;
+       }
+       result = -ENOMEM;
+       ie = kzalloc(sizeof(*ie), GFP_KERNEL);
+       if (ie == NULL)
+               goto error_unlock;
+       ie->hdr.bLength = sizeof(ie->hdr) + sizeof(ie->CDID);
+       ie->hdr.bIEIdentifier = WUIE_ID_RESET_DEVICE;
+       ie->CDID = wusb_dev->cdid;
+       result = wusbhc_mmcie_set(wusbhc, 0xff, 6, &ie->hdr);
+       if (result < 0) {
+               dev_err(dev, "RESET: cant's set MMC: %d\n", result);
+               goto error_kfree;
+       }
+       __wusbhc_dev_disconnect(wusbhc, wusb_port_by_idx(wusbhc, port_idx));
+
+       /* 120ms, hopefully 6 MMCs (FIXME) */
+       msleep(120);
+       wusbhc_mmcie_rm(wusbhc, &ie->hdr);
+error_kfree:
+       kfree(ie);
+error_unlock:
+       mutex_unlock(&wusbhc->mutex);
+       d_fnend(3, dev, "(%p, %u) = %d\n", wusbhc, port_idx, result);
+       return result;
+}
+
+/*
+ * Handle a Device Notification coming a host
+ *
+ * The Device Notification comes from a host (HWA, DWA or WHCI)
+ * wrapped in a set of headers. Somebody else has peeled off those
+ * headers for us and we just get one Device Notifications.
+ *
+ * Invalid DNs (e.g., too short) are discarded.
+ *
+ * @wusbhc shall be referenced
+ *
+ * FIXMES:
+ *  - implement priorities as in WUSB1.0[Table 7-55]?
+ */
+void wusbhc_handle_dn(struct wusbhc *wusbhc, u8 srcaddr,
+                     struct wusb_dn_hdr *dn_hdr, size_t size)
+{
+       struct device *dev = wusbhc->dev;
+       struct wusb_dev *wusb_dev;
+
+       d_fnstart(3, dev, "(%p, %p)\n", wusbhc, dn_hdr);
+
+       if (size < sizeof(struct wusb_dn_hdr)) {
+               dev_err(dev, "DN data shorter than DN header (%d < %d)\n",
+                       (int)size, (int)sizeof(struct wusb_dn_hdr));
+               goto out;
+       }
+
+       wusb_dev = wusbhc_find_dev_by_addr(wusbhc, srcaddr);
+       if (wusb_dev == NULL && dn_hdr->bType != WUSB_DN_CONNECT) {
+               dev_dbg(dev, "ignoring DN %d from unconnected device %02x\n",
+                       dn_hdr->bType, srcaddr);
+               goto out;
+       }
+
+       switch (dn_hdr->bType) {
+       case WUSB_DN_CONNECT:
+               wusbhc_handle_dn_connect(wusbhc, dn_hdr, size);
+               break;
+       case WUSB_DN_ALIVE:
+               wusbhc_handle_dn_alive(wusbhc, wusb_dev);
+               break;
+       case WUSB_DN_DISCONNECT:
+               wusbhc_handle_dn_disconnect(wusbhc, wusb_dev);
+               break;
+       case WUSB_DN_MASAVAILCHANGED:
+       case WUSB_DN_RWAKE:
+       case WUSB_DN_SLEEP:
+               /* FIXME: handle these DNs. */
+               break;
+       case WUSB_DN_EPRDY:
+               /* The hardware handles these. */
+               break;
+       default:
+               dev_warn(dev, "unknown DN %u (%d octets) from %u\n",
+                        dn_hdr->bType, (int)size, srcaddr);
+       }
+out:
+       d_fnend(3, dev, "(%p, %p) = void\n", wusbhc, dn_hdr);
+       return;
+}
+EXPORT_SYMBOL_GPL(wusbhc_handle_dn);
+
+/*
+ * Disconnect a WUSB device from a the cluster
+ *
+ * @wusbhc
+ * @port     Fake port where the device is (wusbhc index, not USB port number).
+ *
+ * In Wireless USB, a disconnect is basically telling the device he is
+ * being disconnected and forgetting about him.
+ *
+ * We send the device a Device Disconnect IE (WUSB1.0[7.5.11]) for 100
+ * ms and then keep going.
+ *
+ * We don't do much in case of error; we always pretend we disabled
+ * the port and disconnected the device. If physically the request
+ * didn't get there (many things can fail in the way there), the stack
+ * will reject the device's communication attempts.
+ *
+ * @wusbhc should be refcounted and locked
+ */
+void __wusbhc_dev_disable(struct wusbhc *wusbhc, u8 port_idx)
+{
+       int result;
+       struct device *dev = wusbhc->dev;
+       struct wusb_dev *wusb_dev;
+       struct wuie_disconnect *ie;
+
+       d_fnstart(3, dev, "(%p, %u)\n", wusbhc, port_idx);
+       result = 0;
+       wusb_dev = wusb_port_by_idx(wusbhc, port_idx)->wusb_dev;
+       if (wusb_dev == NULL) {
+               /* reset no device? ignore */
+               dev_dbg(dev, "DISCONNECT: no device at port %u, ignoring\n",
+                       port_idx);
+               goto error;
+       }
+       __wusbhc_dev_disconnect(wusbhc, wusb_port_by_idx(wusbhc, port_idx));
+
+       result = -ENOMEM;
+       ie = kzalloc(sizeof(*ie), GFP_KERNEL);
+       if (ie == NULL)
+               goto error;
+       ie->hdr.bLength = sizeof(*ie);
+       ie->hdr.bIEIdentifier = WUIE_ID_DEVICE_DISCONNECT;
+       ie->bDeviceAddress = wusb_dev->addr;
+       result = wusbhc_mmcie_set(wusbhc, 0, 0, &ie->hdr);
+       if (result < 0) {
+               dev_err(dev, "DISCONNECT: can't set MMC: %d\n", result);
+               goto error_kfree;
+       }
+
+       /* 120ms, hopefully 6 MMCs */
+       msleep(100);
+       wusbhc_mmcie_rm(wusbhc, &ie->hdr);
+error_kfree:
+       kfree(ie);
+error:
+       d_fnend(3, dev, "(%p, %u) = %d\n", wusbhc, port_idx, result);
+       return;
+}
+
+static void wusb_cap_descr_printf(const unsigned level, struct device *dev,
+                                 const struct usb_wireless_cap_descriptor *wcd)
+{
+       d_printf(level, dev,
+                "WUSB Capability Descriptor\n"
+                "  bDevCapabilityType          0x%02x\n"
+                "  bmAttributes                0x%02x\n"
+                "  wPhyRates                   0x%04x\n"
+                "  bmTFITXPowerInfo            0x%02x\n"
+                "  bmFFITXPowerInfo            0x%02x\n"
+                "  bmBandGroup                 0x%04x\n"
+                "  bReserved                   0x%02x\n",
+                wcd->bDevCapabilityType,
+                wcd->bmAttributes,
+                le16_to_cpu(wcd->wPHYRates),
+                wcd->bmTFITXPowerInfo,
+                wcd->bmFFITXPowerInfo,
+                wcd->bmBandGroup,
+                wcd->bReserved);
+}
+
+/*
+ * Walk over the BOS descriptor, verify and grok it
+ *
+ * @usb_dev: referenced
+ * @wusb_dev: referenced and unlocked
+ *
+ * The BOS descriptor is defined at WUSB1.0[7.4.1], and it defines a
+ * "flexible" way to wrap all kinds of descriptors inside an standard
+ * descriptor (wonder why they didn't use normal descriptors,
+ * btw). Not like they lack code.
+ *
+ * At the end we go to look for the WUSB Device Capabilities
+ * (WUSB1.0[7.4.1.1]) that is wrapped in a device capability descriptor
+ * that is part of the BOS descriptor set. That tells us what does the
+ * device support (dual role, beacon type, UWB PHY rates).
+ */
+static int wusb_dev_bos_grok(struct usb_device *usb_dev,
+                            struct wusb_dev *wusb_dev,
+                            struct usb_bos_descriptor *bos, size_t desc_size)
+{
+       ssize_t result;
+       struct device *dev = &usb_dev->dev;
+       void *itr, *top;
+
+       /* Walk over BOS capabilities, verify them */
+       itr = (void *)bos + sizeof(*bos);
+       top = itr + desc_size - sizeof(*bos);
+       while (itr < top) {
+               struct usb_dev_cap_header *cap_hdr = itr;
+               size_t cap_size;
+               u8 cap_type;
+               if (top - itr < sizeof(*cap_hdr)) {
+                       dev_err(dev, "Device BUG? premature end of BOS header "
+                               "data [offset 0x%02x]: only %zu bytes left\n",
+                               (int)(itr - (void *)bos), top - itr);
+                       result = -ENOSPC;
+                       goto error_bad_cap;
+               }
+               cap_size = cap_hdr->bLength;
+               cap_type = cap_hdr->bDevCapabilityType;
+               d_printf(4, dev, "BOS Capability: 0x%02x (%zu bytes)\n",
+                        cap_type, cap_size);
+               if (cap_size == 0)
+                       break;
+               if (cap_size > top - itr) {
+                       dev_err(dev, "Device BUG? premature end of BOS data "
+                               "[offset 0x%02x cap %02x %zu bytes]: "
+                               "only %zu bytes left\n",
+                               (int)(itr - (void *)bos),
+                               cap_type, cap_size, top - itr);
+                       result = -EBADF;
+                       goto error_bad_cap;
+               }
+               d_dump(3, dev, itr, cap_size);
+               switch (cap_type) {
+               case USB_CAP_TYPE_WIRELESS_USB:
+                       if (cap_size != sizeof(*wusb_dev->wusb_cap_descr))
+                               dev_err(dev, "Device BUG? WUSB Capability "
+                                       "descriptor is %zu bytes vs %zu "
+                                       "needed\n", cap_size,
+                                       sizeof(*wusb_dev->wusb_cap_descr));
+                       else {
+                               wusb_dev->wusb_cap_descr = itr;
+                               wusb_cap_descr_printf(3, dev, itr);
+                       }
+                       break;
+               default:
+                       dev_err(dev, "BUG? Unknown BOS capability 0x%02x "
+                               "(%zu bytes) at offset 0x%02x\n", cap_type,
+                               cap_size, (int)(itr - (void *)bos));
+               }
+               itr += cap_size;
+       }
+       result = 0;
+error_bad_cap:
+       return result;
+}
+
+/*
+ * Add information from the BOS descriptors to the device
+ *
+ * @usb_dev: referenced
+ * @wusb_dev: referenced and unlocked
+ *
+ * So what we do is we alloc a space for the BOS descriptor of 64
+ * bytes; read the first four bytes which include the wTotalLength
+ * field (WUSB1.0[T7-26]) and if it fits in those 64 bytes, read the
+ * whole thing. If not we realloc to that size.
+ *
+ * Then we call the groking function, that will fill up
+ * wusb_dev->wusb_cap_descr, which is what we'll need later on.
+ */
+static int wusb_dev_bos_add(struct usb_device *usb_dev,
+                           struct wusb_dev *wusb_dev)
+{
+       ssize_t result;
+       struct device *dev = &usb_dev->dev;
+       struct usb_bos_descriptor *bos;
+       size_t alloc_size = 32, desc_size = 4;
+
+       bos = kmalloc(alloc_size, GFP_KERNEL);
+       if (bos == NULL)
+               return -ENOMEM;
+       result = usb_get_descriptor(usb_dev, USB_DT_BOS, 0, bos, desc_size);
+       if (result < 4) {
+               dev_err(dev, "Can't get BOS descriptor or too short: %zd\n",
+                       result);
+               goto error_get_descriptor;
+       }
+       desc_size = le16_to_cpu(bos->wTotalLength);
+       if (desc_size >= alloc_size) {
+               kfree(bos);
+               alloc_size = desc_size;
+               bos = kmalloc(alloc_size, GFP_KERNEL);
+               if (bos == NULL)
+                       return -ENOMEM;
+       }
+       result = usb_get_descriptor(usb_dev, USB_DT_BOS, 0, bos, desc_size);
+       if (result < 0 || result != desc_size) {
+               dev_err(dev, "Can't get  BOS descriptor or too short (need "
+                       "%zu bytes): %zd\n", desc_size, result);
+               goto error_get_descriptor;
+       }
+       if (result < sizeof(*bos)
+           || le16_to_cpu(bos->wTotalLength) != desc_size) {
+               dev_err(dev, "Can't get  BOS descriptor or too short (need "
+                       "%zu bytes): %zd\n", desc_size, result);
+               goto error_get_descriptor;
+       }
+       d_printf(2, dev, "Got BOS descriptor %zd bytes, %u capabilities\n",
+                result, bos->bNumDeviceCaps);
+       d_dump(2, dev, bos, result);
+       result = wusb_dev_bos_grok(usb_dev, wusb_dev, bos, result);
+       if (result < 0)
+               goto error_bad_bos;
+       wusb_dev->bos = bos;
+       return 0;
+
+error_bad_bos:
+error_get_descriptor:
+       kfree(bos);
+       wusb_dev->wusb_cap_descr = NULL;
+       return result;
+}
+
+static void wusb_dev_bos_rm(struct wusb_dev *wusb_dev)
+{
+       kfree(wusb_dev->bos);
+       wusb_dev->wusb_cap_descr = NULL;
+};
+
+static struct usb_wireless_cap_descriptor wusb_cap_descr_default = {
+       .bLength = sizeof(wusb_cap_descr_default),
+       .bDescriptorType = USB_DT_DEVICE_CAPABILITY,
+       .bDevCapabilityType = USB_CAP_TYPE_WIRELESS_USB,
+
+       .bmAttributes = USB_WIRELESS_BEACON_NONE,
+       .wPHYRates = cpu_to_le16(USB_WIRELESS_PHY_53),
+       .bmTFITXPowerInfo = 0,
+       .bmFFITXPowerInfo = 0,
+       .bmBandGroup = cpu_to_le16(0x0001),     /* WUSB1.0[7.4.1] bottom */
+       .bReserved = 0
+};
+
+/*
+ * USB stack's device addition Notifier Callback
+ *
+ * Called from drivers/usb/core/hub.c when a new device is added; we
+ * use this hook to perform certain WUSB specific setup work on the
+ * new device. As well, it is the first time we can connect the
+ * wusb_dev and the usb_dev. So we note it down in wusb_dev and take a
+ * reference that we'll drop.
+ *
+ * First we need to determine if the device is a WUSB device (else we
+ * ignore it). For that we use the speed setting (USB_SPEED_VARIABLE)
+ * [FIXME: maybe we'd need something more definitive]. If so, we track
+ * it's usb_busd and from there, the WUSB HC.
+ *
+ * Because all WUSB HCs are contained in a 'struct wusbhc', voila, we
+ * get the wusbhc for the device.
+ *
+ * We have a reference on @usb_dev (as we are called at the end of its
+ * enumeration).
+ *
+ * NOTE: @usb_dev locked
+ */
+static void wusb_dev_add_ncb(struct usb_device *usb_dev)
+{
+       int result = 0;
+       struct wusb_dev *wusb_dev;
+       struct wusbhc *wusbhc;
+       struct device *dev = &usb_dev->dev;
+       u8 port_idx;
+
+       if (usb_dev->wusb == 0 || usb_dev->devnum == 1)
+               return;         /* skip non wusb and wusb RHs */
+
+       d_fnstart(3, dev, "(usb_dev %p)\n", usb_dev);
+
+       wusbhc = wusbhc_get_by_usb_dev(usb_dev);
+       if (wusbhc == NULL)
+               goto error_nodev;
+       mutex_lock(&wusbhc->mutex);
+       wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc, usb_dev);
+       port_idx = wusb_port_no_to_idx(usb_dev->portnum);
+       mutex_unlock(&wusbhc->mutex);
+       if (wusb_dev == NULL)
+               goto error_nodev;
+       wusb_dev->usb_dev = usb_get_dev(usb_dev);
+       usb_dev->wusb_dev = wusb_dev_get(wusb_dev);
+       result = wusb_dev_sec_add(wusbhc, usb_dev, wusb_dev);
+       if (result < 0) {
+               dev_err(dev, "Cannot enable security: %d\n", result);
+               goto error_sec_add;
+       }
+       /* Now query the device for it's BOS and attach it to wusb_dev */
+       result = wusb_dev_bos_add(usb_dev, wusb_dev);
+       if (result < 0) {
+               dev_err(dev, "Cannot get BOS descriptors: %d\n", result);
+               goto error_bos_add;
+       }
+       result = wusb_dev_sysfs_add(wusbhc, usb_dev, wusb_dev);
+       if (result < 0)
+               goto error_add_sysfs;
+out:
+       wusb_dev_put(wusb_dev);
+       wusbhc_put(wusbhc);
+error_nodev:
+       d_fnend(3, dev, "(usb_dev %p) = void\n", usb_dev);
+       return;
+
+       wusb_dev_sysfs_rm(wusb_dev);
+error_add_sysfs:
+       wusb_dev_bos_rm(wusb_dev);
+error_bos_add:
+       wusb_dev_sec_rm(wusb_dev);
+error_sec_add:
+       mutex_lock(&wusbhc->mutex);
+       __wusbhc_dev_disconnect(wusbhc, wusb_port_by_idx(wusbhc, port_idx));
+       mutex_unlock(&wusbhc->mutex);
+       goto out;
+}
+
+/*
+ * Undo all the steps done at connection by the notifier callback
+ *
+ * NOTE: @usb_dev locked
+ */
+static void wusb_dev_rm_ncb(struct usb_device *usb_dev)
+{
+       struct wusb_dev *wusb_dev = usb_dev->wusb_dev;
+
+       if (usb_dev->wusb == 0 || usb_dev->devnum == 1)
+               return;         /* skip non wusb and wusb RHs */
+
+       wusb_dev_sysfs_rm(wusb_dev);
+       wusb_dev_bos_rm(wusb_dev);
+       wusb_dev_sec_rm(wusb_dev);
+       wusb_dev->usb_dev = NULL;
+       usb_dev->wusb_dev = NULL;
+       wusb_dev_put(wusb_dev);
+       usb_put_dev(usb_dev);
+}
+
+/*
+ * Handle notifications from the USB stack (notifier call back)
+ *
+ * This is called when the USB stack does a
+ * usb_{bus,device}_{add,remove}() so we can do WUSB specific
+ * handling. It is called with [for the case of
+ * USB_DEVICE_{ADD,REMOVE} with the usb_dev locked.
+ */
+int wusb_usb_ncb(struct notifier_block *nb, unsigned long val,
+                void *priv)
+{
+       int result = NOTIFY_OK;
+
+       switch (val) {
+       case USB_DEVICE_ADD:
+               wusb_dev_add_ncb(priv);
+               break;
+       case USB_DEVICE_REMOVE:
+               wusb_dev_rm_ncb(priv);
+               break;
+       case USB_BUS_ADD:
+               /* ignore (for now) */
+       case USB_BUS_REMOVE:
+               break;
+       default:
+               WARN_ON(1);
+               result = NOTIFY_BAD;
+       };
+       return result;
+}
+
+/*
+ * Return a referenced wusb_dev given a @wusbhc and @usb_dev
+ */
+struct wusb_dev *__wusb_dev_get_by_usb_dev(struct wusbhc *wusbhc,
+                                          struct usb_device *usb_dev)
+{
+       struct wusb_dev *wusb_dev;
+       u8 port_idx;
+
+       port_idx = wusb_port_no_to_idx(usb_dev->portnum);
+       BUG_ON(port_idx > wusbhc->ports_max);
+       wusb_dev = wusb_port_by_idx(wusbhc, port_idx)->wusb_dev;
+       if (wusb_dev != NULL)           /* ops, device is gone */
+               wusb_dev_get(wusb_dev);
+       return wusb_dev;
+}
+EXPORT_SYMBOL_GPL(__wusb_dev_get_by_usb_dev);
+
+void wusb_dev_destroy(struct kref *_wusb_dev)
+{
+       struct wusb_dev *wusb_dev
+               = container_of(_wusb_dev, struct wusb_dev, refcnt);
+       list_del_init(&wusb_dev->cack_node);
+       wusb_dev_free(wusb_dev);
+       d_fnend(1, NULL, "%s (wusb_dev %p) = void\n", __func__, wusb_dev);
+}
+EXPORT_SYMBOL_GPL(wusb_dev_destroy);
+
+/*
+ * Create all the device connect handling infrastructure
+ *
+ * This is basically the device info array, Connect Acknowledgement
+ * (cack) lists, keep-alive timers (and delayed work thread).
+ */
+int wusbhc_devconnect_create(struct wusbhc *wusbhc)
+{
+       d_fnstart(3, wusbhc->dev, "(wusbhc %p)\n", wusbhc);
+
+       wusbhc->keep_alive_ie.hdr.bIEIdentifier = WUIE_ID_KEEP_ALIVE;
+       wusbhc->keep_alive_ie.hdr.bLength = sizeof(wusbhc->keep_alive_ie.hdr);
+       INIT_DELAYED_WORK(&wusbhc->keep_alive_timer, wusbhc_keep_alive_run);
+
+       wusbhc->cack_ie.hdr.bIEIdentifier = WUIE_ID_CONNECTACK;
+       wusbhc->cack_ie.hdr.bLength = sizeof(wusbhc->cack_ie.hdr);
+       INIT_LIST_HEAD(&wusbhc->cack_list);
+
+       d_fnend(3, wusbhc->dev, "(wusbhc %p) = void\n", wusbhc);
+       return 0;
+}
+
+/*
+ * Release all resources taken by the devconnect stuff
+ */
+void wusbhc_devconnect_destroy(struct wusbhc *wusbhc)
+{
+       d_fnstart(3, wusbhc->dev, "(wusbhc %p)\n", wusbhc);
+       d_fnend(3, wusbhc->dev, "(wusbhc %p) = void\n", wusbhc);
+}
+
+/*
+ * wusbhc_devconnect_start - start accepting device connections
+ * @wusbhc: the WUSB HC
+ *
+ * Sets the Host Info IE to accept all new connections.
+ *
+ * FIXME: This also enables the keep alives but this is not necessary
+ * until there are connected and authenticated devices.
+ */
+int wusbhc_devconnect_start(struct wusbhc *wusbhc,
+                           const struct wusb_ckhdid *chid)
+{
+       struct device *dev = wusbhc->dev;
+       struct wuie_host_info *hi;
+       int result;
+
+       hi = kzalloc(sizeof(*hi), GFP_KERNEL);
+       if (hi == NULL)
+               return -ENOMEM;
+
+       hi->hdr.bLength       = sizeof(*hi);
+       hi->hdr.bIEIdentifier = WUIE_ID_HOST_INFO;
+       hi->attributes        = cpu_to_le16((wusbhc->rsv->stream << 3) | WUIE_HI_CAP_ALL);
+       hi->CHID              = *chid;
+       result = wusbhc_mmcie_set(wusbhc, 0, 0, &hi->hdr);
+       if (result < 0) {
+               dev_err(dev, "Cannot add Host Info MMCIE: %d\n", result);
+               goto error_mmcie_set;
+       }
+       wusbhc->wuie_host_info = hi;
+
+       queue_delayed_work(wusbd, &wusbhc->keep_alive_timer,
+                          (wusbhc->trust_timeout*CONFIG_HZ)/1000/2);
+
+       return 0;
+
+error_mmcie_set:
+       kfree(hi);
+       return result;
+}
+
+/*
+ * wusbhc_devconnect_stop - stop managing connected devices
+ * @wusbhc: the WUSB HC
+ *
+ * Removes the Host Info IE and stops the keep alives.
+ *
+ * FIXME: should this disconnect all devices?
+ */
+void wusbhc_devconnect_stop(struct wusbhc *wusbhc)
+{
+       cancel_delayed_work_sync(&wusbhc->keep_alive_timer);
+       WARN_ON(!list_empty(&wusbhc->cack_list));
+
+       wusbhc_mmcie_rm(wusbhc, &wusbhc->wuie_host_info->hdr);
+       kfree(wusbhc->wuie_host_info);
+       wusbhc->wuie_host_info = NULL;
+}
+
+/*
+ * wusb_set_dev_addr - set the WUSB device address used by the host
+ * @wusbhc: the WUSB HC the device is connect to
+ * @wusb_dev: the WUSB device
+ * @addr: new device address
+ */
+int wusb_set_dev_addr(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev, u8 addr)
+{
+       int result;
+
+       wusb_dev->addr = addr;
+       result = wusbhc->dev_info_set(wusbhc, wusb_dev);
+       if (result < 0)
+               dev_err(wusbhc->dev, "device %d: failed to set device "
+                       "address\n", wusb_dev->port_idx);
+       else
+               dev_info(wusbhc->dev, "device %d: %s addr %u\n",
+                        wusb_dev->port_idx,
+                        (addr & WUSB_DEV_ADDR_UNAUTH) ? "unauth" : "auth",
+                        wusb_dev->addr);
+
+       return result;
+}
diff --git a/drivers/usb/wusbcore/mmc.c b/drivers/usb/wusbcore/mmc.c
new file mode 100644 (file)
index 0000000..cfa77a0
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ * WUSB Wire Adapter: Control/Data Streaming Interface (WUSB[8])
+ * MMC (Microscheduled Management Command) 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.
+ *
+ *
+ * WUIEs and MMC IEs...well, they are almost the same at the end. MMC
+ * IEs are Wireless USB IEs that go into the MMC period...[what is
+ * that? look in Design-overview.txt].
+ *
+ *
+ * This is a simple subsystem to keep track of which IEs are being
+ * sent by the host in the MMC period.
+ *
+ * For each WUIE we ask to send, we keep it in an array, so we can
+ * request its removal later, or replace the content. They are tracked
+ * by pointer, so be sure to use the same pointer if you want to
+ * remove it or update the contents.
+ *
+ * FIXME:
+ *  - add timers that autoremove intervalled IEs?
+ */
+#include <linux/usb/wusb.h>
+#include "wusbhc.h"
+
+/* Initialize the MMCIEs handling mechanism */
+int wusbhc_mmcie_create(struct wusbhc *wusbhc)
+{
+       u8 mmcies = wusbhc->mmcies_max;
+       wusbhc->mmcie = kcalloc(mmcies, sizeof(wusbhc->mmcie[0]), GFP_KERNEL);
+       if (wusbhc->mmcie == NULL)
+               return -ENOMEM;
+       mutex_init(&wusbhc->mmcie_mutex);
+       return 0;
+}
+
+/* Release resources used by the MMCIEs handling mechanism */
+void wusbhc_mmcie_destroy(struct wusbhc *wusbhc)
+{
+       kfree(wusbhc->mmcie);
+}
+
+/*
+ * Add or replace an MMC Wireless USB IE.
+ *
+ * @interval:    See WUSB1.0[8.5.3.1]
+ * @repeat_cnt:  See WUSB1.0[8.5.3.1]
+ * @handle:      See WUSB1.0[8.5.3.1]
+ * @wuie:        Pointer to the header of the WUSB IE data to add.
+ *               MUST BE allocated in a kmalloc buffer (no stack or
+ *               vmalloc).
+ *               THE CALLER ALWAYS OWNS THE POINTER (we don't free it
+ *               on remove, we just forget about it).
+ * @returns:     0 if ok, < 0 errno code on error.
+ *
+ * Goes over the *whole* @wusbhc->mmcie array looking for (a) the
+ * first free spot and (b) if @wuie is already in the array (aka:
+ * transmitted in the MMCs) the spot were it is.
+ *
+ * If present, we "overwrite it" (update).
+ *
+ *
+ * NOTE: Need special ordering rules -- see below WUSB1.0 Table 7-38.
+ *       The host uses the handle as the 'sort' index. We
+ *       allocate the last one always for the WUIE_ID_HOST_INFO, and
+ *       the rest, first come first serve in inverse order.
+ *
+ *       Host software must make sure that it adds the other IEs in
+ *       the right order... the host hardware is responsible for
+ *       placing the WCTA IEs in the right place with the other IEs
+ *       set by host software.
+ *
+ * NOTE: we can access wusbhc->wa_descr without locking because it is
+ *       read only.
+ */
+int wusbhc_mmcie_set(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt,
+                    struct wuie_hdr *wuie)
+{
+       int result = -ENOBUFS;
+       unsigned handle, itr;
+
+       /* Search a handle, taking into account the ordering */
+       mutex_lock(&wusbhc->mmcie_mutex);
+       switch (wuie->bIEIdentifier) {
+       case WUIE_ID_HOST_INFO:
+               /* Always last */
+               handle = wusbhc->mmcies_max - 1;
+               break;
+       case WUIE_ID_ISOCH_DISCARD:
+               dev_err(wusbhc->dev, "Special ordering case for WUIE ID 0x%x "
+                       "unimplemented\n", wuie->bIEIdentifier);
+               result = -ENOSYS;
+               goto error_unlock;
+       default:
+               /* search for it or find the last empty slot */
+               handle = ~0;
+               for (itr = 0; itr < wusbhc->mmcies_max - 1; itr++) {
+                       if (wusbhc->mmcie[itr] == wuie) {
+                               handle = itr;
+                               break;
+                       }
+                       if (wusbhc->mmcie[itr] == NULL)
+                               handle = itr;
+               }
+               if (handle == ~0)
+                       goto error_unlock;
+       }
+       result = (wusbhc->mmcie_add)(wusbhc, interval, repeat_cnt, handle,
+                                    wuie);
+       if (result >= 0)
+               wusbhc->mmcie[handle] = wuie;
+error_unlock:
+       mutex_unlock(&wusbhc->mmcie_mutex);
+       return result;
+}
+EXPORT_SYMBOL_GPL(wusbhc_mmcie_set);
+
+/*
+ * Remove an MMC IE previously added with wusbhc_mmcie_set()
+ *
+ * @wuie       Pointer used to add the WUIE
+ */
+void wusbhc_mmcie_rm(struct wusbhc *wusbhc, struct wuie_hdr *wuie)
+{
+       int result;
+       unsigned handle, itr;
+
+       mutex_lock(&wusbhc->mmcie_mutex);
+       for (itr = 0; itr < wusbhc->mmcies_max; itr++) {
+               if (wusbhc->mmcie[itr] == wuie) {
+                       handle = itr;
+                       goto found;
+               }
+       }
+       mutex_unlock(&wusbhc->mmcie_mutex);
+       return;
+
+found:
+       result = (wusbhc->mmcie_rm)(wusbhc, handle);
+       if (result == 0)
+               wusbhc->mmcie[itr] = NULL;
+       mutex_unlock(&wusbhc->mmcie_mutex);
+}
+EXPORT_SYMBOL_GPL(wusbhc_mmcie_rm);
+
+/*
+ * wusbhc_start - start transmitting MMCs and accepting connections
+ * @wusbhc: the HC to start
+ * @chid: the CHID to use for this host
+ *
+ * Establishes a cluster reservation, enables device connections, and
+ * starts MMCs with appropriate DNTS parameters.
+ */
+int wusbhc_start(struct wusbhc *wusbhc, const struct wusb_ckhdid *chid)
+{
+       int result;
+       struct device *dev = wusbhc->dev;
+
+       WARN_ON(wusbhc->wuie_host_info != NULL);
+
+       result = wusbhc_rsv_establish(wusbhc);
+       if (result < 0) {
+               dev_err(dev, "cannot establish cluster reservation: %d\n",
+                       result);
+               goto error_rsv_establish;
+       }
+
+       result = wusbhc_devconnect_start(wusbhc, chid);
+       if (result < 0) {
+               dev_err(dev, "error enabling device connections: %d\n", result);
+               goto error_devconnect_start;
+       }
+
+       result = wusbhc_sec_start(wusbhc);
+       if (result < 0) {
+               dev_err(dev, "error starting security in the HC: %d\n", result);
+               goto error_sec_start;
+       }
+       /* FIXME: the choice of the DNTS parameters is somewhat
+        * arbitrary */
+       result = wusbhc->set_num_dnts(wusbhc, 0, 15);
+       if (result < 0) {
+               dev_err(dev, "Cannot set DNTS parameters: %d\n", result);
+               goto error_set_num_dnts;
+       }
+       result = wusbhc->start(wusbhc);
+       if (result < 0) {
+               dev_err(dev, "error starting wusbch: %d\n", result);
+               goto error_wusbhc_start;
+       }
+       wusbhc->active = 1;
+       return 0;
+
+error_wusbhc_start:
+       wusbhc_sec_stop(wusbhc);
+error_set_num_dnts:
+error_sec_start:
+       wusbhc_devconnect_stop(wusbhc);
+error_devconnect_start:
+       wusbhc_rsv_terminate(wusbhc);
+error_rsv_establish:
+       return result;
+}
+
+/*
+ * Disconnect all from the WUSB Channel
+ *
+ * Send a Host Disconnect IE in the MMC, wait, don't send it any more
+ */
+static int __wusbhc_host_disconnect_ie(struct wusbhc *wusbhc)
+{
+       int result = -ENOMEM;
+       struct wuie_host_disconnect *host_disconnect_ie;
+       might_sleep();
+       host_disconnect_ie = kmalloc(sizeof(*host_disconnect_ie), GFP_KERNEL);
+       if (host_disconnect_ie == NULL)
+               goto error_alloc;
+       host_disconnect_ie->hdr.bLength       = sizeof(*host_disconnect_ie);
+       host_disconnect_ie->hdr.bIEIdentifier = WUIE_ID_HOST_DISCONNECT;
+       result = wusbhc_mmcie_set(wusbhc, 0, 0, &host_disconnect_ie->hdr);
+       if (result < 0)
+               goto error_mmcie_set;
+
+       /* WUSB1.0[8.5.3.1 & 7.5.2] */
+       msleep(100);
+       wusbhc_mmcie_rm(wusbhc, &host_disconnect_ie->hdr);
+error_mmcie_set:
+       kfree(host_disconnect_ie);
+error_alloc:
+       return result;
+}
+
+/*
+ * wusbhc_stop - stop transmitting MMCs
+ * @wusbhc: the HC to stop
+ *
+ * Send a Host Disconnect IE, wait, remove all the MMCs (stop sending MMCs).
+ *
+ * If we can't allocate a Host Stop IE, screw it, we don't notify the
+ * devices we are disconnecting...
+ */
+void wusbhc_stop(struct wusbhc *wusbhc)
+{
+       if (wusbhc->active) {
+               wusbhc->active = 0;
+               wusbhc->stop(wusbhc);
+               wusbhc_sec_stop(wusbhc);
+               __wusbhc_host_disconnect_ie(wusbhc);
+               wusbhc_devconnect_stop(wusbhc);
+               wusbhc_rsv_terminate(wusbhc);
+       }
+}
+EXPORT_SYMBOL_GPL(wusbhc_stop);
+
+/*
+ * Change the CHID in a WUSB Channel
+ *
+ * If it is just a new CHID, send a Host Disconnect IE and then change
+ * the CHID IE.
+ */
+static int __wusbhc_chid_change(struct wusbhc *wusbhc,
+                               const struct wusb_ckhdid *chid)
+{
+       int result = -ENOSYS;
+       struct device *dev = wusbhc->dev;
+       dev_err(dev, "%s() not implemented yet\n", __func__);
+       return result;
+
+       BUG_ON(wusbhc->wuie_host_info == NULL);
+       __wusbhc_host_disconnect_ie(wusbhc);
+       wusbhc->wuie_host_info->CHID = *chid;
+       result = wusbhc_mmcie_set(wusbhc, 0, 0, &wusbhc->wuie_host_info->hdr);
+       if (result < 0)
+               dev_err(dev, "Can't update Host Info WUSB IE: %d\n", result);
+       return result;
+}
+
+/*
+ * Set/reset/update a new CHID
+ *
+ * Depending on the previous state of the MMCs, start, stop or change
+ * the sent MMC. This effectively switches the host controller on and
+ * off (radio wise).
+ */
+int wusbhc_chid_set(struct wusbhc *wusbhc, const struct wusb_ckhdid *chid)
+{
+       int result = 0;
+
+       if (memcmp(chid, &wusb_ckhdid_zero, sizeof(chid)) == 0)
+               chid = NULL;
+
+       mutex_lock(&wusbhc->mutex);
+       if (wusbhc->active) {
+               if (chid)
+                       result = __wusbhc_chid_change(wusbhc, chid);
+               else
+                       wusbhc_stop(wusbhc);
+       } else {
+               if (chid)
+                       wusbhc_start(wusbhc, chid);
+       }
+       mutex_unlock(&wusbhc->mutex);
+       return result;
+}
+EXPORT_SYMBOL_GPL(wusbhc_chid_set);
diff --git a/drivers/usb/wusbcore/pal.c b/drivers/usb/wusbcore/pal.c
new file mode 100644 (file)
index 0000000..7cc51e9
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Wireless USB Host Controller
+ * UWB Protocol Adaptation Layer (PAL) glue.
+ *
+ * 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 "wusbhc.h"
+
+/**
+ * wusbhc_pal_register - register the WUSB HC as a UWB PAL
+ * @wusbhc: the WUSB HC
+ */
+int wusbhc_pal_register(struct wusbhc *wusbhc)
+{
+       uwb_pal_init(&wusbhc->pal);
+
+       wusbhc->pal.name   = "wusbhc";
+       wusbhc->pal.device = wusbhc->usb_hcd.self.controller;
+
+       return uwb_pal_register(wusbhc->uwb_rc, &wusbhc->pal);
+}
+
+/**
+ * wusbhc_pal_register - unregister the WUSB HC as a UWB PAL
+ * @wusbhc: the WUSB HC
+ */
+void wusbhc_pal_unregister(struct wusbhc *wusbhc)
+{
+       uwb_pal_unregister(wusbhc->uwb_rc, &wusbhc->pal);
+}
diff --git a/drivers/usb/wusbcore/reservation.c b/drivers/usb/wusbcore/reservation.c
new file mode 100644 (file)
index 0000000..fc63e77
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * WUSB cluster reservation management
+ *
+ * Copyright (C) 2007 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 "wusbhc.h"
+
+/*
+ * WUSB cluster reservations are multicast reservations with the
+ * broadcast cluster ID (BCID) as the target DevAddr.
+ *
+ * FIXME: consider adjusting the reservation depending on what devices
+ * are attached.
+ */
+
+static int wusbhc_bwa_set(struct wusbhc *wusbhc, u8 stream,
+       const struct uwb_mas_bm *mas)
+{
+       if (mas == NULL)
+               mas = &uwb_mas_bm_zero;
+       return wusbhc->bwa_set(wusbhc, stream, mas);
+}
+
+/**
+ * wusbhc_rsv_complete_cb - WUSB HC reservation complete callback
+ * @rsv:    the reservation
+ *
+ * Either set or clear the HC's view of the reservation.
+ *
+ * FIXME: when a reservation is denied the HC should be stopped.
+ */
+static void wusbhc_rsv_complete_cb(struct uwb_rsv *rsv)
+{
+       struct wusbhc *wusbhc = rsv->pal_priv;
+       struct device *dev = wusbhc->dev;
+       char buf[72];
+
+       switch (rsv->state) {
+       case UWB_RSV_STATE_O_ESTABLISHED:
+               bitmap_scnprintf(buf, sizeof(buf), rsv->mas.bm, UWB_NUM_MAS);
+               dev_dbg(dev, "established reservation: %s\n", buf);
+               wusbhc_bwa_set(wusbhc, rsv->stream, &rsv->mas);
+               break;
+       case UWB_RSV_STATE_NONE:
+               dev_dbg(dev, "removed reservation\n");
+               wusbhc_bwa_set(wusbhc, 0, NULL);
+               wusbhc->rsv = NULL;
+               break;
+       default:
+               dev_dbg(dev, "unexpected reservation state: %d\n", rsv->state);
+               break;
+       }
+}
+
+
+/**
+ * wusbhc_rsv_establish - establish a reservation for the cluster
+ * @wusbhc: the WUSB HC requesting a bandwith reservation
+ */
+int wusbhc_rsv_establish(struct wusbhc *wusbhc)
+{
+       struct uwb_rc *rc = wusbhc->uwb_rc;
+       struct uwb_rsv *rsv;
+       struct uwb_dev_addr bcid;
+       int ret;
+
+       rsv = uwb_rsv_create(rc, wusbhc_rsv_complete_cb, wusbhc);
+       if (rsv == NULL)
+               return -ENOMEM;
+
+       bcid.data[0] = wusbhc->cluster_id;
+       bcid.data[1] = 0;
+
+       rsv->owner = &rc->uwb_dev;
+       rsv->target.type = UWB_RSV_TARGET_DEVADDR;
+       rsv->target.devaddr = bcid;
+       rsv->type = UWB_DRP_TYPE_PRIVATE;
+       rsv->max_mas = 256;
+       rsv->min_mas = 16;  /* one MAS per zone? */
+       rsv->sparsity = 16; /* at least one MAS in each zone? */
+       rsv->is_multicast = true;
+
+       ret = uwb_rsv_establish(rsv);
+       if (ret == 0)
+               wusbhc->rsv = rsv;
+       else
+               uwb_rsv_destroy(rsv);
+       return ret;
+}
+
+
+/**
+ * wusbhc_rsv_terminate - terminate any cluster reservation
+ * @wusbhc: the WUSB host whose reservation is to be terminated
+ */
+void wusbhc_rsv_terminate(struct wusbhc *wusbhc)
+{
+       if (wusbhc->rsv)
+               uwb_rsv_terminate(wusbhc->rsv);
+}
diff --git a/drivers/usb/wusbcore/rh.c b/drivers/usb/wusbcore/rh.c
new file mode 100644 (file)
index 0000000..267a643
--- /dev/null
@@ -0,0 +1,477 @@
+/*
+ * Wireless USB Host Controller
+ * Root Hub operations
+ *
+ *
+ * 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 fake a root hub that has fake ports (as many as simultaneous
+ * devices the Wireless USB Host Controller can deal with). For each
+ * port we keep an state in @wusbhc->port[index] identical to the one
+ * specified in the USB2.0[ch11] spec and some extra device
+ * information that complements the one in 'struct usb_device' (as
+ * this lacs a hcpriv pointer).
+ *
+ * Note this is common to WHCI and HWA host controllers.
+ *
+ * Through here we enable most of the state changes that the USB stack
+ * will use to connect or disconnect devices. We need to do some
+ * forced adaptation of Wireless USB device states vs. wired:
+ *
+ *        USB:                 WUSB:
+ *
+ * Port   Powered-off          port slot n/a
+ *        Powered-on           port slot available
+ *        Disconnected         port slot available
+ *        Connected            port slot assigned device
+ *                            device sent DN_Connect
+ *                             device was authenticated
+ *        Enabled              device is authenticated, transitioned
+ *                             from unauth -> auth -> default address
+ *                             -> enabled
+ *        Reset                disconnect
+ *        Disable              disconnect
+ *
+ * This maps the standard USB port states with the WUSB device states
+ * so we can fake ports without having to modify the USB stack.
+ *
+ * FIXME: this process will change in the future
+ *
+ *
+ * ENTRY POINTS
+ *
+ * Our entry points into here are, as in hcd.c, the USB stack root hub
+ * ops defined in the usb_hcd struct:
+ *
+ * wusbhc_rh_status_data()     Provide hub and port status data bitmap
+ *
+ * wusbhc_rh_control()          Execution of all the major requests
+ *                              you can do to a hub (Set|Clear
+ *                              features, get descriptors, status, etc).
+ *
+ * wusbhc_rh_[suspend|resume]() That
+ *
+ * wusbhc_rh_start_port_reset() ??? unimplemented
+ */
+#include "wusbhc.h"
+
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+/*
+ * Reset a fake port
+ *
+ * This can be called to reset a port from any other state or to reset
+ * it when connecting. In Wireless USB they are different; when doing
+ * a new connect that involves going over the authentication. When
+ * just reseting, its a different story.
+ *
+ * The Linux USB stack resets a port twice before it considers it
+ * enabled, so we have to detect and ignore that.
+ *
+ * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
+ *
+ * Supposedly we are the only thread accesing @wusbhc->port; in any
+ * case, maybe we should move the mutex locking from
+ * wusbhc_devconnect_auth() to here.
+ *
+ * @port_idx refers to the wusbhc's port index, not the USB port number
+ */
+static int wusbhc_rh_port_reset(struct wusbhc *wusbhc, u8 port_idx)
+{
+       int result = 0;
+       struct wusb_port *port = wusb_port_by_idx(wusbhc, port_idx);
+
+       d_fnstart(3, wusbhc->dev, "(wusbhc %p port_idx %u)\n",
+                 wusbhc, port_idx);
+       if (port->reset_count == 0) {
+               wusbhc_devconnect_auth(wusbhc, port_idx);
+               port->reset_count++;
+       } else if (port->reset_count == 1)
+               /* see header */
+               d_printf(2, wusbhc->dev, "Ignoring second reset on port_idx "
+                       "%u\n", port_idx);
+       else
+               result = wusbhc_dev_reset(wusbhc, port_idx);
+       d_fnend(3, wusbhc->dev, "(wusbhc %p port_idx %u) = %d\n",
+               wusbhc, port_idx, result);
+       return result;
+}
+
+/*
+ * Return the hub change status bitmap
+ *
+ * The bits in the change status bitmap are cleared when a
+ * ClearPortFeature request is issued (USB2.0[11.12.3,11.12.4].
+ *
+ * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
+ *
+ * WARNING!! This gets called from atomic context; we cannot get the
+ *           mutex--the only race condition we can find is some bit
+ *           changing just after we copy it, which shouldn't be too
+ *           big of a problem [and we can't make it an spinlock
+ *           because other parts need to take it and sleep] .
+ *
+ *           @usb_hcd is refcounted, so it won't dissapear under us
+ *           and before killing a host, the polling of the root hub
+ *           would be stopped anyway.
+ */
+int wusbhc_rh_status_data(struct usb_hcd *usb_hcd, char *_buf)
+{
+       struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+       size_t cnt, size;
+       unsigned long *buf = (unsigned long *) _buf;
+
+       d_fnstart(1, wusbhc->dev, "(wusbhc %p)\n", wusbhc);
+       /* WE DON'T LOCK, see comment */
+       size = wusbhc->ports_max + 1 /* hub bit */;
+       size = (size + 8 - 1) / 8;      /* round to bytes */
+       for (cnt = 0; cnt < wusbhc->ports_max; cnt++)
+               if (wusb_port_by_idx(wusbhc, cnt)->change)
+                       set_bit(cnt + 1, buf);
+               else
+                       clear_bit(cnt + 1, buf);
+       d_fnend(1, wusbhc->dev, "(wusbhc %p) %u, buffer:\n", wusbhc, (int)size);
+       d_dump(1, wusbhc->dev, _buf, size);
+       return size;
+}
+EXPORT_SYMBOL_GPL(wusbhc_rh_status_data);
+
+/*
+ * Return the hub's desciptor
+ *
+ * NOTE: almost cut and paste from ehci-hub.c
+ *
+ * @wusbhc is assumed referenced and @wusbhc->mutex unlocked
+ */
+static int wusbhc_rh_get_hub_descr(struct wusbhc *wusbhc, u16 wValue,
+                                  u16 wIndex,
+                                  struct usb_hub_descriptor *descr,
+                                  u16 wLength)
+{
+       u16 temp = 1 + (wusbhc->ports_max / 8);
+       u8 length = 7 + 2 * temp;
+
+       if (wLength < length)
+               return -ENOSPC;
+       descr->bDescLength = 7 + 2 * temp;
+       descr->bDescriptorType = 0x29;  /* HUB type */
+       descr->bNbrPorts = wusbhc->ports_max;
+       descr->wHubCharacteristics = cpu_to_le16(
+               0x00                    /* All ports power at once */
+               | 0x00                  /* not part of compound device */
+               | 0x10                  /* No overcurrent protection */
+               | 0x00                  /* 8 FS think time FIXME ?? */
+               | 0x00);                /* No port indicators */
+       descr->bPwrOn2PwrGood = 0;
+       descr->bHubContrCurrent = 0;
+       /* two bitmaps:  ports removable, and usb 1.0 legacy PortPwrCtrlMask */
+       memset(&descr->bitmap[0], 0, temp);
+       memset(&descr->bitmap[temp], 0xff, temp);
+       return 0;
+}
+
+/*
+ * Clear a hub feature
+ *
+ * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
+ *
+ * Nothing to do, so no locking needed ;)
+ */
+static int wusbhc_rh_clear_hub_feat(struct wusbhc *wusbhc, u16 feature)
+{
+       int result;
+       struct device *dev = wusbhc->dev;
+
+       d_fnstart(4, dev, "(%p, feature 0x%04u)\n", wusbhc, feature);
+       switch (feature) {
+       case C_HUB_LOCAL_POWER:
+               /* FIXME: maybe plug bit 0 to the power input status,
+                * if any?
+                * see wusbhc_rh_get_hub_status() */
+       case C_HUB_OVER_CURRENT:
+               result = 0;
+               break;
+       default:
+               result = -EPIPE;
+       }
+       d_fnend(4, dev, "(%p, feature 0x%04u), %d\n", wusbhc, feature, result);
+       return result;
+}
+
+/*
+ * Return hub status (it is always zero...)
+ *
+ * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
+ *
+ * Nothing to do, so no locking needed ;)
+ */
+static int wusbhc_rh_get_hub_status(struct wusbhc *wusbhc, u32 *buf,
+                                   u16 wLength)
+{
+       /* FIXME: maybe plug bit 0 to the power input status (if any)? */
+       *buf = 0;
+       return 0;
+}
+
+/*
+ * Set a port feature
+ *
+ * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
+ */
+static int wusbhc_rh_set_port_feat(struct wusbhc *wusbhc, u16 feature,
+                                  u8 selector, u8 port_idx)
+{
+       int result = -EINVAL;
+       struct device *dev = wusbhc->dev;
+
+       d_fnstart(4, dev, "(feat 0x%04u, selector 0x%u, port_idx %d)\n",
+                 feature, selector, port_idx);
+
+       if (port_idx > wusbhc->ports_max)
+               goto error;
+
+       switch (feature) {
+               /* According to USB2.0[11.24.2.13]p2, these features
+                * are not required to be implemented. */
+       case USB_PORT_FEAT_C_OVER_CURRENT:
+       case USB_PORT_FEAT_C_ENABLE:
+       case USB_PORT_FEAT_C_SUSPEND:
+       case USB_PORT_FEAT_C_CONNECTION:
+       case USB_PORT_FEAT_C_RESET:
+               result = 0;
+               break;
+
+       case USB_PORT_FEAT_POWER:
+               /* No such thing, but we fake it works */
+               mutex_lock(&wusbhc->mutex);
+               wusb_port_by_idx(wusbhc, port_idx)->status |= USB_PORT_STAT_POWER;
+               mutex_unlock(&wusbhc->mutex);
+               result = 0;
+               break;
+       case USB_PORT_FEAT_RESET:
+               result = wusbhc_rh_port_reset(wusbhc, port_idx);
+               break;
+       case USB_PORT_FEAT_ENABLE:
+       case USB_PORT_FEAT_SUSPEND:
+               dev_err(dev, "(port_idx %d) set feat %d/%d UNIMPLEMENTED\n",
+                       port_idx, feature, selector);
+               result = -ENOSYS;
+               break;
+       default:
+               dev_err(dev, "(port_idx %d) set feat %d/%d UNKNOWN\n",
+                       port_idx, feature, selector);
+               result = -EPIPE;
+               break;
+       }
+error:
+       d_fnend(4, dev, "(feat 0x%04u, selector 0x%u, port_idx %d) = %d\n",
+               feature, selector, port_idx, result);
+       return result;
+}
+
+/*
+ * Clear a port feature...
+ *
+ * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
+ */
+static int wusbhc_rh_clear_port_feat(struct wusbhc *wusbhc, u16 feature,
+                                    u8 selector, u8 port_idx)
+{
+       int result = -EINVAL;
+       struct device *dev = wusbhc->dev;
+
+       d_fnstart(4, dev, "(wusbhc %p feat 0x%04x selector %d port_idx %d)\n",
+                 wusbhc, feature, selector, port_idx);
+
+       if (port_idx > wusbhc->ports_max)
+               goto error;
+
+       mutex_lock(&wusbhc->mutex);
+       result = 0;
+       switch (feature) {
+       case USB_PORT_FEAT_POWER:       /* fake port always on */
+               /* According to USB2.0[11.24.2.7.1.4], no need to implement? */
+       case USB_PORT_FEAT_C_OVER_CURRENT:
+               break;
+       case USB_PORT_FEAT_C_RESET:
+               wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_RESET;
+               break;
+       case USB_PORT_FEAT_C_CONNECTION:
+               wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_CONNECTION;
+               break;
+       case USB_PORT_FEAT_ENABLE:
+               __wusbhc_dev_disable(wusbhc, port_idx);
+               break;
+       case USB_PORT_FEAT_C_ENABLE:
+               wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_ENABLE;
+               break;
+       case USB_PORT_FEAT_SUSPEND:
+       case USB_PORT_FEAT_C_SUSPEND:
+       case 0xffff:            /* ??? FIXME */
+               dev_err(dev, "(port_idx %d) Clear feat %d/%d UNIMPLEMENTED\n",
+                       port_idx, feature, selector);
+               /* dump_stack(); */
+               result = -ENOSYS;
+               break;
+       default:
+               dev_err(dev, "(port_idx %d) Clear feat %d/%d UNKNOWN\n",
+                       port_idx, feature, selector);
+               result = -EPIPE;
+               break;
+       }
+       mutex_unlock(&wusbhc->mutex);
+error:
+       d_fnend(4, dev, "(wusbhc %p feat 0x%04x selector %d port_idx %d) = "
+               "%d\n", wusbhc, feature, selector, port_idx, result);
+       return result;
+}
+
+/*
+ * Return the port's status
+ *
+ * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
+ */
+static int wusbhc_rh_get_port_status(struct wusbhc *wusbhc, u16 port_idx,
+                                    u32 *_buf, u16 wLength)
+{
+       int result = -EINVAL;
+       u16 *buf = (u16 *) _buf;
+
+       d_fnstart(1, wusbhc->dev, "(wusbhc %p port_idx %u wLength %u)\n",
+                 wusbhc, port_idx, wLength);
+       if (port_idx > wusbhc->ports_max)
+               goto error;
+       mutex_lock(&wusbhc->mutex);
+       buf[0] = cpu_to_le16(wusb_port_by_idx(wusbhc, port_idx)->status);
+       buf[1] = cpu_to_le16(wusb_port_by_idx(wusbhc, port_idx)->change);
+       result = 0;
+       mutex_unlock(&wusbhc->mutex);
+error:
+       d_fnend(1, wusbhc->dev, "(wusbhc %p) = %d, buffer:\n", wusbhc, result);
+       d_dump(1, wusbhc->dev, _buf, wLength);
+       return result;
+}
+
+/*
+ * Entry point for Root Hub operations
+ *
+ * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
+ */
+int wusbhc_rh_control(struct usb_hcd *usb_hcd, u16 reqntype, u16 wValue,
+                     u16 wIndex, char *buf, u16 wLength)
+{
+       int result = -ENOSYS;
+       struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+
+       switch (reqntype) {
+       case GetHubDescriptor:
+               result = wusbhc_rh_get_hub_descr(
+                       wusbhc, wValue, wIndex,
+                       (struct usb_hub_descriptor *) buf, wLength);
+               break;
+       case ClearHubFeature:
+               result = wusbhc_rh_clear_hub_feat(wusbhc, wValue);
+               break;
+       case GetHubStatus:
+               result = wusbhc_rh_get_hub_status(wusbhc, (u32 *)buf, wLength);
+               break;
+
+       case SetPortFeature:
+               result = wusbhc_rh_set_port_feat(wusbhc, wValue, wIndex >> 8,
+                                                (wIndex & 0xff) - 1);
+               break;
+       case ClearPortFeature:
+               result = wusbhc_rh_clear_port_feat(wusbhc, wValue, wIndex >> 8,
+                                                  (wIndex & 0xff) - 1);
+               break;
+       case GetPortStatus:
+               result = wusbhc_rh_get_port_status(wusbhc, wIndex - 1,
+                                                  (u32 *)buf, wLength);
+               break;
+
+       case SetHubFeature:
+       default:
+               dev_err(wusbhc->dev, "%s (%p [%p], %x, %x, %x, %p, %x) "
+                       "UNIMPLEMENTED\n", __func__, usb_hcd, wusbhc, reqntype,
+                       wValue, wIndex, buf, wLength);
+               /* dump_stack(); */
+               result = -ENOSYS;
+       }
+       return result;
+}
+EXPORT_SYMBOL_GPL(wusbhc_rh_control);
+
+int wusbhc_rh_suspend(struct usb_hcd *usb_hcd)
+{
+       struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+       dev_err(wusbhc->dev, "%s (%p [%p]) UNIMPLEMENTED\n", __func__,
+               usb_hcd, wusbhc);
+       /* dump_stack(); */
+       return -ENOSYS;
+}
+EXPORT_SYMBOL_GPL(wusbhc_rh_suspend);
+
+int wusbhc_rh_resume(struct usb_hcd *usb_hcd)
+{
+       struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+       dev_err(wusbhc->dev, "%s (%p [%p]) UNIMPLEMENTED\n", __func__,
+               usb_hcd, wusbhc);
+       /* dump_stack(); */
+       return -ENOSYS;
+}
+EXPORT_SYMBOL_GPL(wusbhc_rh_resume);
+
+int wusbhc_rh_start_port_reset(struct usb_hcd *usb_hcd, unsigned port_idx)
+{
+       struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+       dev_err(wusbhc->dev, "%s (%p [%p], port_idx %u) UNIMPLEMENTED\n",
+               __func__, usb_hcd, wusbhc, port_idx);
+       WARN_ON(1);
+       return -ENOSYS;
+}
+EXPORT_SYMBOL_GPL(wusbhc_rh_start_port_reset);
+
+static void wusb_port_init(struct wusb_port *port)
+{
+       port->status |= USB_PORT_STAT_HIGH_SPEED;
+}
+
+/*
+ * Alloc fake port specific fields and status.
+ */
+int wusbhc_rh_create(struct wusbhc *wusbhc)
+{
+       int result = -ENOMEM;
+       size_t port_size, itr;
+       port_size = wusbhc->ports_max * sizeof(wusbhc->port[0]);
+       wusbhc->port = kzalloc(port_size, GFP_KERNEL);
+       if (wusbhc->port == NULL)
+               goto error_port_alloc;
+       for (itr = 0; itr < wusbhc->ports_max; itr++)
+               wusb_port_init(&wusbhc->port[itr]);
+       result = 0;
+error_port_alloc:
+       return result;
+}
+
+void wusbhc_rh_destroy(struct wusbhc *wusbhc)
+{
+       kfree(wusbhc->port);
+}
diff --git a/drivers/usb/wusbcore/security.c b/drivers/usb/wusbcore/security.c
new file mode 100644 (file)
index 0000000..a101cad
--- /dev/null
@@ -0,0 +1,642 @@
+/*
+ * Wireless USB Host Controller
+ * Security support: encryption enablement, etc
+ *
+ * Copyright (C) 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/types.h>
+#include <linux/usb/ch9.h>
+#include <linux/random.h>
+#include "wusbhc.h"
+
+/*
+ * DEBUG & SECURITY WARNING!!!!
+ *
+ * If you enable this past 1, the debug code will weaken the
+ * cryptographic safety of the system (on purpose, for debugging).
+ *
+ * Weaken means:
+ *   we print secret keys and intermediate values all the way,
+ */
+#undef D_LOCAL
+#define D_LOCAL 2
+#include <linux/uwb/debug.h>
+
+static void wusbhc_set_gtk_callback(struct urb *urb);
+static void wusbhc_gtk_rekey_done_work(struct work_struct *work);
+
+int wusbhc_sec_create(struct wusbhc *wusbhc)
+{
+       wusbhc->gtk.descr.bLength = sizeof(wusbhc->gtk.descr) + sizeof(wusbhc->gtk.data);
+       wusbhc->gtk.descr.bDescriptorType = USB_DT_KEY;
+       wusbhc->gtk.descr.bReserved = 0;
+
+       wusbhc->gtk_index = wusb_key_index(0, WUSB_KEY_INDEX_TYPE_GTK,
+                                          WUSB_KEY_INDEX_ORIGINATOR_HOST);
+
+       INIT_WORK(&wusbhc->gtk_rekey_done_work, wusbhc_gtk_rekey_done_work);
+
+       return 0;
+}
+
+
+/* Called when the HC is destroyed */
+void wusbhc_sec_destroy(struct wusbhc *wusbhc)
+{
+}
+
+
+/**
+ * wusbhc_next_tkid - generate a new, currently unused, TKID
+ * @wusbhc:   the WUSB host controller
+ * @wusb_dev: the device whose PTK the TKID is for
+ *            (or NULL for a TKID for a GTK)
+ *
+ * The generated TKID consist of two parts: the device's authenicated
+ * address (or 0 or a GTK); and an incrementing number.  This ensures
+ * that TKIDs cannot be shared between devices and by the time the
+ * incrementing number wraps around the older TKIDs will no longer be
+ * in use (a maximum of two keys may be active at any one time).
+ */
+static u32 wusbhc_next_tkid(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev)
+{
+       u32 *tkid;
+       u32 addr;
+
+       if (wusb_dev == NULL) {
+               tkid = &wusbhc->gtk_tkid;
+               addr = 0;
+       } else {
+               tkid = &wusb_port_by_idx(wusbhc, wusb_dev->port_idx)->ptk_tkid;
+               addr = wusb_dev->addr & 0x7f;
+       }
+
+       *tkid = (addr << 8) | ((*tkid + 1) & 0xff);
+
+       return *tkid;
+}
+
+static void wusbhc_generate_gtk(struct wusbhc *wusbhc)
+{
+       const size_t key_size = sizeof(wusbhc->gtk.data);
+       u32 tkid;
+
+       tkid = wusbhc_next_tkid(wusbhc, NULL);
+
+       wusbhc->gtk.descr.tTKID[0] = (tkid >>  0) & 0xff;
+       wusbhc->gtk.descr.tTKID[1] = (tkid >>  8) & 0xff;
+       wusbhc->gtk.descr.tTKID[2] = (tkid >> 16) & 0xff;
+
+       get_random_bytes(wusbhc->gtk.descr.bKeyData, key_size);
+}
+
+/**
+ * wusbhc_sec_start - start the security management process
+ * @wusbhc: the WUSB host controller
+ *
+ * Generate and set an initial GTK on the host controller.
+ *
+ * Called when the HC is started.
+ */
+int wusbhc_sec_start(struct wusbhc *wusbhc)
+{
+       const size_t key_size = sizeof(wusbhc->gtk.data);
+       int result;
+
+       wusbhc_generate_gtk(wusbhc);
+
+       result = wusbhc->set_gtk(wusbhc, wusbhc->gtk_tkid,
+                                &wusbhc->gtk.descr.bKeyData, key_size);
+       if (result < 0)
+               dev_err(wusbhc->dev, "cannot set GTK for the host: %d\n",
+                       result);
+
+       return result;
+}
+
+/**
+ * wusbhc_sec_stop - stop the security management process
+ * @wusbhc: the WUSB host controller
+ *
+ * Wait for any pending GTK rekeys to stop.
+ */
+void wusbhc_sec_stop(struct wusbhc *wusbhc)
+{
+       cancel_work_sync(&wusbhc->gtk_rekey_done_work);
+}
+
+
+/** @returns encryption type name */
+const char *wusb_et_name(u8 x)
+{
+       switch (x) {
+       case USB_ENC_TYPE_UNSECURE:     return "unsecure";
+       case USB_ENC_TYPE_WIRED:        return "wired";
+       case USB_ENC_TYPE_CCM_1:        return "CCM-1";
+       case USB_ENC_TYPE_RSA_1:        return "RSA-1";
+       default:                        return "unknown";
+       }
+}
+EXPORT_SYMBOL_GPL(wusb_et_name);
+
+/*
+ * Set the device encryption method
+ *
+ * We tell the device which encryption method to use; we do this when
+ * setting up the device's security.
+ */
+static int wusb_dev_set_encryption(struct usb_device *usb_dev, int value)
+{
+       int result;
+       struct device *dev = &usb_dev->dev;
+       struct wusb_dev *wusb_dev = usb_dev->wusb_dev;
+
+       if (value) {
+               value = wusb_dev->ccm1_etd.bEncryptionValue;
+       } else {
+               /* FIXME: should be wusb_dev->etd[UNSECURE].bEncryptionValue */
+               value = 0;
+       }
+       /* Set device's */
+       result = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
+                       USB_REQ_SET_ENCRYPTION,
+                       USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+                       value, 0, NULL, 0, 1000 /* FIXME: arbitrary */);
+       if (result < 0)
+               dev_err(dev, "Can't set device's WUSB encryption to "
+                       "%s (value %d): %d\n",
+                       wusb_et_name(wusb_dev->ccm1_etd.bEncryptionType),
+                       wusb_dev->ccm1_etd.bEncryptionValue,  result);
+       return result;
+}
+
+/*
+ * Set the GTK to be used by a device.
+ *
+ * The device must be authenticated.
+ */
+static int wusb_dev_set_gtk(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev)
+{
+       struct usb_device *usb_dev = wusb_dev->usb_dev;
+
+       return usb_control_msg(
+               usb_dev, usb_sndctrlpipe(usb_dev, 0),
+               USB_REQ_SET_DESCRIPTOR,
+               USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+               USB_DT_KEY << 8 | wusbhc->gtk_index, 0,
+               &wusbhc->gtk.descr, wusbhc->gtk.descr.bLength,
+               1000);
+}
+
+
+/* FIXME: prototype for adding security */
+int wusb_dev_sec_add(struct wusbhc *wusbhc,
+                    struct usb_device *usb_dev, struct wusb_dev *wusb_dev)
+{
+       int result, bytes, secd_size;
+       struct device *dev = &usb_dev->dev;
+       struct usb_security_descriptor secd;
+       const struct usb_encryption_descriptor *etd, *ccm1_etd = NULL;
+       void *secd_buf;
+       const void *itr, *top;
+       char buf[64];
+
+       d_fnstart(3, dev, "(usb_dev %p, wusb_dev %p)\n", usb_dev, wusb_dev);
+       result = usb_get_descriptor(usb_dev, USB_DT_SECURITY,
+                                   0, &secd, sizeof(secd));
+       if (result < sizeof(secd)) {
+               dev_err(dev, "Can't read security descriptor or "
+                       "not enough data: %d\n", result);
+               goto error_secd;
+       }
+       secd_size = le16_to_cpu(secd.wTotalLength);
+       d_printf(5, dev, "got %d bytes of sec descriptor, total is %d\n",
+                result, secd_size);
+       secd_buf = kmalloc(secd_size, GFP_KERNEL);
+       if (secd_buf == NULL) {
+               dev_err(dev, "Can't allocate space for security descriptors\n");
+               goto error_secd_alloc;
+       }
+       result = usb_get_descriptor(usb_dev, USB_DT_SECURITY,
+                                   0, secd_buf, secd_size);
+       if (result < secd_size) {
+               dev_err(dev, "Can't read security descriptor or "
+                       "not enough data: %d\n", result);
+               goto error_secd_all;
+       }
+       d_printf(5, dev, "got %d bytes of sec descriptors\n", result);
+       bytes = 0;
+       itr = secd_buf + sizeof(secd);
+       top = secd_buf + result;
+       while (itr < top) {
+               etd = itr;
+               if (top - itr < sizeof(*etd)) {
+                       dev_err(dev, "BUG: bad device security descriptor; "
+                               "not enough data (%zu vs %zu bytes left)\n",
+                               top - itr, sizeof(*etd));
+                       break;
+               }
+               if (etd->bLength < sizeof(*etd)) {
+                       dev_err(dev, "BUG: bad device encryption descriptor; "
+                               "descriptor is too short "
+                               "(%u vs %zu needed)\n",
+                               etd->bLength, sizeof(*etd));
+                       break;
+               }
+               itr += etd->bLength;
+               bytes += snprintf(buf + bytes, sizeof(buf) - bytes,
+                                 "%s (0x%02x/%02x) ",
+                                 wusb_et_name(etd->bEncryptionType),
+                                 etd->bEncryptionValue, etd->bAuthKeyIndex);
+               if (etd->bEncryptionType == USB_ENC_TYPE_CCM_1)
+                       ccm1_etd = etd;
+       }
+       /* This code only supports CCM1 as of now. */
+       /* FIXME: user has to choose which sec mode to use?
+        * In theory we want CCM */
+       if (ccm1_etd == NULL) {
+               dev_err(dev, "WUSB device doesn't support CCM1 encryption, "
+                       "can't use!\n");
+               result = -EINVAL;
+               goto error_no_ccm1;
+       }
+       wusb_dev->ccm1_etd = *ccm1_etd;
+       dev_info(dev, "supported encryption: %s; using %s (0x%02x/%02x)\n",
+                buf, wusb_et_name(ccm1_etd->bEncryptionType),
+                ccm1_etd->bEncryptionValue, ccm1_etd->bAuthKeyIndex);
+       result = 0;
+       kfree(secd_buf);
+out:
+       d_fnend(3, dev, "(usb_dev %p, wusb_dev %p) = %d\n",
+               usb_dev, wusb_dev, result);
+       return result;
+
+
+error_no_ccm1:
+error_secd_all:
+       kfree(secd_buf);
+error_secd_alloc:
+error_secd:
+       goto out;
+}
+
+void wusb_dev_sec_rm(struct wusb_dev *wusb_dev)
+{
+       /* Nothing so far */
+}
+
+static void hs_printk(unsigned level, struct device *dev,
+                     struct usb_handshake *hs)
+{
+       d_printf(level, dev,
+                "  bMessageNumber: %u\n"
+                "  bStatus:        %u\n"
+                "  tTKID:          %02x %02x %02x\n"
+                "  CDID:           %02x %02x %02x %02x %02x %02x %02x %02x\n"
+                "                  %02x %02x %02x %02x %02x %02x %02x %02x\n"
+                "  nonce:          %02x %02x %02x %02x %02x %02x %02x %02x\n"
+                "                  %02x %02x %02x %02x %02x %02x %02x %02x\n"
+                "  MIC:            %02x %02x %02x %02x %02x %02x %02x %02x\n",
+                hs->bMessageNumber, hs->bStatus,
+                hs->tTKID[2], hs->tTKID[1], hs->tTKID[0],
+                hs->CDID[0], hs->CDID[1], hs->CDID[2], hs->CDID[3],
+                hs->CDID[4], hs->CDID[5], hs->CDID[6], hs->CDID[7],
+                hs->CDID[8], hs->CDID[9], hs->CDID[10], hs->CDID[11],
+                hs->CDID[12], hs->CDID[13], hs->CDID[14], hs->CDID[15],
+                hs->nonce[0], hs->nonce[1], hs->nonce[2], hs->nonce[3],
+                hs->nonce[4], hs->nonce[5], hs->nonce[6], hs->nonce[7],
+                hs->nonce[8], hs->nonce[9], hs->nonce[10], hs->nonce[11],
+                hs->nonce[12], hs->nonce[13], hs->nonce[14], hs->nonce[15],
+                hs->MIC[0], hs->MIC[1], hs->MIC[2], hs->MIC[3],
+                hs->MIC[4], hs->MIC[5], hs->MIC[6], hs->MIC[7]);
+}
+
+/**
+ * Update the address of an unauthenticated WUSB device
+ *
+ * Once we have successfully authenticated, we take it to addr0 state
+ * and then to a normal address.
+ *
+ * Before the device's address (as known by it) was usb_dev->devnum |
+ * 0x80 (unauthenticated address). With this we update it to usb_dev->devnum.
+ */
+static int wusb_dev_update_address(struct wusbhc *wusbhc,
+                                  struct wusb_dev *wusb_dev)
+{
+       int result = -ENOMEM;
+       struct usb_device *usb_dev = wusb_dev->usb_dev;
+       struct device *dev = &usb_dev->dev;
+       u8 new_address = wusb_dev->addr & 0x7F;
+
+       /* Set address 0 */
+       result = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
+                                USB_REQ_SET_ADDRESS, 0,
+                                0, 0, NULL, 0, 1000 /* FIXME: arbitrary */);
+       if (result < 0) {
+               dev_err(dev, "auth failed: can't set address 0: %d\n",
+                       result);
+               goto error_addr0;
+       }
+       result = wusb_set_dev_addr(wusbhc, wusb_dev, 0);
+       if (result < 0)
+               goto error_addr0;
+       usb_ep0_reinit(usb_dev);
+
+       /* Set new (authenticated) address. */
+       result = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
+                                USB_REQ_SET_ADDRESS, 0,
+                                new_address, 0, NULL, 0,
+                                1000 /* FIXME: arbitrary */);
+       if (result < 0) {
+               dev_err(dev, "auth failed: can't set address %u: %d\n",
+                       new_address, result);
+               goto error_addr;
+       }
+       result = wusb_set_dev_addr(wusbhc, wusb_dev, new_address);
+       if (result < 0)
+               goto error_addr;
+       usb_ep0_reinit(usb_dev);
+       usb_dev->authenticated = 1;
+error_addr:
+error_addr0:
+       return result;
+}
+
+/*
+ *
+ *
+ */
+/* FIXME: split and cleanup */
+int wusb_dev_4way_handshake(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev,
+                           struct wusb_ckhdid *ck)
+{
+       int result = -ENOMEM;
+       struct usb_device *usb_dev = wusb_dev->usb_dev;
+       struct device *dev = &usb_dev->dev;
+       u32 tkid;
+       __le32 tkid_le;
+       struct usb_handshake *hs;
+       struct aes_ccm_nonce ccm_n;
+       u8 mic[8];
+       struct wusb_keydvt_in keydvt_in;
+       struct wusb_keydvt_out keydvt_out;
+
+       hs = kzalloc(3*sizeof(hs[0]), GFP_KERNEL);
+       if (hs == NULL) {
+               dev_err(dev, "can't allocate handshake data\n");
+               goto error_kzalloc;
+       }
+
+       /* We need to turn encryption before beginning the 4way
+        * hshake (WUSB1.0[.3.2.2]) */
+       result = wusb_dev_set_encryption(usb_dev, 1);
+       if (result < 0)
+               goto error_dev_set_encryption;
+
+       tkid = wusbhc_next_tkid(wusbhc, wusb_dev);
+       tkid_le = cpu_to_le32(tkid);
+
+       hs[0].bMessageNumber = 1;
+       hs[0].bStatus = 0;
+       memcpy(hs[0].tTKID, &tkid_le, sizeof(hs[0].tTKID));
+       hs[0].bReserved = 0;
+       memcpy(hs[0].CDID, &wusb_dev->cdid, sizeof(hs[0].CDID));
+       get_random_bytes(&hs[0].nonce, sizeof(hs[0].nonce));
+       memset(hs[0].MIC, 0, sizeof(hs[0].MIC));        /* Per WUSB1.0[T7-22] */
+
+       d_printf(1, dev, "I: sending hs1:\n");
+       hs_printk(2, dev, &hs[0]);
+
+       result = usb_control_msg(
+               usb_dev, usb_sndctrlpipe(usb_dev, 0),
+               USB_REQ_SET_HANDSHAKE,
+               USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+               1, 0, &hs[0], sizeof(hs[0]), 1000 /* FIXME: arbitrary */);
+       if (result < 0) {
+               dev_err(dev, "Handshake1: request failed: %d\n", result);
+               goto error_hs1;
+       }
+
+       /* Handshake 2, from the device -- need to verify fields */
+       result = usb_control_msg(
+               usb_dev, usb_rcvctrlpipe(usb_dev, 0),
+               USB_REQ_GET_HANDSHAKE,
+               USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+               2, 0, &hs[1], sizeof(hs[1]), 1000 /* FIXME: arbitrary */);
+       if (result < 0) {
+               dev_err(dev, "Handshake2: request failed: %d\n", result);
+               goto error_hs2;
+       }
+       d_printf(1, dev, "got HS2:\n");
+       hs_printk(2, dev, &hs[1]);
+
+       result = -EINVAL;
+       if (hs[1].bMessageNumber != 2) {
+               dev_err(dev, "Handshake2 failed: bad message number %u\n",
+                       hs[1].bMessageNumber);
+               goto error_hs2;
+       }
+       if (hs[1].bStatus != 0) {
+               dev_err(dev, "Handshake2 failed: bad status %u\n",
+                       hs[1].bStatus);
+               goto error_hs2;
+       }
+       if (memcmp(hs[0].tTKID, hs[1].tTKID, sizeof(hs[0].tTKID))) {
+               dev_err(dev, "Handshake2 failed: TKID mismatch "
+                       "(#1 0x%02x%02x%02x vs #2 0x%02x%02x%02x)\n",
+                       hs[0].tTKID[0], hs[0].tTKID[1], hs[0].tTKID[2],
+                       hs[1].tTKID[0], hs[1].tTKID[1], hs[1].tTKID[2]);
+               goto error_hs2;
+       }
+       if (memcmp(hs[0].CDID, hs[1].CDID, sizeof(hs[0].CDID))) {
+               dev_err(dev, "Handshake2 failed: CDID mismatch\n");
+               goto error_hs2;
+       }
+
+       /* Setup the CCM nonce */
+       memset(&ccm_n.sfn, 0, sizeof(ccm_n.sfn));       /* Per WUSB1.0[6.5.2] */
+       memcpy(ccm_n.tkid, &tkid_le, sizeof(ccm_n.tkid));
+       ccm_n.src_addr = wusbhc->uwb_rc->uwb_dev.dev_addr;
+       ccm_n.dest_addr.data[0] = wusb_dev->addr;
+       ccm_n.dest_addr.data[1] = 0;
+
+       /* Derive the KCK and PTK from CK, the CCM, H and D nonces */
+       memcpy(keydvt_in.hnonce, hs[0].nonce, sizeof(keydvt_in.hnonce));
+       memcpy(keydvt_in.dnonce, hs[1].nonce, sizeof(keydvt_in.dnonce));
+       result = wusb_key_derive(&keydvt_out, ck->data, &ccm_n, &keydvt_in);
+       if (result < 0) {
+               dev_err(dev, "Handshake2 failed: cannot derive keys: %d\n",
+                       result);
+               goto error_hs2;
+       }
+       d_printf(2, dev, "KCK:\n");
+       d_dump(2, dev, keydvt_out.kck, sizeof(keydvt_out.kck));
+       d_printf(2, dev, "PTK:\n");
+       d_dump(2, dev, keydvt_out.ptk, sizeof(keydvt_out.ptk));
+
+       /* Compute MIC and verify it */
+       result = wusb_oob_mic(mic, keydvt_out.kck, &ccm_n, &hs[1]);
+       if (result < 0) {
+               dev_err(dev, "Handshake2 failed: cannot compute MIC: %d\n",
+                       result);
+               goto error_hs2;
+       }
+
+       d_printf(2, dev, "MIC:\n");
+       d_dump(2, dev, mic, sizeof(mic));
+       if (memcmp(hs[1].MIC, mic, sizeof(hs[1].MIC))) {
+               dev_err(dev, "Handshake2 failed: MIC mismatch\n");
+               goto error_hs2;
+       }
+
+       /* Send Handshake3 */
+       hs[2].bMessageNumber = 3;
+       hs[2].bStatus = 0;
+       memcpy(hs[2].tTKID, &tkid_le, sizeof(hs[2].tTKID));
+       hs[2].bReserved = 0;
+       memcpy(hs[2].CDID, &wusb_dev->cdid, sizeof(hs[2].CDID));
+       memcpy(hs[2].nonce, hs[0].nonce, sizeof(hs[2].nonce));
+       result = wusb_oob_mic(hs[2].MIC, keydvt_out.kck, &ccm_n, &hs[2]);
+       if (result < 0) {
+               dev_err(dev, "Handshake3 failed: cannot compute MIC: %d\n",
+                       result);
+               goto error_hs2;
+       }
+
+       d_printf(1, dev, "I: sending hs3:\n");
+       hs_printk(2, dev, &hs[2]);
+
+       result = usb_control_msg(
+               usb_dev, usb_sndctrlpipe(usb_dev, 0),
+               USB_REQ_SET_HANDSHAKE,
+               USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+               3, 0, &hs[2], sizeof(hs[2]), 1000 /* FIXME: arbitrary */);
+       if (result < 0) {
+               dev_err(dev, "Handshake3: request failed: %d\n", result);
+               goto error_hs3;
+       }
+
+       d_printf(1, dev, "I: turning on encryption on host for device\n");
+       d_dump(2, dev, keydvt_out.ptk, sizeof(keydvt_out.ptk));
+       result = wusbhc->set_ptk(wusbhc, wusb_dev->port_idx, tkid,
+                                keydvt_out.ptk, sizeof(keydvt_out.ptk));
+       if (result < 0)
+               goto error_wusbhc_set_ptk;
+
+       d_printf(1, dev, "I: setting a GTK\n");
+       result = wusb_dev_set_gtk(wusbhc, wusb_dev);
+       if (result < 0) {
+               dev_err(dev, "Set GTK for device: request failed: %d\n",
+                       result);
+               goto error_wusbhc_set_gtk;
+       }
+
+       /* Update the device's address from unauth to auth */
+       if (usb_dev->authenticated == 0) {
+               d_printf(1, dev, "I: updating addres to auth from non-auth\n");
+               result = wusb_dev_update_address(wusbhc, wusb_dev);
+               if (result < 0)
+                       goto error_dev_update_address;
+       }
+       result = 0;
+       d_printf(1, dev, "I: 4way handshke done, device authenticated\n");
+
+error_dev_update_address:
+error_wusbhc_set_gtk:
+error_wusbhc_set_ptk:
+error_hs3:
+error_hs2:
+error_hs1:
+       memset(hs, 0, 3*sizeof(hs[0]));
+       memset(&keydvt_out, 0, sizeof(keydvt_out));
+       memset(&keydvt_in, 0, sizeof(keydvt_in));
+       memset(&ccm_n, 0, sizeof(ccm_n));
+       memset(mic, 0, sizeof(mic));
+       if (result < 0) {
+               /* error path */
+               wusb_dev_set_encryption(usb_dev, 0);
+       }
+error_dev_set_encryption:
+       kfree(hs);
+error_kzalloc:
+       return result;
+}
+
+/*
+ * Once all connected and authenticated devices have received the new
+ * GTK, switch the host to using it.
+ */
+static void wusbhc_gtk_rekey_done_work(struct work_struct *work)
+{
+       struct wusbhc *wusbhc = container_of(work, struct wusbhc, gtk_rekey_done_work);
+       size_t key_size = sizeof(wusbhc->gtk.data);
+
+       mutex_lock(&wusbhc->mutex);
+
+       if (--wusbhc->pending_set_gtks == 0)
+               wusbhc->set_gtk(wusbhc, wusbhc->gtk_tkid, &wusbhc->gtk.descr.bKeyData, key_size);
+
+       mutex_unlock(&wusbhc->mutex);
+}
+
+static void wusbhc_set_gtk_callback(struct urb *urb)
+{
+       struct wusbhc *wusbhc = urb->context;
+
+       queue_work(wusbd, &wusbhc->gtk_rekey_done_work);
+}
+
+/**
+ * wusbhc_gtk_rekey - generate and distribute a new GTK
+ * @wusbhc: the WUSB host controller
+ *
+ * Generate a new GTK and distribute it to all connected and
+ * authenticated devices.  When all devices have the new GTK, the host
+ * starts using it.
+ *
+ * This must be called after every device disconnect (see [WUSB]
+ * section 6.2.11.2).
+ */
+void wusbhc_gtk_rekey(struct wusbhc *wusbhc)
+{
+       static const size_t key_size = sizeof(wusbhc->gtk.data);
+       int p;
+
+       wusbhc_generate_gtk(wusbhc);
+
+       for (p = 0; p < wusbhc->ports_max; p++) {
+               struct wusb_dev *wusb_dev;
+
+               wusb_dev = wusbhc->port[p].wusb_dev;
+               if (!wusb_dev || !wusb_dev->usb_dev | !wusb_dev->usb_dev->authenticated)
+                       continue;
+
+               usb_fill_control_urb(wusb_dev->set_gtk_urb, wusb_dev->usb_dev,
+                                    usb_sndctrlpipe(wusb_dev->usb_dev, 0),
+                                    (void *)wusb_dev->set_gtk_req,
+                                    &wusbhc->gtk.descr, wusbhc->gtk.descr.bLength,
+                                    wusbhc_set_gtk_callback, wusbhc);
+               if (usb_submit_urb(wusb_dev->set_gtk_urb, GFP_KERNEL) == 0)
+                       wusbhc->pending_set_gtks++;
+       }
+       if (wusbhc->pending_set_gtks == 0)
+               wusbhc->set_gtk(wusbhc, wusbhc->gtk_tkid, &wusbhc->gtk.descr.bKeyData, key_size);
+}
diff --git a/drivers/usb/wusbcore/wa-hc.c b/drivers/usb/wusbcore/wa-hc.c
new file mode 100644 (file)
index 0000000..9d04722
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Wire Adapter Host Controller Driver
+ * Common items to HWA and DWA based HCDs
+ *
+ * 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 "wusbhc.h"
+#include "wa-hc.h"
+
+/**
+ * Assumes
+ *
+ * wa->usb_dev and wa->usb_iface initialized and refcounted,
+ * wa->wa_descr initialized.
+ */
+int wa_create(struct wahc *wa, struct usb_interface *iface)
+{
+       int result;
+       struct device *dev = &iface->dev;
+
+       result = wa_rpipes_create(wa);
+       if (result < 0)
+               goto error_rpipes_create;
+       /* Fill up Data Transfer EP pointers */
+       wa->dti_epd = &iface->cur_altsetting->endpoint[1].desc;
+       wa->dto_epd = &iface->cur_altsetting->endpoint[2].desc;
+       wa->xfer_result_size = le16_to_cpu(wa->dti_epd->wMaxPacketSize);
+       wa->xfer_result = kmalloc(wa->xfer_result_size, GFP_KERNEL);
+       if (wa->xfer_result == NULL)
+               goto error_xfer_result_alloc;
+       result = wa_nep_create(wa, iface);
+       if (result < 0) {
+               dev_err(dev, "WA-CDS: can't initialize notif endpoint: %d\n",
+                       result);
+               goto error_nep_create;
+       }
+       return 0;
+
+error_nep_create:
+       kfree(wa->xfer_result);
+error_xfer_result_alloc:
+       wa_rpipes_destroy(wa);
+error_rpipes_create:
+       return result;
+}
+EXPORT_SYMBOL_GPL(wa_create);
+
+
+void __wa_destroy(struct wahc *wa)
+{
+       if (wa->dti_urb) {
+               usb_kill_urb(wa->dti_urb);
+               usb_put_urb(wa->dti_urb);
+               usb_kill_urb(wa->buf_in_urb);
+               usb_put_urb(wa->buf_in_urb);
+       }
+       kfree(wa->xfer_result);
+       wa_nep_destroy(wa);
+       wa_rpipes_destroy(wa);
+}
+EXPORT_SYMBOL_GPL(__wa_destroy);
+
+/**
+ * wa_reset_all - reset the WA device
+ * @wa: the WA to be reset
+ *
+ * For HWAs the radio controller and all other PALs are also reset.
+ */
+void wa_reset_all(struct wahc *wa)
+{
+       /* FIXME: assuming HWA. */
+       wusbhc_reset_all(wa->wusb);
+}
+
+MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
+MODULE_DESCRIPTION("Wireless USB Wire Adapter core");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/wusbcore/wa-hc.h b/drivers/usb/wusbcore/wa-hc.h
new file mode 100644 (file)
index 0000000..586d350
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+ * HWA Host Controller Driver
+ * Wire Adapter Control/Data Streaming Iface (WUSB1.0[8])
+ *
+ * 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 implements a USB Host Controller (struct usb_hcd) for a
+ * Wireless USB Host Controller based on the Wireless USB 1.0
+ * Host-Wire-Adapter specification (in layman terms, a USB-dongle that
+ * implements a Wireless USB host).
+ *
+ * Check out the Design-overview.txt file in the source documentation
+ * for other details on the implementation.
+ *
+ * Main blocks:
+ *
+ *  driver     glue with the driver API, workqueue daemon
+ *
+ *  lc         RC instance life cycle management (create, destroy...)
+ *
+ *  hcd        glue with the USB API Host Controller Interface API.
+ *
+ *  nep        Notification EndPoint managent: collect notifications
+ *             and queue them with the workqueue daemon.
+ *
+ *             Handle notifications as coming from the NEP. Sends them
+ *             off others to their respective modules (eg: connect,
+ *             disconnect and reset go to devconnect).
+ *
+ *  rpipe      Remote Pipe management; rpipe is what we use to write
+ *             to an endpoint on a WUSB device that is connected to a
+ *             HWA RC.
+ *
+ *  xfer       Transfer managment -- this is all the code that gets a
+ *             buffer and pushes it to a device (or viceversa). *
+ *
+ * Some day a lot of this code will be shared between this driver and
+ * the drivers for DWA (xfer, rpipe).
+ *
+ * All starts at driver.c:hwahc_probe(), when one of this guys is
+ * connected. hwahc_disconnect() stops it.
+ *
+ * During operation, the main driver is devices connecting or
+ * disconnecting. They cause the HWA RC to send notifications into
+ * nep.c:hwahc_nep_cb() that will dispatch them to
+ * notif.c:wa_notif_dispatch(). From there they will fan to cause
+ * device connects, disconnects, etc.
+ *
+ * Note much of the activity is difficult to follow. For example a
+ * device connect goes to devconnect, which will cause the "fake" root
+ * hub port to show a connect and stop there. Then khubd will notice
+ * and call into the rh.c:hwahc_rc_port_reset() code to authenticate
+ * the device (and this might require user intervention) and enable
+ * the port.
+ *
+ * We also have a timer workqueue going from devconnect.c that
+ * schedules in hwahc_devconnect_create().
+ *
+ * The rest of the traffic is in the usual entry points of a USB HCD,
+ * which are hooked up in driver.c:hwahc_rc_driver, and defined in
+ * hcd.c.
+ */
+
+#ifndef __HWAHC_INTERNAL_H__
+#define __HWAHC_INTERNAL_H__
+
+#include <linux/completion.h>
+#include <linux/usb.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/uwb.h>
+#include <linux/usb/wusb.h>
+#include <linux/usb/wusb-wa.h>
+
+struct wusbhc;
+struct wahc;
+extern void wa_urb_enqueue_run(struct work_struct *ws);
+
+/**
+ * RPipe instance
+ *
+ * @descr's fields are kept in LE, as we need to send it back and
+ * forth.
+ *
+ * @wa is referenced when set
+ *
+ * @segs_available is the number of requests segments that still can
+ *                 be submitted to the controller without overloading
+ *                 it. It is initialized to descr->wRequests when
+ *                 aiming.
+ *
+ * A rpipe supports a max of descr->wRequests at the same time; before
+ * submitting seg_lock has to be taken. If segs_avail > 0, then we can
+ * submit; if not, we have to queue them.
+ */
+struct wa_rpipe {
+       struct kref refcnt;
+       struct usb_rpipe_descriptor descr;
+       struct usb_host_endpoint *ep;
+       struct wahc *wa;
+       spinlock_t seg_lock;
+       struct list_head seg_list;
+       atomic_t segs_available;
+       u8 buffer[1];   /* For reads/writes on USB */
+};
+
+
+/**
+ * Instance of a HWA Host Controller
+ *
+ * Except where a more specific lock/mutex applies or atomic, all
+ * fields protected by @mutex.
+ *
+ * @wa_descr  Can be accessed without locking because it is in
+ *            the same area where the device descriptors were
+ *            read, so it is guaranteed to exist umodified while
+ *            the device exists.
+ *
+ *            Endianess has been converted to CPU's.
+ *
+ * @nep_* can be accessed without locking as its processing is
+ *        serialized; we submit a NEP URB and it comes to
+ *        hwahc_nep_cb(), which won't issue another URB until it is
+ *        done processing it.
+ *
+ * @xfer_list:
+ *
+ *   List of active transfers to verify existence from a xfer id
+ *   gotten from the xfer result message. Can't use urb->list because
+ *   it goes by endpoint, and we don't know the endpoint at the time
+ *   when we get the xfer result message. We can't really rely on the
+ *   pointer (will have to change for 64 bits) as the xfer id is 32 bits.
+ *
+ * @xfer_delayed_list:   List of transfers that need to be started
+ *                       (with a workqueue, because they were
+ *                       submitted from an atomic context).
+ *
+ * FIXME: this needs to be layered up: a wusbhc layer (for sharing
+ *        comonalities with WHCI), a wa layer (for sharing
+ *        comonalities with DWA-RC).
+ */
+struct wahc {
+       struct usb_device *usb_dev;
+       struct usb_interface *usb_iface;
+
+       /* HC to deliver notifications */
+       union {
+               struct wusbhc *wusb;
+               struct dwahc *dwa;
+       };
+
+       const struct usb_endpoint_descriptor *dto_epd, *dti_epd;
+       const struct usb_wa_descriptor *wa_descr;
+
+       struct urb *nep_urb;            /* Notification EndPoint [lockless] */
+       struct edc nep_edc;
+       void *nep_buffer;
+       size_t nep_buffer_size;
+
+       atomic_t notifs_queued;
+
+       u16 rpipes;
+       unsigned long *rpipe_bm;        /* rpipe usage bitmap */
+       spinlock_t rpipe_bm_lock;       /* protect rpipe_bm */
+       struct mutex rpipe_mutex;       /* assigning resources to endpoints */
+
+       struct urb *dti_urb;            /* URB for reading xfer results */
+       struct urb *buf_in_urb;         /* URB for reading data in */
+       struct edc dti_edc;             /* DTI error density counter */
+       struct wa_xfer_result *xfer_result; /* real size = dti_ep maxpktsize */
+       size_t xfer_result_size;
+
+       s32 status;                     /* For reading status */
+
+       struct list_head xfer_list;
+       struct list_head xfer_delayed_list;
+       spinlock_t xfer_list_lock;
+       struct work_struct xfer_work;
+       atomic_t xfer_id_count;
+};
+
+
+extern int wa_create(struct wahc *wa, struct usb_interface *iface);
+extern void __wa_destroy(struct wahc *wa);
+void wa_reset_all(struct wahc *wa);
+
+
+/* Miscellaneous constants */
+enum {
+       /** Max number of EPROTO errors we tolerate on the NEP in a
+        * period of time */
+       HWAHC_EPROTO_MAX = 16,
+       /** Period of time for EPROTO errors (in jiffies) */
+       HWAHC_EPROTO_PERIOD = 4 * HZ,
+};
+
+
+/* Notification endpoint handling */
+extern int wa_nep_create(struct wahc *, struct usb_interface *);
+extern void wa_nep_destroy(struct wahc *);
+
+static inline int wa_nep_arm(struct wahc *wa, gfp_t gfp_mask)
+{
+       struct urb *urb = wa->nep_urb;
+       urb->transfer_buffer = wa->nep_buffer;
+       urb->transfer_buffer_length = wa->nep_buffer_size;
+       return usb_submit_urb(urb, gfp_mask);
+}
+
+static inline void wa_nep_disarm(struct wahc *wa)
+{
+       usb_kill_urb(wa->nep_urb);
+}
+
+
+/* RPipes */
+static inline void wa_rpipe_init(struct wahc *wa)
+{
+       spin_lock_init(&wa->rpipe_bm_lock);
+       mutex_init(&wa->rpipe_mutex);
+}
+
+static inline void wa_init(struct wahc *wa)
+{
+       edc_init(&wa->nep_edc);
+       atomic_set(&wa->notifs_queued, 0);
+       wa_rpipe_init(wa);
+       edc_init(&wa->dti_edc);
+       INIT_LIST_HEAD(&wa->xfer_list);
+       INIT_LIST_HEAD(&wa->xfer_delayed_list);
+       spin_lock_init(&wa->xfer_list_lock);
+       INIT_WORK(&wa->xfer_work, wa_urb_enqueue_run);
+       atomic_set(&wa->xfer_id_count, 1);
+}
+
+/**
+ * Destroy a pipe (when refcount drops to zero)
+ *
+ * Assumes it has been moved to the "QUIESCING" state.
+ */
+struct wa_xfer;
+extern void rpipe_destroy(struct kref *_rpipe);
+static inline
+void __rpipe_get(struct wa_rpipe *rpipe)
+{
+       kref_get(&rpipe->refcnt);
+}
+extern int rpipe_get_by_ep(struct wahc *, struct usb_host_endpoint *,
+                          struct urb *, gfp_t);
+static inline void rpipe_put(struct wa_rpipe *rpipe)
+{
+       kref_put(&rpipe->refcnt, rpipe_destroy);
+
+}
+extern void rpipe_ep_disable(struct wahc *, struct usb_host_endpoint *);
+extern int wa_rpipes_create(struct wahc *);
+extern void wa_rpipes_destroy(struct wahc *);
+static inline void rpipe_avail_dec(struct wa_rpipe *rpipe)
+{
+       atomic_dec(&rpipe->segs_available);
+}
+
+/**
+ * Returns true if the rpipe is ready to submit more segments.
+ */
+static inline int rpipe_avail_inc(struct wa_rpipe *rpipe)
+{
+       return atomic_inc_return(&rpipe->segs_available) > 0
+               && !list_empty(&rpipe->seg_list);
+}
+
+
+/* Transferring data */
+extern int wa_urb_enqueue(struct wahc *, struct usb_host_endpoint *,
+                         struct urb *, gfp_t);
+extern int wa_urb_dequeue(struct wahc *, struct urb *);
+extern void wa_handle_notif_xfer(struct wahc *, struct wa_notif_hdr *);
+
+
+/* Misc
+ *
+ * FIXME: Refcounting for the actual @hwahc object is not correct; I
+ *        mean, this should be refcounting on the HCD underneath, but
+ *        it is not. In any case, the semantics for HCD refcounting
+ *        are *weird*...on refcount reaching zero it just frees
+ *        it...no RC specific function is called...unless I miss
+ *        something.
+ *
+ * FIXME: has to go away in favour of an 'struct' hcd based sollution
+ */
+static inline struct wahc *wa_get(struct wahc *wa)
+{
+       usb_get_intf(wa->usb_iface);
+       return wa;
+}
+
+static inline void wa_put(struct wahc *wa)
+{
+       usb_put_intf(wa->usb_iface);
+}
+
+
+static inline int __wa_feature(struct wahc *wa, unsigned op, u16 feature)
+{
+       return usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+                       op ? USB_REQ_SET_FEATURE : USB_REQ_CLEAR_FEATURE,
+                       USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+                       feature,
+                       wa->usb_iface->cur_altsetting->desc.bInterfaceNumber,
+                       NULL, 0, 1000 /* FIXME: arbitrary */);
+}
+
+
+static inline int __wa_set_feature(struct wahc *wa, u16 feature)
+{
+       return  __wa_feature(wa, 1, feature);
+}
+
+
+static inline int __wa_clear_feature(struct wahc *wa, u16 feature)
+{
+       return __wa_feature(wa, 0, feature);
+}
+
+
+/**
+ * Return the status of a Wire Adapter
+ *
+ * @wa:                Wire Adapter instance
+ * @returns     < 0 errno code on error, or status bitmap as described
+ *              in WUSB1.0[8.3.1.6].
+ *
+ * NOTE: need malloc, some arches don't take USB from the stack
+ */
+static inline
+s32 __wa_get_status(struct wahc *wa)
+{
+       s32 result;
+       result = usb_control_msg(
+               wa->usb_dev, usb_rcvctrlpipe(wa->usb_dev, 0),
+               USB_REQ_GET_STATUS,
+               USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+               0, wa->usb_iface->cur_altsetting->desc.bInterfaceNumber,
+               &wa->status, sizeof(wa->status),
+               1000 /* FIXME: arbitrary */);
+       if (result >= 0)
+               result = wa->status;
+       return result;
+}
+
+
+/**
+ * Waits until the Wire Adapter's status matches @mask/@value
+ *
+ * @wa:                Wire Adapter instance.
+ * @returns     < 0 errno code on error, otherwise status.
+ *
+ * Loop until the WAs status matches the mask and value (status & mask
+ * == value). Timeout if it doesn't happen.
+ *
+ * FIXME: is there an official specification on how long status
+ *        changes can take?
+ */
+static inline s32 __wa_wait_status(struct wahc *wa, u32 mask, u32 value)
+{
+       s32 result;
+       unsigned loops = 10;
+       do {
+               msleep(50);
+               result = __wa_get_status(wa);
+               if ((result & mask) == value)
+                       break;
+               if (loops-- == 0) {
+                       result = -ETIMEDOUT;
+                       break;
+               }
+       } while (result >= 0);
+       return result;
+}
+
+
+/** Command @hwahc to stop, @returns 0 if ok, < 0 errno code on error */
+static inline int __wa_stop(struct wahc *wa)
+{
+       int result;
+       struct device *dev = &wa->usb_iface->dev;
+
+       result = __wa_clear_feature(wa, WA_ENABLE);
+       if (result < 0 && result != -ENODEV) {
+               dev_err(dev, "error commanding HC to stop: %d\n", result);
+               goto out;
+       }
+       result = __wa_wait_status(wa, WA_ENABLE, 0);
+       if (result < 0 && result != -ENODEV)
+               dev_err(dev, "error waiting for HC to stop: %d\n", result);
+out:
+       return 0;
+}
+
+
+#endif /* #ifndef __HWAHC_INTERNAL_H__ */
diff --git a/drivers/usb/wusbcore/wa-nep.c b/drivers/usb/wusbcore/wa-nep.c
new file mode 100644 (file)
index 0000000..3f54299
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ * WUSB Wire Adapter: Control/Data Streaming Interface (WUSB[8])
+ * Notification EndPoint support
+ *
+ * Copyright (C) 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 part takes care of getting the notification from the hw
+ * only and dispatching through wusbwad into
+ * wa_notif_dispatch. Handling is done there.
+ *
+ * WA notifications are limited in size; most of them are three or
+ * four bytes long, and the longest is the HWA Device Notification,
+ * which would not exceed 38 bytes (DNs are limited in payload to 32
+ * bytes plus 3 bytes header (WUSB1.0[7.6p2]), plus 3 bytes HWA
+ * header (WUSB1.0[8.5.4.2]).
+ *
+ * It is not clear if more than one Device Notification can be packed
+ * in a HWA Notification, I assume no because of the wording in
+ * WUSB1.0[8.5.4.2]. In any case, the bigger any notification could
+ * get is 256 bytes (as the bLength field is a byte).
+ *
+ * So what we do is we have this buffer and read into it; when a
+ * notification arrives we schedule work to a specific, single thread
+ * workqueue (so notifications are serialized) and copy the
+ * notification data. After scheduling the work, we rearm the read from
+ * the notification endpoint.
+ *
+ * Entry points here are:
+ *
+ * wa_nep_[create|destroy]()   To initialize/release this subsystem
+ *
+ * wa_nep_cb()                 Callback for the notification
+ *                                endpoint; when data is ready, this
+ *                                does the dispatching.
+ */
+#include <linux/workqueue.h>
+#include <linux/ctype.h>
+#include <linux/uwb/debug.h>
+#include "wa-hc.h"
+#include "wusbhc.h"
+
+/* Structure for queueing notifications to the workqueue */
+struct wa_notif_work {
+       struct work_struct work;
+       struct wahc *wa;
+       size_t size;
+       u8 data[];
+};
+
+/*
+ * Process incoming notifications from the WA's Notification EndPoint
+ * [the wuswad daemon, basically]
+ *
+ * @_nw:       Pointer to a descriptor which has the pointer to the
+ *             @wa, the size of the buffer and the work queue
+ *             structure (so we can free all when done).
+ * @returns     0 if ok, < 0 errno code on error.
+ *
+ * All notifications follow the same format; they need to start with a
+ * 'struct wa_notif_hdr' header, so it is easy to parse through
+ * them. We just break the buffer in individual notifications (the
+ * standard doesn't say if it can be done or is forbidden, so we are
+ * cautious) and dispatch each.
+ *
+ * So the handling layers are is:
+ *
+ *   WA specific notification (from NEP)
+ *      Device Notification Received -> wa_handle_notif_dn()
+ *        WUSB Device notification generic handling
+ *      BPST Adjustment -> wa_handle_notif_bpst_adj()
+ *      ... -> ...
+ *
+ * @wa has to be referenced
+ */
+static void wa_notif_dispatch(struct work_struct *ws)
+{
+       void *itr;
+       u8 missing = 0;
+       struct wa_notif_work *nw = container_of(ws, struct wa_notif_work, work);
+       struct wahc *wa = nw->wa;
+       struct wa_notif_hdr *notif_hdr;
+       size_t size;
+
+       struct device *dev = &wa->usb_iface->dev;
+
+#if 0
+       /* FIXME: need to check for this??? */
+       if (usb_hcd->state == HC_STATE_QUIESCING)       /* Going down? */
+               goto out;                               /* screw it */
+#endif
+       atomic_dec(&wa->notifs_queued);         /* Throttling ctl */
+       dev = &wa->usb_iface->dev;
+       size = nw->size;
+       itr = nw->data;
+
+       while (size) {
+               if (size < sizeof(*notif_hdr)) {
+                       missing = sizeof(*notif_hdr) - size;
+                       goto exhausted_buffer;
+               }
+               notif_hdr = itr;
+               if (size < notif_hdr->bLength)
+                       goto exhausted_buffer;
+               itr += notif_hdr->bLength;
+               size -= notif_hdr->bLength;
+               /* Dispatch the notification [don't use itr or size!] */
+               switch (notif_hdr->bNotifyType) {
+               case HWA_NOTIF_DN: {
+                       struct hwa_notif_dn *hwa_dn;
+                       hwa_dn = container_of(notif_hdr, struct hwa_notif_dn,
+                                             hdr);
+                       wusbhc_handle_dn(wa->wusb, hwa_dn->bSourceDeviceAddr,
+                                        hwa_dn->dndata,
+                                        notif_hdr->bLength - sizeof(*hwa_dn));
+                       break;
+               }
+               case WA_NOTIF_TRANSFER:
+                       wa_handle_notif_xfer(wa, notif_hdr);
+                       break;
+               case DWA_NOTIF_RWAKE:
+               case DWA_NOTIF_PORTSTATUS:
+               case HWA_NOTIF_BPST_ADJ:
+                       /* FIXME: unimplemented WA NOTIFs */
+                       /* fallthru */
+               default:
+                       if (printk_ratelimit()) {
+                               dev_err(dev, "HWA: unknown notification 0x%x, "
+                                       "%zu bytes; discarding\n",
+                                       notif_hdr->bNotifyType,
+                                       (size_t)notif_hdr->bLength);
+                               dump_bytes(dev, notif_hdr, 16);
+                       }
+                       break;
+               }
+       }
+out:
+       wa_put(wa);
+       kfree(nw);
+       return;
+
+       /* THIS SHOULD NOT HAPPEN
+        *
+        * Buffer exahusted with partial data remaining; just warn and
+        * discard the data, as this should not happen.
+        */
+exhausted_buffer:
+       if (!printk_ratelimit())
+               goto out;
+       dev_warn(dev, "HWA: device sent short notification, "
+                "%d bytes missing; discarding %d bytes.\n",
+                missing, (int)size);
+       dump_bytes(dev, itr, size);
+       goto out;
+}
+
+/*
+ * Deliver incoming WA notifications to the wusbwa workqueue
+ *
+ * @wa:        Pointer the Wire Adapter Controller Data Streaming
+ *              instance (part of an 'struct usb_hcd').
+ * @size:       Size of the received buffer
+ * @returns     0 if ok, < 0 errno code on error.
+ *
+ * The input buffer is @wa->nep_buffer, with @size bytes
+ * (guaranteed to fit in the allocated space,
+ * @wa->nep_buffer_size).
+ */
+static int wa_nep_queue(struct wahc *wa, size_t size)
+{
+       int result = 0;
+       struct device *dev = &wa->usb_iface->dev;
+       struct wa_notif_work *nw;
+
+       /* dev_fnstart(dev, "(wa %p, size %zu)\n", wa, size); */
+       BUG_ON(size > wa->nep_buffer_size);
+       if (size == 0)
+               goto out;
+       if (atomic_read(&wa->notifs_queued) > 200) {
+               if (printk_ratelimit())
+                       dev_err(dev, "Too many notifications queued, "
+                               "throttling back\n");
+               goto out;
+       }
+       nw = kzalloc(sizeof(*nw) + size, GFP_ATOMIC);
+       if (nw == NULL) {
+               if (printk_ratelimit())
+                       dev_err(dev, "No memory to queue notification\n");
+               goto out;
+       }
+       INIT_WORK(&nw->work, wa_notif_dispatch);
+       nw->wa = wa_get(wa);
+       nw->size = size;
+       memcpy(nw->data, wa->nep_buffer, size);
+       atomic_inc(&wa->notifs_queued);         /* Throttling ctl */
+       queue_work(wusbd, &nw->work);
+out:
+       /* dev_fnend(dev, "(wa %p, size %zu) = result\n", wa, size, result); */
+       return result;
+}
+
+/*
+ * Callback for the notification event endpoint
+ *
+ * Check's that everything is fine and then passes the data to be
+ * queued to the workqueue.
+ */
+static void wa_nep_cb(struct urb *urb)
+{
+       int result;
+       struct wahc *wa = urb->context;
+       struct device *dev = &wa->usb_iface->dev;
+
+       switch (result = urb->status) {
+       case 0:
+               result = wa_nep_queue(wa, urb->actual_length);
+               if (result < 0)
+                       dev_err(dev, "NEP: unable to process notification(s): "
+                               "%d\n", result);
+               break;
+       case -ECONNRESET:       /* Not an error, but a controlled situation; */
+       case -ENOENT:           /* (we killed the URB)...so, no broadcast */
+       case -ESHUTDOWN:
+               dev_dbg(dev, "NEP: going down %d\n", urb->status);
+               goto out;
+       default:        /* On general errors, we retry unless it gets ugly */
+               if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS,
+                           EDC_ERROR_TIMEFRAME)) {
+                       dev_err(dev, "NEP: URB max acceptable errors "
+                               "exceeded, resetting device\n");
+                       wa_reset_all(wa);
+                       goto out;
+               }
+               dev_err(dev, "NEP: URB error %d\n", urb->status);
+       }
+       result = wa_nep_arm(wa, GFP_ATOMIC);
+       if (result < 0) {
+               dev_err(dev, "NEP: cannot submit URB: %d\n", result);
+               wa_reset_all(wa);
+       }
+out:
+       return;
+}
+
+/*
+ * Initialize @wa's notification and event's endpoint stuff
+ *
+ * This includes the allocating the read buffer, the context ID
+ * allocation bitmap, the URB and submitting the URB.
+ */
+int wa_nep_create(struct wahc *wa, struct usb_interface *iface)
+{
+       int result;
+       struct usb_endpoint_descriptor *epd;
+       struct usb_device *usb_dev = interface_to_usbdev(iface);
+       struct device *dev = &iface->dev;
+
+       edc_init(&wa->nep_edc);
+       epd = &iface->cur_altsetting->endpoint[0].desc;
+       wa->nep_buffer_size = 1024;
+       wa->nep_buffer = kmalloc(wa->nep_buffer_size, GFP_KERNEL);
+       if (wa->nep_buffer == NULL) {
+               dev_err(dev, "Unable to allocate notification's read buffer\n");
+               goto error_nep_buffer;
+       }
+       wa->nep_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (wa->nep_urb == NULL) {
+               dev_err(dev, "Unable to allocate notification URB\n");
+               goto error_urb_alloc;
+       }
+       usb_fill_int_urb(wa->nep_urb, usb_dev,
+                        usb_rcvintpipe(usb_dev, epd->bEndpointAddress),
+                        wa->nep_buffer, wa->nep_buffer_size,
+                        wa_nep_cb, wa, epd->bInterval);
+       result = wa_nep_arm(wa, GFP_KERNEL);
+       if (result < 0) {
+               dev_err(dev, "Cannot submit notification URB: %d\n", result);
+               goto error_nep_arm;
+       }
+       return 0;
+
+error_nep_arm:
+       usb_free_urb(wa->nep_urb);
+error_urb_alloc:
+       kfree(wa->nep_buffer);
+error_nep_buffer:
+       return -ENOMEM;
+}
+
+void wa_nep_destroy(struct wahc *wa)
+{
+       wa_nep_disarm(wa);
+       usb_free_urb(wa->nep_urb);
+       kfree(wa->nep_buffer);
+}
diff --git a/drivers/usb/wusbcore/wa-rpipe.c b/drivers/usb/wusbcore/wa-rpipe.c
new file mode 100644 (file)
index 0000000..f18e4aa
--- /dev/null
@@ -0,0 +1,562 @@
+/*
+ * WUSB Wire Adapter
+ * rpipe 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
+ *
+ * RPIPE
+ *
+ *   Targetted at different downstream endpoints
+ *
+ *   Descriptor: use to config the remote pipe.
+ *
+ *   The number of blocks could be dynamic (wBlocks in descriptor is
+ *   0)--need to schedule them then.
+ *
+ * Each bit in wa->rpipe_bm represents if an rpipe is being used or
+ * not. Rpipes are represented with a 'struct wa_rpipe' that is
+ * attached to the hcpriv member of a 'struct usb_host_endpoint'.
+ *
+ * When you need to xfer data to an endpoint, you get an rpipe for it
+ * with wa_ep_rpipe_get(), which gives you a reference to the rpipe
+ * and keeps a single one (the first one) with the endpoint. When you
+ * are done transferring, you drop that reference. At the end the
+ * rpipe is always allocated and bound to the endpoint. There it might
+ * be recycled when not used.
+ *
+ * Addresses:
+ *
+ *  We use a 1:1 mapping mechanism between port address (0 based
+ *  index, actually) and the address. The USB stack knows about this.
+ *
+ *  USB Stack port number    4 (1 based)
+ *  WUSB code port index     3 (0 based)
+ *  USB Addresss             5 (2 based -- 0 is for default, 1 for root hub)
+ *
+ *  Now, because we don't use the concept as default address exactly
+ *  like the (wired) USB code does, we need to kind of skip it. So we
+ *  never take addresses from the urb->pipe, but from the
+ *  urb->dev->devnum, to make sure that we always have the right
+ *  destination address.
+ */
+#include <linux/init.h>
+#include <asm/atomic.h>
+#include <linux/bitmap.h>
+#include "wusbhc.h"
+#include "wa-hc.h"
+
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+
+static int __rpipe_get_descr(struct wahc *wa,
+                            struct usb_rpipe_descriptor *descr, u16 index)
+{
+       ssize_t result;
+       struct device *dev = &wa->usb_iface->dev;
+
+       /* Get the RPIPE descriptor -- we cannot use the usb_get_descriptor()
+        * function because the arguments are different.
+        */
+       d_printf(1, dev, "rpipe %u: get descr\n", index);
+       result = usb_control_msg(
+               wa->usb_dev, usb_rcvctrlpipe(wa->usb_dev, 0),
+               USB_REQ_GET_DESCRIPTOR,
+               USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_RPIPE,
+               USB_DT_RPIPE<<8, index, descr, sizeof(*descr),
+               1000 /* FIXME: arbitrary */);
+       if (result < 0) {
+               dev_err(dev, "rpipe %u: get descriptor failed: %d\n",
+                       index, (int)result);
+               goto error;
+       }
+       if (result < sizeof(*descr)) {
+               dev_err(dev, "rpipe %u: got short descriptor "
+                       "(%zd vs %zd bytes needed)\n",
+                       index, result, sizeof(*descr));
+               result = -EINVAL;
+               goto error;
+       }
+       result = 0;
+
+error:
+       return result;
+}
+
+/*
+ *
+ * The descriptor is assumed to be properly initialized (ie: you got
+ * it through __rpipe_get_descr()).
+ */
+static int __rpipe_set_descr(struct wahc *wa,
+                            struct usb_rpipe_descriptor *descr, u16 index)
+{
+       ssize_t result;
+       struct device *dev = &wa->usb_iface->dev;
+
+       /* we cannot use the usb_get_descriptor() function because the
+        * arguments are different.
+        */
+       d_printf(1, dev, "rpipe %u: set descr\n", index);
+       result = usb_control_msg(
+               wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+               USB_REQ_SET_DESCRIPTOR,
+               USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_RPIPE,
+               USB_DT_RPIPE<<8, index, descr, sizeof(*descr),
+               HZ / 10);
+       if (result < 0) {
+               dev_err(dev, "rpipe %u: set descriptor failed: %d\n",
+                       index, (int)result);
+               goto error;
+       }
+       if (result < sizeof(*descr)) {
+               dev_err(dev, "rpipe %u: sent short descriptor "
+                       "(%zd vs %zd bytes required)\n",
+                       index, result, sizeof(*descr));
+               result = -EINVAL;
+               goto error;
+       }
+       result = 0;
+
+error:
+       return result;
+
+}
+
+static void rpipe_init(struct wa_rpipe *rpipe)
+{
+       kref_init(&rpipe->refcnt);
+       spin_lock_init(&rpipe->seg_lock);
+       INIT_LIST_HEAD(&rpipe->seg_list);
+}
+
+static unsigned rpipe_get_idx(struct wahc *wa, unsigned rpipe_idx)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&wa->rpipe_bm_lock, flags);
+       rpipe_idx = find_next_zero_bit(wa->rpipe_bm, wa->rpipes, rpipe_idx);
+       if (rpipe_idx < wa->rpipes)
+               set_bit(rpipe_idx, wa->rpipe_bm);
+       spin_unlock_irqrestore(&wa->rpipe_bm_lock, flags);
+
+       return rpipe_idx;
+}
+
+static void rpipe_put_idx(struct wahc *wa, unsigned rpipe_idx)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&wa->rpipe_bm_lock, flags);
+       clear_bit(rpipe_idx, wa->rpipe_bm);
+       spin_unlock_irqrestore(&wa->rpipe_bm_lock, flags);
+}
+
+void rpipe_destroy(struct kref *_rpipe)
+{
+       struct wa_rpipe *rpipe = container_of(_rpipe, struct wa_rpipe, refcnt);
+       u8 index = le16_to_cpu(rpipe->descr.wRPipeIndex);
+       d_fnstart(1, NULL, "(rpipe %p %u)\n", rpipe, index);
+       if (rpipe->ep)
+               rpipe->ep->hcpriv = NULL;
+       rpipe_put_idx(rpipe->wa, index);
+       wa_put(rpipe->wa);
+       kfree(rpipe);
+       d_fnend(1, NULL, "(rpipe %p %u)\n", rpipe, index);
+}
+EXPORT_SYMBOL_GPL(rpipe_destroy);
+
+/*
+ * Locate an idle rpipe, create an structure for it and return it
+ *
+ * @wa           is referenced and unlocked
+ * @crs   enum rpipe_attr, required endpoint characteristics
+ *
+ * The rpipe can be used only sequentially (not in parallel).
+ *
+ * The rpipe is moved into the "ready" state.
+ */
+static int rpipe_get_idle(struct wa_rpipe **prpipe, struct wahc *wa, u8 crs,
+                         gfp_t gfp)
+{
+       int result;
+       unsigned rpipe_idx;
+       struct wa_rpipe *rpipe;
+       struct device *dev = &wa->usb_iface->dev;
+
+       d_fnstart(3, dev, "(wa %p crs 0x%02x)\n", wa, crs);
+       rpipe = kzalloc(sizeof(*rpipe), gfp);
+       if (rpipe == NULL)
+               return -ENOMEM;
+       rpipe_init(rpipe);
+
+       /* Look for an idle pipe */
+       for (rpipe_idx = 0; rpipe_idx < wa->rpipes; rpipe_idx++) {
+               rpipe_idx = rpipe_get_idx(wa, rpipe_idx);
+               if (rpipe_idx >= wa->rpipes)    /* no more pipes :( */
+                       break;
+               result =  __rpipe_get_descr(wa, &rpipe->descr, rpipe_idx);
+               if (result < 0)
+                       dev_err(dev, "Can't get descriptor for rpipe %u: %d\n",
+                               rpipe_idx, result);
+               else if ((rpipe->descr.bmCharacteristics & crs) != 0)
+                       goto found;
+               rpipe_put_idx(wa, rpipe_idx);
+       }
+       *prpipe = NULL;
+       kfree(rpipe);
+       d_fnend(3, dev, "(wa %p crs 0x%02x) = -ENXIO\n", wa, crs);
+       return -ENXIO;
+
+found:
+       set_bit(rpipe_idx, wa->rpipe_bm);
+       rpipe->wa = wa_get(wa);
+       *prpipe = rpipe;
+       d_fnstart(3, dev, "(wa %p crs 0x%02x) = 0\n", wa, crs);
+       return 0;
+}
+
+static int __rpipe_reset(struct wahc *wa, unsigned index)
+{
+       int result;
+       struct device *dev = &wa->usb_iface->dev;
+
+       d_printf(1, dev, "rpipe %u: reset\n", index);
+       result = usb_control_msg(
+               wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+               USB_REQ_RPIPE_RESET,
+               USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_RPIPE,
+               0, index, NULL, 0, 1000 /* FIXME: arbitrary */);
+       if (result < 0)
+               dev_err(dev, "rpipe %u: reset failed: %d\n",
+                       index, result);
+       return result;
+}
+
+/*
+ * Fake companion descriptor for ep0
+ *
+ * See WUSB1.0[7.4.4], most of this is zero for bulk/int/ctl
+ */
+static struct usb_wireless_ep_comp_descriptor epc0 = {
+       .bLength = sizeof(epc0),
+       .bDescriptorType = USB_DT_WIRELESS_ENDPOINT_COMP,
+/*     .bMaxBurst = 1, */
+       .bMaxSequence = 31,
+};
+
+/*
+ * Look for EP companion descriptor
+ *
+ * Get there, look for Inara in the endpoint's extra descriptors
+ */
+static struct usb_wireless_ep_comp_descriptor *rpipe_epc_find(
+               struct device *dev, struct usb_host_endpoint *ep)
+{
+       void *itr;
+       size_t itr_size;
+       struct usb_descriptor_header *hdr;
+       struct usb_wireless_ep_comp_descriptor *epcd;
+
+       d_fnstart(3, dev, "(ep %p)\n", ep);
+       if (ep->desc.bEndpointAddress == 0) {
+               epcd = &epc0;
+               goto out;
+       }
+       itr = ep->extra;
+       itr_size = ep->extralen;
+       epcd = NULL;
+       while (itr_size > 0) {
+               if (itr_size < sizeof(*hdr)) {
+                       dev_err(dev, "HW Bug? ep 0x%02x: extra descriptors "
+                               "at offset %zu: only %zu bytes left\n",
+                               ep->desc.bEndpointAddress,
+                               itr - (void *) ep->extra, itr_size);
+                       break;
+               }
+               hdr = itr;
+               if (hdr->bDescriptorType == USB_DT_WIRELESS_ENDPOINT_COMP) {
+                       epcd = itr;
+                       break;
+               }
+               if (hdr->bLength > itr_size) {
+                       dev_err(dev, "HW Bug? ep 0x%02x: extra descriptor "
+                               "at offset %zu (type 0x%02x) "
+                               "length %d but only %zu bytes left\n",
+                               ep->desc.bEndpointAddress,
+                               itr - (void *) ep->extra, hdr->bDescriptorType,
+                               hdr->bLength, itr_size);
+                       break;
+               }
+               itr += hdr->bLength;
+               itr_size -= hdr->bDescriptorType;
+       }
+out:
+       d_fnend(3, dev, "(ep %p) = %p\n", ep, epcd);
+       return epcd;
+}
+
+/*
+ * Aim an rpipe to its device & endpoint destination
+ *
+ * Make sure we change the address to unauthenticathed if the device
+ * is WUSB and it is not authenticated.
+ */
+static int rpipe_aim(struct wa_rpipe *rpipe, struct wahc *wa,
+                    struct usb_host_endpoint *ep, struct urb *urb, gfp_t gfp)
+{
+       int result = -ENOMSG;   /* better code for lack of companion? */
+       struct device *dev = &wa->usb_iface->dev;
+       struct usb_device *usb_dev = urb->dev;
+       struct usb_wireless_ep_comp_descriptor *epcd;
+       u8 unauth;
+
+       d_fnstart(3, dev, "(rpipe %p wa %p ep %p, urb %p)\n",
+                   rpipe, wa, ep, urb);
+       epcd = rpipe_epc_find(dev, ep);
+       if (epcd == NULL) {
+               dev_err(dev, "ep 0x%02x: can't find companion descriptor\n",
+                       ep->desc.bEndpointAddress);
+               goto error;
+       }
+       unauth = usb_dev->wusb && !usb_dev->authenticated ? 0x80 : 0;
+       __rpipe_reset(wa, le16_to_cpu(rpipe->descr.wRPipeIndex));
+       atomic_set(&rpipe->segs_available, le16_to_cpu(rpipe->descr.wRequests));
+       /* FIXME: block allocation system; request with queuing and timeout */
+       /* FIXME: compute so seg_size > ep->maxpktsize */
+       rpipe->descr.wBlocks = cpu_to_le16(16);         /* given */
+       /* ep0 maxpktsize is 0x200 (WUSB1.0[4.8.1]) */
+       rpipe->descr.wMaxPacketSize = cpu_to_le16(ep->desc.wMaxPacketSize);
+       rpipe->descr.bHSHubAddress = 0;                 /* reserved: zero */
+       rpipe->descr.bHSHubPort = wusb_port_no_to_idx(urb->dev->portnum);
+       /* FIXME: use maximum speed as supported or recommended by device */
+       rpipe->descr.bSpeed = usb_pipeendpoint(urb->pipe) == 0 ?
+               UWB_PHY_RATE_53 : UWB_PHY_RATE_200;
+       d_printf(2, dev, "addr %u (0x%02x) rpipe #%u ep# %u speed %d\n",
+                urb->dev->devnum, urb->dev->devnum | unauth,
+                le16_to_cpu(rpipe->descr.wRPipeIndex),
+                usb_pipeendpoint(urb->pipe), rpipe->descr.bSpeed);
+       /* see security.c:wusb_update_address() */
+       if (unlikely(urb->dev->devnum == 0x80))
+               rpipe->descr.bDeviceAddress = 0;
+       else
+               rpipe->descr.bDeviceAddress = urb->dev->devnum | unauth;
+       rpipe->descr.bEndpointAddress = ep->desc.bEndpointAddress;
+       /* FIXME: bDataSequence */
+       rpipe->descr.bDataSequence = 0;
+       /* FIXME: dwCurrentWindow */
+       rpipe->descr.dwCurrentWindow = cpu_to_le32(1);
+       /* FIXME: bMaxDataSequence */
+       rpipe->descr.bMaxDataSequence = epcd->bMaxSequence - 1;
+       rpipe->descr.bInterval = ep->desc.bInterval;
+       /* FIXME: bOverTheAirInterval */
+       rpipe->descr.bOverTheAirInterval = 0;   /* 0 if not isoc */
+       /* FIXME: xmit power & preamble blah blah */
+       rpipe->descr.bmAttribute = ep->desc.bmAttributes & 0x03;
+       /* rpipe->descr.bmCharacteristics RO */
+       /* FIXME: bmRetryOptions */
+       rpipe->descr.bmRetryOptions = 15;
+       /* FIXME: use for assessing link quality? */
+       rpipe->descr.wNumTransactionErrors = 0;
+       result = __rpipe_set_descr(wa, &rpipe->descr,
+                                  le16_to_cpu(rpipe->descr.wRPipeIndex));
+       if (result < 0) {
+               dev_err(dev, "Cannot aim rpipe: %d\n", result);
+               goto error;
+       }
+       result = 0;
+error:
+       d_fnend(3, dev, "(rpipe %p wa %p ep %p urb %p) = %d\n",
+                 rpipe, wa, ep, urb, result);
+       return result;
+}
+
+/*
+ * Check an aimed rpipe to make sure it points to where we want
+ *
+ * We use bit 19 of the Linux USB pipe bitmap for unauth vs auth
+ * space; when it is like that, we or 0x80 to make an unauth address.
+ */
+static int rpipe_check_aim(const struct wa_rpipe *rpipe, const struct wahc *wa,
+                          const struct usb_host_endpoint *ep,
+                          const struct urb *urb, gfp_t gfp)
+{
+       int result = 0;         /* better code for lack of companion? */
+       struct device *dev = &wa->usb_iface->dev;
+       struct usb_device *usb_dev = urb->dev;
+       u8 unauth = (usb_dev->wusb && !usb_dev->authenticated) ? 0x80 : 0;
+       u8 portnum = wusb_port_no_to_idx(urb->dev->portnum);
+
+       d_fnstart(3, dev, "(rpipe %p wa %p ep %p, urb %p)\n",
+                   rpipe, wa, ep, urb);
+#define AIM_CHECK(rdf, val, text)                                      \
+       do {                                                            \
+               if (rpipe->descr.rdf != (val)) {                        \
+                       dev_err(dev,                                    \
+                               "rpipe aim discrepancy: " #rdf " " text "\n", \
+                               rpipe->descr.rdf, (val));               \
+                       result = -EINVAL;                               \
+                       WARN_ON(1);                                     \
+               }                                                       \
+       } while (0)
+       AIM_CHECK(wMaxPacketSize, cpu_to_le16(ep->desc.wMaxPacketSize),
+                 "(%u vs %u)");
+       AIM_CHECK(bHSHubPort, portnum, "(%u vs %u)");
+       AIM_CHECK(bSpeed, usb_pipeendpoint(urb->pipe) == 0 ?
+                       UWB_PHY_RATE_53 : UWB_PHY_RATE_200,
+                 "(%u vs %u)");
+       AIM_CHECK(bDeviceAddress, urb->dev->devnum | unauth, "(%u vs %u)");
+       AIM_CHECK(bEndpointAddress, ep->desc.bEndpointAddress, "(%u vs %u)");
+       AIM_CHECK(bInterval, ep->desc.bInterval, "(%u vs %u)");
+       AIM_CHECK(bmAttribute, ep->desc.bmAttributes & 0x03, "(%u vs %u)");
+#undef AIM_CHECK
+       return result;
+}
+
+#ifndef CONFIG_BUG
+#define CONFIG_BUG 0
+#endif
+
+/*
+ * Make sure there is an rpipe allocated for an endpoint
+ *
+ * If already allocated, we just refcount it; if not, we get an
+ * idle one, aim it to the right location and take it.
+ *
+ * Attaches to ep->hcpriv and rpipe->ep to ep.
+ */
+int rpipe_get_by_ep(struct wahc *wa, struct usb_host_endpoint *ep,
+                   struct urb *urb, gfp_t gfp)
+{
+       int result = 0;
+       struct device *dev = &wa->usb_iface->dev;
+       struct wa_rpipe *rpipe;
+       u8 eptype;
+
+       d_fnstart(3, dev, "(wa %p ep %p urb %p gfp 0x%08x)\n", wa, ep, urb,
+                 gfp);
+       mutex_lock(&wa->rpipe_mutex);
+       rpipe = ep->hcpriv;
+       if (rpipe != NULL) {
+               if (CONFIG_BUG == 1) {
+                       result = rpipe_check_aim(rpipe, wa, ep, urb, gfp);
+                       if (result < 0)
+                               goto error;
+               }
+               __rpipe_get(rpipe);
+               d_printf(2, dev, "ep 0x%02x: reusing rpipe %u\n",
+                        ep->desc.bEndpointAddress,
+                        le16_to_cpu(rpipe->descr.wRPipeIndex));
+       } else {
+               /* hmm, assign idle rpipe, aim it */
+               result = -ENOBUFS;
+               eptype = ep->desc.bmAttributes & 0x03;
+               result = rpipe_get_idle(&rpipe, wa, 1 << eptype, gfp);
+               if (result < 0)
+                       goto error;
+               result = rpipe_aim(rpipe, wa, ep, urb, gfp);
+               if (result < 0) {
+                       rpipe_put(rpipe);
+                       goto error;
+               }
+               ep->hcpriv = rpipe;
+               rpipe->ep = ep;
+               __rpipe_get(rpipe);     /* for caching into ep->hcpriv */
+               d_printf(2, dev, "ep 0x%02x: using rpipe %u\n",
+                        ep->desc.bEndpointAddress,
+                        le16_to_cpu(rpipe->descr.wRPipeIndex));
+       }
+       d_dump(4, dev, &rpipe->descr, sizeof(rpipe->descr));
+error:
+       mutex_unlock(&wa->rpipe_mutex);
+       d_fnend(3, dev, "(wa %p ep %p urb %p gfp 0x%08x)\n", wa, ep, urb, gfp);
+       return result;
+}
+
+/*
+ * Allocate the bitmap for each rpipe.
+ */
+int wa_rpipes_create(struct wahc *wa)
+{
+       wa->rpipes = wa->wa_descr->wNumRPipes;
+       wa->rpipe_bm = kzalloc(BITS_TO_LONGS(wa->rpipes)*sizeof(unsigned long),
+                              GFP_KERNEL);
+       if (wa->rpipe_bm == NULL)
+               return -ENOMEM;
+       return 0;
+}
+
+void wa_rpipes_destroy(struct wahc *wa)
+{
+       struct device *dev = &wa->usb_iface->dev;
+       d_fnstart(3, dev, "(wa %p)\n", wa);
+       if (!bitmap_empty(wa->rpipe_bm, wa->rpipes)) {
+               char buf[256];
+               WARN_ON(1);
+               bitmap_scnprintf(buf, sizeof(buf), wa->rpipe_bm, wa->rpipes);
+               dev_err(dev, "BUG: pipes not released on exit: %s\n", buf);
+       }
+       kfree(wa->rpipe_bm);
+       d_fnend(3, dev, "(wa %p)\n", wa);
+}
+
+/*
+ * Release resources allocated for an endpoint
+ *
+ * If there is an associated rpipe to this endpoint, Abort any pending
+ * transfers and put it. If the rpipe ends up being destroyed,
+ * __rpipe_destroy() will cleanup ep->hcpriv.
+ *
+ * This is called before calling hcd->stop(), so you don't need to do
+ * anything else in there.
+ */
+void rpipe_ep_disable(struct wahc *wa, struct usb_host_endpoint *ep)
+{
+       struct device *dev = &wa->usb_iface->dev;
+       struct wa_rpipe *rpipe;
+       d_fnstart(2, dev, "(wa %p ep %p)\n", wa, ep);
+       mutex_lock(&wa->rpipe_mutex);
+       rpipe = ep->hcpriv;
+       if (rpipe != NULL) {
+               unsigned rc = atomic_read(&rpipe->refcnt.refcount);
+               int result;
+               u16 index = le16_to_cpu(rpipe->descr.wRPipeIndex);
+
+               if (rc != 1)
+                       d_printf(1, dev, "(wa %p ep %p) rpipe %p refcnt %u\n",
+                                wa, ep, rpipe, rc);
+
+               d_printf(1, dev, "rpipe %u: abort\n", index);
+               result = usb_control_msg(
+                       wa->usb_dev, usb_rcvctrlpipe(wa->usb_dev, 0),
+                       USB_REQ_RPIPE_ABORT,
+                       USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_RPIPE,
+                       0, index, NULL, 0, 1000 /* FIXME: arbitrary */);
+               if (result < 0 && result != -ENODEV /* dev is gone */)
+                       d_printf(1, dev, "(wa %p rpipe %u): abort failed: %d\n",
+                                wa, index, result);
+               rpipe_put(rpipe);
+       }
+       mutex_unlock(&wa->rpipe_mutex);
+       d_fnend(2, dev, "(wa %p ep %p)\n", wa, ep);
+       return;
+}
+EXPORT_SYMBOL_GPL(rpipe_ep_disable);
diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c
new file mode 100644 (file)
index 0000000..c038635
--- /dev/null
@@ -0,0 +1,1709 @@
+/*
+ * WUSB Wire Adapter
+ * Data transfer and URB enqueing
+ *
+ * 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.
+ *
+ *
+ * How transfers work: get a buffer, break it up in segments (segment
+ * size is a multiple of the maxpacket size). For each segment issue a
+ * segment request (struct wa_xfer_*), then send the data buffer if
+ * out or nothing if in (all over the DTO endpoint).
+ *
+ * For each submitted segment request, a notification will come over
+ * the NEP endpoint and a transfer result (struct xfer_result) will
+ * arrive in the DTI URB. Read it, get the xfer ID, see if there is
+ * data coming (inbound transfer), schedule a read and handle it.
+ *
+ * Sounds simple, it is a pain to implement.
+ *
+ *
+ * ENTRY POINTS
+ *
+ *   FIXME
+ *
+ * LIFE CYCLE / STATE DIAGRAM
+ *
+ *   FIXME
+ *
+ * THIS CODE IS DISGUSTING
+ *
+ *   Warned you are; it's my second try and still not happy with it.
+ *
+ * NOTES:
+ *
+ *   - No iso
+ *
+ *   - Supports DMA xfers, control, bulk and maybe interrupt
+ *
+ *   - Does not recycle unused rpipes
+ *
+ *     An rpipe is assigned to an endpoint the first time it is used,
+ *     and then it's there, assigned, until the endpoint is disabled
+ *     (destroyed [{h,d}wahc_op_ep_disable()]. The assignment of the
+ *     rpipe to the endpoint is done under the wa->rpipe_sem semaphore
+ *     (should be a mutex).
+ *
+ *     Two methods it could be done:
+ *
+ *     (a) set up a timer everytime an rpipe's use count drops to 1
+ *         (which means unused) or when a transfer ends. Reset the
+ *         timer when a xfer is queued. If the timer expires, release
+ *         the rpipe [see rpipe_ep_disable()].
+ *
+ *     (b) when looking for free rpipes to attach [rpipe_get_by_ep()],
+ *         when none are found go over the list, check their endpoint
+ *         and their activity record (if no last-xfer-done-ts in the
+ *         last x seconds) take it
+ *
+ *     However, due to the fact that we have a set of limited
+ *     resources (max-segments-at-the-same-time per xfer,
+ *     xfers-per-ripe, blocks-per-rpipe, rpipes-per-host), at the end
+ *     we are going to have to rebuild all this based on an scheduler,
+ *     to where we have a list of transactions to do and based on the
+ *     availability of the different requried components (blocks,
+ *     rpipes, segment slots, etc), we go scheduling them. Painful.
+ */
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/hash.h>
+#include "wa-hc.h"
+#include "wusbhc.h"
+
+#undef D_LOCAL
+#define D_LOCAL 0 /* 0 disabled, > 0 different levels... */
+#include <linux/uwb/debug.h>
+
+enum {
+       WA_SEGS_MAX = 255,
+};
+
+enum wa_seg_status {
+       WA_SEG_NOTREADY,
+       WA_SEG_READY,
+       WA_SEG_DELAYED,
+       WA_SEG_SUBMITTED,
+       WA_SEG_PENDING,
+       WA_SEG_DTI_PENDING,
+       WA_SEG_DONE,
+       WA_SEG_ERROR,
+       WA_SEG_ABORTED,
+};
+
+static void wa_xfer_delayed_run(struct wa_rpipe *);
+
+/*
+ * Life cycle governed by 'struct urb' (the refcount of the struct is
+ * that of the 'struct urb' and usb_free_urb() would free the whole
+ * struct).
+ */
+struct wa_seg {
+       struct urb urb;
+       struct urb *dto_urb;            /* for data output? */
+       struct list_head list_node;     /* for rpipe->req_list */
+       struct wa_xfer *xfer;           /* out xfer */
+       u8 index;                       /* which segment we are */
+       enum wa_seg_status status;
+       ssize_t result;                 /* bytes xfered or error */
+       struct wa_xfer_hdr xfer_hdr;
+       u8 xfer_extra[];                /* xtra space for xfer_hdr_ctl */
+};
+
+static void wa_seg_init(struct wa_seg *seg)
+{
+       /* usb_init_urb() repeats a lot of work, so we do it here */
+       kref_init(&seg->urb.kref);
+}
+
+/*
+ * Protected by xfer->lock
+ *
+ */
+struct wa_xfer {
+       struct kref refcnt;
+       struct list_head list_node;
+       spinlock_t lock;
+       u32 id;
+
+       struct wahc *wa;                /* Wire adapter we are plugged to */
+       struct usb_host_endpoint *ep;
+       struct urb *urb;                /* URB we are transfering for */
+       struct wa_seg **seg;            /* transfer segments */
+       u8 segs, segs_submitted, segs_done;
+       unsigned is_inbound:1;
+       unsigned is_dma:1;
+       size_t seg_size;
+       int result;
+
+       gfp_t gfp;                      /* allocation mask */
+
+       struct wusb_dev *wusb_dev;      /* for activity timestamps */
+};
+
+static inline void wa_xfer_init(struct wa_xfer *xfer)
+{
+       kref_init(&xfer->refcnt);
+       INIT_LIST_HEAD(&xfer->list_node);
+       spin_lock_init(&xfer->lock);
+}
+
+/*
+ * Destory a transfer structure
+ *
+ * Note that the xfer->seg[index] thingies follow the URB life cycle,
+ * so we need to put them, not free them.
+ */
+static void wa_xfer_destroy(struct kref *_xfer)
+{
+       struct wa_xfer *xfer = container_of(_xfer, struct wa_xfer, refcnt);
+       if (xfer->seg) {
+               unsigned cnt;
+               for (cnt = 0; cnt < xfer->segs; cnt++) {
+                       if (xfer->is_inbound)
+                               usb_put_urb(xfer->seg[cnt]->dto_urb);
+                       usb_put_urb(&xfer->seg[cnt]->urb);
+               }
+       }
+       kfree(xfer);
+       d_printf(2, NULL, "xfer %p destroyed\n", xfer);
+}
+
+static void wa_xfer_get(struct wa_xfer *xfer)
+{
+       kref_get(&xfer->refcnt);
+}
+
+static void wa_xfer_put(struct wa_xfer *xfer)
+{
+       d_fnstart(3, NULL, "(xfer %p) -- ref count bef put %d\n",
+                   xfer, atomic_read(&xfer->refcnt.refcount));
+       kref_put(&xfer->refcnt, wa_xfer_destroy);
+       d_fnend(3, NULL, "(xfer %p) = void\n", xfer);
+}
+
+/*
+ * xfer is referenced
+ *
+ * xfer->lock has to be unlocked
+ *
+ * We take xfer->lock for setting the result; this is a barrier
+ * against drivers/usb/core/hcd.c:unlink1() being called after we call
+ * usb_hcd_giveback_urb() and wa_urb_dequeue() trying to get a
+ * reference to the transfer.
+ */
+static void wa_xfer_giveback(struct wa_xfer *xfer)
+{
+       unsigned long flags;
+       d_fnstart(3, NULL, "(xfer %p)\n", xfer);
+       spin_lock_irqsave(&xfer->wa->xfer_list_lock, flags);
+       list_del_init(&xfer->list_node);
+       spin_unlock_irqrestore(&xfer->wa->xfer_list_lock, flags);
+       /* FIXME: segmentation broken -- kills DWA */
+       wusbhc_giveback_urb(xfer->wa->wusb, xfer->urb, xfer->result);
+       wa_put(xfer->wa);
+       wa_xfer_put(xfer);
+       d_fnend(3, NULL, "(xfer %p) = void\n", xfer);
+}
+
+/*
+ * xfer is referenced
+ *
+ * xfer->lock has to be unlocked
+ */
+static void wa_xfer_completion(struct wa_xfer *xfer)
+{
+       d_fnstart(3, NULL, "(xfer %p)\n", xfer);
+       if (xfer->wusb_dev)
+               wusb_dev_put(xfer->wusb_dev);
+       rpipe_put(xfer->ep->hcpriv);
+       wa_xfer_giveback(xfer);
+       d_fnend(3, NULL, "(xfer %p) = void\n", xfer);
+       return;
+}
+
+/*
+ * If transfer is done, wrap it up and return true
+ *
+ * xfer->lock has to be locked
+ */
+static unsigned __wa_xfer_is_done(struct wa_xfer *xfer)
+{
+       unsigned result, cnt;
+       struct wa_seg *seg;
+       struct urb *urb = xfer->urb;
+       unsigned found_short = 0;
+
+       d_fnstart(3, NULL, "(xfer %p)\n", xfer);
+       result = xfer->segs_done == xfer->segs_submitted;
+       if (result == 0)
+               goto out;
+       urb->actual_length = 0;
+       for (cnt = 0; cnt < xfer->segs; cnt++) {
+               seg = xfer->seg[cnt];
+               switch (seg->status) {
+               case WA_SEG_DONE:
+                       if (found_short && seg->result > 0) {
+                               if (printk_ratelimit())
+                                       printk(KERN_ERR "xfer %p#%u: bad short "
+                                              "segments (%zu)\n", xfer, cnt,
+                                              seg->result);
+                               urb->status = -EINVAL;
+                               goto out;
+                       }
+                       urb->actual_length += seg->result;
+                       if (seg->result < xfer->seg_size
+                           && cnt != xfer->segs-1)
+                               found_short = 1;
+                       d_printf(2, NULL, "xfer %p#%u: DONE short %d "
+                                "result %zu urb->actual_length %d\n",
+                                xfer, seg->index, found_short, seg->result,
+                                urb->actual_length);
+                       break;
+               case WA_SEG_ERROR:
+                       xfer->result = seg->result;
+                       d_printf(2, NULL, "xfer %p#%u: ERROR result %zu\n",
+                                xfer, seg->index, seg->result);
+                       goto out;
+               case WA_SEG_ABORTED:
+                       WARN_ON(urb->status != -ECONNRESET
+                               && urb->status != -ENOENT);
+                       d_printf(2, NULL, "xfer %p#%u ABORTED: result %d\n",
+                                xfer, seg->index, urb->status);
+                       xfer->result = urb->status;
+                       goto out;
+               default:
+                       /* if (printk_ratelimit()) */
+                               printk(KERN_ERR "xfer %p#%u: "
+                                      "is_done bad state %d\n",
+                                      xfer, cnt, seg->status);
+                       xfer->result = -EINVAL;
+                       WARN_ON(1);
+                       goto out;
+               }
+       }
+       xfer->result = 0;
+out:
+       d_fnend(3, NULL, "(xfer %p) = void\n", xfer);
+       return result;
+}
+
+/*
+ * Initialize a transfer's ID
+ *
+ * We need to use a sequential number; if we use the pointer or the
+ * hash of the pointer, it can repeat over sequential transfers and
+ * then it will confuse the HWA....wonder why in hell they put a 32
+ * bit handle in there then.
+ */
+static void wa_xfer_id_init(struct wa_xfer *xfer)
+{
+       xfer->id = atomic_add_return(1, &xfer->wa->xfer_id_count);
+}
+
+/*
+ * Return the xfer's ID associated with xfer
+ *
+ * Need to generate a
+ */
+static u32 wa_xfer_id(struct wa_xfer *xfer)
+{
+       return xfer->id;
+}
+
+/*
+ * Search for a transfer list ID on the HCD's URB list
+ *
+ * For 32 bit architectures, we use the pointer itself; for 64 bits, a
+ * 32-bit hash of the pointer.
+ *
+ * @returns NULL if not found.
+ */
+static struct wa_xfer *wa_xfer_get_by_id(struct wahc *wa, u32 id)
+{
+       unsigned long flags;
+       struct wa_xfer *xfer_itr;
+       spin_lock_irqsave(&wa->xfer_list_lock, flags);
+       list_for_each_entry(xfer_itr, &wa->xfer_list, list_node) {
+               if (id == xfer_itr->id) {
+                       wa_xfer_get(xfer_itr);
+                       goto out;
+               }
+       }
+       xfer_itr = NULL;
+out:
+       spin_unlock_irqrestore(&wa->xfer_list_lock, flags);
+       return xfer_itr;
+}
+
+struct wa_xfer_abort_buffer {
+       struct urb urb;
+       struct wa_xfer_abort cmd;
+};
+
+static void __wa_xfer_abort_cb(struct urb *urb)
+{
+       struct wa_xfer_abort_buffer *b = urb->context;
+       usb_put_urb(&b->urb);
+}
+
+/*
+ * Aborts an ongoing transaction
+ *
+ * Assumes the transfer is referenced and locked and in a submitted
+ * state (mainly that there is an endpoint/rpipe assigned).
+ *
+ * The callback (see above) does nothing but freeing up the data by
+ * putting the URB. Because the URB is allocated at the head of the
+ * struct, the whole space we allocated is kfreed.
+ *
+ * We'll get an 'aborted transaction' xfer result on DTI, that'll
+ * politely ignore because at this point the transaction has been
+ * marked as aborted already.
+ */
+static void __wa_xfer_abort(struct wa_xfer *xfer)
+{
+       int result;
+       struct device *dev = &xfer->wa->usb_iface->dev;
+       struct wa_xfer_abort_buffer *b;
+       struct wa_rpipe *rpipe = xfer->ep->hcpriv;
+
+       b = kmalloc(sizeof(*b), GFP_ATOMIC);
+       if (b == NULL)
+               goto error_kmalloc;
+       b->cmd.bLength =  sizeof(b->cmd);
+       b->cmd.bRequestType = WA_XFER_ABORT;
+       b->cmd.wRPipe = rpipe->descr.wRPipeIndex;
+       b->cmd.dwTransferID = wa_xfer_id(xfer);
+
+       usb_init_urb(&b->urb);
+       usb_fill_bulk_urb(&b->urb, xfer->wa->usb_dev,
+               usb_sndbulkpipe(xfer->wa->usb_dev,
+                               xfer->wa->dto_epd->bEndpointAddress),
+               &b->cmd, sizeof(b->cmd), __wa_xfer_abort_cb, b);
+       result = usb_submit_urb(&b->urb, GFP_ATOMIC);
+       if (result < 0)
+               goto error_submit;
+       return;                         /* callback frees! */
+
+
+error_submit:
+       if (printk_ratelimit())
+               dev_err(dev, "xfer %p: Can't submit abort request: %d\n",
+                       xfer, result);
+       kfree(b);
+error_kmalloc:
+       return;
+
+}
+
+/*
+ *
+ * @returns < 0 on error, transfer segment request size if ok
+ */
+static ssize_t __wa_xfer_setup_sizes(struct wa_xfer *xfer,
+                                    enum wa_xfer_type *pxfer_type)
+{
+       ssize_t result;
+       struct device *dev = &xfer->wa->usb_iface->dev;
+       size_t maxpktsize;
+       struct urb *urb = xfer->urb;
+       struct wa_rpipe *rpipe = xfer->ep->hcpriv;
+
+       d_fnstart(3, dev, "(xfer %p [rpipe %p] urb %p)\n",
+                 xfer, rpipe, urb);
+       switch (rpipe->descr.bmAttribute & 0x3) {
+       case USB_ENDPOINT_XFER_CONTROL:
+               *pxfer_type = WA_XFER_TYPE_CTL;
+               result = sizeof(struct wa_xfer_ctl);
+               break;
+       case USB_ENDPOINT_XFER_INT:
+       case USB_ENDPOINT_XFER_BULK:
+               *pxfer_type = WA_XFER_TYPE_BI;
+               result = sizeof(struct wa_xfer_bi);
+               break;
+       case USB_ENDPOINT_XFER_ISOC:
+               dev_err(dev, "FIXME: ISOC not implemented\n");
+               result = -ENOSYS;
+               goto error;
+       default:
+               /* never happens */
+               BUG();
+               result = -EINVAL;       /* shut gcc up */
+       };
+       xfer->is_inbound = urb->pipe & USB_DIR_IN ? 1 : 0;
+       xfer->is_dma = urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP ? 1 : 0;
+       xfer->seg_size = le16_to_cpu(rpipe->descr.wBlocks)
+               * 1 << (xfer->wa->wa_descr->bRPipeBlockSize - 1);
+       /* Compute the segment size and make sure it is a multiple of
+        * the maxpktsize (WUSB1.0[8.3.3.1])...not really too much of
+        * a check (FIXME) */
+       maxpktsize = le16_to_cpu(rpipe->descr.wMaxPacketSize);
+       if (xfer->seg_size < maxpktsize) {
+               dev_err(dev, "HW BUG? seg_size %zu smaller than maxpktsize "
+                       "%zu\n", xfer->seg_size, maxpktsize);
+               result = -EINVAL;
+               goto error;
+       }
+       xfer->seg_size = (xfer->seg_size / maxpktsize) * maxpktsize;
+       xfer->segs = (urb->transfer_buffer_length + xfer->seg_size - 1)
+               / xfer->seg_size;
+       if (xfer->segs >= WA_SEGS_MAX) {
+               dev_err(dev, "BUG? ops, number of segments %d bigger than %d\n",
+                       (int)(urb->transfer_buffer_length / xfer->seg_size),
+                       WA_SEGS_MAX);
+               result = -EINVAL;
+               goto error;
+       }
+       if (xfer->segs == 0 && *pxfer_type == WA_XFER_TYPE_CTL)
+               xfer->segs = 1;
+error:
+       d_fnend(3, dev, "(xfer %p [rpipe %p] urb %p) = %d\n",
+               xfer, rpipe, urb, (int)result);
+       return result;
+}
+
+/** Fill in the common request header and xfer-type specific data. */
+static void __wa_xfer_setup_hdr0(struct wa_xfer *xfer,
+                                struct wa_xfer_hdr *xfer_hdr0,
+                                enum wa_xfer_type xfer_type,
+                                size_t xfer_hdr_size)
+{
+       struct wa_rpipe *rpipe = xfer->ep->hcpriv;
+
+       xfer_hdr0 = &xfer->seg[0]->xfer_hdr;
+       xfer_hdr0->bLength = xfer_hdr_size;
+       xfer_hdr0->bRequestType = xfer_type;
+       xfer_hdr0->wRPipe = rpipe->descr.wRPipeIndex;
+       xfer_hdr0->dwTransferID = wa_xfer_id(xfer);
+       xfer_hdr0->bTransferSegment = 0;
+       switch (xfer_type) {
+       case WA_XFER_TYPE_CTL: {
+               struct wa_xfer_ctl *xfer_ctl =
+                       container_of(xfer_hdr0, struct wa_xfer_ctl, hdr);
+               xfer_ctl->bmAttribute = xfer->is_inbound ? 1 : 0;
+               BUG_ON(xfer->urb->transfer_flags & URB_NO_SETUP_DMA_MAP
+                      && xfer->urb->setup_packet == NULL);
+               memcpy(&xfer_ctl->baSetupData, xfer->urb->setup_packet,
+                      sizeof(xfer_ctl->baSetupData));
+               break;
+       }
+       case WA_XFER_TYPE_BI:
+               break;
+       case WA_XFER_TYPE_ISO:
+               printk(KERN_ERR "FIXME: ISOC not implemented\n");
+       default:
+               BUG();
+       };
+}
+
+/*
+ * Callback for the OUT data phase of the segment request
+ *
+ * Check wa_seg_cb(); most comments also apply here because this
+ * function does almost the same thing and they work closely
+ * together.
+ *
+ * If the seg request has failed but this DTO phase has suceeded,
+ * wa_seg_cb() has already failed the segment and moved the
+ * status to WA_SEG_ERROR, so this will go through 'case 0' and
+ * effectively do nothing.
+ */
+static void wa_seg_dto_cb(struct urb *urb)
+{
+       struct wa_seg *seg = urb->context;
+       struct wa_xfer *xfer = seg->xfer;
+       struct wahc *wa;
+       struct device *dev;
+       struct wa_rpipe *rpipe;
+       unsigned long flags;
+       unsigned rpipe_ready = 0;
+       u8 done = 0;
+
+       d_fnstart(3, NULL, "(urb %p [%d])\n", urb, urb->status);
+       switch (urb->status) {
+       case 0:
+               spin_lock_irqsave(&xfer->lock, flags);
+               wa = xfer->wa;
+               dev = &wa->usb_iface->dev;
+               d_printf(2, dev, "xfer %p#%u: data out done (%d bytes)\n",
+                          xfer, seg->index, urb->actual_length);
+               if (seg->status < WA_SEG_PENDING)
+                       seg->status = WA_SEG_PENDING;
+               seg->result = urb->actual_length;
+               spin_unlock_irqrestore(&xfer->lock, flags);
+               break;
+       case -ECONNRESET:       /* URB unlinked; no need to do anything */
+       case -ENOENT:           /* as it was done by the who unlinked us */
+               break;
+       default:                /* Other errors ... */
+               spin_lock_irqsave(&xfer->lock, flags);
+               wa = xfer->wa;
+               dev = &wa->usb_iface->dev;
+               rpipe = xfer->ep->hcpriv;
+               if (printk_ratelimit())
+                       dev_err(dev, "xfer %p#%u: data out error %d\n",
+                               xfer, seg->index, urb->status);
+               if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS,
+                           EDC_ERROR_TIMEFRAME)){
+                       dev_err(dev, "DTO: URB max acceptable errors "
+                               "exceeded, resetting device\n");
+                       wa_reset_all(wa);
+               }
+               if (seg->status != WA_SEG_ERROR) {
+                       seg->status = WA_SEG_ERROR;
+                       seg->result = urb->status;
+                       xfer->segs_done++;
+                       __wa_xfer_abort(xfer);
+                       rpipe_ready = rpipe_avail_inc(rpipe);
+                       done = __wa_xfer_is_done(xfer);
+               }
+               spin_unlock_irqrestore(&xfer->lock, flags);
+               if (done)
+                       wa_xfer_completion(xfer);
+               if (rpipe_ready)
+                       wa_xfer_delayed_run(rpipe);
+       }
+       d_fnend(3, NULL, "(urb %p [%d]) = void\n", urb, urb->status);
+}
+
+/*
+ * Callback for the segment request
+ *
+ * If succesful transition state (unless already transitioned or
+ * outbound transfer); otherwise, take a note of the error, mark this
+ * segment done and try completion.
+ *
+ * Note we don't access until we are sure that the transfer hasn't
+ * been cancelled (ECONNRESET, ENOENT), which could mean that
+ * seg->xfer could be already gone.
+ *
+ * We have to check before setting the status to WA_SEG_PENDING
+ * because sometimes the xfer result callback arrives before this
+ * callback (geeeeeeze), so it might happen that we are already in
+ * another state. As well, we don't set it if the transfer is inbound,
+ * as in that case, wa_seg_dto_cb will do it when the OUT data phase
+ * finishes.
+ */
+static void wa_seg_cb(struct urb *urb)
+{
+       struct wa_seg *seg = urb->context;
+       struct wa_xfer *xfer = seg->xfer;
+       struct wahc *wa;
+       struct device *dev;
+       struct wa_rpipe *rpipe;
+       unsigned long flags;
+       unsigned rpipe_ready;
+       u8 done = 0;
+
+       d_fnstart(3, NULL, "(urb %p [%d])\n", urb, urb->status);
+       switch (urb->status) {
+       case 0:
+               spin_lock_irqsave(&xfer->lock, flags);
+               wa = xfer->wa;
+               dev = &wa->usb_iface->dev;
+               d_printf(2, dev, "xfer %p#%u: request done\n",
+                          xfer, seg->index);
+               if (xfer->is_inbound && seg->status < WA_SEG_PENDING)
+                       seg->status = WA_SEG_PENDING;
+               spin_unlock_irqrestore(&xfer->lock, flags);
+               break;
+       case -ECONNRESET:       /* URB unlinked; no need to do anything */
+       case -ENOENT:           /* as it was done by the who unlinked us */
+               break;
+       default:                /* Other errors ... */
+               spin_lock_irqsave(&xfer->lock, flags);
+               wa = xfer->wa;
+               dev = &wa->usb_iface->dev;
+               rpipe = xfer->ep->hcpriv;
+               if (printk_ratelimit())
+                       dev_err(dev, "xfer %p#%u: request error %d\n",
+                               xfer, seg->index, urb->status);
+               if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS,
+                           EDC_ERROR_TIMEFRAME)){
+                       dev_err(dev, "DTO: URB max acceptable errors "
+                               "exceeded, resetting device\n");
+                       wa_reset_all(wa);
+               }
+               usb_unlink_urb(seg->dto_urb);
+               seg->status = WA_SEG_ERROR;
+               seg->result = urb->status;
+               xfer->segs_done++;
+               __wa_xfer_abort(xfer);
+               rpipe_ready = rpipe_avail_inc(rpipe);
+               done = __wa_xfer_is_done(xfer);
+               spin_unlock_irqrestore(&xfer->lock, flags);
+               if (done)
+                       wa_xfer_completion(xfer);
+               if (rpipe_ready)
+                       wa_xfer_delayed_run(rpipe);
+       }
+       d_fnend(3, NULL, "(urb %p [%d]) = void\n", urb, urb->status);
+}
+
+/*
+ * Allocate the segs array and initialize each of them
+ *
+ * The segments are freed by wa_xfer_destroy() when the xfer use count
+ * drops to zero; however, because each segment is given the same life
+ * cycle as the USB URB it contains, it is actually freed by
+ * usb_put_urb() on the contained USB URB (twisted, eh?).
+ */
+static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size)
+{
+       int result, cnt;
+       size_t alloc_size = sizeof(*xfer->seg[0])
+               - sizeof(xfer->seg[0]->xfer_hdr) + xfer_hdr_size;
+       struct usb_device *usb_dev = xfer->wa->usb_dev;
+       const struct usb_endpoint_descriptor *dto_epd = xfer->wa->dto_epd;
+       struct wa_seg *seg;
+       size_t buf_itr, buf_size, buf_itr_size;
+
+       result = -ENOMEM;
+       xfer->seg = kcalloc(xfer->segs, sizeof(xfer->seg[0]), GFP_ATOMIC);
+       if (xfer->seg == NULL)
+               goto error_segs_kzalloc;
+       buf_itr = 0;
+       buf_size = xfer->urb->transfer_buffer_length;
+       for (cnt = 0; cnt < xfer->segs; cnt++) {
+               seg = xfer->seg[cnt] = kzalloc(alloc_size, GFP_ATOMIC);
+               if (seg == NULL)
+                       goto error_seg_kzalloc;
+               wa_seg_init(seg);
+               seg->xfer = xfer;
+               seg->index = cnt;
+               usb_fill_bulk_urb(&seg->urb, usb_dev,
+                                 usb_sndbulkpipe(usb_dev,
+                                                 dto_epd->bEndpointAddress),
+                                 &seg->xfer_hdr, xfer_hdr_size,
+                                 wa_seg_cb, seg);
+               buf_itr_size = buf_size > xfer->seg_size ?
+                       xfer->seg_size : buf_size;
+               if (xfer->is_inbound == 0 && buf_size > 0) {
+                       seg->dto_urb = usb_alloc_urb(0, GFP_ATOMIC);
+                       if (seg->dto_urb == NULL)
+                               goto error_dto_alloc;
+                       usb_fill_bulk_urb(
+                               seg->dto_urb, usb_dev,
+                               usb_sndbulkpipe(usb_dev,
+                                               dto_epd->bEndpointAddress),
+                               NULL, 0, wa_seg_dto_cb, seg);
+                       if (xfer->is_dma) {
+                               seg->dto_urb->transfer_dma =
+                                       xfer->urb->transfer_dma + buf_itr;
+                               seg->dto_urb->transfer_flags |=
+                                       URB_NO_TRANSFER_DMA_MAP;
+                       } else
+                               seg->dto_urb->transfer_buffer =
+                                       xfer->urb->transfer_buffer + buf_itr;
+                       seg->dto_urb->transfer_buffer_length = buf_itr_size;
+               }
+               seg->status = WA_SEG_READY;
+               buf_itr += buf_itr_size;
+               buf_size -= buf_itr_size;
+       }
+       return 0;
+
+error_dto_alloc:
+       kfree(xfer->seg[cnt]);
+       cnt--;
+error_seg_kzalloc:
+       /* use the fact that cnt is left at were it failed */
+       for (; cnt > 0; cnt--) {
+               if (xfer->is_inbound == 0)
+                       kfree(xfer->seg[cnt]->dto_urb);
+               kfree(xfer->seg[cnt]);
+       }
+error_segs_kzalloc:
+       return result;
+}
+
+/*
+ * Allocates all the stuff needed to submit a transfer
+ *
+ * Breaks the whole data buffer in a list of segments, each one has a
+ * structure allocated to it and linked in xfer->seg[index]
+ *
+ * FIXME: merge setup_segs() and the last part of this function, no
+ *        need to do two for loops when we could run everything in a
+ *        single one
+ */
+static int __wa_xfer_setup(struct wa_xfer *xfer, struct urb *urb)
+{
+       int result;
+       struct device *dev = &xfer->wa->usb_iface->dev;
+       enum wa_xfer_type xfer_type = 0; /* shut up GCC */
+       size_t xfer_hdr_size, cnt, transfer_size;
+       struct wa_xfer_hdr *xfer_hdr0, *xfer_hdr;
+
+       d_fnstart(3, dev, "(xfer %p [rpipe %p] urb %p)\n",
+                 xfer, xfer->ep->hcpriv, urb);
+
+       result = __wa_xfer_setup_sizes(xfer, &xfer_type);
+       if (result < 0)
+               goto error_setup_sizes;
+       xfer_hdr_size = result;
+       result = __wa_xfer_setup_segs(xfer, xfer_hdr_size);
+       if (result < 0) {
+               dev_err(dev, "xfer %p: Failed to allocate %d segments: %d\n",
+                       xfer, xfer->segs, result);
+               goto error_setup_segs;
+       }
+       /* Fill the first header */
+       xfer_hdr0 = &xfer->seg[0]->xfer_hdr;
+       wa_xfer_id_init(xfer);
+       __wa_xfer_setup_hdr0(xfer, xfer_hdr0, xfer_type, xfer_hdr_size);
+
+       /* Fill remainig headers */
+       xfer_hdr = xfer_hdr0;
+       transfer_size = urb->transfer_buffer_length;
+       xfer_hdr0->dwTransferLength = transfer_size > xfer->seg_size ?
+               xfer->seg_size : transfer_size;
+       transfer_size -=  xfer->seg_size;
+       for (cnt = 1; cnt < xfer->segs; cnt++) {
+               xfer_hdr = &xfer->seg[cnt]->xfer_hdr;
+               memcpy(xfer_hdr, xfer_hdr0, xfer_hdr_size);
+               xfer_hdr->bTransferSegment = cnt;
+               xfer_hdr->dwTransferLength = transfer_size > xfer->seg_size ?
+                       cpu_to_le32(xfer->seg_size)
+                       : cpu_to_le32(transfer_size);
+               xfer->seg[cnt]->status = WA_SEG_READY;
+               transfer_size -=  xfer->seg_size;
+       }
+       xfer_hdr->bTransferSegment |= 0x80;     /* this is the last segment */
+       result = 0;
+error_setup_segs:
+error_setup_sizes:
+       d_fnend(3, dev, "(xfer %p [rpipe %p] urb %p) = %d\n",
+               xfer, xfer->ep->hcpriv, urb, result);
+       return result;
+}
+
+/*
+ *
+ *
+ * rpipe->seg_lock is held!
+ */
+static int __wa_seg_submit(struct wa_rpipe *rpipe, struct wa_xfer *xfer,
+                          struct wa_seg *seg)
+{
+       int result;
+       result = usb_submit_urb(&seg->urb, GFP_ATOMIC);
+       if (result < 0) {
+               printk(KERN_ERR "xfer %p#%u: REQ submit failed: %d\n",
+                      xfer, seg->index, result);
+               goto error_seg_submit;
+       }
+       if (seg->dto_urb) {
+               result = usb_submit_urb(seg->dto_urb, GFP_ATOMIC);
+               if (result < 0) {
+                       printk(KERN_ERR "xfer %p#%u: DTO submit failed: %d\n",
+                              xfer, seg->index, result);
+                       goto error_dto_submit;
+               }
+       }
+       seg->status = WA_SEG_SUBMITTED;
+       rpipe_avail_dec(rpipe);
+       return 0;
+
+error_dto_submit:
+       usb_unlink_urb(&seg->urb);
+error_seg_submit:
+       seg->status = WA_SEG_ERROR;
+       seg->result = result;
+       return result;
+}
+
+/*
+ * Execute more queued request segments until the maximum concurrent allowed
+ *
+ * The ugly unlock/lock sequence on the error path is needed as the
+ * xfer->lock normally nests the seg_lock and not viceversa.
+ *
+ */
+static void wa_xfer_delayed_run(struct wa_rpipe *rpipe)
+{
+       int result;
+       struct device *dev = &rpipe->wa->usb_iface->dev;
+       struct wa_seg *seg;
+       struct wa_xfer *xfer;
+       unsigned long flags;
+
+       d_fnstart(1, dev, "(rpipe #%d) %d segments available\n",
+                 le16_to_cpu(rpipe->descr.wRPipeIndex),
+                 atomic_read(&rpipe->segs_available));
+       spin_lock_irqsave(&rpipe->seg_lock, flags);
+       while (atomic_read(&rpipe->segs_available) > 0
+             && !list_empty(&rpipe->seg_list)) {
+               seg = list_entry(rpipe->seg_list.next, struct wa_seg,
+                                list_node);
+               list_del(&seg->list_node);
+               xfer = seg->xfer;
+               result = __wa_seg_submit(rpipe, xfer, seg);
+               d_printf(1, dev, "xfer %p#%u submitted from delayed "
+                        "[%d segments available] %d\n",
+                        xfer, seg->index,
+                        atomic_read(&rpipe->segs_available), result);
+               if (unlikely(result < 0)) {
+                       spin_unlock_irqrestore(&rpipe->seg_lock, flags);
+                       spin_lock_irqsave(&xfer->lock, flags);
+                       __wa_xfer_abort(xfer);
+                       xfer->segs_done++;
+                       spin_unlock_irqrestore(&xfer->lock, flags);
+                       spin_lock_irqsave(&rpipe->seg_lock, flags);
+               }
+       }
+       spin_unlock_irqrestore(&rpipe->seg_lock, flags);
+       d_fnend(1, dev, "(rpipe #%d) = void, %d segments available\n",
+               le16_to_cpu(rpipe->descr.wRPipeIndex),
+               atomic_read(&rpipe->segs_available));
+
+}
+
+/*
+ *
+ * xfer->lock is taken
+ *
+ * On failure submitting we just stop submitting and return error;
+ * wa_urb_enqueue_b() will execute the completion path
+ */
+static int __wa_xfer_submit(struct wa_xfer *xfer)
+{
+       int result;
+       struct wahc *wa = xfer->wa;
+       struct device *dev = &wa->usb_iface->dev;
+       unsigned cnt;
+       struct wa_seg *seg;
+       unsigned long flags;
+       struct wa_rpipe *rpipe = xfer->ep->hcpriv;
+       size_t maxrequests = le16_to_cpu(rpipe->descr.wRequests);
+       u8 available;
+       u8 empty;
+
+       d_fnstart(3, dev, "(xfer %p [rpipe %p])\n",
+                 xfer, xfer->ep->hcpriv);
+
+       spin_lock_irqsave(&wa->xfer_list_lock, flags);
+       list_add_tail(&xfer->list_node, &wa->xfer_list);
+       spin_unlock_irqrestore(&wa->xfer_list_lock, flags);
+
+       BUG_ON(atomic_read(&rpipe->segs_available) > maxrequests);
+       result = 0;
+       spin_lock_irqsave(&rpipe->seg_lock, flags);
+       for (cnt = 0; cnt < xfer->segs; cnt++) {
+               available = atomic_read(&rpipe->segs_available);
+               empty = list_empty(&rpipe->seg_list);
+               seg = xfer->seg[cnt];
+               d_printf(2, dev, "xfer %p#%u: available %u empty %u (%s)\n",
+                        xfer, cnt, available, empty,
+                        available == 0 || !empty ? "delayed" : "submitted");
+               if (available == 0 || !empty) {
+                       d_printf(1, dev, "xfer %p#%u: delayed\n", xfer, cnt);
+                       seg->status = WA_SEG_DELAYED;
+                       list_add_tail(&seg->list_node, &rpipe->seg_list);
+               } else {
+                       result = __wa_seg_submit(rpipe, xfer, seg);
+                       if (result < 0)
+                               goto error_seg_submit;
+               }
+               xfer->segs_submitted++;
+       }
+       spin_unlock_irqrestore(&rpipe->seg_lock, flags);
+       d_fnend(3, dev, "(xfer %p [rpipe %p]) = void\n", xfer,
+               xfer->ep->hcpriv);
+       return result;
+
+error_seg_submit:
+       __wa_xfer_abort(xfer);
+       spin_unlock_irqrestore(&rpipe->seg_lock, flags);
+       d_fnend(3, dev, "(xfer %p [rpipe %p]) = void\n", xfer,
+               xfer->ep->hcpriv);
+       return result;
+}
+
+/*
+ * Second part of a URB/transfer enqueuement
+ *
+ * Assumes this comes from wa_urb_enqueue() [maybe through
+ * wa_urb_enqueue_run()]. At this point:
+ *
+ * xfer->wa    filled and refcounted
+ * xfer->ep    filled with rpipe refcounted if
+ *              delayed == 0
+ * xfer->urb   filled and refcounted (this is the case when called
+ *              from wa_urb_enqueue() as we come from usb_submit_urb()
+ *              and when called by wa_urb_enqueue_run(), as we took an
+ *              extra ref dropped by _run() after we return).
+ * xfer->gfp   filled
+ *
+ * If we fail at __wa_xfer_submit(), then we just check if we are done
+ * and if so, we run the completion procedure. However, if we are not
+ * yet done, we do nothing and wait for the completion handlers from
+ * the submitted URBs or from the xfer-result path to kick in. If xfer
+ * result never kicks in, the xfer will timeout from the USB code and
+ * dequeue() will be called.
+ */
+static void wa_urb_enqueue_b(struct wa_xfer *xfer)
+{
+       int result;
+       unsigned long flags;
+       struct urb *urb = xfer->urb;
+       struct wahc *wa = xfer->wa;
+       struct wusbhc *wusbhc = wa->wusb;
+       struct device *dev = &wa->usb_iface->dev;
+       struct wusb_dev *wusb_dev;
+       unsigned done;
+
+       d_fnstart(3, dev, "(wa %p urb %p)\n", wa, urb);
+       result = rpipe_get_by_ep(wa, xfer->ep, urb, xfer->gfp);
+       if (result < 0)
+               goto error_rpipe_get;
+       result = -ENODEV;
+       /* FIXME: segmentation broken -- kills DWA */
+       mutex_lock(&wusbhc->mutex);             /* get a WUSB dev */
+       if (urb->dev == NULL)
+               goto error_dev_gone;
+       wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc, urb->dev);
+       if (wusb_dev == NULL) {
+               mutex_unlock(&wusbhc->mutex);
+               goto error_dev_gone;
+       }
+       mutex_unlock(&wusbhc->mutex);
+
+       spin_lock_irqsave(&xfer->lock, flags);
+       xfer->wusb_dev = wusb_dev;
+       result = urb->status;
+       if (urb->status != -EINPROGRESS)
+               goto error_dequeued;
+
+       result = __wa_xfer_setup(xfer, urb);
+       if (result < 0)
+               goto error_xfer_setup;
+       result = __wa_xfer_submit(xfer);
+       if (result < 0)
+               goto error_xfer_submit;
+       spin_unlock_irqrestore(&xfer->lock, flags);
+       d_fnend(3, dev, "(wa %p urb %p) = void\n", wa, urb);
+       return;
+
+       /* this is basically wa_xfer_completion() broken up wa_xfer_giveback()
+        * does a wa_xfer_put() that will call wa_xfer_destroy() and clean
+        * upundo setup().
+        */
+error_xfer_setup:
+error_dequeued:
+       spin_unlock_irqrestore(&xfer->lock, flags);
+       /* FIXME: segmentation broken, kills DWA */
+       if (wusb_dev)
+               wusb_dev_put(wusb_dev);
+error_dev_gone:
+       rpipe_put(xfer->ep->hcpriv);
+error_rpipe_get:
+       xfer->result = result;
+       wa_xfer_giveback(xfer);
+       d_fnend(3, dev, "(wa %p urb %p) = (void) %d\n", wa, urb, result);
+       return;
+
+error_xfer_submit:
+       done = __wa_xfer_is_done(xfer);
+       xfer->result = result;
+       spin_unlock_irqrestore(&xfer->lock, flags);
+       if (done)
+               wa_xfer_completion(xfer);
+       d_fnend(3, dev, "(wa %p urb %p) = (void) %d\n", wa, urb, result);
+       return;
+}
+
+/*
+ * Execute the delayed transfers in the Wire Adapter @wa
+ *
+ * We need to be careful here, as dequeue() could be called in the
+ * middle.  That's why we do the whole thing under the
+ * wa->xfer_list_lock. If dequeue() jumps in, it first locks urb->lock
+ * and then checks the list -- so as we would be acquiring in inverse
+ * order, we just drop the lock once we have the xfer and reacquire it
+ * later.
+ */
+void wa_urb_enqueue_run(struct work_struct *ws)
+{
+       struct wahc *wa = container_of(ws, struct wahc, xfer_work);
+       struct device *dev = &wa->usb_iface->dev;
+       struct wa_xfer *xfer, *next;
+       struct urb *urb;
+
+       d_fnstart(3, dev, "(wa %p)\n", wa);
+       spin_lock_irq(&wa->xfer_list_lock);
+       list_for_each_entry_safe(xfer, next, &wa->xfer_delayed_list,
+                                list_node) {
+               list_del_init(&xfer->list_node);
+               spin_unlock_irq(&wa->xfer_list_lock);
+
+               urb = xfer->urb;
+               wa_urb_enqueue_b(xfer);
+               usb_put_urb(urb);       /* taken when queuing */
+
+               spin_lock_irq(&wa->xfer_list_lock);
+       }
+       spin_unlock_irq(&wa->xfer_list_lock);
+       d_fnend(3, dev, "(wa %p) = void\n", wa);
+}
+EXPORT_SYMBOL_GPL(wa_urb_enqueue_run);
+
+/*
+ * Submit a transfer to the Wire Adapter in a delayed way
+ *
+ * The process of enqueuing involves possible sleeps() [see
+ * enqueue_b(), for the rpipe_get() and the mutex_lock()]. If we are
+ * in an atomic section, we defer the enqueue_b() call--else we call direct.
+ *
+ * @urb: We own a reference to it done by the HCI Linux USB stack that
+ *       will be given up by calling usb_hcd_giveback_urb() or by
+ *       returning error from this function -> ergo we don't have to
+ *       refcount it.
+ */
+int wa_urb_enqueue(struct wahc *wa, struct usb_host_endpoint *ep,
+                  struct urb *urb, gfp_t gfp)
+{
+       int result;
+       struct device *dev = &wa->usb_iface->dev;
+       struct wa_xfer *xfer;
+       unsigned long my_flags;
+       unsigned cant_sleep = irqs_disabled() | in_atomic();
+
+       d_fnstart(3, dev, "(wa %p ep %p urb %p [%d] gfp 0x%x)\n",
+                 wa, ep, urb, urb->transfer_buffer_length, gfp);
+
+       if (urb->transfer_buffer == NULL
+           && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
+           && urb->transfer_buffer_length != 0) {
+               dev_err(dev, "BUG? urb %p: NULL xfer buffer & NODMA\n", urb);
+               dump_stack();
+       }
+
+       result = -ENOMEM;
+       xfer = kzalloc(sizeof(*xfer), gfp);
+       if (xfer == NULL)
+               goto error_kmalloc;
+
+       result = -ENOENT;
+       if (urb->status != -EINPROGRESS)        /* cancelled */
+               goto error_dequeued;            /* before starting? */
+       wa_xfer_init(xfer);
+       xfer->wa = wa_get(wa);
+       xfer->urb = urb;
+       xfer->gfp = gfp;
+       xfer->ep = ep;
+       urb->hcpriv = xfer;
+       d_printf(2, dev, "xfer %p urb %p pipe 0x%02x [%d bytes] %s %s %s\n",
+                xfer, urb, urb->pipe, urb->transfer_buffer_length,
+                urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP ? "dma" : "nodma",
+                urb->pipe & USB_DIR_IN ? "inbound" : "outbound",
+                cant_sleep ? "deferred" : "inline");
+       if (cant_sleep) {
+               usb_get_urb(urb);
+               spin_lock_irqsave(&wa->xfer_list_lock, my_flags);
+               list_add_tail(&xfer->list_node, &wa->xfer_delayed_list);
+               spin_unlock_irqrestore(&wa->xfer_list_lock, my_flags);
+               queue_work(wusbd, &wa->xfer_work);
+       } else {
+               wa_urb_enqueue_b(xfer);
+       }
+       d_fnend(3, dev, "(wa %p ep %p urb %p [%d] gfp 0x%x) = 0\n",
+               wa, ep, urb, urb->transfer_buffer_length, gfp);
+       return 0;
+
+error_dequeued:
+       kfree(xfer);
+error_kmalloc:
+       d_fnend(3, dev, "(wa %p ep %p urb %p [%d] gfp 0x%x) = %d\n",
+               wa, ep, urb, urb->transfer_buffer_length, gfp, result);
+       return result;
+}
+EXPORT_SYMBOL_GPL(wa_urb_enqueue);
+
+/*
+ * Dequeue a URB and make sure uwb_hcd_giveback_urb() [completion
+ * handler] is called.
+ *
+ * Until a transfer goes successfully through wa_urb_enqueue() it
+ * needs to be dequeued with completion calling; when stuck in delayed
+ * or before wa_xfer_setup() is called, we need to do completion.
+ *
+ *  not setup  If there is no hcpriv yet, that means that that enqueue
+ *             still had no time to set the xfer up. Because
+ *             urb->status should be other than -EINPROGRESS,
+ *             enqueue() will catch that and bail out.
+ *
+ * If the transfer has gone through setup, we just need to clean it
+ * up. If it has gone through submit(), we have to abort it [with an
+ * asynch request] and then make sure we cancel each segment.
+ *
+ */
+int wa_urb_dequeue(struct wahc *wa, struct urb *urb)
+{
+       struct device *dev = &wa->usb_iface->dev;
+       unsigned long flags, flags2;
+       struct wa_xfer *xfer;
+       struct wa_seg *seg;
+       struct wa_rpipe *rpipe;
+       unsigned cnt;
+       unsigned rpipe_ready = 0;
+
+       d_fnstart(3, dev, "(wa %p, urb %p)\n", wa, urb);
+
+       d_printf(1, dev, "xfer %p urb %p: aborting\n", urb->hcpriv, urb);
+       xfer = urb->hcpriv;
+       if (xfer == NULL) {
+               /* NOthing setup yet enqueue will see urb->status !=
+                * -EINPROGRESS (by hcd layer) and bail out with
+                * error, no need to do completion
+                */
+               BUG_ON(urb->status == -EINPROGRESS);
+               goto out;
+       }
+       spin_lock_irqsave(&xfer->lock, flags);
+       rpipe = xfer->ep->hcpriv;
+       /* Check the delayed list -> if there, release and complete */
+       spin_lock_irqsave(&wa->xfer_list_lock, flags2);
+       if (!list_empty(&xfer->list_node) && xfer->seg == NULL)
+               goto dequeue_delayed;
+       spin_unlock_irqrestore(&wa->xfer_list_lock, flags2);
+       if (xfer->seg == NULL)          /* still hasn't reached */
+               goto out_unlock;        /* setup(), enqueue_b() completes */
+       /* Ok, the xfer is in flight already, it's been setup and submitted.*/
+       __wa_xfer_abort(xfer);
+       for (cnt = 0; cnt < xfer->segs; cnt++) {
+               seg = xfer->seg[cnt];
+               switch (seg->status) {
+               case WA_SEG_NOTREADY:
+               case WA_SEG_READY:
+                       printk(KERN_ERR "xfer %p#%u: dequeue bad state %u\n",
+                              xfer, cnt, seg->status);
+                       WARN_ON(1);
+                       break;
+               case WA_SEG_DELAYED:
+                       seg->status = WA_SEG_ABORTED;
+                       spin_lock_irqsave(&rpipe->seg_lock, flags2);
+                       list_del(&seg->list_node);
+                       xfer->segs_done++;
+                       rpipe_ready = rpipe_avail_inc(rpipe);
+                       spin_unlock_irqrestore(&rpipe->seg_lock, flags2);
+                       break;
+               case WA_SEG_SUBMITTED:
+                       seg->status = WA_SEG_ABORTED;
+                       usb_unlink_urb(&seg->urb);
+                       if (xfer->is_inbound == 0)
+                               usb_unlink_urb(seg->dto_urb);
+                       xfer->segs_done++;
+                       rpipe_ready = rpipe_avail_inc(rpipe);
+                       break;
+               case WA_SEG_PENDING:
+                       seg->status = WA_SEG_ABORTED;
+                       xfer->segs_done++;
+                       rpipe_ready = rpipe_avail_inc(rpipe);
+                       break;
+               case WA_SEG_DTI_PENDING:
+                       usb_unlink_urb(wa->dti_urb);
+                       seg->status = WA_SEG_ABORTED;
+                       xfer->segs_done++;
+                       rpipe_ready = rpipe_avail_inc(rpipe);
+                       break;
+               case WA_SEG_DONE:
+               case WA_SEG_ERROR:
+               case WA_SEG_ABORTED:
+                       break;
+               }
+       }
+       xfer->result = urb->status;     /* -ENOENT or -ECONNRESET */
+       __wa_xfer_is_done(xfer);
+       spin_unlock_irqrestore(&xfer->lock, flags);
+       wa_xfer_completion(xfer);
+       if (rpipe_ready)
+               wa_xfer_delayed_run(rpipe);
+       d_fnend(3, dev, "(wa %p, urb %p) = 0\n", wa, urb);
+       return 0;
+
+out_unlock:
+       spin_unlock_irqrestore(&xfer->lock, flags);
+out:
+       d_fnend(3, dev, "(wa %p, urb %p) = 0\n", wa, urb);
+       return 0;
+
+dequeue_delayed:
+       list_del_init(&xfer->list_node);
+       spin_unlock_irqrestore(&wa->xfer_list_lock, flags2);
+       xfer->result = urb->status;
+       spin_unlock_irqrestore(&xfer->lock, flags);
+       wa_xfer_giveback(xfer);
+       usb_put_urb(urb);               /* we got a ref in enqueue() */
+       d_fnend(3, dev, "(wa %p, urb %p) = 0\n", wa, urb);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(wa_urb_dequeue);
+
+/*
+ * Translation from WA status codes (WUSB1.0 Table 8.15) to errno
+ * codes
+ *
+ * Positive errno values are internal inconsistencies and should be
+ * flagged louder. Negative are to be passed up to the user in the
+ * normal way.
+ *
+ * @status: USB WA status code -- high two bits are stripped.
+ */
+static int wa_xfer_status_to_errno(u8 status)
+{
+       int errno;
+       u8 real_status = status;
+       static int xlat[] = {
+               [WA_XFER_STATUS_SUCCESS] =              0,
+               [WA_XFER_STATUS_HALTED] =               -EPIPE,
+               [WA_XFER_STATUS_DATA_BUFFER_ERROR] =    -ENOBUFS,
+               [WA_XFER_STATUS_BABBLE] =               -EOVERFLOW,
+               [WA_XFER_RESERVED] =                    EINVAL,
+               [WA_XFER_STATUS_NOT_FOUND] =            0,
+               [WA_XFER_STATUS_INSUFFICIENT_RESOURCE] = -ENOMEM,
+               [WA_XFER_STATUS_TRANSACTION_ERROR] =    -EILSEQ,
+               [WA_XFER_STATUS_ABORTED] =              -EINTR,
+               [WA_XFER_STATUS_RPIPE_NOT_READY] =      EINVAL,
+               [WA_XFER_INVALID_FORMAT] =              EINVAL,
+               [WA_XFER_UNEXPECTED_SEGMENT_NUMBER] =   EINVAL,
+               [WA_XFER_STATUS_RPIPE_TYPE_MISMATCH] =  EINVAL,
+       };
+       status &= 0x3f;
+
+       if (status == 0)
+               return 0;
+       if (status >= ARRAY_SIZE(xlat)) {
+               if (printk_ratelimit())
+                       printk(KERN_ERR "%s(): BUG? "
+                              "Unknown WA transfer status 0x%02x\n",
+                              __func__, real_status);
+               return -EINVAL;
+       }
+       errno = xlat[status];
+       if (unlikely(errno > 0)) {
+               if (printk_ratelimit())
+                       printk(KERN_ERR "%s(): BUG? "
+                              "Inconsistent WA status: 0x%02x\n",
+                              __func__, real_status);
+               errno = -errno;
+       }
+       return errno;
+}
+
+/*
+ * Process a xfer result completion message
+ *
+ * inbound transfers: need to schedule a DTI read
+ *
+ * FIXME: this functio needs to be broken up in parts
+ */
+static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer)
+{
+       int result;
+       struct device *dev = &wa->usb_iface->dev;
+       unsigned long flags;
+       u8 seg_idx;
+       struct wa_seg *seg;
+       struct wa_rpipe *rpipe;
+       struct wa_xfer_result *xfer_result = wa->xfer_result;
+       u8 done = 0;
+       u8 usb_status;
+       unsigned rpipe_ready = 0;
+
+       d_fnstart(3, dev, "(wa %p xfer %p)\n", wa, xfer);
+       spin_lock_irqsave(&xfer->lock, flags);
+       seg_idx = xfer_result->bTransferSegment & 0x7f;
+       if (unlikely(seg_idx >= xfer->segs))
+               goto error_bad_seg;
+       seg = xfer->seg[seg_idx];
+       rpipe = xfer->ep->hcpriv;
+       usb_status = xfer_result->bTransferStatus;
+       d_printf(2, dev, "xfer %p#%u: bTransferStatus 0x%02x (seg %u)\n",
+                xfer, seg_idx, usb_status, seg->status);
+       if (seg->status == WA_SEG_ABORTED
+           || seg->status == WA_SEG_ERROR)     /* already handled */
+               goto segment_aborted;
+       if (seg->status == WA_SEG_SUBMITTED)    /* ops, got here */
+               seg->status = WA_SEG_PENDING;   /* before wa_seg{_dto}_cb() */
+       if (seg->status != WA_SEG_PENDING) {
+               if (printk_ratelimit())
+                       dev_err(dev, "xfer %p#%u: Bad segment state %u\n",
+                               xfer, seg_idx, seg->status);
+               seg->status = WA_SEG_PENDING;   /* workaround/"fix" it */
+       }
+       if (usb_status & 0x80) {
+               seg->result = wa_xfer_status_to_errno(usb_status);
+               dev_err(dev, "DTI: xfer %p#%u failed (0x%02x)\n",
+                       xfer, seg->index, usb_status);
+               goto error_complete;
+       }
+       /* FIXME: we ignore warnings, tally them for stats */
+       if (usb_status & 0x40)          /* Warning?... */
+               usb_status = 0;         /* ... pass */
+       if (xfer->is_inbound) { /* IN data phase: read to buffer */
+               seg->status = WA_SEG_DTI_PENDING;
+               BUG_ON(wa->buf_in_urb->status == -EINPROGRESS);
+               if (xfer->is_dma) {
+                       wa->buf_in_urb->transfer_dma =
+                               xfer->urb->transfer_dma
+                               + seg_idx * xfer->seg_size;
+                       wa->buf_in_urb->transfer_flags
+                               |= URB_NO_TRANSFER_DMA_MAP;
+               } else {
+                       wa->buf_in_urb->transfer_buffer =
+                               xfer->urb->transfer_buffer
+                               + seg_idx * xfer->seg_size;
+                       wa->buf_in_urb->transfer_flags
+                               &= ~URB_NO_TRANSFER_DMA_MAP;
+               }
+               wa->buf_in_urb->transfer_buffer_length =
+                       le32_to_cpu(xfer_result->dwTransferLength);
+               wa->buf_in_urb->context = seg;
+               result = usb_submit_urb(wa->buf_in_urb, GFP_ATOMIC);
+               if (result < 0)
+                       goto error_submit_buf_in;
+       } else {
+               /* OUT data phase, complete it -- */
+               seg->status = WA_SEG_DONE;
+               seg->result = le32_to_cpu(xfer_result->dwTransferLength);
+               xfer->segs_done++;
+               rpipe_ready = rpipe_avail_inc(rpipe);
+               done = __wa_xfer_is_done(xfer);
+       }
+       spin_unlock_irqrestore(&xfer->lock, flags);
+       if (done)
+               wa_xfer_completion(xfer);
+       if (rpipe_ready)
+               wa_xfer_delayed_run(rpipe);
+       d_fnend(3, dev, "(wa %p xfer %p) = void\n", wa, xfer);
+       return;
+
+
+error_submit_buf_in:
+       if (edc_inc(&wa->dti_edc, EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
+               dev_err(dev, "DTI: URB max acceptable errors "
+                       "exceeded, resetting device\n");
+               wa_reset_all(wa);
+       }
+       if (printk_ratelimit())
+               dev_err(dev, "xfer %p#%u: can't submit DTI data phase: %d\n",
+                       xfer, seg_idx, result);
+       seg->result = result;
+error_complete:
+       seg->status = WA_SEG_ERROR;
+       xfer->segs_done++;
+       rpipe_ready = rpipe_avail_inc(rpipe);
+       __wa_xfer_abort(xfer);
+       done = __wa_xfer_is_done(xfer);
+       spin_unlock_irqrestore(&xfer->lock, flags);
+       if (done)
+               wa_xfer_completion(xfer);
+       if (rpipe_ready)
+               wa_xfer_delayed_run(rpipe);
+       d_fnend(3, dev, "(wa %p xfer %p) = void [segment/DTI-submit error]\n",
+               wa, xfer);
+       return;
+
+
+error_bad_seg:
+       spin_unlock_irqrestore(&xfer->lock, flags);
+       wa_urb_dequeue(wa, xfer->urb);
+       if (printk_ratelimit())
+               dev_err(dev, "xfer %p#%u: bad segment\n", xfer, seg_idx);
+       if (edc_inc(&wa->dti_edc, EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
+               dev_err(dev, "DTI: URB max acceptable errors "
+                       "exceeded, resetting device\n");
+               wa_reset_all(wa);
+       }
+       d_fnend(3, dev, "(wa %p xfer %p) = void [bad seg]\n", wa, xfer);
+       return;
+
+
+segment_aborted:
+       /* nothing to do, as the aborter did the completion */
+       spin_unlock_irqrestore(&xfer->lock, flags);
+       d_fnend(3, dev, "(wa %p xfer %p) = void [segment aborted]\n",
+               wa, xfer);
+       return;
+
+}
+
+/*
+ * Callback for the IN data phase
+ *
+ * If succesful transition state; otherwise, take a note of the
+ * error, mark this segment done and try completion.
+ *
+ * Note we don't access until we are sure that the transfer hasn't
+ * been cancelled (ECONNRESET, ENOENT), which could mean that
+ * seg->xfer could be already gone.
+ */
+static void wa_buf_in_cb(struct urb *urb)
+{
+       struct wa_seg *seg = urb->context;
+       struct wa_xfer *xfer = seg->xfer;
+       struct wahc *wa;
+       struct device *dev;
+       struct wa_rpipe *rpipe;
+       unsigned rpipe_ready;
+       unsigned long flags;
+       u8 done = 0;
+
+       d_fnstart(3, NULL, "(urb %p [%d])\n", urb, urb->status);
+       switch (urb->status) {
+       case 0:
+               spin_lock_irqsave(&xfer->lock, flags);
+               wa = xfer->wa;
+               dev = &wa->usb_iface->dev;
+               rpipe = xfer->ep->hcpriv;
+               d_printf(2, dev, "xfer %p#%u: data in done (%zu bytes)\n",
+                          xfer, seg->index, (size_t)urb->actual_length);
+               seg->status = WA_SEG_DONE;
+               seg->result = urb->actual_length;
+               xfer->segs_done++;
+               rpipe_ready = rpipe_avail_inc(rpipe);
+               done = __wa_xfer_is_done(xfer);
+               spin_unlock_irqrestore(&xfer->lock, flags);
+               if (done)
+                       wa_xfer_completion(xfer);
+               if (rpipe_ready)
+                       wa_xfer_delayed_run(rpipe);
+               break;
+       case -ECONNRESET:       /* URB unlinked; no need to do anything */
+       case -ENOENT:           /* as it was done by the who unlinked us */
+               break;
+       default:                /* Other errors ... */
+               spin_lock_irqsave(&xfer->lock, flags);
+               wa = xfer->wa;
+               dev = &wa->usb_iface->dev;
+               rpipe = xfer->ep->hcpriv;
+               if (printk_ratelimit())
+                       dev_err(dev, "xfer %p#%u: data in error %d\n",
+                               xfer, seg->index, urb->status);
+               if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS,
+                           EDC_ERROR_TIMEFRAME)){
+                       dev_err(dev, "DTO: URB max acceptable errors "
+                               "exceeded, resetting device\n");
+                       wa_reset_all(wa);
+               }
+               seg->status = WA_SEG_ERROR;
+               seg->result = urb->status;
+               xfer->segs_done++;
+               rpipe_ready = rpipe_avail_inc(rpipe);
+               __wa_xfer_abort(xfer);
+               done = __wa_xfer_is_done(xfer);
+               spin_unlock_irqrestore(&xfer->lock, flags);
+               if (done)
+                       wa_xfer_completion(xfer);
+               if (rpipe_ready)
+                       wa_xfer_delayed_run(rpipe);
+       }
+       d_fnend(3, NULL, "(urb %p [%d]) = void\n", urb, urb->status);
+}
+
+/*
+ * Handle an incoming transfer result buffer
+ *
+ * Given a transfer result buffer, it completes the transfer (possibly
+ * scheduling and buffer in read) and then resubmits the DTI URB for a
+ * new transfer result read.
+ *
+ *
+ * The xfer_result DTI URB state machine
+ *
+ * States: OFF | RXR (Read-Xfer-Result) | RBI (Read-Buffer-In)
+ *
+ * We start in OFF mode, the first xfer_result notification [through
+ * wa_handle_notif_xfer()] moves us to RXR by posting the DTI-URB to
+ * read.
+ *
+ * We receive a buffer -- if it is not a xfer_result, we complain and
+ * repost the DTI-URB. If it is a xfer_result then do the xfer seg
+ * request accounting. If it is an IN segment, we move to RBI and post
+ * a BUF-IN-URB to the right buffer. The BUF-IN-URB callback will
+ * repost the DTI-URB and move to RXR state. if there was no IN
+ * segment, it will repost the DTI-URB.
+ *
+ * We go back to OFF when we detect a ENOENT or ESHUTDOWN (or too many
+ * errors) in the URBs.
+ */
+static void wa_xfer_result_cb(struct urb *urb)
+{
+       int result;
+       struct wahc *wa = urb->context;
+       struct device *dev = &wa->usb_iface->dev;
+       struct wa_xfer_result *xfer_result;
+       u32 xfer_id;
+       struct wa_xfer *xfer;
+       u8 usb_status;
+
+       d_fnstart(3, dev, "(%p)\n", wa);
+       BUG_ON(wa->dti_urb != urb);
+       switch (wa->dti_urb->status) {
+       case 0:
+               /* We have a xfer result buffer; check it */
+               d_printf(2, dev, "DTI: xfer result %d bytes at %p\n",
+                          urb->actual_length, urb->transfer_buffer);
+               d_dump(3, dev, urb->transfer_buffer, urb->actual_length);
+               if (wa->dti_urb->actual_length != sizeof(*xfer_result)) {
+                       dev_err(dev, "DTI Error: xfer result--bad size "
+                               "xfer result (%d bytes vs %zu needed)\n",
+                               urb->actual_length, sizeof(*xfer_result));
+                       break;
+               }
+               xfer_result = wa->xfer_result;
+               if (xfer_result->hdr.bLength != sizeof(*xfer_result)) {
+                       dev_err(dev, "DTI Error: xfer result--"
+                               "bad header length %u\n",
+                               xfer_result->hdr.bLength);
+                       break;
+               }
+               if (xfer_result->hdr.bNotifyType != WA_XFER_RESULT) {
+                       dev_err(dev, "DTI Error: xfer result--"
+                               "bad header type 0x%02x\n",
+                               xfer_result->hdr.bNotifyType);
+                       break;
+               }
+               usb_status = xfer_result->bTransferStatus & 0x3f;
+               if (usb_status == WA_XFER_STATUS_ABORTED
+                   || usb_status == WA_XFER_STATUS_NOT_FOUND)
+                       /* taken care of already */
+                       break;
+               xfer_id = xfer_result->dwTransferID;
+               xfer = wa_xfer_get_by_id(wa, xfer_id);
+               if (xfer == NULL) {
+                       /* FIXME: transaction might have been cancelled */
+                       dev_err(dev, "DTI Error: xfer result--"
+                               "unknown xfer 0x%08x (status 0x%02x)\n",
+                               xfer_id, usb_status);
+                       break;
+               }
+               wa_xfer_result_chew(wa, xfer);
+               wa_xfer_put(xfer);
+               break;
+       case -ENOENT:           /* (we killed the URB)...so, no broadcast */
+       case -ESHUTDOWN:        /* going away! */
+               dev_dbg(dev, "DTI: going down! %d\n", urb->status);
+               goto out;
+       default:
+               /* Unknown error */
+               if (edc_inc(&wa->dti_edc, EDC_MAX_ERRORS,
+                           EDC_ERROR_TIMEFRAME)) {
+                       dev_err(dev, "DTI: URB max acceptable errors "
+                               "exceeded, resetting device\n");
+                       wa_reset_all(wa);
+                       goto out;
+               }
+               if (printk_ratelimit())
+                       dev_err(dev, "DTI: URB error %d\n", urb->status);
+               break;
+       }
+       /* Resubmit the DTI URB */
+       result = usb_submit_urb(wa->dti_urb, GFP_ATOMIC);
+       if (result < 0) {
+               dev_err(dev, "DTI Error: Could not submit DTI URB (%d), "
+                       "resetting\n", result);
+               wa_reset_all(wa);
+       }
+out:
+       d_fnend(3, dev, "(%p) = void\n", wa);
+       return;
+}
+
+/*
+ * Transfer complete notification
+ *
+ * Called from the notif.c code. We get a notification on EP2 saying
+ * that some endpoint has some transfer result data available. We are
+ * about to read it.
+ *
+ * To speed up things, we always have a URB reading the DTI URB; we
+ * don't really set it up and start it until the first xfer complete
+ * notification arrives, which is what we do here.
+ *
+ * Follow up in wa_xfer_result_cb(), as that's where the whole state
+ * machine starts.
+ *
+ * So here we just initialize the DTI URB for reading transfer result
+ * notifications and also the buffer-in URB, for reading buffers. Then
+ * we just submit the DTI URB.
+ *
+ * @wa shall be referenced
+ */
+void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr)
+{
+       int result;
+       struct device *dev = &wa->usb_iface->dev;
+       struct wa_notif_xfer *notif_xfer;
+       const struct usb_endpoint_descriptor *dti_epd = wa->dti_epd;
+
+       d_fnstart(4, dev, "(%p, %p)\n", wa, notif_hdr);
+       notif_xfer = container_of(notif_hdr, struct wa_notif_xfer, hdr);
+       BUG_ON(notif_hdr->bNotifyType != WA_NOTIF_TRANSFER);
+
+       if ((0x80 | notif_xfer->bEndpoint) != dti_epd->bEndpointAddress) {
+               /* FIXME: hardcoded limitation, adapt */
+               dev_err(dev, "BUG: DTI ep is %u, not %u (hack me)\n",
+                       notif_xfer->bEndpoint, dti_epd->bEndpointAddress);
+               goto error;
+       }
+       if (wa->dti_urb != NULL)        /* DTI URB already started */
+               goto out;
+
+       wa->dti_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (wa->dti_urb == NULL) {
+               dev_err(dev, "Can't allocate DTI URB\n");
+               goto error_dti_urb_alloc;
+       }
+       usb_fill_bulk_urb(
+               wa->dti_urb, wa->usb_dev,
+               usb_rcvbulkpipe(wa->usb_dev, 0x80 | notif_xfer->bEndpoint),
+               wa->xfer_result, wa->xfer_result_size,
+               wa_xfer_result_cb, wa);
+
+       wa->buf_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (wa->buf_in_urb == NULL) {
+               dev_err(dev, "Can't allocate BUF-IN URB\n");
+               goto error_buf_in_urb_alloc;
+       }
+       usb_fill_bulk_urb(
+               wa->buf_in_urb, wa->usb_dev,
+               usb_rcvbulkpipe(wa->usb_dev, 0x80 | notif_xfer->bEndpoint),
+               NULL, 0, wa_buf_in_cb, wa);
+       result = usb_submit_urb(wa->dti_urb, GFP_KERNEL);
+       if (result < 0) {
+               dev_err(dev, "DTI Error: Could not submit DTI URB (%d), "
+                       "resetting\n", result);
+               goto error_dti_urb_submit;
+       }
+out:
+       d_fnend(4, dev, "(%p, %p) = void\n", wa, notif_hdr);
+       return;
+
+error_dti_urb_submit:
+       usb_put_urb(wa->buf_in_urb);
+error_buf_in_urb_alloc:
+       usb_put_urb(wa->dti_urb);
+       wa->dti_urb = NULL;
+error_dti_urb_alloc:
+error:
+       wa_reset_all(wa);
+       d_fnend(4, dev, "(%p, %p) = void\n", wa, notif_hdr);
+       return;
+}
diff --git a/drivers/usb/wusbcore/wusbhc.c b/drivers/usb/wusbcore/wusbhc.c
new file mode 100644 (file)
index 0000000..07c63a3
--- /dev/null
@@ -0,0 +1,418 @@
+/*
+ * Wireless USB Host Controller
+ * sysfs glue, wusbcore module support and life cycle 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.
+ *
+ *
+ * Creation/destruction of wusbhc is split in two parts; that that
+ * doesn't require the HCD to be added (wusbhc_{create,destroy}) and
+ * the one that requires (phase B, wusbhc_b_{create,destroy}).
+ *
+ * This is so because usb_add_hcd() will start the HC, and thus, all
+ * the HC specific stuff has to be already initialiazed (like sysfs
+ * thingies).
+ */
+#include <linux/device.h>
+#include <linux/module.h>
+#include "wusbhc.h"
+
+/**
+ * Extract the wusbhc that corresponds to a USB Host Controller class device
+ *
+ * WARNING! Apply only if @dev is that of a
+ *          wusbhc.usb_hcd.self->class_dev; otherwise, you loose.
+ */
+static struct wusbhc *usbhc_dev_to_wusbhc(struct device *dev)
+{
+       struct usb_bus *usb_bus = dev_get_drvdata(dev);
+       struct usb_hcd *usb_hcd = bus_to_hcd(usb_bus);
+       return usb_hcd_to_wusbhc(usb_hcd);
+}
+
+/*
+ * Show & store the current WUSB trust timeout
+ *
+ * We don't do locking--it is an 'atomic' value.
+ *
+ * The units that we store/show are always MILLISECONDS. However, the
+ * value of trust_timeout is jiffies.
+ */
+static ssize_t wusb_trust_timeout_show(struct device *dev,
+                                      struct device_attribute *attr, char *buf)
+{
+       struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev);
+
+       return scnprintf(buf, PAGE_SIZE, "%u\n", wusbhc->trust_timeout);
+}
+
+static ssize_t wusb_trust_timeout_store(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t size)
+{
+       struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev);
+       ssize_t result = -ENOSYS;
+       unsigned trust_timeout;
+
+       result = sscanf(buf, "%u", &trust_timeout);
+       if (result != 1) {
+               result = -EINVAL;
+               goto out;
+       }
+       /* FIXME: maybe we should check for range validity? */
+       wusbhc->trust_timeout = trust_timeout;
+       cancel_delayed_work(&wusbhc->keep_alive_timer);
+       flush_workqueue(wusbd);
+       queue_delayed_work(wusbd, &wusbhc->keep_alive_timer,
+                          (trust_timeout * CONFIG_HZ)/1000/2);
+out:
+       return result < 0 ? result : size;
+}
+static DEVICE_ATTR(wusb_trust_timeout, 0644, wusb_trust_timeout_show,
+                                            wusb_trust_timeout_store);
+
+/*
+ * Show & store the current WUSB CHID
+ */
+static ssize_t wusb_chid_show(struct device *dev,
+                             struct device_attribute *attr, char *buf)
+{
+       struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev);
+       ssize_t result = 0;
+
+       if (wusbhc->wuie_host_info != NULL)
+               result += ckhdid_printf(buf, PAGE_SIZE,
+                                       &wusbhc->wuie_host_info->CHID);
+       return result;
+}
+
+/*
+ * Store a new CHID
+ *
+ * This will (FIXME) trigger many changes.
+ *
+ * - Send an all zeros CHID and it will stop the controller
+ * - Send a non-zero CHID and it will start it
+ *   (unless it was started, it will just change the CHID,
+ *   diconnecting all devices first).
+ *
+ * So first we scan the MMC we are sent and then we act on it.  We
+ * read it in the same format as we print it, an ASCII string of 16
+ * hex bytes.
+ *
+ * See wusbhc_chid_set() for more info.
+ */
+static ssize_t wusb_chid_store(struct device *dev,
+                              struct device_attribute *attr,
+                              const char *buf, size_t size)
+{
+       struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev);
+       struct wusb_ckhdid chid;
+       ssize_t result;
+
+       result = sscanf(buf,
+                       "%02hhx %02hhx %02hhx %02hhx "
+                       "%02hhx %02hhx %02hhx %02hhx "
+                       "%02hhx %02hhx %02hhx %02hhx "
+                       "%02hhx %02hhx %02hhx %02hhx\n",
+                       &chid.data[0] , &chid.data[1] ,
+                       &chid.data[2] , &chid.data[3] ,
+                       &chid.data[4] , &chid.data[5] ,
+                       &chid.data[6] , &chid.data[7] ,
+                       &chid.data[8] , &chid.data[9] ,
+                       &chid.data[10], &chid.data[11],
+                       &chid.data[12], &chid.data[13],
+                       &chid.data[14], &chid.data[15]);
+       if (result != 16) {
+               dev_err(dev, "Unrecognized CHID (need 16 8-bit hex digits): "
+                       "%d\n", (int)result);
+               return -EINVAL;
+       }
+       result = wusbhc_chid_set(wusbhc, &chid);
+       return result < 0 ? result : size;
+}
+static DEVICE_ATTR(wusb_chid, 0644, wusb_chid_show, wusb_chid_store);
+
+/* Group all the WUSBHC attributes */
+static struct attribute *wusbhc_attrs[] = {
+               &dev_attr_wusb_trust_timeout.attr,
+               &dev_attr_wusb_chid.attr,
+               NULL,
+};
+
+static struct attribute_group wusbhc_attr_group = {
+       .name = NULL,   /* we want them in the same directory */
+       .attrs = wusbhc_attrs,
+};
+
+/*
+ * Create a wusbhc instance
+ *
+ * NOTEs:
+ *
+ *  - assumes *wusbhc has been zeroed and wusbhc->usb_hcd has been
+ *    initialized but not added.
+ *
+ *  - fill out ports_max, mmcies_max and mmcie_{add,rm} before calling.
+ *
+ *  - fill out wusbhc->uwb_rc and refcount it before calling
+ *  - fill out the wusbhc->sec_modes array
+ */
+int wusbhc_create(struct wusbhc *wusbhc)
+{
+       int result = 0;
+
+       wusbhc->trust_timeout = WUSB_TRUST_TIMEOUT_MS;
+       mutex_init(&wusbhc->mutex);
+       result = wusbhc_mmcie_create(wusbhc);
+       if (result < 0)
+               goto error_mmcie_create;
+       result = wusbhc_devconnect_create(wusbhc);
+       if (result < 0)
+               goto error_devconnect_create;
+       result = wusbhc_rh_create(wusbhc);
+       if (result < 0)
+               goto error_rh_create;
+       result = wusbhc_sec_create(wusbhc);
+       if (result < 0)
+               goto error_sec_create;
+       return 0;
+
+error_sec_create:
+       wusbhc_rh_destroy(wusbhc);
+error_rh_create:
+       wusbhc_devconnect_destroy(wusbhc);
+error_devconnect_create:
+       wusbhc_mmcie_destroy(wusbhc);
+error_mmcie_create:
+       return result;
+}
+EXPORT_SYMBOL_GPL(wusbhc_create);
+
+static inline struct kobject *wusbhc_kobj(struct wusbhc *wusbhc)
+{
+       return &wusbhc->usb_hcd.self.controller->kobj;
+}
+
+/*
+ * Phase B of a wusbhc instance creation
+ *
+ * Creates fields that depend on wusbhc->usb_hcd having been
+ * added. This is where we create the sysfs files in
+ * /sys/class/usb_host/usb_hostX/.
+ *
+ * NOTE: Assumes wusbhc->usb_hcd has been already added by the upper
+ *       layer (hwahc or whci)
+ */
+int wusbhc_b_create(struct wusbhc *wusbhc)
+{
+       int result = 0;
+       struct device *dev = wusbhc->usb_hcd.self.controller;
+
+       result = sysfs_create_group(wusbhc_kobj(wusbhc), &wusbhc_attr_group);
+       if (result < 0) {
+               dev_err(dev, "Cannot register WUSBHC attributes: %d\n", result);
+               goto error_create_attr_group;
+       }
+
+       result = wusbhc_pal_register(wusbhc);
+       if (result < 0)
+               goto error_pal_register;
+       return 0;
+
+error_pal_register:
+       sysfs_remove_group(wusbhc_kobj(wusbhc), &wusbhc_attr_group);
+error_create_attr_group:
+       return result;
+}
+EXPORT_SYMBOL_GPL(wusbhc_b_create);
+
+void wusbhc_b_destroy(struct wusbhc *wusbhc)
+{
+       wusbhc_pal_unregister(wusbhc);
+       sysfs_remove_group(wusbhc_kobj(wusbhc), &wusbhc_attr_group);
+}
+EXPORT_SYMBOL_GPL(wusbhc_b_destroy);
+
+void wusbhc_destroy(struct wusbhc *wusbhc)
+{
+       wusbhc_sec_destroy(wusbhc);
+       wusbhc_rh_destroy(wusbhc);
+       wusbhc_devconnect_destroy(wusbhc);
+       wusbhc_mmcie_destroy(wusbhc);
+}
+EXPORT_SYMBOL_GPL(wusbhc_destroy);
+
+struct workqueue_struct *wusbd;
+EXPORT_SYMBOL_GPL(wusbd);
+
+/*
+ * WUSB Cluster ID allocation map
+ *
+ * Each WUSB bus in a channel is identified with a Cluster Id in the
+ * unauth address pace (WUSB1.0[4.3]). We take the range 0xe0 to 0xff
+ * (that's space for 31 WUSB controllers, as 0xff can't be taken). We
+ * start taking from 0xff, 0xfe, 0xfd... (hence the += or -= 0xff).
+ *
+ * For each one we taken, we pin it in the bitap
+ */
+#define CLUSTER_IDS 32
+static DECLARE_BITMAP(wusb_cluster_id_table, CLUSTER_IDS);
+static DEFINE_SPINLOCK(wusb_cluster_ids_lock);
+
+/*
+ * Get a WUSB Cluster ID
+ *
+ * Need to release with wusb_cluster_id_put() when done w/ it.
+ */
+/* FIXME: coordinate with the choose_addres() from the USB stack */
+/* we want to leave the top of the 128 range for cluster addresses and
+ * the bottom for device addresses (as we map them one on one with
+ * ports). */
+u8 wusb_cluster_id_get(void)
+{
+       u8 id;
+       spin_lock(&wusb_cluster_ids_lock);
+       id = find_first_zero_bit(wusb_cluster_id_table, CLUSTER_IDS);
+       if (id > CLUSTER_IDS) {
+               id = 0;
+               goto out;
+       }
+       set_bit(id, wusb_cluster_id_table);
+       id = (u8) 0xff - id;
+out:
+       spin_unlock(&wusb_cluster_ids_lock);
+       return id;
+
+}
+EXPORT_SYMBOL_GPL(wusb_cluster_id_get);
+
+/*
+ * Release a WUSB Cluster ID
+ *
+ * Obtained it with wusb_cluster_id_get()
+ */
+void wusb_cluster_id_put(u8 id)
+{
+       id = 0xff - id;
+       BUG_ON(id >= CLUSTER_IDS);
+       spin_lock(&wusb_cluster_ids_lock);
+       WARN_ON(!test_bit(id, wusb_cluster_id_table));
+       clear_bit(id, wusb_cluster_id_table);
+       spin_unlock(&wusb_cluster_ids_lock);
+}
+EXPORT_SYMBOL_GPL(wusb_cluster_id_put);
+
+/**
+ * wusbhc_giveback_urb - return an URB to the USB core
+ * @wusbhc: the host controller the URB is from.
+ * @urb:    the URB.
+ * @status: the URB's status.
+ *
+ * Return an URB to the USB core doing some additional WUSB specific
+ * processing.
+ *
+ *  - After a successful transfer, update the trust timeout timestamp
+ *    for the WUSB device.
+ *
+ *  - [WUSB] sections 4.13 and 7.5.1 specifies the stop retrasmittion
+ *    condition for the WCONNECTACK_IE is that the host has observed
+ *    the associated device responding to a control transfer.
+ */
+void wusbhc_giveback_urb(struct wusbhc *wusbhc, struct urb *urb, int status)
+{
+       struct wusb_dev *wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc, urb->dev);
+
+       if (status == 0) {
+               wusb_dev->entry_ts = jiffies;
+
+               /* wusbhc_devconnect_acked() can't be called from from
+                  atomic context so defer it to a work queue. */
+               if (!list_empty(&wusb_dev->cack_node))
+                       queue_work(wusbd, &wusb_dev->devconnect_acked_work);
+       }
+
+       usb_hcd_giveback_urb(&wusbhc->usb_hcd, urb, status);
+}
+EXPORT_SYMBOL_GPL(wusbhc_giveback_urb);
+
+/**
+ * wusbhc_reset_all - reset the HC hardware
+ * @wusbhc: the host controller to reset.
+ *
+ * Request a full hardware reset of the chip.  This will also reset
+ * the radio controller and any other PALs.
+ */
+void wusbhc_reset_all(struct wusbhc *wusbhc)
+{
+       uwb_rc_reset_all(wusbhc->uwb_rc);
+}
+EXPORT_SYMBOL_GPL(wusbhc_reset_all);
+
+static struct notifier_block wusb_usb_notifier = {
+       .notifier_call = wusb_usb_ncb,
+       .priority = INT_MAX     /* Need to be called first of all */
+};
+
+static int __init wusbcore_init(void)
+{
+       int result;
+       result = wusb_crypto_init();
+       if (result < 0)
+               goto error_crypto_init;
+       /* WQ is singlethread because we need to serialize notifications */
+       wusbd = create_singlethread_workqueue("wusbd");
+       if (wusbd == NULL) {
+               result = -ENOMEM;
+               printk(KERN_ERR "WUSB-core: Cannot create wusbd workqueue\n");
+               goto error_wusbd_create;
+       }
+       usb_register_notify(&wusb_usb_notifier);
+       bitmap_zero(wusb_cluster_id_table, CLUSTER_IDS);
+       set_bit(0, wusb_cluster_id_table);      /* reserve Cluster ID 0xff */
+       return 0;
+
+error_wusbd_create:
+       wusb_crypto_exit();
+error_crypto_init:
+       return result;
+
+}
+module_init(wusbcore_init);
+
+static void __exit wusbcore_exit(void)
+{
+       clear_bit(0, wusb_cluster_id_table);
+       if (!bitmap_empty(wusb_cluster_id_table, CLUSTER_IDS)) {
+               char buf[256];
+               bitmap_scnprintf(buf, sizeof(buf), wusb_cluster_id_table,
+                                CLUSTER_IDS);
+               printk(KERN_ERR "BUG: WUSB Cluster IDs not released "
+                      "on exit: %s\n", buf);
+               WARN_ON(1);
+       }
+       usb_unregister_notify(&wusb_usb_notifier);
+       destroy_workqueue(wusbd);
+       wusb_crypto_exit();
+}
+module_exit(wusbcore_exit);
+
+MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
+MODULE_DESCRIPTION("Wireless USB core");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/wusbcore/wusbhc.h b/drivers/usb/wusbcore/wusbhc.h
new file mode 100644 (file)
index 0000000..d0c1324
--- /dev/null
@@ -0,0 +1,495 @@
+/*
+ * Wireless USB Host Controller
+ * Common infrastructure for WHCI and HWA WUSB-HC drivers
+ *
+ *
+ * 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 implements parts common to all Wireless USB Host
+ * Controllers (struct wusbhc, embedding a struct usb_hcd) and is used
+ * by:
+ *
+ *   - hwahc: HWA, USB-dongle that implements a Wireless USB host
+ *     controller, (Wireless USB 1.0 Host-Wire-Adapter specification).
+ *
+ *   - whci: WHCI, a PCI card with a wireless host controller
+ *     (Wireless Host Controller Interface 1.0 specification).
+ *
+ * Check out the Design-overview.txt file in the source documentation
+ * for other details on the implementation.
+ *
+ * Main blocks:
+ *
+ *  rh         Root Hub emulation (part of the HCD glue)
+ *
+ *  devconnect Handle all the issues related to device connection,
+ *             authentication, disconnection, timeout, reseting,
+ *             keepalives, etc.
+ *
+ *  mmc        MMC IE broadcasting handling
+ *
+ * A host controller driver just initializes its stuff and as part of
+ * that, creates a 'struct wusbhc' instance that handles all the
+ * common WUSB mechanisms. Links in the function ops that are specific
+ * to it and then registers the host controller. Ready to run.
+ */
+
+#ifndef __WUSBHC_H__
+#define __WUSBHC_H__
+
+#include <linux/usb.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/kref.h>
+#include <linux/workqueue.h>
+/* FIXME: Yes, I know: BAD--it's not my fault the USB HC iface is not
+ *        public */
+#include <linux/../../drivers/usb/core/hcd.h>
+#include <linux/uwb.h>
+#include <linux/usb/wusb.h>
+
+
+/**
+ * Wireless USB device
+ *
+ * Describe a WUSB device connected to the cluster. This struct
+ * belongs to the 'struct wusb_port' it is attached to and it is
+ * responsible for putting and clearing the pointer to it.
+ *
+ * Note this "complements" the 'struct usb_device' that the usb_hcd
+ * keeps for each connected USB device. However, it extends some
+ * information that is not available (there is no hcpriv ptr in it!)
+ * *and* most importantly, it's life cycle is different. It is created
+ * as soon as we get a DN_Connect (connect request notification) from
+ * the device through the WUSB host controller; the USB stack doesn't
+ * create the device until we authenticate it. FIXME: this will
+ * change.
+ *
+ * @bos:    This is allocated when the BOS descriptors are read from
+ *          the device and freed upon the wusb_dev struct dying.
+ * @wusb_cap_descr: points into @bos, and has been verified to be size
+ *                  safe.
+ */
+struct wusb_dev {
+       struct kref refcnt;
+       struct wusbhc *wusbhc;
+       struct list_head cack_node;     /* Connect-Ack list */
+       u8 port_idx;
+       u8 addr;
+       u8 beacon_type:4;
+       struct usb_encryption_descriptor ccm1_etd;
+       struct wusb_ckhdid cdid;
+       unsigned long entry_ts;
+       struct usb_bos_descriptor *bos;
+       struct usb_wireless_cap_descriptor *wusb_cap_descr;
+       struct uwb_mas_bm availability;
+       struct work_struct devconnect_acked_work;
+       struct urb *set_gtk_urb;
+       struct usb_ctrlrequest *set_gtk_req;
+       struct usb_device *usb_dev;
+};
+
+#define WUSB_DEV_ADDR_UNAUTH 0x80
+
+static inline void wusb_dev_init(struct wusb_dev *wusb_dev)
+{
+       kref_init(&wusb_dev->refcnt);
+       /* no need to init the cack_node */
+}
+
+extern void wusb_dev_destroy(struct kref *_wusb_dev);
+
+static inline struct wusb_dev *wusb_dev_get(struct wusb_dev *wusb_dev)
+{
+       kref_get(&wusb_dev->refcnt);
+       return wusb_dev;
+}
+
+static inline void wusb_dev_put(struct wusb_dev *wusb_dev)
+{
+       kref_put(&wusb_dev->refcnt, wusb_dev_destroy);
+}
+
+/**
+ * Wireless USB Host Controlller root hub "fake" ports
+ * (state and device information)
+ *
+ * Wireless USB is wireless, so there are no ports; but we
+ * fake'em. Each RC can connect a max of devices at the same time
+ * (given in the Wireless Adapter descriptor, bNumPorts or WHCI's
+ * caps), referred to in wusbhc->ports_max.
+ *
+ * See rh.c for more information.
+ *
+ * The @status and @change use the same bits as in USB2.0[11.24.2.7],
+ * so we don't have to do much when getting the port's status.
+ *
+ * WUSB1.0[7.1], USB2.0[11.24.2.7.1,fig 11-10],
+ * include/linux/usb_ch9.h (#define USB_PORT_STAT_*)
+ */
+struct wusb_port {
+       u16 status;
+       u16 change;
+       struct wusb_dev *wusb_dev;      /* connected device's info */
+       unsigned reset_count;
+       u32 ptk_tkid;
+};
+
+/**
+ * WUSB Host Controller specifics
+ *
+ * All fields that are common to all Wireless USB controller types
+ * (HWA and WHCI) are grouped here. Host Controller
+ * functions/operations that only deal with general Wireless USB HC
+ * issues use this data type to refer to the host.
+ *
+ * @usb_hcd       Instantiation of a USB host controller
+ *                 (initialized by upper layer [HWA=HC or WHCI].
+ *
+ * @dev                   Device that implements this; initialized by the
+ *                 upper layer (HWA-HC, WHCI...); this device should
+ *                 have a refcount.
+ *
+ * @trust_timeout  After this time without hearing for device
+ *                 activity, we consider the device gone and we have to
+ *                 re-authenticate.
+ *
+ *                 Can be accessed w/o locking--however, read to a
+ *                 local variable then use.
+ *
+ * @chid           WUSB Cluster Host ID: this is supposed to be a
+ *                 unique value that doesn't change across reboots (so
+ *                 that your devices do not require re-association).
+ *
+ *                 Read/Write protected by @mutex
+ *
+ * @dev_info       This array has ports_max elements. It is used to
+ *                 give the HC information about the WUSB devices (see
+ *                 'struct wusb_dev_info').
+ *
+ *                For HWA we need to allocate it in heap; for WHCI it
+ *                 needs to be permanently mapped, so we keep it for
+ *                 both and make it easy. Call wusbhc->dev_info_set()
+ *                 to update an entry.
+ *
+ * @ports_max     Number of simultaneous device connections (fake
+ *                 ports) this HC will take. Read-only.
+ *
+ * @port          Array of port status for each fake root port. Guaranteed to
+ *                 always be the same lenght during device existence
+ *                 [this allows for some unlocked but referenced reading].
+ *
+ * @mmcies_max    Max number of Information Elements this HC can send
+ *                 in its MMC. Read-only.
+ *
+ * @mmcie_add     HC specific operation (WHCI or HWA) for adding an
+ *                 MMCIE.
+ *
+ * @mmcie_rm      HC specific operation (WHCI or HWA) for removing an
+ *                 MMCIE.
+ *
+ * @enc_types     Array which describes the encryptions methods
+ *                 supported by the host as described in WUSB1.0 --
+ *                 one entry per supported method. As of WUSB1.0 there
+ *                 is only four methods, we make space for eight just in
+ *                 case they decide to add some more (and pray they do
+ *                 it in sequential order). if 'enc_types[enc_method]
+ *                 != 0', then it is supported by the host. enc_method
+ *                 is USB_ENC_TYPE*.
+ *
+ * @set_ptk:       Set the PTK and enable encryption for a device. Or, if
+ *                 the supplied key is NULL, disable encryption for that
+ *                 device.
+ *
+ * @set_gtk:       Set the GTK to be used for all future broadcast packets
+ *                 (i.e., MMCs).  With some hardware, setting the GTK may start
+ *                 MMC transmission.
+ *
+ * NOTE:
+ *
+ *  - If wusb_dev->usb_dev is not NULL, then usb_dev is valid
+ *    (wusb_dev has a refcount on it). Likewise, if usb_dev->wusb_dev
+ *    is not NULL, usb_dev->wusb_dev is valid (usb_dev keeps a
+ *    refcount on it).
+ *
+ *    Most of the times when you need to use it, it will be non-NULL,
+ *    so there is no real need to check for it (wusb_dev will
+ *    dissapear before usb_dev).
+ *
+ *  - The following fields need to be filled out before calling
+ *    wusbhc_create(): ports_max, mmcies_max, mmcie_{add,rm}.
+ *
+ *  - there is no wusbhc_init() method, we do everything in
+ *    wusbhc_create().
+ *
+ *  - Creation is done in two phases, wusbhc_create() and
+ *    wusbhc_create_b(); b are the parts that need to be called after
+ *    calling usb_hcd_add(&wusbhc->usb_hcd).
+ */
+struct wusbhc {
+       struct usb_hcd usb_hcd;         /* HAS TO BE 1st */
+       struct device *dev;
+       struct uwb_rc *uwb_rc;
+       struct uwb_pal pal;
+
+       unsigned trust_timeout;                 /* in jiffies */
+       struct wuie_host_info *wuie_host_info;  /* Includes CHID */
+
+       struct mutex mutex;                     /* locks everything else */
+       u16 cluster_id;                         /* Wireless USB Cluster ID */
+       struct wusb_port *port;                 /* Fake port status handling */
+       struct wusb_dev_info *dev_info;         /* for Set Device Info mgmt */
+       u8 ports_max;
+       unsigned active:1;                      /* currently xmit'ing MMCs */
+       struct wuie_keep_alive keep_alive_ie;   /* protected by mutex */
+       struct delayed_work keep_alive_timer;
+       struct list_head cack_list;             /* Connect acknowledging */
+       size_t cack_count;                      /* protected by 'mutex' */
+       struct wuie_connect_ack cack_ie;
+       struct uwb_rsv *rsv;            /* cluster bandwidth reservation */
+
+       struct mutex mmcie_mutex;               /* MMC WUIE handling */
+       struct wuie_hdr **mmcie;                /* WUIE array */
+       u8 mmcies_max;
+       /* FIXME: make wusbhc_ops? */
+       int (*start)(struct wusbhc *wusbhc);
+       void (*stop)(struct wusbhc *wusbhc);
+       int (*mmcie_add)(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt,
+                        u8 handle, struct wuie_hdr *wuie);
+       int (*mmcie_rm)(struct wusbhc *wusbhc, u8 handle);
+       int (*dev_info_set)(struct wusbhc *, struct wusb_dev *wusb_dev);
+       int (*bwa_set)(struct wusbhc *wusbhc, s8 stream_index,
+                      const struct uwb_mas_bm *);
+       int (*set_ptk)(struct wusbhc *wusbhc, u8 port_idx,
+                      u32 tkid, const void *key, size_t key_size);
+       int (*set_gtk)(struct wusbhc *wusbhc,
+                      u32 tkid, const void *key, size_t key_size);
+       int (*set_num_dnts)(struct wusbhc *wusbhc, u8 interval, u8 slots);
+
+       struct {
+               struct usb_key_descriptor descr;
+               u8 data[16];                            /* GTK key data */
+       } __attribute__((packed)) gtk;
+       u8 gtk_index;
+       u32 gtk_tkid;
+       struct work_struct gtk_rekey_done_work;
+       int pending_set_gtks;
+
+       struct usb_encryption_descriptor *ccm1_etd;
+};
+
+#define usb_hcd_to_wusbhc(u) container_of((u), struct wusbhc, usb_hcd)
+
+
+extern int wusbhc_create(struct wusbhc *);
+extern int wusbhc_b_create(struct wusbhc *);
+extern void wusbhc_b_destroy(struct wusbhc *);
+extern void wusbhc_destroy(struct wusbhc *);
+extern int wusb_dev_sysfs_add(struct wusbhc *, struct usb_device *,
+                             struct wusb_dev *);
+extern void wusb_dev_sysfs_rm(struct wusb_dev *);
+extern int wusbhc_sec_create(struct wusbhc *);
+extern int wusbhc_sec_start(struct wusbhc *);
+extern void wusbhc_sec_stop(struct wusbhc *);
+extern void wusbhc_sec_destroy(struct wusbhc *);
+extern void wusbhc_giveback_urb(struct wusbhc *wusbhc, struct urb *urb,
+                               int status);
+void wusbhc_reset_all(struct wusbhc *wusbhc);
+
+int wusbhc_pal_register(struct wusbhc *wusbhc);
+void wusbhc_pal_unregister(struct wusbhc *wusbhc);
+
+/*
+ * Return @usb_dev's @usb_hcd (properly referenced) or NULL if gone
+ *
+ * @usb_dev: USB device, UNLOCKED and referenced (or otherwise, safe ptr)
+ *
+ * This is a safe assumption as @usb_dev->bus is referenced all the
+ * time during the @usb_dev life cycle.
+ */
+static inline struct usb_hcd *usb_hcd_get_by_usb_dev(struct usb_device *usb_dev)
+{
+       struct usb_hcd *usb_hcd;
+       usb_hcd = container_of(usb_dev->bus, struct usb_hcd, self);
+       return usb_get_hcd(usb_hcd);
+}
+
+/*
+ * Increment the reference count on a wusbhc.
+ *
+ * @wusbhc's life cycle is identical to that of the underlying usb_hcd.
+ */
+static inline struct wusbhc *wusbhc_get(struct wusbhc *wusbhc)
+{
+       return usb_get_hcd(&wusbhc->usb_hcd) ? wusbhc : NULL;
+}
+
+/*
+ * Return the wusbhc associated to a @usb_dev
+ *
+ * @usb_dev: USB device, UNLOCKED and referenced (or otherwise, safe ptr)
+ *
+ * @returns: wusbhc for @usb_dev; NULL if the @usb_dev is being torn down.
+ *           WARNING: referenced at the usb_hcd level, unlocked
+ *
+ * FIXME: move offline
+ */
+static inline struct wusbhc *wusbhc_get_by_usb_dev(struct usb_device *usb_dev)
+{
+       struct wusbhc *wusbhc = NULL;
+       struct usb_hcd *usb_hcd;
+       if (usb_dev->devnum > 1 && !usb_dev->wusb) {
+               /* but root hubs */
+               dev_err(&usb_dev->dev, "devnum %d wusb %d\n", usb_dev->devnum,
+                       usb_dev->wusb);
+               BUG_ON(usb_dev->devnum > 1 && !usb_dev->wusb);
+       }
+       usb_hcd = usb_hcd_get_by_usb_dev(usb_dev);
+       if (usb_hcd == NULL)
+               return NULL;
+       BUG_ON(usb_hcd->wireless == 0);
+       return wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+}
+
+
+static inline void wusbhc_put(struct wusbhc *wusbhc)
+{
+       usb_put_hcd(&wusbhc->usb_hcd);
+}
+
+int wusbhc_start(struct wusbhc *wusbhc, const struct wusb_ckhdid *chid);
+void wusbhc_stop(struct wusbhc *wusbhc);
+extern int wusbhc_chid_set(struct wusbhc *, const struct wusb_ckhdid *);
+
+/* Device connect handling */
+extern int wusbhc_devconnect_create(struct wusbhc *);
+extern void wusbhc_devconnect_destroy(struct wusbhc *);
+extern int wusbhc_devconnect_start(struct wusbhc *wusbhc,
+                                  const struct wusb_ckhdid *chid);
+extern void wusbhc_devconnect_stop(struct wusbhc *wusbhc);
+extern int wusbhc_devconnect_auth(struct wusbhc *, u8);
+extern void wusbhc_handle_dn(struct wusbhc *, u8 srcaddr,
+                            struct wusb_dn_hdr *dn_hdr, size_t size);
+extern int wusbhc_dev_reset(struct wusbhc *wusbhc, u8 port);
+extern void __wusbhc_dev_disable(struct wusbhc *wusbhc, u8 port);
+extern int wusb_usb_ncb(struct notifier_block *nb, unsigned long val,
+                       void *priv);
+extern int wusb_set_dev_addr(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev,
+                            u8 addr);
+
+/* Wireless USB fake Root Hub methods */
+extern int wusbhc_rh_create(struct wusbhc *);
+extern void wusbhc_rh_destroy(struct wusbhc *);
+
+extern int wusbhc_rh_status_data(struct usb_hcd *, char *);
+extern int wusbhc_rh_control(struct usb_hcd *, u16, u16, u16, char *, u16);
+extern int wusbhc_rh_suspend(struct usb_hcd *);
+extern int wusbhc_rh_resume(struct usb_hcd *);
+extern int wusbhc_rh_start_port_reset(struct usb_hcd *, unsigned);
+
+/* MMC handling */
+extern int wusbhc_mmcie_create(struct wusbhc *);
+extern void wusbhc_mmcie_destroy(struct wusbhc *);
+extern int wusbhc_mmcie_set(struct wusbhc *, u8 interval, u8 repeat_cnt,
+                           struct wuie_hdr *);
+extern void wusbhc_mmcie_rm(struct wusbhc *, struct wuie_hdr *);
+
+/* Bandwidth reservation */
+int wusbhc_rsv_establish(struct wusbhc *wusbhc);
+void wusbhc_rsv_terminate(struct wusbhc *wusbhc);
+
+/*
+ * I've always said
+ * I wanted a wedding in a church...
+ *
+ * but lately I've been thinking about
+ * the Botanical Gardens.
+ *
+ * We could do it by the tulips.
+ * It'll be beautiful
+ *
+ * --Security!
+ */
+extern int wusb_dev_sec_add(struct wusbhc *, struct usb_device *,
+                               struct wusb_dev *);
+extern void wusb_dev_sec_rm(struct wusb_dev *) ;
+extern int wusb_dev_4way_handshake(struct wusbhc *, struct wusb_dev *,
+                                  struct wusb_ckhdid *ck);
+void wusbhc_gtk_rekey(struct wusbhc *wusbhc);
+
+
+/* WUSB Cluster ID handling */
+extern u8 wusb_cluster_id_get(void);
+extern void wusb_cluster_id_put(u8);
+
+/*
+ * wusb_port_by_idx - return the port associated to a zero-based port index
+ *
+ * NOTE: valid without locking as long as wusbhc is referenced (as the
+ *       number of ports doesn't change). The data pointed to has to
+ *       be verified though :)
+ */
+static inline struct wusb_port *wusb_port_by_idx(struct wusbhc *wusbhc,
+                                                u8 port_idx)
+{
+       return &wusbhc->port[port_idx];
+}
+
+/*
+ * wusb_port_no_to_idx - Convert port number (per usb_dev->portnum) to
+ * a port_idx.
+ *
+ * USB stack USB ports are 1 based!!
+ *
+ * NOTE: only valid for WUSB devices!!!
+ */
+static inline u8 wusb_port_no_to_idx(u8 port_no)
+{
+       return port_no - 1;
+}
+
+extern struct wusb_dev *__wusb_dev_get_by_usb_dev(struct wusbhc *,
+                                                 struct usb_device *);
+
+/*
+ * Return a referenced wusb_dev given a @usb_dev
+ *
+ * Returns NULL if the usb_dev is being torn down.
+ *
+ * FIXME: move offline
+ */
+static inline
+struct wusb_dev *wusb_dev_get_by_usb_dev(struct usb_device *usb_dev)
+{
+       struct wusbhc *wusbhc;
+       struct wusb_dev *wusb_dev;
+       wusbhc = wusbhc_get_by_usb_dev(usb_dev);
+       if (wusbhc == NULL)
+               return NULL;
+       mutex_lock(&wusbhc->mutex);
+       wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc, usb_dev);
+       mutex_unlock(&wusbhc->mutex);
+       wusbhc_put(wusbhc);
+       return wusb_dev;
+}
+
+/* Misc */
+
+extern struct workqueue_struct *wusbd;
+#endif /* #ifndef __WUSBHC_H__ */
diff --git a/drivers/uwb/Kconfig b/drivers/uwb/Kconfig
new file mode 100644 (file)
index 0000000..ca78312
--- /dev/null
@@ -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 (file)
index 0000000..257e690
--- /dev/null
@@ -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 (file)
index 0000000..1664ae5
--- /dev/null
@@ -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 (file)
index 0000000..46b18ee
--- /dev/null
@@ -0,0 +1,642 @@
+/*
+ * 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(struct uwb_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 expires;
+
+       mutex_lock(&uwb_beca.mutex);
+       list_for_each_entry_safe(bce, next, &uwb_beca.list, node) {
+               expires = bce->ts_jiffies + msecs_to_jiffies(beacon_timeout_ms);
+               if (time_after(jiffies, expires)) {
+                       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;
+       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;
+
+       /* FIXME: handle alien beacons. */
+       if (be->bBeaconType == UWB_RC_BEACON_TYPE_OL_ALIEN ||
+           be->bBeaconType == UWB_RC_BEACON_TYPE_NOL_ALIEN) {
+               return -ENOSYS;
+       }
+
+       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 (file)
index 0000000..521cdeb
--- /dev/null
@@ -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 (file)
index 0000000..3febd85
--- /dev/null
@@ -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 (file)
index 0000000..882724c
--- /dev/null
@@ -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 (file)
index 0000000..c0b1e5e
--- /dev/null
@@ -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 (file)
index 0000000..5fe566b
--- /dev/null
@@ -0,0 +1,477 @@
+/*
+ * 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? */
+               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? */
+               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) {
+                       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;
+       }
+       dev_dbg(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 (file)
index 0000000..3d26fa0
--- /dev/null
@@ -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 (file)
index 0000000..212bbc7
--- /dev/null
@@ -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 (file)
index 0000000..bd1b9f2
--- /dev/null
@@ -0,0 +1,9 @@
+obj-$(CONFIG_UWB_I1480U)       += i1480-dfu-usb.o
+
+i1480-dfu-usb-objs := \
+       dfu.o   \
+       mac.o   \
+       phy.o   \
+       usb.o
+
+
diff --git a/drivers/uwb/i1480/dfu/dfu.c b/drivers/uwb/i1480/dfu/dfu.c
new file mode 100644 (file)
index 0000000..9097b3b
--- /dev/null
@@ -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 (file)
index 0000000..46f45e8
--- /dev/null
@@ -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 (file)
index 0000000..2e4d8f0
--- /dev/null
@@ -0,0 +1,527 @@
+/*
+ * 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;
+       }
+       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 (file)
index 0000000..3b1a87d
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Intel Wireless UWB Link 1480
+ * PHY parameters upload
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * Code for uploading the PHY parameters to the PHY through the UWB
+ * Radio Control interface.
+ *
+ * We just send the data through the MPI interface using HWA-like
+ * commands and then reset the PHY to make sure it is ok.
+ */
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/usb/wusb.h>
+#include "i1480-dfu.h"
+
+
+/**
+ * Write a value array to an address of the MPI interface
+ *
+ * @i1480:     Device descriptor
+ * @data:      Data array to write
+ * @size:      Size of the data array
+ * @returns:   0 if ok, < 0 errno code on error.
+ *
+ * The data array is organized into pairs:
+ *
+ * ADDRESS VALUE
+ *
+ * ADDRESS is BE 16 bit unsigned, VALUE 8 bit unsigned. Size thus has
+ * to be a multiple of three.
+ */
+static
+int i1480_mpi_write(struct i1480 *i1480, const void *data, size_t size)
+{
+       int result;
+       struct i1480_cmd_mpi_write *cmd = i1480->cmd_buf;
+       struct i1480_evt_confirm *reply = i1480->evt_buf;
+
+       BUG_ON(size > 480);
+       result = -ENOMEM;
+       cmd->rccb.bCommandType = i1480_CET_VS1;
+       cmd->rccb.wCommand = cpu_to_le16(i1480_CMD_MPI_WRITE);
+       cmd->size = cpu_to_le16(size);
+       memcpy(cmd->data, data, size);
+       reply->rceb.bEventType = i1480_CET_VS1;
+       reply->rceb.wEvent = i1480_CMD_MPI_WRITE;
+       result = i1480_cmd(i1480, "MPI-WRITE", sizeof(*cmd) + size, sizeof(*reply));
+       if (result < 0)
+               goto out;
+       if (reply->bResultCode != UWB_RC_RES_SUCCESS) {
+               dev_err(i1480->dev, "MPI-WRITE: command execution failed: %d\n",
+                       reply->bResultCode);
+               result = -EIO;
+       }
+out:
+       return result;
+}
+
+
+/**
+ * Read a value array to from an address of the MPI interface
+ *
+ * @i1480:     Device descriptor
+ * @data:      where to place the read array
+ * @srcaddr:   Where to read from
+ * @size:      Size of the data read array
+ * @returns:   0 if ok, < 0 errno code on error.
+ *
+ * The command data array is organized into pairs ADDR0 ADDR1..., and
+ * the returned data in ADDR0 VALUE0 ADDR1 VALUE1...
+ *
+ * We generate the command array to be a sequential read and then
+ * rearrange the result.
+ *
+ * We use the i1480->cmd_buf for the command, i1480->evt_buf for the reply.
+ *
+ * As the reply has to fit in 512 bytes (i1480->evt_buffer), the max amount
+ * of values we can read is (512 - sizeof(*reply)) / 3
+ */
+static
+int i1480_mpi_read(struct i1480 *i1480, u8 *data, u16 srcaddr, size_t size)
+{
+       int result;
+       struct i1480_cmd_mpi_read *cmd = i1480->cmd_buf;
+       struct i1480_evt_mpi_read *reply = i1480->evt_buf;
+       unsigned cnt;
+
+       memset(i1480->cmd_buf, 0x69, 512);
+       memset(i1480->evt_buf, 0x69, 512);
+
+       BUG_ON(size > (i1480->buf_size - sizeof(*reply)) / 3);
+       result = -ENOMEM;
+       cmd->rccb.bCommandType = i1480_CET_VS1;
+       cmd->rccb.wCommand = cpu_to_le16(i1480_CMD_MPI_READ);
+       cmd->size = cpu_to_le16(3*size);
+       for (cnt = 0; cnt < size; cnt++) {
+               cmd->data[cnt].page = (srcaddr + cnt) >> 8;
+               cmd->data[cnt].offset = (srcaddr + cnt) & 0xff;
+       }
+       reply->rceb.bEventType = i1480_CET_VS1;
+       reply->rceb.wEvent = i1480_CMD_MPI_READ;
+       result = i1480_cmd(i1480, "MPI-READ", sizeof(*cmd) + 2*size,
+                       sizeof(*reply) + 3*size);
+       if (result < 0)
+               goto out;
+       if (reply->bResultCode != UWB_RC_RES_SUCCESS) {
+               dev_err(i1480->dev, "MPI-READ: command execution failed: %d\n",
+                       reply->bResultCode);
+               result = -EIO;
+       }
+       for (cnt = 0; cnt < size; cnt++) {
+               if (reply->data[cnt].page != (srcaddr + cnt) >> 8)
+                       dev_err(i1480->dev, "MPI-READ: page inconsistency at "
+                               "index %u: expected 0x%02x, got 0x%02x\n", cnt,
+                               (srcaddr + cnt) >> 8, reply->data[cnt].page);
+               if (reply->data[cnt].offset != ((srcaddr + cnt) & 0x00ff))
+                       dev_err(i1480->dev, "MPI-READ: offset inconsistency at "
+                               "index %u: expected 0x%02x, got 0x%02x\n", cnt,
+                               (srcaddr + cnt) & 0x00ff,
+                               reply->data[cnt].offset);
+               data[cnt] = reply->data[cnt].value;
+       }
+       result = 0;
+out:
+       return result;
+}
+
+
+/**
+ * Upload a PHY firmware, wait for it to start
+ *
+ * @i1480:     Device instance
+ * @fw_name: Name of the file that contains the firmware
+ *
+ * We assume the MAC fw is up and running. This means we can use the
+ * MPI interface to write the PHY firmware. Once done, we issue an
+ * MBOA Reset, which will force the MAC to reset and reinitialize the
+ * PHY. If that works, we are ready to go.
+ *
+ * Max packet size for the MPI write is 512, so the max buffer is 480
+ * (which gives us 160 byte triads of MSB, LSB and VAL for the data).
+ */
+int i1480_phy_fw_upload(struct i1480 *i1480)
+{
+       int result;
+       const struct firmware *fw;
+       const char *data_itr, *data_top;
+       const size_t MAX_BLK_SIZE = 480;        /* 160 triads */
+       size_t data_size;
+       u8 phy_stat;
+
+       result = request_firmware(&fw, i1480->phy_fw_name, i1480->dev);
+       if (result < 0)
+               goto out;
+       /* Loop writing data in chunks as big as possible until done. */
+       for (data_itr = fw->data, data_top = data_itr + fw->size;
+            data_itr < data_top; data_itr += MAX_BLK_SIZE) {
+               data_size = min(MAX_BLK_SIZE, (size_t) (data_top - data_itr));
+               result = i1480_mpi_write(i1480, data_itr, data_size);
+               if (result < 0)
+                       goto error_mpi_write;
+       }
+       /* Read MPI page 0, offset 6; if 0, PHY was initialized correctly. */
+       result = i1480_mpi_read(i1480, &phy_stat, 0x0006, 1);
+       if (result < 0) {
+               dev_err(i1480->dev, "PHY: can't get status: %d\n", result);
+               goto error_mpi_status;
+       }
+       if (phy_stat != 0) {
+               result = -ENODEV;
+               dev_info(i1480->dev, "error, PHY not ready: %u\n", phy_stat);
+               goto error_phy_status;
+       }
+       dev_info(i1480->dev, "PHY fw '%s': uploaded\n", i1480->phy_fw_name);
+error_phy_status:
+error_mpi_status:
+error_mpi_write:
+       release_firmware(fw);
+       if (result < 0)
+               dev_err(i1480->dev, "PHY fw '%s': failed to upload (%d), "
+                       "power cycle device\n", i1480->phy_fw_name, result);
+out:
+       return result;
+}
diff --git a/drivers/uwb/i1480/dfu/usb.c b/drivers/uwb/i1480/dfu/usb.c
new file mode 100644 (file)
index 0000000..98eeeff
--- /dev/null
@@ -0,0 +1,500 @@
+/*
+ * Intel Wireless UWB Link 1480
+ * USB SKU firmware upload implementation
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * This driver will prepare the i1480 device to behave as a real
+ * Wireless USB HWA adaptor by uploading the firmware.
+ *
+ * When the device is connected or driver is loaded, i1480_usb_probe()
+ * is called--this will allocate and initialize the device structure,
+ * fill in the pointers to the common functions (read, write,
+ * wait_init_done and cmd for HWA command execution) and once that is
+ * done, call the common firmware uploading routine. Then clean up and
+ * return -ENODEV, as we don't attach to the device.
+ *
+ * The rest are the basic ops we implement that the fw upload code
+ * uses to do its job. All the ops in the common code are i1480->NAME,
+ * the functions are i1480_usb_NAME().
+ */
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/usb.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/uwb.h>
+#include <linux/usb/wusb.h>
+#include <linux/usb/wusb-wa.h>
+#include "i1480-dfu.h"
+
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+
+struct i1480_usb {
+       struct i1480 i1480;
+       struct usb_device *usb_dev;
+       struct usb_interface *usb_iface;
+       struct urb *neep_urb;   /* URB for reading from EP1 */
+};
+
+
+static
+void i1480_usb_init(struct i1480_usb *i1480_usb)
+{
+       i1480_init(&i1480_usb->i1480);
+}
+
+
+static
+int i1480_usb_create(struct i1480_usb *i1480_usb, struct usb_interface *iface)
+{
+       struct usb_device *usb_dev = interface_to_usbdev(iface);
+       int result = -ENOMEM;
+
+       i1480_usb->usb_dev = usb_get_dev(usb_dev);      /* bind the USB device */
+       i1480_usb->usb_iface = usb_get_intf(iface);
+       usb_set_intfdata(iface, i1480_usb);             /* Bind the driver to iface0 */
+       i1480_usb->neep_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (i1480_usb->neep_urb == NULL)
+               goto error;
+       return 0;
+
+error:
+       usb_set_intfdata(iface, NULL);
+       usb_put_intf(iface);
+       usb_put_dev(usb_dev);
+       return result;
+}
+
+
+static
+void i1480_usb_destroy(struct i1480_usb *i1480_usb)
+{
+       usb_kill_urb(i1480_usb->neep_urb);
+       usb_free_urb(i1480_usb->neep_urb);
+       usb_set_intfdata(i1480_usb->usb_iface, NULL);
+       usb_put_intf(i1480_usb->usb_iface);
+       usb_put_dev(i1480_usb->usb_dev);
+}
+
+
+/**
+ * Write a buffer to a memory address in the i1480 device
+ *
+ * @i1480:  i1480 instance
+ * @memory_address:
+ *          Address where to write the data buffer to.
+ * @buffer: Buffer to the data
+ * @size:   Size of the buffer [has to be < 512].
+ * @returns: 0 if ok, < 0 errno code on error.
+ *
+ * Data buffers to USB cannot be on the stack or in vmalloc'ed areas,
+ * so we copy it to the local i1480 buffer before proceeding. In any
+ * case, we have a max size we can send, soooo.
+ */
+static
+int i1480_usb_write(struct i1480 *i1480, u32 memory_address,
+                   const void *buffer, size_t size)
+{
+       int result = 0;
+       struct i1480_usb *i1480_usb = container_of(i1480, struct i1480_usb, i1480);
+       size_t buffer_size, itr = 0;
+
+       d_fnstart(3, i1480->dev, "(%p, 0x%08x, %p, %zu)\n",
+                 i1480, memory_address, buffer, size);
+       BUG_ON(size & 0x3); /* Needs to be a multiple of 4 */
+       while (size > 0) {
+               buffer_size = size < i1480->buf_size ? size : i1480->buf_size;
+               memcpy(i1480->cmd_buf, buffer + itr, buffer_size);
+               result = usb_control_msg(
+                       i1480_usb->usb_dev, usb_sndctrlpipe(i1480_usb->usb_dev, 0),
+                       0xf0, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       cpu_to_le16(memory_address & 0xffff),
+                       cpu_to_le16((memory_address >> 16) & 0xffff),
+                       i1480->cmd_buf, buffer_size, 100 /* FIXME: arbitrary */);
+               if (result < 0)
+                       break;
+               d_printf(3, i1480->dev,
+                        "wrote @ 0x%08x %u bytes (of %zu bytes requested)\n",
+                        memory_address, result, buffer_size);
+               d_dump(4, i1480->dev, i1480->cmd_buf, result);
+               itr += result;
+               memory_address += result;
+               size -= result;
+       }
+       d_fnend(3, i1480->dev, "(%p, 0x%08x, %p, %zu) = %d\n",
+               i1480, memory_address, buffer, size, result);
+       return result;
+}
+
+
+/**
+ * Read a block [max size 512] of the device's memory to @i1480's buffer.
+ *
+ * @i1480: i1480 instance
+ * @memory_address:
+ *         Address where to read from.
+ * @size:  Size to read. Smaller than or equal to 512.
+ * @returns: >= 0 number of bytes written if ok, < 0 errno code on error.
+ *
+ * NOTE: if the memory address or block is incorrect, you might get a
+ *       stall or a different memory read. Caller has to verify the
+ *       memory address and size passed back in the @neh structure.
+ */
+static
+int i1480_usb_read(struct i1480 *i1480, u32 addr, size_t size)
+{
+       ssize_t result = 0, bytes = 0;
+       size_t itr, read_size = i1480->buf_size;
+       struct i1480_usb *i1480_usb = container_of(i1480, struct i1480_usb, i1480);
+
+       d_fnstart(3, i1480->dev, "(%p, 0x%08x, %zu)\n",
+                 i1480, addr, size);
+       BUG_ON(size > i1480->buf_size);
+       BUG_ON(size & 0x3); /* Needs to be a multiple of 4 */
+       BUG_ON(read_size > 512);
+
+       if (addr >= 0x8000d200 && addr < 0x8000d400)    /* Yeah, HW quirk */
+               read_size = 4;
+
+       for (itr = 0; itr < size; itr += read_size) {
+               size_t itr_addr = addr + itr;
+               size_t itr_size = min(read_size, size - itr);
+               result = usb_control_msg(
+                       i1480_usb->usb_dev, usb_rcvctrlpipe(i1480_usb->usb_dev, 0),
+                       0xf0, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       cpu_to_le16(itr_addr & 0xffff),
+                       cpu_to_le16((itr_addr >> 16) & 0xffff),
+                       i1480->cmd_buf + itr, itr_size,
+                       100 /* FIXME: arbitrary */);
+               if (result < 0) {
+                       dev_err(i1480->dev, "%s: USB read error: %zd\n",
+                               __func__, result);
+                       goto out;
+               }
+               if (result != itr_size) {
+                       result = -EIO;
+                       dev_err(i1480->dev,
+                               "%s: partial read got only %zu bytes vs %zu expected\n",
+                               __func__, result, itr_size);
+                       goto out;
+               }
+               bytes += result;
+       }
+       result = bytes;
+out:
+       d_fnend(3, i1480->dev, "(%p, 0x%08x, %zu) = %zd\n",
+               i1480, addr, size, result);
+       if (result > 0)
+               d_dump(4, i1480->dev, i1480->cmd_buf, result);
+       return result;
+}
+
+
+/**
+ * Callback for reads on the notification/event endpoint
+ *
+ * Just enables the completion read handler.
+ */
+static
+void i1480_usb_neep_cb(struct urb *urb)
+{
+       struct i1480 *i1480 = urb->context;
+       struct device *dev = i1480->dev;
+
+       switch (urb->status) {
+       case 0:
+               break;
+       case -ECONNRESET:       /* Not an error, but a controlled situation; */
+       case -ENOENT:           /* (we killed the URB)...so, no broadcast */
+               dev_dbg(dev, "NEEP: reset/noent %d\n", urb->status);
+               break;
+       case -ESHUTDOWN:        /* going away! */
+               dev_dbg(dev, "NEEP: down %d\n", urb->status);
+               break;
+       default:
+               dev_err(dev, "NEEP: unknown status %d\n", urb->status);
+               break;
+       }
+       i1480->evt_result = urb->actual_length;
+       complete(&i1480->evt_complete);
+       return;
+}
+
+
+/**
+ * Wait for the MAC FW to initialize
+ *
+ * MAC FW sends a 0xfd/0101/00 notification to EP1 when done
+ * initializing. Get that notification into i1480->evt_buf; upper layer
+ * will verify it.
+ *
+ * Set i1480->evt_result with the result of getting the event or its
+ * size (if succesful).
+ *
+ * Delivers the data directly to i1480->evt_buf
+ */
+static
+int i1480_usb_wait_init_done(struct i1480 *i1480)
+{
+       int result;
+       struct device *dev = i1480->dev;
+       struct i1480_usb *i1480_usb = container_of(i1480, struct i1480_usb, i1480);
+       struct usb_endpoint_descriptor *epd;
+
+       d_fnstart(3, dev, "(%p)\n", i1480);
+       init_completion(&i1480->evt_complete);
+       i1480->evt_result = -EINPROGRESS;
+       epd = &i1480_usb->usb_iface->cur_altsetting->endpoint[0].desc;
+       usb_fill_int_urb(i1480_usb->neep_urb, i1480_usb->usb_dev,
+                        usb_rcvintpipe(i1480_usb->usb_dev, epd->bEndpointAddress),
+                        i1480->evt_buf, i1480->buf_size,
+                        i1480_usb_neep_cb, i1480, epd->bInterval);
+       result = usb_submit_urb(i1480_usb->neep_urb, GFP_KERNEL);
+       if (result < 0) {
+               dev_err(dev, "init done: cannot submit NEEP read: %d\n",
+                       result);
+               goto error_submit;
+       }
+       /* Wait for the USB callback to get the data */
+       result = wait_for_completion_interruptible_timeout(
+               &i1480->evt_complete, HZ);
+       if (result <= 0) {
+               result = result == 0 ? -ETIMEDOUT : result;
+               goto error_wait;
+       }
+       usb_kill_urb(i1480_usb->neep_urb);
+       d_fnend(3, dev, "(%p) = 0\n", i1480);
+       return 0;
+
+error_wait:
+       usb_kill_urb(i1480_usb->neep_urb);
+error_submit:
+       i1480->evt_result = result;
+       d_fnend(3, dev, "(%p) = %d\n", i1480, result);
+       return result;
+}
+
+
+/**
+ * Generic function for issuing commands to the i1480
+ *
+ * @i1480:      i1480 instance
+ * @cmd_name:   Name of the command (for error messages)
+ * @cmd:        Pointer to command buffer
+ * @cmd_size:   Size of the command buffer
+ * @reply:      Buffer for the reply event
+ * @reply_size: Expected size back (including RCEB); the reply buffer
+ *              is assumed to be as big as this.
+ * @returns:    >= 0 size of the returned event data if ok,
+ *              < 0 errno code on error.
+ *
+ * Arms the NE handle, issues the command to the device and checks the
+ * basics of the reply event.
+ */
+static
+int i1480_usb_cmd(struct i1480 *i1480, const char *cmd_name, size_t cmd_size)
+{
+       int result;
+       struct device *dev = i1480->dev;
+       struct i1480_usb *i1480_usb = container_of(i1480, struct i1480_usb, i1480);
+       struct usb_endpoint_descriptor *epd;
+       struct uwb_rccb *cmd = i1480->cmd_buf;
+       u8 iface_no;
+
+       d_fnstart(3, dev, "(%p, %s, %zu)\n", i1480, cmd_name, cmd_size);
+       /* Post a read on the notification & event endpoint */
+       iface_no = i1480_usb->usb_iface->cur_altsetting->desc.bInterfaceNumber;
+       epd = &i1480_usb->usb_iface->cur_altsetting->endpoint[0].desc;
+       usb_fill_int_urb(
+               i1480_usb->neep_urb, i1480_usb->usb_dev,
+               usb_rcvintpipe(i1480_usb->usb_dev, epd->bEndpointAddress),
+               i1480->evt_buf, i1480->buf_size,
+               i1480_usb_neep_cb, i1480, epd->bInterval);
+       result = usb_submit_urb(i1480_usb->neep_urb, GFP_KERNEL);
+       if (result < 0) {
+               dev_err(dev, "%s: cannot submit NEEP read: %d\n",
+                       cmd_name, result);
+                       goto error_submit_ep1;
+       }
+       /* Now post the command on EP0 */
+       result = usb_control_msg(
+               i1480_usb->usb_dev, usb_sndctrlpipe(i1480_usb->usb_dev, 0),
+               WA_EXEC_RC_CMD,
+               USB_DIR_OUT | USB_RECIP_INTERFACE | USB_TYPE_CLASS,
+               0, iface_no,
+               cmd, cmd_size,
+               100 /* FIXME: this is totally arbitrary */);
+       if (result < 0) {
+               dev_err(dev, "%s: control request failed: %d\n",
+                       cmd_name, result);
+               goto error_submit_ep0;
+       }
+       d_fnend(3, dev, "(%p, %s, %zu) = %d\n",
+               i1480, cmd_name, cmd_size, result);
+       return result;
+
+error_submit_ep0:
+       usb_kill_urb(i1480_usb->neep_urb);
+error_submit_ep1:
+       d_fnend(3, dev, "(%p, %s, %zu) = %d\n",
+               i1480, cmd_name, cmd_size, result);
+       return result;
+}
+
+
+/*
+ * Probe a i1480 device for uploading firmware.
+ *
+ * We attach only to interface #0, which is the radio control interface.
+ */
+static
+int i1480_usb_probe(struct usb_interface *iface, const struct usb_device_id *id)
+{
+       struct i1480_usb *i1480_usb;
+       struct i1480 *i1480;
+       struct device *dev = &iface->dev;
+       int result;
+
+       result = -ENODEV;
+       if (iface->cur_altsetting->desc.bInterfaceNumber != 0) {
+               dev_dbg(dev, "not attaching to iface %d\n",
+                       iface->cur_altsetting->desc.bInterfaceNumber);
+               goto error;
+       }
+       if (iface->num_altsetting > 1
+           && interface_to_usbdev(iface)->descriptor.idProduct == 0xbabe) {
+               /* Need altsetting #1 [HW QUIRK] or EP1 won't work */
+               result = usb_set_interface(interface_to_usbdev(iface), 0, 1);
+               if (result < 0)
+                       dev_warn(dev,
+                                "can't set altsetting 1 on iface 0: %d\n",
+                                result);
+       }
+
+       result = -ENOMEM;
+       i1480_usb = kzalloc(sizeof(*i1480_usb), GFP_KERNEL);
+       if (i1480_usb == NULL) {
+               dev_err(dev, "Unable to allocate instance\n");
+               goto error;
+       }
+       i1480_usb_init(i1480_usb);
+
+       i1480 = &i1480_usb->i1480;
+       i1480->buf_size = 512;
+       i1480->cmd_buf = kmalloc(2 * i1480->buf_size, GFP_KERNEL);
+       if (i1480->cmd_buf == NULL) {
+               dev_err(dev, "Cannot allocate transfer buffers\n");
+               result = -ENOMEM;
+               goto error_buf_alloc;
+       }
+       i1480->evt_buf = i1480->cmd_buf + i1480->buf_size;
+
+       result = i1480_usb_create(i1480_usb, iface);
+       if (result < 0) {
+               dev_err(dev, "Cannot create instance: %d\n", result);
+               goto error_create;
+       }
+
+       /* setup the fops and upload the firmare */
+       i1480->pre_fw_name = "i1480-pre-phy-0.0.bin";
+       i1480->mac_fw_name = "i1480-usb-0.0.bin";
+       i1480->mac_fw_name_deprecate = "ptc-0.0.bin";
+       i1480->phy_fw_name = "i1480-phy-0.0.bin";
+       i1480->dev = &iface->dev;
+       i1480->write = i1480_usb_write;
+       i1480->read = i1480_usb_read;
+       i1480->rc_setup = NULL;
+       i1480->wait_init_done = i1480_usb_wait_init_done;
+       i1480->cmd = i1480_usb_cmd;
+
+       result = i1480_fw_upload(&i1480_usb->i1480);    /* the real thing */
+       if (result >= 0) {
+               usb_reset_device(i1480_usb->usb_dev);
+               result = -ENODEV;       /* we don't want to bind to the iface */
+       }
+       i1480_usb_destroy(i1480_usb);
+error_create:
+       kfree(i1480->cmd_buf);
+error_buf_alloc:
+       kfree(i1480_usb);
+error:
+       return result;
+}
+
+#define i1480_USB_DEV(v, p)                            \
+{                                                      \
+       .match_flags = USB_DEVICE_ID_MATCH_DEVICE       \
+                | USB_DEVICE_ID_MATCH_DEV_INFO         \
+                | USB_DEVICE_ID_MATCH_INT_INFO,        \
+       .idVendor = (v),                                \
+       .idProduct = (p),                               \
+       .bDeviceClass = 0xff,                           \
+       .bDeviceSubClass = 0xff,                        \
+       .bDeviceProtocol = 0xff,                        \
+       .bInterfaceClass = 0xff,                        \
+       .bInterfaceSubClass = 0xff,                     \
+       .bInterfaceProtocol = 0xff,                     \
+}
+
+
+/** USB device ID's that we handle */
+static struct usb_device_id i1480_usb_id_table[] = {
+       i1480_USB_DEV(0x8086, 0xdf3b),
+       i1480_USB_DEV(0x15a9, 0x0005),
+       i1480_USB_DEV(0x07d1, 0x3802),
+       i1480_USB_DEV(0x050d, 0x305a),
+       i1480_USB_DEV(0x3495, 0x3007),
+       {},
+};
+MODULE_DEVICE_TABLE(usb, i1480_usb_id_table);
+
+
+static struct usb_driver i1480_dfu_driver = {
+       .name =         "i1480-dfu-usb",
+       .id_table =     i1480_usb_id_table,
+       .probe =        i1480_usb_probe,
+       .disconnect =   NULL,
+};
+
+
+/*
+ * Initialize the i1480 DFU driver.
+ *
+ * We also need to register our function for guessing event sizes.
+ */
+static int __init i1480_dfu_driver_init(void)
+{
+       return usb_register(&i1480_dfu_driver);
+}
+module_init(i1480_dfu_driver_init);
+
+
+static void __exit i1480_dfu_driver_exit(void)
+{
+       usb_deregister(&i1480_dfu_driver);
+}
+module_exit(i1480_dfu_driver_exit);
+
+
+MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
+MODULE_DESCRIPTION("Intel Wireless UWB Link 1480 firmware uploader for USB");
+MODULE_LICENSE("GPL");
diff --git a/drivers/uwb/i1480/i1480-est.c b/drivers/uwb/i1480/i1480-est.c
new file mode 100644 (file)
index 0000000..7bf8c6f
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Intel Wireless UWB Link 1480
+ * Event Size tables for Wired Adaptors
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: docs
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/uwb.h>
+#include "dfu/i1480-dfu.h"
+
+
+/** Event size table for wEvents 0x00XX */
+static struct uwb_est_entry i1480_est_fd00[] = {
+       /* Anybody expecting this response has to use
+        * neh->extra_size to specify the real size that will
+        * come back. */
+       [i1480_EVT_CONFIRM] = { .size = sizeof(struct i1480_evt_confirm) },
+       [i1480_CMD_SET_IP_MAS] = { .size = sizeof(struct i1480_evt_confirm) },
+#ifdef i1480_RCEB_EXTENDED
+       [0x09] = {
+               .size = sizeof(struct i1480_rceb),
+               .offset = 1 + offsetof(struct i1480_rceb, wParamLength),
+       },
+#endif
+};
+
+/** Event size table for wEvents 0x01XX */
+static struct uwb_est_entry i1480_est_fd01[] = {
+       [0xff & i1480_EVT_RM_INIT_DONE] = { .size = sizeof(struct i1480_rceb) },
+       [0xff & i1480_EVT_DEV_ADD] = { .size = sizeof(struct i1480_rceb) + 9 },
+       [0xff & i1480_EVT_DEV_RM] = { .size = sizeof(struct i1480_rceb) + 9 },
+       [0xff & i1480_EVT_DEV_ID_CHANGE] = {
+               .size = sizeof(struct i1480_rceb) + 2 },
+};
+
+static int i1480_est_init(void)
+{
+       int result = uwb_est_register(i1480_CET_VS1, 0x00, 0x8086, 0x0c3b,
+                                     i1480_est_fd00,
+                                     ARRAY_SIZE(i1480_est_fd00));
+       if (result < 0) {
+               printk(KERN_ERR "Can't register EST table fd00: %d\n", result);
+               return result;
+       }
+       result = uwb_est_register(i1480_CET_VS1, 0x01, 0x8086, 0x0c3b,
+                                 i1480_est_fd01, ARRAY_SIZE(i1480_est_fd01));
+       if (result < 0) {
+               printk(KERN_ERR "Can't register EST table fd01: %d\n", result);
+               return result;
+       }
+       return 0;
+}
+module_init(i1480_est_init);
+
+static void i1480_est_exit(void)
+{
+       uwb_est_unregister(i1480_CET_VS1, 0x00, 0x8086, 0x0c3b,
+                          i1480_est_fd00, ARRAY_SIZE(i1480_est_fd00));
+       uwb_est_unregister(i1480_CET_VS1, 0x01, 0x8086, 0x0c3b,
+                          i1480_est_fd01, ARRAY_SIZE(i1480_est_fd01));
+}
+module_exit(i1480_est_exit);
+
+MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
+MODULE_DESCRIPTION("i1480's Vendor Specific Event Size Tables");
+MODULE_LICENSE("GPL");
+
+/**
+ * USB device ID's that we handle
+ *
+ * [so we are loaded when this kind device is connected]
+ */
+static struct usb_device_id i1480_est_id_table[] = {
+       { USB_DEVICE(0x8086, 0xdf3b), },
+       { USB_DEVICE(0x8086, 0x0c3b), },
+       { },
+};
+MODULE_DEVICE_TABLE(usb, i1480_est_id_table);
diff --git a/drivers/uwb/i1480/i1480-wlp.h b/drivers/uwb/i1480/i1480-wlp.h
new file mode 100644 (file)
index 0000000..18a8b0e
--- /dev/null
@@ -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 (file)
index 0000000..fe6709b
--- /dev/null
@@ -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 (file)
index 0000000..5f1b295
--- /dev/null
@@ -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 (file)
index 0000000..737d60c
--- /dev/null
@@ -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 (file)
index 0000000..8802ac4
--- /dev/null
@@ -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 (file)
index 0000000..9fc0353
--- /dev/null
@@ -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 (file)
index 0000000..a1d8ca6
--- /dev/null
@@ -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 (file)
index 0000000..3426bfb
--- /dev/null
@@ -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 (file)
index 0000000..cf6f3d1
--- /dev/null
@@ -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 (file)
index 0000000..15f856c
--- /dev/null
@@ -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 = kzalloc(sizeof(struct 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 (file)
index 0000000..ee5772f
--- /dev/null
@@ -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 (file)
index 0000000..9b4eb64
--- /dev/null
@@ -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
+                       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 (file)
index 0000000..1afb38e
--- /dev/null
@@ -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 (file)
index 0000000..8de856f
--- /dev/null
@@ -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 (file)
index 0000000..bae1620
--- /dev/null
@@ -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 (file)
index 0000000..2d27074
--- /dev/null
@@ -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 (file)
index 0000000..2d8d62d
--- /dev/null
@@ -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 (file)
index 0000000..aa44e1c
--- /dev/null
@@ -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 (file)
index 0000000..367b5eb
--- /dev/null
@@ -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 (file)
index 0000000..6d232c3
--- /dev/null
@@ -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 (file)
index 0000000..2ad307d
--- /dev/null
@@ -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 (file)
index 0000000..7890841
--- /dev/null
@@ -0,0 +1,410 @@
+/*
+ * 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)
+{
+       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) {
+               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) {
+               printk(KERN_ERR "UWBD: event type %u: unknown\n", type);
+               return -EINVAL;
+       }
+       if (event > type_table->size) {
+               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) {
+               printk(KERN_ERR "UWBD: event %s[%u]: unknown\n", type_table->name, event);
+               return -EINVAL;
+       }
+       return (*handler)(evt);
+}
+
+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 (file)
index 0000000..1711dea
--- /dev/null
@@ -0,0 +1,520 @@
+/*
+ * 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;
+
+       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 (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 (file)
index 0000000..3df2388
--- /dev/null
@@ -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 (file)
index 0000000..c72c11d
--- /dev/null
@@ -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 (file)
index 0000000..cb8d699
--- /dev/null
@@ -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 (file)
index 0000000..cdfe8df
--- /dev/null
@@ -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 (file)
index 0000000..a64cb82
--- /dev/null
@@ -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 (file)
index 0000000..1bb9b1f
--- /dev/null
@@ -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 (file)
index 0000000..c701bd1
--- /dev/null
@@ -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 (file)
index 0000000..1c94fab
--- /dev/null
@@ -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 (file)
index 0000000..0799402
--- /dev/null
@@ -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 (file)
index 0000000..96b18c9
--- /dev/null
@@ -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);
index 89781fd48859bb957bbaafabce4c87ad0affd2a0..5379913aca52c8a40ca30f5f8a523dca5b495582 100644 (file)
@@ -130,6 +130,7 @@ extern void bitmap_fold(unsigned long *dst, const unsigned long *orig,
 extern int bitmap_find_free_region(unsigned long *bitmap, int bits, int order);
 extern void bitmap_release_region(unsigned long *bitmap, int pos, int order);
 extern int bitmap_allocate_region(unsigned long *bitmap, int pos, int order);
+extern void bitmap_copy_le(void *dst, const unsigned long *src, int nbits);
 
 #define BITMAP_LAST_WORD_MASK(nbits)                                   \
 (                                                                      \
diff --git a/include/linux/usb/wusb-wa.h b/include/linux/usb/wusb-wa.h
new file mode 100644 (file)
index 0000000..a102561
--- /dev/null
@@ -0,0 +1,271 @@
+/*
+ * Wireless USB Wire Adapter constants and structures.
+ *
+ * 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: organize properly, group logically
+ *
+ * All the event structures are defined in uwb/spec.h, as they are
+ * common to the WHCI and WUSB radio control interfaces.
+ *
+ * References:
+ *   [WUSB] Wireless Universal Serial Bus Specification, revision 1.0, ch8
+ */
+#ifndef __LINUX_USB_WUSB_WA_H
+#define __LINUX_USB_WUSB_WA_H
+
+/**
+ * Radio Command Request for the Radio Control Interface
+ *
+ * Radio Control Interface command and event codes are the same as
+ * WHCI, and listed in include/linux/uwb.h:UWB_RC_{CMD,EVT}_*
+ */
+enum {
+       WA_EXEC_RC_CMD = 40,    /* Radio Control command Request */
+};
+
+/* Wireless Adapter Requests ([WUSB] table 8-51) */
+enum {
+       WUSB_REQ_ADD_MMC_IE     = 20,
+       WUSB_REQ_REMOVE_MMC_IE  = 21,
+       WUSB_REQ_SET_NUM_DNTS   = 22,
+       WUSB_REQ_SET_CLUSTER_ID = 23,
+       WUSB_REQ_SET_DEV_INFO   = 24,
+       WUSB_REQ_GET_TIME       = 25,
+       WUSB_REQ_SET_STREAM_IDX = 26,
+       WUSB_REQ_SET_WUSB_MAS   = 27,
+};
+
+
+/* Wireless Adapter WUSB Channel Time types ([WUSB] table 8-52) */
+enum {
+       WUSB_TIME_ADJ   = 0,
+       WUSB_TIME_BPST  = 1,
+       WUSB_TIME_WUSB  = 2,
+};
+
+enum {
+       WA_ENABLE = 0x01,
+       WA_RESET = 0x02,
+       RPIPE_PAUSE = 0x1,
+};
+
+/* Responses from Get Status request ([WUSB] section 8.3.1.6) */
+enum {
+       WA_STATUS_ENABLED = 0x01,
+       WA_STATUS_RESETTING = 0x02
+};
+
+enum rpipe_crs {
+       RPIPE_CRS_CTL = 0x01,
+       RPIPE_CRS_ISO = 0x02,
+       RPIPE_CRS_BULK = 0x04,
+       RPIPE_CRS_INTR = 0x08
+};
+
+/**
+ * RPipe descriptor ([WUSB] section 8.5.2.11)
+ *
+ * FIXME: explain rpipes
+ */
+struct usb_rpipe_descriptor {
+       u8      bLength;
+       u8      bDescriptorType;
+       __le16  wRPipeIndex;
+       __le16  wRequests;
+       __le16  wBlocks;                /* rw if 0 */
+       __le16  wMaxPacketSize;         /* rw? */
+       u8      bHSHubAddress;          /* reserved: 0 */
+       u8      bHSHubPort;             /* ??? FIXME ??? */
+       u8      bSpeed;                 /* rw: xfer rate 'enum uwb_phy_rate' */
+       u8      bDeviceAddress;         /* rw: Target device address */
+       u8      bEndpointAddress;       /* rw: Target EP address */
+       u8      bDataSequence;          /* ro: Current Data sequence */
+       __le32  dwCurrentWindow;        /* ro */
+       u8      bMaxDataSequence;       /* ro?: max supported seq */
+       u8      bInterval;              /* rw:  */
+       u8      bOverTheAirInterval;    /* rw:  */
+       u8      bmAttribute;            /* ro?  */
+       u8      bmCharacteristics;      /* ro? enum rpipe_attr, supported xsactions */
+       u8      bmRetryOptions;         /* rw? */
+       __le16  wNumTransactionErrors;  /* rw */
+} __attribute__ ((packed));
+
+/**
+ * Wire Adapter Notification types ([WUSB] sections 8.4.5 & 8.5.4)
+ *
+ * These are the notifications coming on the notification endpoint of
+ * an HWA and a DWA.
+ */
+enum wa_notif_type {
+       DWA_NOTIF_RWAKE = 0x91,
+       DWA_NOTIF_PORTSTATUS = 0x92,
+       WA_NOTIF_TRANSFER = 0x93,
+       HWA_NOTIF_BPST_ADJ = 0x94,
+       HWA_NOTIF_DN = 0x95,
+};
+
+/**
+ * Wire Adapter notification header
+ *
+ * Notifications coming from a wire adapter use a common header
+ * defined in [WUSB] sections 8.4.5 & 8.5.4.
+ */
+struct wa_notif_hdr {
+       u8 bLength;
+       u8 bNotifyType;                 /* enum wa_notif_type */
+} __attribute__((packed));
+
+/**
+ * HWA DN Received notification [(WUSB] section 8.5.4.2)
+ *
+ * The DNData is specified in WUSB1.0[7.6]. For each device
+ * notification we received, we just need to dispatch it.
+ *
+ * @dndata:  this is really an array of notifications, but all start
+ *           with the same header.
+ */
+struct hwa_notif_dn {
+       struct wa_notif_hdr hdr;
+       u8 bSourceDeviceAddr;           /* from errata 2005/07 */
+       u8 bmAttributes;
+       struct wusb_dn_hdr dndata[];
+} __attribute__((packed));
+
+/* [WUSB] section 8.3.3 */
+enum wa_xfer_type {
+       WA_XFER_TYPE_CTL = 0x80,
+       WA_XFER_TYPE_BI = 0x81,         /* bulk/interrupt */
+       WA_XFER_TYPE_ISO = 0x82,
+       WA_XFER_RESULT = 0x83,
+       WA_XFER_ABORT = 0x84,
+};
+
+/* [WUSB] section 8.3.3 */
+struct wa_xfer_hdr {
+       u8 bLength;                     /* 0x18 */
+       u8 bRequestType;                /* 0x80 WA_REQUEST_TYPE_CTL */
+       __le16 wRPipe;                  /* RPipe index */
+       __le32 dwTransferID;            /* Host-assigned ID */
+       __le32 dwTransferLength;        /* Length of data to xfer */
+       u8 bTransferSegment;
+} __attribute__((packed));
+
+struct wa_xfer_ctl {
+       struct wa_xfer_hdr hdr;
+       u8 bmAttribute;
+       __le16 wReserved;
+       struct usb_ctrlrequest baSetupData;
+} __attribute__((packed));
+
+struct wa_xfer_bi {
+       struct wa_xfer_hdr hdr;
+       u8 bReserved;
+       __le16 wReserved;
+} __attribute__((packed));
+
+struct wa_xfer_hwaiso {
+       struct wa_xfer_hdr hdr;
+       u8 bReserved;
+       __le16 wPresentationTime;
+       __le32 dwNumOfPackets;
+       /* FIXME: u8 pktdata[]? */
+} __attribute__((packed));
+
+/* [WUSB] section 8.3.3.5 */
+struct wa_xfer_abort {
+       u8 bLength;
+       u8 bRequestType;
+       __le16 wRPipe;                  /* RPipe index */
+       __le32 dwTransferID;            /* Host-assigned ID */
+} __attribute__((packed));
+
+/**
+ * WA Transfer Complete notification ([WUSB] section 8.3.3.3)
+ *
+ */
+struct wa_notif_xfer {
+       struct wa_notif_hdr hdr;
+       u8 bEndpoint;
+       u8 Reserved;
+} __attribute__((packed));
+
+/** Transfer result basic codes [WUSB] table 8-15 */
+enum {
+       WA_XFER_STATUS_SUCCESS,
+       WA_XFER_STATUS_HALTED,
+       WA_XFER_STATUS_DATA_BUFFER_ERROR,
+       WA_XFER_STATUS_BABBLE,
+       WA_XFER_RESERVED,
+       WA_XFER_STATUS_NOT_FOUND,
+       WA_XFER_STATUS_INSUFFICIENT_RESOURCE,
+       WA_XFER_STATUS_TRANSACTION_ERROR,
+       WA_XFER_STATUS_ABORTED,
+       WA_XFER_STATUS_RPIPE_NOT_READY,
+       WA_XFER_INVALID_FORMAT,
+       WA_XFER_UNEXPECTED_SEGMENT_NUMBER,
+       WA_XFER_STATUS_RPIPE_TYPE_MISMATCH,
+};
+
+/** [WUSB] section 8.3.3.4 */
+struct wa_xfer_result {
+       struct wa_notif_hdr hdr;
+       __le32 dwTransferID;
+       __le32 dwTransferLength;
+       u8     bTransferSegment;
+       u8     bTransferStatus;
+       __le32 dwNumOfPackets;
+} __attribute__((packed));
+
+/**
+ * Wire Adapter Class Descriptor ([WUSB] section 8.5.2.7).
+ *
+ * NOTE: u16 fields are read Little Endian from the hardware.
+ *
+ * @bNumPorts is the original max number of devices that the host can
+ *            connect; we might chop this so the stack can handle
+ *            it. In case you need to access it, use wusbhc->ports_max
+ *            if it is a Wireless USB WA.
+ */
+struct usb_wa_descriptor {
+       u8      bLength;
+       u8      bDescriptorType;
+       u16     bcdWAVersion;
+       u8      bNumPorts;              /* don't use!! */
+       u8      bmAttributes;           /* Reserved == 0 */
+       u16     wNumRPipes;
+       u16     wRPipeMaxBlock;
+       u8      bRPipeBlockSize;
+       u8      bPwrOn2PwrGood;
+       u8      bNumMMCIEs;
+       u8      DeviceRemovable;        /* FIXME: in DWA this is up to 16 bytes */
+} __attribute__((packed));
+
+/**
+ * HWA Device Information Buffer (WUSB1.0[T8.54])
+ */
+struct hwa_dev_info {
+       u8      bmDeviceAvailability[32];       /* FIXME: ignored for now */
+       u8      bDeviceAddress;
+       __le16  wPHYRates;
+       u8      bmDeviceAttribute;
+} __attribute__((packed));
+
+#endif /* #ifndef __LINUX_USB_WUSB_WA_H */
diff --git a/include/linux/usb/wusb.h b/include/linux/usb/wusb.h
new file mode 100644 (file)
index 0000000..5f401b6
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * Wireless USB Standard Definitions
+ * Event Size Tables
+ *
+ * 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: organize properly, group logically
+ *
+ * All the event structures are defined in uwb/spec.h, as they are
+ * common to the WHCI and WUSB radio control interfaces.
+ */
+
+#ifndef __WUSB_H__
+#define __WUSB_H__
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/uwb/spec.h>
+#include <linux/usb/ch9.h>
+#include <linux/param.h>
+
+/**
+ * WUSB Information Element header
+ *
+ * I don't know why, they decided to make it different to the MBOA MAC
+ * IE Header; beats me.
+ */
+struct wuie_hdr {
+       u8 bLength;
+       u8 bIEIdentifier;
+} __attribute__((packed));
+
+enum {
+       WUIE_ID_WCTA = 0x80,
+       WUIE_ID_CONNECTACK,
+       WUIE_ID_HOST_INFO,
+       WUIE_ID_CHANGE_ANNOUNCE,
+       WUIE_ID_DEVICE_DISCONNECT,
+       WUIE_ID_HOST_DISCONNECT,
+       WUIE_ID_KEEP_ALIVE = 0x89,
+       WUIE_ID_ISOCH_DISCARD,
+       WUIE_ID_RESET_DEVICE,
+};
+
+/**
+ * Maximum number of array elements in a WUSB IE.
+ *
+ * WUSB1.0[7.5 before table 7-38] says that in WUSB IEs that
+ * are "arrays" have to limited to 4 elements. So we define it
+ * like that to ease up and submit only the neeed size.
+ */
+#define WUIE_ELT_MAX 4
+
+/**
+ * Wrapper for the data that defines a CHID, a CDID or a CK
+ *
+ * WUSB defines that CHIDs, CDIDs and CKs are a 16 byte string of
+ * data. In order to avoid confusion and enforce types, we wrap it.
+ *
+ * Make it packed, as we use it in some hw defintions.
+ */
+struct wusb_ckhdid {
+       u8 data[16];
+} __attribute__((packed));
+
+const static
+struct wusb_ckhdid wusb_ckhdid_zero = { .data = { 0 } };
+
+#define WUSB_CKHDID_STRSIZE (3 * sizeof(struct wusb_ckhdid) + 1)
+
+/**
+ * WUSB IE: Host Information (WUSB1.0[7.5.2])
+ *
+ * Used to provide information about the host to the Wireless USB
+ * devices in range (CHID can be used as an ASCII string).
+ */
+struct wuie_host_info {
+       struct wuie_hdr hdr;
+       __le16 attributes;
+       struct wusb_ckhdid CHID;
+} __attribute__((packed));
+
+/**
+ * WUSB IE: Connect Ack (WUSB1.0[7.5.1])
+ *
+ * Used to acknowledge device connect requests. See note for
+ * WUIE_ELT_MAX.
+ */
+struct wuie_connect_ack {
+       struct wuie_hdr hdr;
+       struct {
+               struct wusb_ckhdid CDID;
+               u8 bDeviceAddress;      /* 0 means unused */
+               u8 bReserved;
+       } blk[WUIE_ELT_MAX];
+} __attribute__((packed));
+
+/**
+ * WUSB IE Host Information Element, Connect Availability
+ *
+ * WUSB1.0[7.5.2], bmAttributes description
+ */
+enum {
+       WUIE_HI_CAP_RECONNECT = 0,
+       WUIE_HI_CAP_LIMITED,
+       WUIE_HI_CAP_RESERVED,
+       WUIE_HI_CAP_ALL,
+};
+
+/**
+ * WUSB IE: Channel Stop (WUSB1.0[7.5.8])
+ *
+ * Tells devices the host is going to stop sending MMCs and will dissapear.
+ */
+struct wuie_channel_stop {
+       struct wuie_hdr hdr;
+       u8 attributes;
+       u8 timestamp[3];
+} __attribute__((packed));
+
+/**
+ * WUSB IE: Keepalive (WUSB1.0[7.5.9])
+ *
+ * Ask device(s) to send keepalives.
+ */
+struct wuie_keep_alive {
+       struct wuie_hdr hdr;
+       u8 bDeviceAddress[WUIE_ELT_MAX];
+} __attribute__((packed));
+
+/**
+ * WUSB IE: Reset device (WUSB1.0[7.5.11])
+ *
+ * Tell device to reset; in all truth, we can fit 4 CDIDs, but we only
+ * use it for one at the time...
+ *
+ * In any case, this request is a wee bit silly: why don't they target
+ * by address??
+ */
+struct wuie_reset {
+       struct wuie_hdr hdr;
+       struct wusb_ckhdid CDID;
+} __attribute__((packed));
+
+/**
+ * WUSB IE: Disconnect device (WUSB1.0[7.5.11])
+ *
+ * Tell device to disconnect; we can fit 4 addresses, but we only use
+ * it for one at the time...
+ */
+struct wuie_disconnect {
+       struct wuie_hdr hdr;
+       u8 bDeviceAddress;
+       u8 padding;
+} __attribute__((packed));
+
+/**
+ * WUSB IE: Host disconnect ([WUSB] section 7.5.5)
+ *
+ * Tells all connected devices to disconnect.
+ */
+struct wuie_host_disconnect {
+       struct wuie_hdr hdr;
+} __attribute__((packed));
+
+/**
+ * WUSB Device Notification header (WUSB1.0[7.6])
+ */
+struct wusb_dn_hdr {
+       u8 bType;
+       u8 notifdata[];
+} __attribute__((packed));
+
+/** Device Notification codes (WUSB1.0[Table 7-54]) */
+enum WUSB_DN {
+       WUSB_DN_CONNECT = 0x01,
+       WUSB_DN_DISCONNECT = 0x02,
+       WUSB_DN_EPRDY = 0x03,
+       WUSB_DN_MASAVAILCHANGED = 0x04,
+       WUSB_DN_RWAKE = 0x05,
+       WUSB_DN_SLEEP = 0x06,
+       WUSB_DN_ALIVE = 0x07,
+};
+
+/** WUSB Device Notification Connect */
+struct wusb_dn_connect {
+       struct wusb_dn_hdr hdr;
+       __le16 attributes;
+       struct wusb_ckhdid CDID;
+} __attribute__((packed));
+
+static inline int wusb_dn_connect_prev_dev_addr(const struct wusb_dn_connect *dn)
+{
+       return le16_to_cpu(dn->attributes) & 0xff;
+}
+
+static inline int wusb_dn_connect_new_connection(const struct wusb_dn_connect *dn)
+{
+       return (le16_to_cpu(dn->attributes) >> 8) & 0x1;
+}
+
+static inline int wusb_dn_connect_beacon_behavior(const struct wusb_dn_connect *dn)
+{
+       return (le16_to_cpu(dn->attributes) >> 9) & 0x03;
+}
+
+/** Device is alive (aka: pong) (WUSB1.0[7.6.7]) */
+struct wusb_dn_alive {
+       struct wusb_dn_hdr hdr;
+} __attribute__((packed));
+
+/** Device is disconnecting (WUSB1.0[7.6.2]) */
+struct wusb_dn_disconnect {
+       struct wusb_dn_hdr hdr;
+} __attribute__((packed));
+
+/* General constants */
+enum {
+       WUSB_TRUST_TIMEOUT_MS = 4000,   /* [WUSB] section 4.15.1 */
+};
+
+static inline size_t ckhdid_printf(char *pr_ckhdid, size_t size,
+                                  const struct wusb_ckhdid *ckhdid)
+{
+       return scnprintf(pr_ckhdid, size,
+                        "%02hx %02hx %02hx %02hx %02hx %02hx %02hx %02hx "
+                        "%02hx %02hx %02hx %02hx %02hx %02hx %02hx %02hx",
+                        ckhdid->data[0],  ckhdid->data[1],
+                        ckhdid->data[2],  ckhdid->data[3],
+                        ckhdid->data[4],  ckhdid->data[5],
+                        ckhdid->data[6],  ckhdid->data[7],
+                        ckhdid->data[8],  ckhdid->data[9],
+                        ckhdid->data[10], ckhdid->data[11],
+                        ckhdid->data[12], ckhdid->data[13],
+                        ckhdid->data[14], ckhdid->data[15]);
+}
+
+/*
+ * WUSB Crypto stuff (WUSB1.0[6])
+ */
+
+extern const char *wusb_et_name(u8);
+
+/**
+ * WUSB key index WUSB1.0[7.3.2.4], for usage when setting keys for
+ * the host or the device.
+ */
+static inline u8 wusb_key_index(int index, int type, int originator)
+{
+       return (originator << 6) | (type << 4) | index;
+}
+
+#define WUSB_KEY_INDEX_TYPE_PTK                        0 /* for HWA only */
+#define WUSB_KEY_INDEX_TYPE_ASSOC              1
+#define WUSB_KEY_INDEX_TYPE_GTK                        2
+#define WUSB_KEY_INDEX_ORIGINATOR_HOST         0
+#define WUSB_KEY_INDEX_ORIGINATOR_DEVICE       1
+
+/* A CCM Nonce, defined in WUSB1.0[6.4.1] */
+struct aes_ccm_nonce {
+       u8 sfn[6];              /* Little Endian */
+       u8 tkid[3];             /* LE */
+       struct uwb_dev_addr dest_addr;
+       struct uwb_dev_addr src_addr;
+} __attribute__((packed));
+
+/* A CCM operation label, defined on WUSB1.0[6.5.x] */
+struct aes_ccm_label {
+       u8 data[14];
+} __attribute__((packed));
+
+/*
+ * Input to the key derivation sequence defined in
+ * WUSB1.0[6.5.1]. Rest of the data is in the CCM Nonce passed to the
+ * PRF function.
+ */
+struct wusb_keydvt_in {
+       u8 hnonce[16];
+       u8 dnonce[16];
+} __attribute__((packed));
+
+/*
+ * Output from the key derivation sequence defined in
+ * WUSB1.0[6.5.1].
+ */
+struct wusb_keydvt_out {
+       u8 kck[16];
+       u8 ptk[16];
+} __attribute__((packed));
+
+/* Pseudo Random Function WUSB1.0[6.5] */
+extern int wusb_crypto_init(void);
+extern void wusb_crypto_exit(void);
+extern ssize_t wusb_prf(void *out, size_t out_size,
+                       const u8 key[16], const struct aes_ccm_nonce *_n,
+                       const struct aes_ccm_label *a,
+                       const void *b, size_t blen, size_t len);
+
+static inline int wusb_prf_64(void *out, size_t out_size, const u8 key[16],
+                             const struct aes_ccm_nonce *n,
+                             const struct aes_ccm_label *a,
+                             const void *b, size_t blen)
+{
+       return wusb_prf(out, out_size, key, n, a, b, blen, 64);
+}
+
+static inline int wusb_prf_128(void *out, size_t out_size, const u8 key[16],
+                              const struct aes_ccm_nonce *n,
+                              const struct aes_ccm_label *a,
+                              const void *b, size_t blen)
+{
+       return wusb_prf(out, out_size, key, n, a, b, blen, 128);
+}
+
+static inline int wusb_prf_256(void *out, size_t out_size, const u8 key[16],
+                              const struct aes_ccm_nonce *n,
+                              const struct aes_ccm_label *a,
+                              const void *b, size_t blen)
+{
+       return wusb_prf(out, out_size, key, n, a, b, blen, 256);
+}
+
+/* Key derivation WUSB1.0[6.5.1] */
+static inline int wusb_key_derive(struct wusb_keydvt_out *keydvt_out,
+                                 const u8 key[16],
+                                 const struct aes_ccm_nonce *n,
+                                 const struct wusb_keydvt_in *keydvt_in)
+{
+       const struct aes_ccm_label a = { .data = "Pair-wise keys" };
+       return wusb_prf_256(keydvt_out, sizeof(*keydvt_out), key, n, &a,
+                           keydvt_in, sizeof(*keydvt_in));
+}
+
+/*
+ * Out-of-band MIC Generation WUSB1.0[6.5.2]
+ *
+ * Compute the MIC over @key, @n and @hs and place it in @mic_out.
+ *
+ * @mic_out:  Where to place the 8 byte MIC tag
+ * @key:      KCK from the derivation process
+ * @n:        CCM nonce, n->sfn == 0, TKID as established in the
+ *            process.
+ * @hs:       Handshake struct for phase 2 of the 4-way.
+ *            hs->bStatus and hs->bReserved are zero.
+ *            hs->bMessageNumber is 2 (WUSB1.0[7.3.2.5.2]
+ *            hs->dest_addr is the device's USB address padded with 0
+ *            hs->src_addr is the hosts's UWB device address
+ *            hs->mic is ignored (as we compute that value).
+ */
+static inline int wusb_oob_mic(u8 mic_out[8], const u8 key[16],
+                              const struct aes_ccm_nonce *n,
+                              const struct usb_handshake *hs)
+{
+       const struct aes_ccm_label a = { .data = "out-of-bandMIC" };
+       return wusb_prf_64(mic_out, 8, key, n, &a,
+                          hs, sizeof(*hs) - sizeof(hs->MIC));
+}
+
+#endif /* #ifndef __WUSB_H__ */
diff --git a/include/linux/uwb.h b/include/linux/uwb.h
new file mode 100644 (file)
index 0000000..f9ccbd9
--- /dev/null
@@ -0,0 +1,765 @@
+/*
+ * Ultra Wide Band
+ * UWB 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.
+ *
+ *
+ * FIXME: doc: overview of the API, different parts and pointers
+ */
+
+#ifndef __LINUX__UWB_H__
+#define __LINUX__UWB_H__
+
+#include <linux/limits.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+#include <linux/uwb/spec.h>
+
+struct uwb_dev;
+struct uwb_beca_e;
+struct uwb_rc;
+struct uwb_rsv;
+struct uwb_dbg;
+
+/**
+ * struct uwb_dev - a UWB Device
+ * @rc: UWB Radio Controller that discovered the device (kind of its
+ *     parent).
+ * @bce: a beacon cache entry for this device; or NULL if the device
+ *     is a local radio controller.
+ * @mac_addr: the EUI-48 address of this device.
+ * @dev_addr: the current DevAddr used by this device.
+ * @beacon_slot: the slot number the beacon is using.
+ * @streams: bitmap of streams allocated to reservations targeted at
+ *     this device.  For an RC, this is the streams allocated for
+ *     reservations targeted at DevAddrs.
+ *
+ * A UWB device may either by a neighbor or part of a local radio
+ * controller.
+ */
+struct uwb_dev {
+       struct mutex mutex;
+       struct list_head list_node;
+       struct device dev;
+       struct uwb_rc *rc;              /* radio controller */
+       struct uwb_beca_e *bce;         /* Beacon Cache Entry */
+
+       struct uwb_mac_addr mac_addr;
+       struct uwb_dev_addr dev_addr;
+       int beacon_slot;
+       DECLARE_BITMAP(streams, UWB_NUM_STREAMS);
+};
+#define to_uwb_dev(d) container_of(d, struct uwb_dev, dev)
+
+/**
+ * UWB HWA/WHCI Radio Control {Command|Event} Block context IDs
+ *
+ * RC[CE]Bs have a 'context ID' field that matches the command with
+ * the event received to confirm it.
+ *
+ * Maximum number of context IDs
+ */
+enum { UWB_RC_CTX_MAX = 256 };
+
+
+/** Notification chain head for UWB generated events to listeners */
+struct uwb_notifs_chain {
+       struct list_head list;
+       struct mutex mutex;
+};
+
+/**
+ * struct uwb_mas_bm - a bitmap of all MAS in a superframe
+ * @bm: a bitmap of length #UWB_NUM_MAS
+ */
+struct uwb_mas_bm {
+       DECLARE_BITMAP(bm, UWB_NUM_MAS);
+};
+
+/**
+ * uwb_rsv_state - UWB Reservation state.
+ *
+ * NONE - reservation is not active (no DRP IE being transmitted).
+ *
+ * Owner reservation states:
+ *
+ * INITIATED - owner has sent an initial DRP request.
+ * PENDING - target responded with pending Reason Code.
+ * MODIFIED - reservation manager is modifying an established
+ * reservation with a different MAS allocation.
+ * ESTABLISHED - the reservation has been successfully negotiated.
+ *
+ * Target reservation states:
+ *
+ * DENIED - request is denied.
+ * ACCEPTED - request is accepted.
+ * PENDING - PAL has yet to make a decision to whether to accept or
+ * deny.
+ *
+ * FIXME: further target states TBD.
+ */
+enum uwb_rsv_state {
+       UWB_RSV_STATE_NONE,
+       UWB_RSV_STATE_O_INITIATED,
+       UWB_RSV_STATE_O_PENDING,
+       UWB_RSV_STATE_O_MODIFIED,
+       UWB_RSV_STATE_O_ESTABLISHED,
+       UWB_RSV_STATE_T_ACCEPTED,
+       UWB_RSV_STATE_T_DENIED,
+       UWB_RSV_STATE_T_PENDING,
+
+       UWB_RSV_STATE_LAST,
+};
+
+enum uwb_rsv_target_type {
+       UWB_RSV_TARGET_DEV,
+       UWB_RSV_TARGET_DEVADDR,
+};
+
+/**
+ * struct uwb_rsv_target - the target of a reservation.
+ *
+ * Reservations unicast and targeted at a single device
+ * (UWB_RSV_TARGET_DEV); or (e.g., in the case of WUSB) targeted at a
+ * specific (private) DevAddr (UWB_RSV_TARGET_DEVADDR).
+ */
+struct uwb_rsv_target {
+       enum uwb_rsv_target_type type;
+       union {
+               struct uwb_dev *dev;
+               struct uwb_dev_addr devaddr;
+       };
+};
+
+/*
+ * Number of streams reserved for reservations targeted at DevAddrs.
+ */
+#define UWB_NUM_GLOBAL_STREAMS 1
+
+typedef void (*uwb_rsv_cb_f)(struct uwb_rsv *rsv);
+
+/**
+ * struct uwb_rsv - a DRP reservation
+ *
+ * Data structure management:
+ *
+ * @rc:             the radio controller this reservation is for
+ *                  (as target or owner)
+ * @rc_node:        a list node for the RC
+ * @pal_node:       a list node for the PAL
+ *
+ * Owner and target parameters:
+ *
+ * @owner:          the UWB device owning this reservation
+ * @target:         the target UWB device
+ * @type:           reservation type
+ *
+ * Owner parameters:
+ *
+ * @max_mas:        maxiumum number of MAS
+ * @min_mas:        minimum number of MAS
+ * @sparsity:       owner selected sparsity
+ * @is_multicast:   true iff multicast
+ *
+ * @callback:       callback function when the reservation completes
+ * @pal_priv:       private data for the PAL making the reservation
+ *
+ * Reservation status:
+ *
+ * @status:         negotiation status
+ * @stream:         stream index allocated for this reservation
+ * @mas:            reserved MAS
+ * @drp_ie:         the DRP IE
+ * @ie_valid:       true iff the DRP IE matches the reservation parameters
+ *
+ * DRP reservations are uniquely identified by the owner, target and
+ * stream index.  However, when using a DevAddr as a target (e.g., for
+ * a WUSB cluster reservation) the responses may be received from
+ * devices with different DevAddrs.  In this case, reservations are
+ * uniquely identified by just the stream index.  A number of stream
+ * indexes (UWB_NUM_GLOBAL_STREAMS) are reserved for this.
+ */
+struct uwb_rsv {
+       struct uwb_rc *rc;
+       struct list_head rc_node;
+       struct list_head pal_node;
+
+       struct uwb_dev *owner;
+       struct uwb_rsv_target target;
+       enum uwb_drp_type type;
+       int max_mas;
+       int min_mas;
+       int sparsity;
+       bool is_multicast;
+
+       uwb_rsv_cb_f callback;
+       void *pal_priv;
+
+       enum uwb_rsv_state state;
+       u8 stream;
+       struct uwb_mas_bm mas;
+       struct uwb_ie_drp *drp_ie;
+       bool ie_valid;
+       struct timer_list timer;
+       bool expired;
+};
+
+static const
+struct uwb_mas_bm uwb_mas_bm_zero = { .bm = { 0 } };
+
+static inline void uwb_mas_bm_copy_le(void *dst, const struct uwb_mas_bm *mas)
+{
+       bitmap_copy_le(dst, mas->bm, UWB_NUM_MAS);
+}
+
+/**
+ * struct uwb_drp_avail - a radio controller's view of MAS usage
+ * @global:   MAS unused by neighbors (excluding reservations targetted
+ *            or owned by the local radio controller) or the beaon period
+ * @local:    MAS unused by local established reservations
+ * @pending:  MAS unused by local pending reservations
+ * @ie:       DRP Availability IE to be included in the beacon
+ * @ie_valid: true iff @ie is valid and does not need to regenerated from
+ *            @global and @local
+ *
+ * Each radio controller maintains a view of MAS usage or
+ * availability. MAS available for a new reservation are determined
+ * from the intersection of @global, @local, and @pending.
+ *
+ * The radio controller must transmit a DRP Availability IE that's the
+ * intersection of @global and @local.
+ *
+ * A set bit indicates the MAS is unused and available.
+ *
+ * rc->rsvs_mutex should be held before accessing this data structure.
+ *
+ * [ECMA-368] section 17.4.3.
+ */
+struct uwb_drp_avail {
+       DECLARE_BITMAP(global, UWB_NUM_MAS);
+       DECLARE_BITMAP(local, UWB_NUM_MAS);
+       DECLARE_BITMAP(pending, UWB_NUM_MAS);
+       struct uwb_ie_drp_avail ie;
+       bool ie_valid;
+};
+
+
+const char *uwb_rsv_state_str(enum uwb_rsv_state state);
+const char *uwb_rsv_type_str(enum uwb_drp_type type);
+
+struct uwb_rsv *uwb_rsv_create(struct uwb_rc *rc, uwb_rsv_cb_f cb,
+                              void *pal_priv);
+void uwb_rsv_destroy(struct uwb_rsv *rsv);
+
+int uwb_rsv_establish(struct uwb_rsv *rsv);
+int uwb_rsv_modify(struct uwb_rsv *rsv,
+                  int max_mas, int min_mas, int sparsity);
+void uwb_rsv_terminate(struct uwb_rsv *rsv);
+
+void uwb_rsv_accept(struct uwb_rsv *rsv, uwb_rsv_cb_f cb, void *pal_priv);
+
+/**
+ * Radio Control Interface instance
+ *
+ *
+ * Life cycle rules: those of the UWB Device.
+ *
+ * @index:    an index number for this radio controller, as used in the
+ *            device name.
+ * @version:  version of protocol supported by this device
+ * @priv:     Backend implementation; rw with uwb_dev.dev.sem taken.
+ * @cmd:      Backend implementation to execute commands; rw and call
+ *            only  with uwb_dev.dev.sem taken.
+ * @reset:    Hardware reset of radio controller and any PAL controllers.
+ * @filter:   Backend implementation to manipulate data to and from device
+ *            to be compliant to specification assumed by driver (WHCI
+ *            0.95).
+ *
+ *            uwb_dev.dev.mutex is used to execute commands and update
+ *            the corresponding structures; can't use a spinlock
+ *            because rc->cmd() can sleep.
+ * @ies:         This is a dynamically allocated array cacheing the
+ *               IEs (settable by the host) that the beacon of this
+ *               radio controller is currently sending.
+ *
+ *               In reality, we store here the full command we set to
+ *               the radio controller (which is basically a command
+ *               prefix followed by all the IEs the beacon currently
+ *               contains). This way we don't have to realloc and
+ *               memcpy when setting it.
+ *
+ *               We set this up in uwb_rc_ie_setup(), where we alloc
+ *               this struct, call get_ie() [so we know which IEs are
+ *               currently being sent, if any].
+ *
+ * @ies_capacity:Amount of space (in bytes) allocated in @ies. The
+ *               amount used is given by sizeof(*ies) plus ies->wIELength
+ *               (which is a little endian quantity all the time).
+ * @ies_mutex:   protect the IE cache
+ * @dbg:         information for the debug interface
+ */
+struct uwb_rc {
+       struct uwb_dev uwb_dev;
+       int index;
+       u16 version;
+
+       struct module *owner;
+       void *priv;
+       int (*start)(struct uwb_rc *rc);
+       void (*stop)(struct uwb_rc *rc);
+       int (*cmd)(struct uwb_rc *, const struct uwb_rccb *, size_t);
+       int (*reset)(struct uwb_rc *rc);
+       int (*filter_cmd)(struct uwb_rc *, struct uwb_rccb **, size_t *);
+       int (*filter_event)(struct uwb_rc *, struct uwb_rceb **, const size_t,
+                           size_t *, size_t *);
+
+       spinlock_t neh_lock;            /* protects neh_* and ctx_* */
+       struct list_head neh_list;      /* Open NE handles */
+       unsigned long ctx_bm[UWB_RC_CTX_MAX / 8 / sizeof(unsigned long)];
+       u8 ctx_roll;
+
+       int beaconing;                  /* Beaconing state [channel number] */
+       int scanning;
+       enum uwb_scan_type scan_type:3;
+       unsigned ready:1;
+       struct uwb_notifs_chain notifs_chain;
+
+       struct uwb_drp_avail drp_avail;
+       struct list_head reservations;
+       struct mutex rsvs_mutex;
+       struct workqueue_struct *rsv_workq;
+       struct work_struct rsv_update_work;
+
+       struct mutex ies_mutex;
+       struct uwb_rc_cmd_set_ie *ies;
+       size_t ies_capacity;
+
+       spinlock_t pal_lock;
+       struct list_head pals;
+
+       struct uwb_dbg *dbg;
+};
+
+
+/**
+ * struct uwb_pal - a UWB PAL
+ * @name:    descriptive name for this PAL (wushc, wlp, etc.).
+ * @device:  a device for the PAL.  Used to link the PAL and the radio
+ *           controller in sysfs.
+ * @new_rsv: called when a peer requests a reservation (may be NULL if
+ *           the PAL cannot accept reservation requests).
+ *
+ * A Protocol Adaptation Layer (PAL) is a user of the WiMedia UWB
+ * radio platform (e.g., WUSB, WLP or Bluetooth UWB AMP).
+ *
+ * The PALs using a radio controller must register themselves to
+ * permit the UWB stack to coordinate usage of the radio between the
+ * various PALs or to allow PALs to response to certain requests from
+ * peers.
+ *
+ * A struct uwb_pal should be embedded in a containing structure
+ * belonging to the PAL and initialized with uwb_pal_init()).  Fields
+ * should be set appropriately by the PAL before registering the PAL
+ * with uwb_pal_register().
+ */
+struct uwb_pal {
+       struct list_head node;
+       const char *name;
+       struct device *device;
+       void (*new_rsv)(struct uwb_rsv *rsv);
+};
+
+void uwb_pal_init(struct uwb_pal *pal);
+int uwb_pal_register(struct uwb_rc *rc, struct uwb_pal *pal);
+void uwb_pal_unregister(struct uwb_rc *rc, struct uwb_pal *pal);
+
+/*
+ * General public API
+ *
+ * This API can be used by UWB device drivers or by those implementing
+ * UWB Radio Controllers
+ */
+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_rc(struct uwb_dev *, struct uwb_rc *);
+static inline void uwb_dev_get(struct uwb_dev *uwb_dev)
+{
+       get_device(&uwb_dev->dev);
+}
+static inline void uwb_dev_put(struct uwb_dev *uwb_dev)
+{
+       put_device(&uwb_dev->dev);
+}
+struct uwb_dev *uwb_dev_try_get(struct uwb_rc *rc, struct uwb_dev *uwb_dev);
+
+/**
+ * Callback function for 'uwb_{dev,rc}_foreach()'.
+ *
+ * @dev:  Linux device instance
+ *        'uwb_dev = container_of(dev, struct uwb_dev, dev)'
+ * @priv: Data passed by the caller to 'uwb_{dev,rc}_foreach()'.
+ *
+ * @returns: 0 to continue the iterations, any other val to stop
+ *           iterating and return the value to the caller of
+ *           _foreach().
+ */
+typedef int (*uwb_dev_for_each_f)(struct device *dev, void *priv);
+int uwb_dev_for_each(struct uwb_rc *rc, uwb_dev_for_each_f func, void *priv);
+
+struct uwb_rc *uwb_rc_alloc(void);
+struct uwb_rc *uwb_rc_get_by_dev(const struct uwb_dev_addr *);
+struct uwb_rc *uwb_rc_get_by_grandpa(const struct device *);
+void uwb_rc_put(struct uwb_rc *rc);
+
+typedef void (*uwb_rc_cmd_cb_f)(struct uwb_rc *rc, void *arg,
+                                struct uwb_rceb *reply, ssize_t reply_size);
+
+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);
+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);
+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);
+ssize_t uwb_rc_get_ie(struct uwb_rc *, struct uwb_rc_evt_get_ie **);
+int uwb_bg_joined(struct uwb_rc *rc);
+
+size_t __uwb_addr_print(char *, size_t, const unsigned char *, int);
+
+int uwb_rc_dev_addr_set(struct uwb_rc *, const struct uwb_dev_addr *);
+int uwb_rc_dev_addr_get(struct uwb_rc *, struct uwb_dev_addr *);
+int uwb_rc_mac_addr_set(struct uwb_rc *, const struct uwb_mac_addr *);
+int uwb_rc_mac_addr_get(struct uwb_rc *, struct uwb_mac_addr *);
+int __uwb_mac_addr_assigned_check(struct device *, void *);
+int __uwb_dev_addr_assigned_check(struct device *, void *);
+
+/* Print in @buf a pretty repr of @addr */
+static inline size_t uwb_dev_addr_print(char *buf, size_t buf_size,
+                                       const struct uwb_dev_addr *addr)
+{
+       return __uwb_addr_print(buf, buf_size, addr->data, 0);
+}
+
+/* Print in @buf a pretty repr of @addr */
+static inline size_t uwb_mac_addr_print(char *buf, size_t buf_size,
+                                       const struct uwb_mac_addr *addr)
+{
+       return __uwb_addr_print(buf, buf_size, addr->data, 1);
+}
+
+/* @returns 0 if device addresses @addr2 and @addr1 are equal */
+static inline int uwb_dev_addr_cmp(const struct uwb_dev_addr *addr1,
+                                  const struct uwb_dev_addr *addr2)
+{
+       return memcmp(addr1, addr2, sizeof(*addr1));
+}
+
+/* @returns 0 if MAC addresses @addr2 and @addr1 are equal */
+static inline int uwb_mac_addr_cmp(const struct uwb_mac_addr *addr1,
+                                  const struct uwb_mac_addr *addr2)
+{
+       return memcmp(addr1, addr2, sizeof(*addr1));
+}
+
+/* @returns !0 if a MAC @addr is a broadcast address */
+static inline int uwb_mac_addr_bcast(const struct uwb_mac_addr *addr)
+{
+       struct uwb_mac_addr bcast = {
+               .data = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }
+       };
+       return !uwb_mac_addr_cmp(addr, &bcast);
+}
+
+/* @returns !0 if a MAC @addr is all zeroes*/
+static inline int uwb_mac_addr_unset(const struct uwb_mac_addr *addr)
+{
+       struct uwb_mac_addr unset = {
+               .data = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
+       };
+       return !uwb_mac_addr_cmp(addr, &unset);
+}
+
+/* @returns !0 if the address is in use. */
+static inline unsigned __uwb_dev_addr_assigned(struct uwb_rc *rc,
+                                              struct uwb_dev_addr *addr)
+{
+       return uwb_dev_for_each(rc, __uwb_dev_addr_assigned_check, addr);
+}
+
+/*
+ * UWB Radio Controller API
+ *
+ * This API is used (in addition to the general API) to implement UWB
+ * Radio Controllers.
+ */
+void uwb_rc_init(struct uwb_rc *);
+int uwb_rc_add(struct uwb_rc *, struct device *dev, void *rc_priv);
+void uwb_rc_rm(struct uwb_rc *);
+void uwb_rc_neh_grok(struct uwb_rc *, void *, size_t);
+void uwb_rc_neh_error(struct uwb_rc *, int);
+void uwb_rc_reset_all(struct uwb_rc *rc);
+
+/**
+ * uwb_rsv_is_owner - is the owner of this reservation the RC?
+ * @rsv: the reservation
+ */
+static inline bool uwb_rsv_is_owner(struct uwb_rsv *rsv)
+{
+       return rsv->owner == &rsv->rc->uwb_dev;
+}
+
+/**
+ * Events generated by UWB that can be passed to any listeners
+ *
+ * Higher layers can register callback functions with the radio
+ * controller using uwb_notifs_register(). The radio controller
+ * maintains a list of all registered handlers and will notify all
+ * nodes when an event occurs.
+ */
+enum uwb_notifs {
+       UWB_NOTIF_BG_JOIN = 0,  /* radio controller joined a beacon group */
+       UWB_NOTIF_BG_LEAVE = 1, /* radio controller left a beacon group */
+       UWB_NOTIF_ONAIR,
+       UWB_NOTIF_OFFAIR,
+};
+
+/* Callback function registered with UWB */
+struct uwb_notifs_handler {
+       struct list_head list_node;
+       void (*cb)(void *, struct uwb_dev *, enum uwb_notifs);
+       void *data;
+};
+
+int uwb_notifs_register(struct uwb_rc *, struct uwb_notifs_handler *);
+int uwb_notifs_deregister(struct uwb_rc *, struct uwb_notifs_handler *);
+
+
+/**
+ * UWB radio controller Event Size Entry (for creating entry tables)
+ *
+ * WUSB and WHCI define events and notifications, and they might have
+ * fixed or variable size.
+ *
+ * Each event/notification has a size which is not necessarily known
+ * in advance based on the event code. As well, vendor specific
+ * events/notifications will have a size impossible to determine
+ * unless we know about the device's specific details.
+ *
+ * It was way too smart of the spec writers not to think that it would
+ * be impossible for a generic driver to skip over vendor specific
+ * events/notifications if there are no LENGTH fields in the HEADER of
+ * each message...the transaction size cannot be counted on as the
+ * spec does not forbid to pack more than one event in a single
+ * transaction.
+ *
+ * Thus, we guess sizes with tables (or for events, when you know the
+ * size ahead of time you can use uwb_rc_neh_extra_size*()). We
+ * register tables with the known events and their sizes, and then we
+ * traverse those tables. For those with variable length, we provide a
+ * way to lookup the size inside the event/notification's
+ * payload. This allows device-specific event size tables to be
+ * registered.
+ *
+ * @size:   Size of the payload
+ *
+ * @offset: if != 0, at offset @offset-1 starts a field with a length
+ *          that has to be added to @size. The format of the field is
+ *          given by @type.
+ *
+ * @type:   Type and length of the offset field. Most common is LE 16
+ *          bits (that's why that is zero); others are there mostly to
+ *          cover for bugs and weirdos.
+ */
+struct uwb_est_entry {
+       size_t size;
+       unsigned offset;
+       enum { UWB_EST_16 = 0, UWB_EST_8 = 1 } type;
+};
+
+int uwb_est_register(u8 type, u8 code_high, u16 vendor, u16 product,
+                    const struct uwb_est_entry *, size_t entries);
+int uwb_est_unregister(u8 type, u8 code_high, u16 vendor, u16 product,
+                      const struct uwb_est_entry *, size_t entries);
+ssize_t uwb_est_find_size(struct uwb_rc *rc, const struct uwb_rceb *rceb,
+                         size_t len);
+
+/* -- Misc */
+
+enum {
+       EDC_MAX_ERRORS = 10,
+       EDC_ERROR_TIMEFRAME = HZ,
+};
+
+/* error density counter */
+struct edc {
+       unsigned long timestart;
+       u16 errorcount;
+};
+
+static inline
+void edc_init(struct edc *edc)
+{
+       edc->timestart = jiffies;
+}
+
+/* Called when an error occured.
+ * This is way to determine if the number of acceptable errors per time
+ * period has been exceeded. It is not accurate as there are cases in which
+ * this scheme will not work, for example if there are periodic occurences
+ * of errors that straddle updates to the start time. This scheme is
+ * sufficient for our usage.
+ *
+ * @returns 1 if maximum acceptable errors per timeframe has been exceeded.
+ */
+static inline int edc_inc(struct edc *err_hist, u16 max_err, u16 timeframe)
+{
+       unsigned long now;
+
+       now = jiffies;
+       if (now - err_hist->timestart > timeframe) {
+               err_hist->errorcount = 1;
+               err_hist->timestart = now;
+       } else if (++err_hist->errorcount > max_err) {
+                       err_hist->errorcount = 0;
+                       err_hist->timestart = now;
+                       return 1;
+       }
+       return 0;
+}
+
+
+/* Information Element handling */
+
+/* For representing the state of writing to a buffer when iterating */
+struct uwb_buf_ctx {
+       char *buf;
+       size_t bytes, size;
+};
+
+typedef int (*uwb_ie_f)(struct uwb_dev *, const struct uwb_ie_hdr *,
+                       size_t, void *);
+struct uwb_ie_hdr *uwb_ie_next(void **ptr, size_t *len);
+ssize_t uwb_ie_for_each(struct uwb_dev *uwb_dev, uwb_ie_f fn, void *data,
+                       const void *buf, size_t size);
+int uwb_ie_dump_hex(struct uwb_dev *, const struct uwb_ie_hdr *,
+                   size_t, void *);
+int uwb_rc_set_ie(struct uwb_rc *, struct uwb_rc_cmd_set_ie *);
+struct uwb_ie_hdr *uwb_ie_next(void **ptr, size_t *len);
+
+
+/*
+ * Transmission statistics
+ *
+ * UWB uses LQI and RSSI (one byte values) for reporting radio signal
+ * strength and line quality indication. We do quick and dirty
+ * averages of those. They are signed values, btw.
+ *
+ * For 8 bit quantities, we keep the min, the max, an accumulator
+ * (@sigma) and a # of samples. When @samples gets to 255, we compute
+ * the average (@sigma / @samples), place it in @sigma and reset
+ * @samples to 1 (so we use it as the first sample).
+ *
+ * Now, statistically speaking, probably I am kicking the kidneys of
+ * some books I have in my shelves collecting dust, but I just want to
+ * get an approx, not the Nobel.
+ *
+ * LOCKING: there is no locking per se, but we try to keep a lockless
+ * schema. Only _add_samples() modifies the values--as long as you
+ * have other locking on top that makes sure that no two calls of
+ * _add_sample() happen at the same time, then we are fine. Now, for
+ * resetting the values we just set @samples to 0 and that makes the
+ * next _add_sample() to start with defaults. Reading the values in
+ * _show() currently can race, so you need to make sure the calls are
+ * under the same lock that protects calls to _add_sample(). FIXME:
+ * currently unlocked (It is not ultraprecise but does the trick. Bite
+ * me).
+ */
+struct stats {
+       s8 min, max;
+       s16 sigma;
+       atomic_t samples;
+};
+
+static inline
+void stats_init(struct stats *stats)
+{
+       atomic_set(&stats->samples, 0);
+       wmb();
+}
+
+static inline
+void stats_add_sample(struct stats *stats, s8 sample)
+{
+       s8 min, max;
+       s16 sigma;
+       unsigned samples = atomic_read(&stats->samples);
+       if (samples == 0) {     /* it was zero before, so we initialize */
+               min = 127;
+               max = -128;
+               sigma = 0;
+       } else {
+               min = stats->min;
+               max = stats->max;
+               sigma = stats->sigma;
+       }
+
+       if (sample < min)       /* compute new values */
+               min = sample;
+       else if (sample > max)
+               max = sample;
+       sigma += sample;
+
+       stats->min = min;       /* commit */
+       stats->max = max;
+       stats->sigma = sigma;
+       if (atomic_add_return(1, &stats->samples) > 255) {
+               /* wrapped around! reset */
+               stats->sigma = sigma / 256;
+               atomic_set(&stats->samples, 1);
+       }
+}
+
+static inline ssize_t stats_show(struct stats *stats, char *buf)
+{
+       int min, max, avg;
+       int samples = atomic_read(&stats->samples);
+       if (samples == 0)
+               min = max = avg = 0;
+       else {
+               min = stats->min;
+               max = stats->max;
+               avg = stats->sigma / samples;
+       }
+       return scnprintf(buf, PAGE_SIZE, "%d %d %d\n", min, max, avg);
+}
+
+static inline ssize_t stats_store(struct stats *stats, const char *buf,
+                                 size_t size)
+{
+       stats_init(stats);
+       return size;
+}
+
+#endif /* #ifndef __LINUX__UWB_H__ */
diff --git a/include/linux/uwb/debug-cmd.h b/include/linux/uwb/debug-cmd.h
new file mode 100644 (file)
index 0000000..1141f41
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Ultra Wide Band
+ * Debug interface commands
+ *
+ * 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/>.
+ */
+#ifndef __LINUX__UWB__DEBUG_CMD_H__
+#define __LINUX__UWB__DEBUG_CMD_H__
+
+#include <linux/types.h>
+
+/*
+ * Debug interface commands
+ *
+ * UWB_DBG_CMD_RSV_ESTABLISH: Establish a new unicast reservation.
+ *
+ * UWB_DBG_CMD_RSV_TERMINATE: Terminate the Nth reservation.
+ */
+
+enum uwb_dbg_cmd_type {
+       UWB_DBG_CMD_RSV_ESTABLISH = 1,
+       UWB_DBG_CMD_RSV_TERMINATE = 2,
+};
+
+struct uwb_dbg_cmd_rsv_establish {
+       __u8  target[6];
+       __u8  type;
+       __u16 max_mas;
+       __u16 min_mas;
+       __u8  sparsity;
+};
+
+struct uwb_dbg_cmd_rsv_terminate {
+       int index;
+};
+
+struct uwb_dbg_cmd {
+       __u32 type;
+       union {
+               struct uwb_dbg_cmd_rsv_establish rsv_establish;
+               struct uwb_dbg_cmd_rsv_terminate rsv_terminate;
+       };
+};
+
+#endif /* #ifndef __LINUX__UWB__DEBUG_CMD_H__ */
diff --git a/include/linux/uwb/debug.h b/include/linux/uwb/debug.h
new file mode 100644 (file)
index 0000000..a86a73f
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * 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
+ * Invoke like:
+ *
+ * #define D_LOCAL 4
+ * #include <linux/uwb/debug.h>
+ *
+ * At the end of your include files.
+ */
+#include <linux/types.h>
+
+struct device;
+extern void dump_bytes(struct device *dev, const void *_buf, size_t rsize);
+
+/* Master debug switch; !0 enables, 0 disables */
+#define D_MASTER (!0)
+
+/* Local (per-file) debug switch; #define before #including */
+#ifndef D_LOCAL
+#define D_LOCAL 0
+#endif
+
+#undef __d_printf
+#undef d_fnstart
+#undef d_fnend
+#undef d_printf
+#undef d_dump
+
+#define __d_printf(l, _tag, _dev, f, a...)                             \
+do {                                                                   \
+       struct device *__dev = (_dev);                                  \
+       if (D_MASTER && D_LOCAL >= (l)) {                               \
+               char __head[64] = "";                                   \
+               if (_dev != NULL) {                                     \
+                       if ((unsigned long)__dev < 4096)                \
+                               printk(KERN_ERR "E: Corrupt dev %p\n",  \
+                                       __dev);                         \
+                       else                                            \
+                               snprintf(__head, sizeof(__head),        \
+                                        "%s %s: ",                     \
+                                        dev_driver_string(__dev),      \
+                                        __dev->bus_id);                \
+               }                                                       \
+               printk(KERN_ERR "%s%s" _tag ": " f, __head,             \
+                       __func__, ## a);                                \
+       }                                                               \
+} while (0 && _dev)
+
+#define d_fnstart(l, _dev, f, a...)    \
+       __d_printf(l, " FNSTART", _dev, f, ## a)
+#define d_fnend(l, _dev, f, a...)      \
+       __d_printf(l, " FNEND", _dev, f, ## a)
+#define d_printf(l, _dev, f, a...)     \
+       __d_printf(l, "", _dev, f, ## a)
+#define d_dump(l, _dev, ptr, size)             \
+do {                                           \
+       struct device *__dev = _dev;            \
+       if (D_MASTER && D_LOCAL >= (l))         \
+               dump_bytes(__dev, ptr, size);   \
+} while (0 && _dev)
+#define d_test(l) (D_MASTER && D_LOCAL >= (l))
diff --git a/include/linux/uwb/spec.h b/include/linux/uwb/spec.h
new file mode 100644 (file)
index 0000000..198c15f
--- /dev/null
@@ -0,0 +1,727 @@
+/*
+ * Ultra Wide Band
+ * UWB Standard 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.
+ *
+ *
+ * All these definitions are based on the ECMA-368 standard.
+ *
+ * Note all definitions are Little Endian in the wire, and we will
+ * convert them to host order before operating on the bitfields (that
+ * yes, we use extensively).
+ */
+
+#ifndef __LINUX__UWB_SPEC_H__
+#define __LINUX__UWB_SPEC_H__
+
+#include <linux/types.h>
+#include <linux/bitmap.h>
+
+#define i1480_FW 0x00000303
+/* #define i1480_FW 0x00000302 */
+
+/**
+ * Number of Medium Access Slots in a superframe.
+ *
+ * UWB divides time in SuperFrames, each one divided in 256 pieces, or
+ * Medium Access Slots. See MBOA MAC[5.4.5] for details. The MAS is the
+ * basic bandwidth allocation unit in UWB.
+ */
+enum { UWB_NUM_MAS = 256 };
+
+/**
+ * Number of Zones in superframe.
+ *
+ * UWB divides the superframe into zones with numbering starting from BPST.
+ * See MBOA MAC[16.8.6]
+ */
+enum { UWB_NUM_ZONES = 16 };
+
+/*
+ * Number of MAS in a zone.
+ */
+#define UWB_MAS_PER_ZONE (UWB_NUM_MAS / UWB_NUM_ZONES)
+
+/*
+ * Number of streams per DRP reservation between a pair of devices.
+ *
+ * [ECMA-368] section 16.8.6.
+ */
+enum { UWB_NUM_STREAMS = 8 };
+
+/*
+ * mMasLength
+ *
+ * The length of a MAS in microseconds.
+ *
+ * [ECMA-368] section 17.16.
+ */
+enum { UWB_MAS_LENGTH_US = 256 };
+
+/*
+ * mBeaconSlotLength
+ *
+ * The length of the beacon slot in microseconds.
+ *
+ * [ECMA-368] section 17.16
+ */
+enum { UWB_BEACON_SLOT_LENGTH_US = 85 };
+
+/*
+ * mMaxLostBeacons
+ *
+ * The number beacons missing in consecutive superframes before a
+ * device can be considered as unreachable.
+ *
+ * [ECMA-368] section 17.16
+ */
+enum { UWB_MAX_LOST_BEACONS = 3 };
+
+/*
+ * Length of a superframe in microseconds.
+ */
+#define UWB_SUPERFRAME_LENGTH_US (UWB_MAS_LENGTH_US * UWB_NUM_MAS)
+
+/**
+ * UWB MAC address
+ *
+ * It is *imperative* that this struct is exactly 6 packed bytes (as
+ * it is also used to define headers sent down and up the wire/radio).
+ */
+struct uwb_mac_addr {
+       u8 data[6];
+} __attribute__((packed));
+
+
+/**
+ * UWB device address
+ *
+ * It is *imperative* that this struct is exactly 6 packed bytes (as
+ * it is also used to define headers sent down and up the wire/radio).
+ */
+struct uwb_dev_addr {
+       u8 data[2];
+} __attribute__((packed));
+
+
+/**
+ * Types of UWB addresses
+ *
+ * Order matters (by size).
+ */
+enum uwb_addr_type {
+       UWB_ADDR_DEV = 0,
+       UWB_ADDR_MAC = 1,
+};
+
+
+/** Size of a char buffer for printing a MAC/device address */
+enum { UWB_ADDR_STRSIZE = 32 };
+
+
+/** UWB WiMedia protocol IDs. */
+enum uwb_prid {
+       UWB_PRID_WLP_RESERVED   = 0x0000,
+       UWB_PRID_WLP            = 0x0001,
+       UWB_PRID_WUSB_BOT       = 0x0010,
+       UWB_PRID_WUSB           = 0x0010,
+       UWB_PRID_WUSB_TOP       = 0x001F,
+};
+
+
+/** PHY Rate (MBOA MAC[7.8.12, Table 61]) */
+enum uwb_phy_rate {
+       UWB_PHY_RATE_53 = 0,
+       UWB_PHY_RATE_80,
+       UWB_PHY_RATE_106,
+       UWB_PHY_RATE_160,
+       UWB_PHY_RATE_200,
+       UWB_PHY_RATE_320,
+       UWB_PHY_RATE_400,
+       UWB_PHY_RATE_480,
+       UWB_PHY_RATE_INVALID
+};
+
+
+/**
+ * Different ways to scan (MBOA MAC[6.2.2, Table 8], WUSB[Table 8-78])
+ */
+enum uwb_scan_type {
+       UWB_SCAN_ONLY = 0,
+       UWB_SCAN_OUTSIDE_BP,
+       UWB_SCAN_WHILE_INACTIVE,
+       UWB_SCAN_DISABLED,
+       UWB_SCAN_ONLY_STARTTIME,
+       UWB_SCAN_TOP
+};
+
+
+/** ACK Policy types (MBOA MAC[7.2.1.3]) */
+enum uwb_ack_pol {
+       UWB_ACK_NO = 0,
+       UWB_ACK_INM = 1,
+       UWB_ACK_B = 2,
+       UWB_ACK_B_REQ = 3,
+};
+
+
+/** DRP reservation types ([ECMA-368 table 106) */
+enum uwb_drp_type {
+       UWB_DRP_TYPE_ALIEN_BP = 0,
+       UWB_DRP_TYPE_HARD,
+       UWB_DRP_TYPE_SOFT,
+       UWB_DRP_TYPE_PRIVATE,
+       UWB_DRP_TYPE_PCA,
+};
+
+
+/** DRP Reason Codes ([ECMA-368] table 107) */
+enum uwb_drp_reason {
+       UWB_DRP_REASON_ACCEPTED = 0,
+       UWB_DRP_REASON_CONFLICT,
+       UWB_DRP_REASON_PENDING,
+       UWB_DRP_REASON_DENIED,
+       UWB_DRP_REASON_MODIFIED,
+};
+
+/**
+ *  DRP Notification Reason Codes (WHCI 0.95 [3.1.4.9])
+ */
+enum uwb_drp_notif_reason {
+       UWB_DRP_NOTIF_DRP_IE_RCVD = 0,
+       UWB_DRP_NOTIF_CONFLICT,
+       UWB_DRP_NOTIF_TERMINATE,
+};
+
+
+/** Allocation of MAS slots in a DRP request MBOA MAC[7.8.7] */
+struct uwb_drp_alloc {
+       __le16 zone_bm;
+       __le16 mas_bm;
+} __attribute__((packed));
+
+
+/** General MAC Header format (ECMA-368[16.2]) */
+struct uwb_mac_frame_hdr {
+       __le16 Frame_Control;
+       struct uwb_dev_addr DestAddr;
+       struct uwb_dev_addr SrcAddr;
+       __le16 Sequence_Control;
+       __le16 Access_Information;
+} __attribute__((packed));
+
+
+/**
+ * uwb_beacon_frame - a beacon frame including MAC headers
+ *
+ * [ECMA] section 16.3.
+ */
+struct uwb_beacon_frame {
+       struct uwb_mac_frame_hdr hdr;
+       struct uwb_mac_addr Device_Identifier;  /* may be a NULL EUI-48 */
+       u8 Beacon_Slot_Number;
+       u8 Device_Control;
+       u8 IEData[];
+} __attribute__((packed));
+
+
+/** Information Element codes (MBOA MAC[T54]) */
+enum uwb_ie {
+       UWB_PCA_AVAILABILITY = 2,
+       UWB_IE_DRP_AVAILABILITY = 8,
+       UWB_IE_DRP = 9,
+       UWB_BP_SWITCH_IE = 11,
+       UWB_MAC_CAPABILITIES_IE = 12,
+       UWB_PHY_CAPABILITIES_IE = 13,
+       UWB_APP_SPEC_PROBE_IE = 15,
+       UWB_IDENTIFICATION_IE = 19,
+       UWB_MASTER_KEY_ID_IE = 20,
+       UWB_IE_WLP = 250, /* WiMedia Logical Link Control Protocol WLP 0.99 */
+       UWB_APP_SPEC_IE = 255,
+};
+
+
+/**
+ * Header common to all Information Elements (IEs)
+ */
+struct uwb_ie_hdr {
+       u8 element_id;  /* enum uwb_ie */
+       u8 length;
+} __attribute__((packed));
+
+
+/** Dynamic Reservation Protocol IE (MBOA MAC[7.8.6]) */
+struct uwb_ie_drp {
+       struct uwb_ie_hdr       hdr;
+       __le16                  drp_control;
+       struct uwb_dev_addr     dev_addr;
+       struct uwb_drp_alloc    allocs[];
+} __attribute__((packed));
+
+static inline int uwb_ie_drp_type(struct uwb_ie_drp *ie)
+{
+       return (le16_to_cpu(ie->drp_control) >> 0) & 0x7;
+}
+
+static inline int uwb_ie_drp_stream_index(struct uwb_ie_drp *ie)
+{
+       return (le16_to_cpu(ie->drp_control) >> 3) & 0x7;
+}
+
+static inline int uwb_ie_drp_reason_code(struct uwb_ie_drp *ie)
+{
+       return (le16_to_cpu(ie->drp_control) >> 6) & 0x7;
+}
+
+static inline int uwb_ie_drp_status(struct uwb_ie_drp *ie)
+{
+       return (le16_to_cpu(ie->drp_control) >> 9) & 0x1;
+}
+
+static inline int uwb_ie_drp_owner(struct uwb_ie_drp *ie)
+{
+       return (le16_to_cpu(ie->drp_control) >> 10) & 0x1;
+}
+
+static inline int uwb_ie_drp_tiebreaker(struct uwb_ie_drp *ie)
+{
+       return (le16_to_cpu(ie->drp_control) >> 11) & 0x1;
+}
+
+static inline int uwb_ie_drp_unsafe(struct uwb_ie_drp *ie)
+{
+       return (le16_to_cpu(ie->drp_control) >> 12) & 0x1;
+}
+
+static inline void uwb_ie_drp_set_type(struct uwb_ie_drp *ie, enum uwb_drp_type type)
+{
+       u16 drp_control = le16_to_cpu(ie->drp_control);
+       drp_control = (drp_control & ~(0x7 << 0)) | (type << 0);
+       ie->drp_control = cpu_to_le16(drp_control);
+}
+
+static inline void uwb_ie_drp_set_stream_index(struct uwb_ie_drp *ie, int stream_index)
+{
+       u16 drp_control = le16_to_cpu(ie->drp_control);
+       drp_control = (drp_control & ~(0x7 << 3)) | (stream_index << 3);
+       ie->drp_control = cpu_to_le16(drp_control);
+}
+
+static inline void uwb_ie_drp_set_reason_code(struct uwb_ie_drp *ie,
+                                      enum uwb_drp_reason reason_code)
+{
+       u16 drp_control = le16_to_cpu(ie->drp_control);
+       drp_control = (ie->drp_control & ~(0x7 << 6)) | (reason_code << 6);
+       ie->drp_control = cpu_to_le16(drp_control);
+}
+
+static inline void uwb_ie_drp_set_status(struct uwb_ie_drp *ie, int status)
+{
+       u16 drp_control = le16_to_cpu(ie->drp_control);
+       drp_control = (drp_control & ~(0x1 << 9)) | (status << 9);
+       ie->drp_control = cpu_to_le16(drp_control);
+}
+
+static inline void uwb_ie_drp_set_owner(struct uwb_ie_drp *ie, int owner)
+{
+       u16 drp_control = le16_to_cpu(ie->drp_control);
+       drp_control = (drp_control & ~(0x1 << 10)) | (owner << 10);
+       ie->drp_control = cpu_to_le16(drp_control);
+}
+
+static inline void uwb_ie_drp_set_tiebreaker(struct uwb_ie_drp *ie, int tiebreaker)
+{
+       u16 drp_control = le16_to_cpu(ie->drp_control);
+       drp_control = (drp_control & ~(0x1 << 11)) | (tiebreaker << 11);
+       ie->drp_control = cpu_to_le16(drp_control);
+}
+
+static inline void uwb_ie_drp_set_unsafe(struct uwb_ie_drp *ie, int unsafe)
+{
+       u16 drp_control = le16_to_cpu(ie->drp_control);
+       drp_control = (drp_control & ~(0x1 << 12)) | (unsafe << 12);
+       ie->drp_control = cpu_to_le16(drp_control);
+}
+
+/** Dynamic Reservation Protocol IE (MBOA MAC[7.8.7]) */
+struct uwb_ie_drp_avail {
+       struct uwb_ie_hdr       hdr;
+       DECLARE_BITMAP(bmp, UWB_NUM_MAS);
+} __attribute__((packed));
+
+/**
+ * The Vendor ID is set to an OUI that indicates the vendor of the device.
+ * ECMA-368 [16.8.10]
+ */
+struct uwb_vendor_id {
+       u8 data[3];
+} __attribute__((packed));
+
+/**
+ * The device type ID
+ * FIXME: clarify what this means
+ * ECMA-368 [16.8.10]
+ */
+struct uwb_device_type_id {
+       u8 data[3];
+} __attribute__((packed));
+
+
+/**
+ * UWB device information types
+ * ECMA-368 [16.8.10]
+ */
+enum uwb_dev_info_type {
+       UWB_DEV_INFO_VENDOR_ID = 0,
+       UWB_DEV_INFO_VENDOR_TYPE,
+       UWB_DEV_INFO_NAME,
+};
+
+/**
+ * UWB device information found in Identification IE
+ * ECMA-368 [16.8.10]
+ */
+struct uwb_dev_info {
+       u8 type;        /* enum uwb_dev_info_type */
+       u8 length;
+       u8 data[];
+} __attribute__((packed));
+
+/**
+ * UWB Identification IE
+ * ECMA-368 [16.8.10]
+ */
+struct uwb_identification_ie {
+       struct uwb_ie_hdr hdr;
+       struct uwb_dev_info info[];
+} __attribute__((packed));
+
+/*
+ * UWB Radio Controller
+ *
+ * These definitions are common to the Radio Control layers as
+ * exported by the WUSB1.0 HWA and WHCI interfaces.
+ */
+
+/** Radio Control Command Block (WUSB1.0[Table 8-65] and WHCI 0.95) */
+struct uwb_rccb {
+       u8 bCommandType;                /* enum hwa_cet */
+       __le16 wCommand;                /* Command code */
+       u8 bCommandContext;             /* Context ID */
+} __attribute__((packed));
+
+
+/** Radio Control Event Block (WUSB[table 8-66], WHCI 0.95) */
+struct uwb_rceb {
+       u8 bEventType;                  /* enum hwa_cet */
+       __le16 wEvent;                  /* Event code */
+       u8 bEventContext;               /* Context ID */
+} __attribute__((packed));
+
+
+enum {
+       UWB_RC_CET_GENERAL = 0,         /* General Command/Event type */
+       UWB_RC_CET_EX_TYPE_1 = 1,       /* Extended Type 1 Command/Event type */
+};
+
+/* Commands to the radio controller */
+enum uwb_rc_cmd {
+       UWB_RC_CMD_CHANNEL_CHANGE = 16,
+       UWB_RC_CMD_DEV_ADDR_MGMT = 17,  /* Device Address Management */
+       UWB_RC_CMD_GET_IE = 18,         /* GET Information Elements */
+       UWB_RC_CMD_RESET = 19,
+       UWB_RC_CMD_SCAN = 20,           /* Scan management  */
+       UWB_RC_CMD_SET_BEACON_FILTER = 21,
+       UWB_RC_CMD_SET_DRP_IE = 22,     /* Dynamic Reservation Protocol IEs */
+       UWB_RC_CMD_SET_IE = 23,         /* Information Element management */
+       UWB_RC_CMD_SET_NOTIFICATION_FILTER = 24,
+       UWB_RC_CMD_SET_TX_POWER = 25,
+       UWB_RC_CMD_SLEEP = 26,
+       UWB_RC_CMD_START_BEACON = 27,
+       UWB_RC_CMD_STOP_BEACON = 28,
+       UWB_RC_CMD_BP_MERGE = 29,
+       UWB_RC_CMD_SEND_COMMAND_FRAME = 30,
+       UWB_RC_CMD_SET_ASIE_NOTIF = 31,
+};
+
+/* Notifications from the radio controller */
+enum uwb_rc_evt {
+       UWB_RC_EVT_IE_RCV = 0,
+       UWB_RC_EVT_BEACON = 1,
+       UWB_RC_EVT_BEACON_SIZE = 2,
+       UWB_RC_EVT_BPOIE_CHANGE = 3,
+       UWB_RC_EVT_BP_SLOT_CHANGE = 4,
+       UWB_RC_EVT_BP_SWITCH_IE_RCV = 5,
+       UWB_RC_EVT_DEV_ADDR_CONFLICT = 6,
+       UWB_RC_EVT_DRP_AVAIL = 7,
+       UWB_RC_EVT_DRP = 8,
+       UWB_RC_EVT_BP_SWITCH_STATUS = 9,
+       UWB_RC_EVT_CMD_FRAME_RCV = 10,
+       UWB_RC_EVT_CHANNEL_CHANGE_IE_RCV = 11,
+       /* Events (command responses) use the same code as the command */
+       UWB_RC_EVT_UNKNOWN_CMD_RCV = 65535,
+};
+
+enum uwb_rc_extended_type_1_cmd {
+       UWB_RC_SET_DAA_ENERGY_MASK = 32,
+       UWB_RC_SET_NOTIFICATION_FILTER_EX = 33,
+};
+
+enum uwb_rc_extended_type_1_evt {
+       UWB_RC_DAA_ENERGY_DETECTED = 0,
+};
+
+/* Radio Control Result Code. [WHCI] table 3-3. */
+enum {
+       UWB_RC_RES_SUCCESS = 0,
+       UWB_RC_RES_FAIL,
+       UWB_RC_RES_FAIL_HARDWARE,
+       UWB_RC_RES_FAIL_NO_SLOTS,
+       UWB_RC_RES_FAIL_BEACON_TOO_LARGE,
+       UWB_RC_RES_FAIL_INVALID_PARAMETER,
+       UWB_RC_RES_FAIL_UNSUPPORTED_PWR_LEVEL,
+       UWB_RC_RES_FAIL_INVALID_IE_DATA,
+       UWB_RC_RES_FAIL_BEACON_SIZE_EXCEEDED,
+       UWB_RC_RES_FAIL_CANCELLED,
+       UWB_RC_RES_FAIL_INVALID_STATE,
+       UWB_RC_RES_FAIL_INVALID_SIZE,
+       UWB_RC_RES_FAIL_ACK_NOT_RECEIVED,
+       UWB_RC_RES_FAIL_NO_MORE_ASIE_NOTIF,
+       UWB_RC_RES_FAIL_TIME_OUT = 255,
+};
+
+/* Confirm event. [WHCI] section 3.1.3.1 etc. */
+struct uwb_rc_evt_confirm {
+       struct uwb_rceb rceb;
+       u8 bResultCode;
+} __attribute__((packed));
+
+/* Device Address Management event. [WHCI] section 3.1.3.2. */
+struct uwb_rc_evt_dev_addr_mgmt {
+       struct uwb_rceb rceb;
+       u8 baAddr[6];
+       u8 bResultCode;
+} __attribute__((packed));
+
+
+/* Get IE Event. [WHCI] section 3.1.3.3. */
+struct uwb_rc_evt_get_ie {
+       struct uwb_rceb rceb;
+       __le16 wIELength;
+       u8 IEData[];
+} __attribute__((packed));
+
+/* Set DRP IE Event. [WHCI] section 3.1.3.7. */
+struct uwb_rc_evt_set_drp_ie {
+       struct uwb_rceb rceb;
+       __le16 wRemainingSpace;
+       u8 bResultCode;
+} __attribute__((packed));
+
+/* Set IE Event. [WHCI] section 3.1.3.8. */
+struct uwb_rc_evt_set_ie {
+       struct uwb_rceb rceb;
+       __le16 RemainingSpace;
+       u8 bResultCode;
+} __attribute__((packed));
+
+/* Scan command. [WHCI] 3.1.3.5. */
+struct uwb_rc_cmd_scan {
+       struct uwb_rccb rccb;
+       u8 bChannelNumber;
+       u8 bScanState;
+       __le16 wStartTime;
+} __attribute__((packed));
+
+/* Set DRP IE command. [WHCI] section 3.1.3.7. */
+struct uwb_rc_cmd_set_drp_ie {
+       struct uwb_rccb rccb;
+       __le16 wIELength;
+       struct uwb_ie_drp IEData[];
+} __attribute__((packed));
+
+/* Set IE command. [WHCI] section 3.1.3.8. */
+struct uwb_rc_cmd_set_ie {
+       struct uwb_rccb rccb;
+       __le16 wIELength;
+       u8 IEData[];
+} __attribute__((packed));
+
+/* Set DAA Energy Mask event. [WHCI 0.96] section 3.1.3.17. */
+struct uwb_rc_evt_set_daa_energy_mask {
+       struct uwb_rceb rceb;
+       __le16 wLength;
+       u8 result;
+} __attribute__((packed));
+
+/* Set Notification Filter Extended event. [WHCI 0.96] section 3.1.3.18. */
+struct uwb_rc_evt_set_notification_filter_ex {
+       struct uwb_rceb rceb;
+       __le16 wLength;
+       u8 result;
+} __attribute__((packed));
+
+/* IE Received notification. [WHCI] section 3.1.4.1. */
+struct uwb_rc_evt_ie_rcv {
+       struct uwb_rceb rceb;
+       struct uwb_dev_addr SrcAddr;
+       __le16 wIELength;
+       u8 IEData[];
+} __attribute__((packed));
+
+/* Type of the received beacon. [WHCI] section 3.1.4.2. */
+enum uwb_rc_beacon_type {
+       UWB_RC_BEACON_TYPE_SCAN = 0,
+       UWB_RC_BEACON_TYPE_NEIGHBOR,
+       UWB_RC_BEACON_TYPE_OL_ALIEN,
+       UWB_RC_BEACON_TYPE_NOL_ALIEN,
+};
+
+/* Beacon received notification. [WHCI] 3.1.4.2. */
+struct uwb_rc_evt_beacon {
+       struct uwb_rceb rceb;
+       u8      bChannelNumber;
+       u8      bBeaconType;
+       __le16  wBPSTOffset;
+       u8      bLQI;
+       u8      bRSSI;
+       __le16  wBeaconInfoLength;
+       u8      BeaconInfo[];
+} __attribute__((packed));
+
+
+/* Beacon Size Change notification. [WHCI] section 3.1.4.3 */
+struct uwb_rc_evt_beacon_size {
+       struct uwb_rceb rceb;
+       __le16 wNewBeaconSize;
+} __attribute__((packed));
+
+
+/* BPOIE Change notification. [WHCI] section 3.1.4.4. */
+struct uwb_rc_evt_bpoie_change {
+       struct uwb_rceb rceb;
+       __le16 wBPOIELength;
+       u8 BPOIE[];
+} __attribute__((packed));
+
+
+/* Beacon Slot Change notification. [WHCI] section 3.1.4.5. */
+struct uwb_rc_evt_bp_slot_change {
+       struct uwb_rceb rceb;
+       u8 slot_info;
+} __attribute__((packed));
+
+static inline int uwb_rc_evt_bp_slot_change_slot_num(
+       const struct uwb_rc_evt_bp_slot_change *evt)
+{
+       return evt->slot_info & 0x7f;
+}
+
+static inline int uwb_rc_evt_bp_slot_change_no_slot(
+       const struct uwb_rc_evt_bp_slot_change *evt)
+{
+       return (evt->slot_info & 0x80) >> 7;
+}
+
+/* BP Switch IE Received notification. [WHCI] section 3.1.4.6. */
+struct uwb_rc_evt_bp_switch_ie_rcv {
+       struct uwb_rceb rceb;
+       struct uwb_dev_addr wSrcAddr;
+       __le16 wIELength;
+       u8 IEData[];
+} __attribute__((packed));
+
+/* DevAddr Conflict notification. [WHCI] section 3.1.4.7. */
+struct uwb_rc_evt_dev_addr_conflict {
+       struct uwb_rceb rceb;
+} __attribute__((packed));
+
+/* DRP notification. [WHCI] section 3.1.4.9. */
+struct uwb_rc_evt_drp {
+       struct uwb_rceb           rceb;
+       struct uwb_dev_addr       src_addr;
+       u8                        reason;
+       u8                        beacon_slot_number;
+       __le16                    ie_length;
+       u8                        ie_data[];
+} __attribute__((packed));
+
+static inline enum uwb_drp_notif_reason uwb_rc_evt_drp_reason(struct uwb_rc_evt_drp *evt)
+{
+       return evt->reason & 0x0f;
+}
+
+
+/* DRP Availability Change notification. [WHCI] section 3.1.4.8. */
+struct uwb_rc_evt_drp_avail {
+       struct uwb_rceb rceb;
+       DECLARE_BITMAP(bmp, UWB_NUM_MAS);
+} __attribute__((packed));
+
+/* BP switch status notification. [WHCI] section 3.1.4.10. */
+struct uwb_rc_evt_bp_switch_status {
+       struct uwb_rceb rceb;
+       u8 status;
+       u8 slot_offset;
+       __le16 bpst_offset;
+       u8 move_countdown;
+} __attribute__((packed));
+
+/* Command Frame Received notification. [WHCI] section 3.1.4.11. */
+struct uwb_rc_evt_cmd_frame_rcv {
+       struct uwb_rceb rceb;
+       __le16 receive_time;
+       struct uwb_dev_addr wSrcAddr;
+       struct uwb_dev_addr wDstAddr;
+       __le16 control;
+       __le16 reserved;
+       __le16 dataLength;
+       u8 data[];
+} __attribute__((packed));
+
+/* Channel Change IE Received notification. [WHCI] section 3.1.4.12. */
+struct uwb_rc_evt_channel_change_ie_rcv {
+       struct uwb_rceb rceb;
+       struct uwb_dev_addr wSrcAddr;
+       __le16 wIELength;
+       u8 IEData[];
+} __attribute__((packed));
+
+/* DAA Energy Detected notification. [WHCI 0.96] section 3.1.4.14. */
+struct uwb_rc_evt_daa_energy_detected {
+       struct uwb_rceb rceb;
+       __le16 wLength;
+       u8 bandID;
+       u8 reserved;
+       u8 toneBmp[16];
+} __attribute__((packed));
+
+
+/**
+ * Radio Control Interface Class Descriptor
+ *
+ *  WUSB 1.0 [8.6.1.2]
+ */
+struct uwb_rc_control_intf_class_desc {
+       u8 bLength;
+       u8 bDescriptorType;
+       __le16 bcdRCIVersion;
+} __attribute__((packed));
+
+#endif /* #ifndef __LINUX__UWB_SPEC_H__ */
diff --git a/include/linux/uwb/umc.h b/include/linux/uwb/umc.h
new file mode 100644 (file)
index 0000000..36a39e3
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * UWB Multi-interface Controller support.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
+ *
+ * This file is released under the GPLv2
+ *
+ * UMC (UWB Multi-interface Controller) capabilities (e.g., radio
+ * controller, host controller) are presented as devices on the "umc"
+ * bus.
+ *
+ * The radio controller is not strictly a UMC capability but it's
+ * useful to present it as such.
+ *
+ * References:
+ *
+ *   [WHCI] Wireless Host Controller Interface Specification for
+ *          Certified Wireless Universal Serial Bus, revision 0.95.
+ *
+ * How this works is kind of convoluted but simple. The whci.ko driver
+ * loads when WHCI devices are detected. These WHCI devices expose
+ * many devices in the same PCI function (they couldn't have reused
+ * functions, no), so for each PCI function that exposes these many
+ * devices, whci ceates a umc_dev [whci_probe() -> whci_add_cap()]
+ * with umc_device_create() and adds it to the bus with
+ * umc_device_register().
+ *
+ * umc_device_register() calls device_register() which will push the
+ * bus management code to load your UMC driver's somehting_probe()
+ * that you have registered for that capability code.
+ *
+ * Now when the WHCI device is removed, whci_remove() will go over
+ * each umc_dev assigned to each of the PCI function's capabilities
+ * and through whci_del_cap() call umc_device_unregister() each
+ * created umc_dev. Of course, if you are bound to the device, your
+ * driver's something_remove() will be called.
+ */
+
+#ifndef _LINUX_UWB_UMC_H_
+#define _LINUX_UWB_UMC_H_
+
+#include <linux/device.h>
+#include <linux/pci.h>
+
+/*
+ * UMC capability IDs.
+ *
+ * 0x00 is reserved so use it for the radio controller device.
+ *
+ * [WHCI] table 2-8
+ */
+#define UMC_CAP_ID_WHCI_RC      0x00 /* radio controller */
+#define UMC_CAP_ID_WHCI_WUSB_HC 0x01 /* WUSB host controller */
+
+/**
+ * struct umc_dev - UMC capability device
+ *
+ * @version:  version of the specification this capability conforms to.
+ * @cap_id:   capability ID.
+ * @bar:      PCI Bar (64 bit) where the resource lies
+ * @resource: register space resource.
+ * @irq:      interrupt line.
+ */
+struct umc_dev {
+       u16             version;
+       u8              cap_id;
+       u8              bar;
+       struct resource resource;
+       unsigned        irq;
+       struct device   dev;
+};
+
+#define to_umc_dev(d) container_of(d, struct umc_dev, dev)
+
+/**
+ * struct umc_driver - UMC capability driver
+ * @cap_id: supported capability ID.
+ * @match: driver specific capability matching function.
+ * @match_data: driver specific data for match() (e.g., a
+ * table of pci_device_id's if umc_match_pci_id() is used).
+ */
+struct umc_driver {
+       char *name;
+       u8 cap_id;
+       int (*match)(struct umc_driver *, struct umc_dev *);
+       const void *match_data;
+
+       int  (*probe)(struct umc_dev *);
+       void (*remove)(struct umc_dev *);
+       int  (*suspend)(struct umc_dev *, pm_message_t state);
+       int  (*resume)(struct umc_dev *);
+
+       struct device_driver driver;
+};
+
+#define to_umc_driver(d) container_of(d, struct umc_driver, driver)
+
+extern struct bus_type umc_bus_type;
+
+struct umc_dev *umc_device_create(struct device *parent, int n);
+int __must_check umc_device_register(struct umc_dev *umc);
+void umc_device_unregister(struct umc_dev *umc);
+
+int __must_check __umc_driver_register(struct umc_driver *umc_drv,
+                                      struct module *mod,
+                                      const char *mod_name);
+
+/**
+ * umc_driver_register - register a UMC capabiltity driver.
+ * @umc_drv:  pointer to the driver.
+ */
+static inline int __must_check umc_driver_register(struct umc_driver *umc_drv)
+{
+       return __umc_driver_register(umc_drv, THIS_MODULE, KBUILD_MODNAME);
+}
+void umc_driver_unregister(struct umc_driver *umc_drv);
+
+/*
+ * Utility function you can use to match (umc_driver->match) against a
+ * null-terminated array of 'struct pci_device_id' in
+ * umc_driver->match_data.
+ */
+int umc_match_pci_id(struct umc_driver *umc_drv, struct umc_dev *umc);
+
+/**
+ * umc_parent_pci_dev - return the UMC's parent PCI device or NULL if none
+ * @umc_dev: UMC device whose parent PCI device we are looking for
+ *
+ * DIRTY!!! DON'T RELY ON THIS
+ *
+ * FIXME: This is as dirty as it gets, but we need some way to check
+ * the correct type of umc_dev->parent (so that for example, we can
+ * cast to pci_dev). Casting to pci_dev is necesary because at some
+ * point we need to request resources from the device. Mapping is
+ * easily over come (ioremap and stuff are bus agnostic), but hooking
+ * up to some error handlers (such as pci error handlers) might need
+ * this.
+ *
+ * THIS might (probably will) be removed in the future, so don't count
+ * on it.
+ */
+static inline struct pci_dev *umc_parent_pci_dev(struct umc_dev *umc_dev)
+{
+       struct pci_dev *pci_dev = NULL;
+       if (umc_dev->dev.parent->bus == &pci_bus_type)
+               pci_dev = to_pci_dev(umc_dev->dev.parent);
+       return pci_dev;
+}
+
+/**
+ * umc_dev_get() - reference a UMC device.
+ * @umc_dev: Pointer to UMC device.
+ *
+ * NOTE: we are assuming in this whole scheme that the parent device
+ *       is referenced at _probe() time and unreferenced at _remove()
+ *       time by the parent's subsystem.
+ */
+static inline struct umc_dev *umc_dev_get(struct umc_dev *umc_dev)
+{
+       get_device(&umc_dev->dev);
+       return umc_dev;
+}
+
+/**
+ * umc_dev_put() - unreference a UMC device.
+ * @umc_dev: Pointer to UMC device.
+ */
+static inline void umc_dev_put(struct umc_dev *umc_dev)
+{
+       put_device(&umc_dev->dev);
+}
+
+/**
+ * umc_set_drvdata - set UMC device's driver data.
+ * @umc_dev: Pointer to UMC device.
+ * @data:    Data to set.
+ */
+static inline void umc_set_drvdata(struct umc_dev *umc_dev, void *data)
+{
+       dev_set_drvdata(&umc_dev->dev, data);
+}
+
+/**
+ * umc_get_drvdata - recover UMC device's driver data.
+ * @umc_dev: Pointer to UMC device.
+ */
+static inline void *umc_get_drvdata(struct umc_dev *umc_dev)
+{
+       return dev_get_drvdata(&umc_dev->dev);
+}
+
+int umc_controller_reset(struct umc_dev *umc);
+
+#endif /* #ifndef _LINUX_UWB_UMC_H_ */
diff --git a/include/linux/uwb/whci.h b/include/linux/uwb/whci.h
new file mode 100644 (file)
index 0000000..915ec23
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Wireless Host Controller Interface for Ultra-Wide-Band and Wireless USB
+ *
+ * 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.
+ *
+ *
+ *
+ * References:
+ *   [WHCI] Wireless Host Controller Interface Specification for
+ *          Certified Wireless Universal Serial Bus, revision 0.95.
+ */
+#ifndef _LINUX_UWB_WHCI_H_
+#define _LINUX_UWB_WHCI_H_
+
+#include <linux/pci.h>
+
+/*
+ * UWB interface capability registers (offsets from UWBBASE)
+ *
+ * [WHCI] section 2.2
+ */
+#define UWBCAPINFO     0x00 /* == UWBCAPDATA(0) */
+#  define UWBCAPINFO_TO_N_CAPS(c)      (((c) >> 0)  & 0xFull)
+#define UWBCAPDATA(n)  (8*(n))
+#  define UWBCAPDATA_TO_VERSION(c)     (((c) >> 32) & 0xFFFFull)
+#  define UWBCAPDATA_TO_OFFSET(c)      (((c) >> 18) & 0x3FFFull)
+#  define UWBCAPDATA_TO_BAR(c)         (((c) >> 16) & 0x3ull)
+#  define UWBCAPDATA_TO_SIZE(c)                ((((c) >> 8) & 0xFFull) * sizeof(u32))
+#  define UWBCAPDATA_TO_CAP_ID(c)      (((c) >> 0)  & 0xFFull)
+
+/* Size of the WHCI capability data (including the RC capability) for
+   a device with n capabilities. */
+#define UWBCAPDATA_SIZE(n) (8 + 8*(n))
+
+
+/*
+ * URC registers (offsets from URCBASE)
+ *
+ * [WHCI] section 2.3
+ */
+#define URCCMD         0x00
+#  define URCCMD_RESET         (1 << 31)  /* UMC Hardware reset */
+#  define URCCMD_RS            (1 << 30)  /* Run/Stop */
+#  define URCCMD_EARV          (1 << 29)  /* Event Address Register Valid */
+#  define URCCMD_ACTIVE                (1 << 15)  /* Command is active */
+#  define URCCMD_IWR           (1 << 14)  /* Interrupt When Ready */
+#  define URCCMD_SIZE_MASK     0x00000fff /* Command size mask */
+#define URCSTS         0x04
+#  define URCSTS_EPS           (1 << 17)  /* Event Processing Status */
+#  define URCSTS_HALTED                (1 << 16)  /* RC halted */
+#  define URCSTS_HSE           (1 << 10)  /* Host System Error...fried */
+#  define URCSTS_ER            (1 <<  9)  /* Event Ready */
+#  define URCSTS_RCI           (1 <<  8)  /* Ready for Command Interrupt */
+#  define URCSTS_INT_MASK      0x00000700 /* URC interrupt sources */
+#  define URCSTS_ISI           0x000000ff /* Interrupt Source Identification */
+#define URCINTR                0x08
+#  define URCINTR_EN_ALL       0x000007ff /* Enable all interrupt sources */
+#define URCCMDADDR     0x10
+#define URCEVTADDR     0x18
+#  define URCEVTADDR_OFFSET_MASK 0xfff    /* Event pointer offset mask */
+
+
+/** Write 32 bit @value to little endian register at @addr */
+static inline
+void le_writel(u32 value, void __iomem *addr)
+{
+       iowrite32(value, addr);
+}
+
+
+/** Read from 32 bit little endian register at @addr */
+static inline
+u32 le_readl(void __iomem *addr)
+{
+       return ioread32(addr);
+}
+
+
+/** Write 64 bit @value to little endian register at @addr */
+static inline
+void le_writeq(u64 value, void __iomem *addr)
+{
+       iowrite32(value, addr);
+       iowrite32(value >> 32, addr + 4);
+}
+
+
+/** Read from 64 bit little endian register at @addr */
+static inline
+u64 le_readq(void __iomem *addr)
+{
+       u64 value;
+       value  = ioread32(addr);
+       value |= (u64)ioread32(addr + 4) << 32;
+       return value;
+}
+
+extern int whci_wait_for(struct device *dev, u32 __iomem *reg,
+                        u32 mask, u32 result,
+                        unsigned long max_ms,  const char *tag);
+
+#endif /* #ifndef _LINUX_UWB_WHCI_H_ */
diff --git a/include/linux/wlp.h b/include/linux/wlp.h
new file mode 100644 (file)
index 0000000..033545e
--- /dev/null
@@ -0,0 +1,735 @@
+/*
+ * 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
+ *
+ * - Does not (yet) include support for WLP control frames
+ *   WLP Draft 0.99 [6.5].
+ *
+ *   A visual representation of the data structures.
+ *
+ *                              wssidB      wssidB
+ *                               ^           ^
+ *                               |           |
+ *                              wssidA      wssidA
+ *   wlp interface {             ^           ^
+ *       ...                     |           |
+ *       ...               ...  wssid      wssid ...
+ *       wlp --- ...             |           |
+ *   };          neighbors --> neighbA --> neighbB
+ *               ...
+ *               wss
+ *               ...
+ *               eda cache  --> neighborA --> neighborB --> neighborC ...
+ */
+
+#ifndef __LINUX__WLP_H_
+#define __LINUX__WLP_H_
+
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/list.h>
+#include <linux/uwb.h>
+
+/**
+ * WLP Protocol ID
+ * WLP Draft 0.99 [6.2]
+ *
+ * The MUX header for all WLP frames
+ */
+#define WLP_PROTOCOL_ID 0x0100
+
+/**
+ * WLP Version
+ * WLP version placed in the association frames (WLP 0.99 [6.6])
+ */
+#define WLP_VERSION 0x10
+
+/**
+ * Bytes needed to print UUID as string
+ */
+#define WLP_WSS_UUID_STRSIZE 48
+
+/**
+ * Bytes needed to print nonce as string
+ */
+#define WLP_WSS_NONCE_STRSIZE 48
+
+
+/**
+ * Size used for WLP name size
+ *
+ * The WSS name is set to 65 bytes, 1 byte larger than the maximum
+ * allowed by the WLP spec. This is to have a null terminated string
+ * for display to the user. A maximum of 64 bytes will still be used
+ * when placing the WSS name field in association frames.
+ */
+#define WLP_WSS_NAME_SIZE 65
+
+/**
+ * Number of bytes added by WLP to data frame
+ *
+ * A data frame transmitted from a host will be placed in a Standard or
+ * Abbreviated WLP frame. These have an extra 4 bytes of header (struct
+ * wlp_frame_std_abbrv_hdr).
+ * When the stack sends this data frame for transmission it needs to ensure
+ * there is enough headroom for this header.
+ */
+#define WLP_DATA_HLEN 4
+
+/**
+ * State of device regarding WLP Service Set
+ *
+ * WLP_WSS_STATE_NONE: the host does not participate in any WSS
+ * WLP_WSS_STATE_PART_ENROLLED: used as part of the enrollment sequence
+ *                            ("Partial Enroll"). This state is used to
+ *                            indicate the first part of enrollment that is
+ *                            unsecure. If the WSS is unsecure then the
+ *                            state will promptly go to WLP_WSS_STATE_ENROLLED,
+ *                            if the WSS is not secure then the enrollment
+ *                            procedure is a few more steps before we are
+ *                            enrolled.
+ * WLP_WSS_STATE_ENROLLED: the host is enrolled in a WSS
+ * WLP_WSS_STATE_ACTIVE: WSS is activated
+ * WLP_WSS_STATE_CONNECTED: host is connected to neighbor in WSS
+ *
+ */
+enum wlp_wss_state {
+       WLP_WSS_STATE_NONE = 0,
+       WLP_WSS_STATE_PART_ENROLLED,
+       WLP_WSS_STATE_ENROLLED,
+       WLP_WSS_STATE_ACTIVE,
+       WLP_WSS_STATE_CONNECTED,
+};
+
+/**
+ * WSS Secure status
+ * WLP 0.99 Table 6
+ *
+ * Set to one if the WSS is secure, zero if it is not secure
+ */
+enum wlp_wss_sec_status {
+       WLP_WSS_UNSECURE = 0,
+       WLP_WSS_SECURE,
+};
+
+/**
+ * WLP frame type
+ * WLP Draft 0.99 [6.2 Table 1]
+ */
+enum wlp_frame_type {
+       WLP_FRAME_STANDARD = 0,
+       WLP_FRAME_ABBREVIATED,
+       WLP_FRAME_CONTROL,
+       WLP_FRAME_ASSOCIATION,
+};
+
+/**
+ * WLP Association Message Type
+ * WLP Draft 0.99 [6.6.1.2 Table 8]
+ */
+enum wlp_assoc_type {
+       WLP_ASSOC_D1 = 2,
+       WLP_ASSOC_D2 = 3,
+       WLP_ASSOC_M1 = 4,
+       WLP_ASSOC_M2 = 5,
+       WLP_ASSOC_M3 = 7,
+       WLP_ASSOC_M4 = 8,
+       WLP_ASSOC_M5 = 9,
+       WLP_ASSOC_M6 = 10,
+       WLP_ASSOC_M7 = 11,
+       WLP_ASSOC_M8 = 12,
+       WLP_ASSOC_F0 = 14,
+       WLP_ASSOC_E1 = 32,
+       WLP_ASSOC_E2 = 33,
+       WLP_ASSOC_C1 = 34,
+       WLP_ASSOC_C2 = 35,
+       WLP_ASSOC_C3 = 36,
+       WLP_ASSOC_C4 = 37,
+};
+
+/**
+ * WLP Attribute Type
+ * WLP Draft 0.99 [6.6.1 Table 6]
+ */
+enum wlp_attr_type {
+       WLP_ATTR_AUTH           = 0x1005, /* Authenticator */
+       WLP_ATTR_DEV_NAME       = 0x1011, /* Device Name */
+       WLP_ATTR_DEV_PWD_ID     = 0x1012, /* Device Password ID */
+       WLP_ATTR_E_HASH1        = 0x1014, /* E-Hash1 */
+       WLP_ATTR_E_HASH2        = 0x1015, /* E-Hash2 */
+       WLP_ATTR_E_SNONCE1      = 0x1016, /* E-SNonce1 */
+       WLP_ATTR_E_SNONCE2      = 0x1017, /* E-SNonce2 */
+       WLP_ATTR_ENCR_SET       = 0x1018, /* Encrypted Settings */
+       WLP_ATTR_ENRL_NONCE     = 0x101A, /* Enrollee Nonce */
+       WLP_ATTR_KEYWRAP_AUTH   = 0x101E, /* Key Wrap Authenticator */
+       WLP_ATTR_MANUF          = 0x1021, /* Manufacturer */
+       WLP_ATTR_MSG_TYPE       = 0x1022, /* Message Type */
+       WLP_ATTR_MODEL_NAME     = 0x1023, /* Model Name */
+       WLP_ATTR_MODEL_NR       = 0x1024, /* Model Number */
+       WLP_ATTR_PUB_KEY        = 0x1032, /* Public Key */
+       WLP_ATTR_REG_NONCE      = 0x1039, /* Registrar Nonce */
+       WLP_ATTR_R_HASH1        = 0x103D, /* R-Hash1 */
+       WLP_ATTR_R_HASH2        = 0x103E, /* R-Hash2 */
+       WLP_ATTR_R_SNONCE1      = 0x103F, /* R-SNonce1 */
+       WLP_ATTR_R_SNONCE2      = 0x1040, /* R-SNonce2 */
+       WLP_ATTR_SERIAL         = 0x1042, /* Serial number */
+       WLP_ATTR_UUID_E         = 0x1047, /* UUID-E */
+       WLP_ATTR_UUID_R         = 0x1048, /* UUID-R */
+       WLP_ATTR_PRI_DEV_TYPE   = 0x1054, /* Primary Device Type */
+       WLP_ATTR_SEC_DEV_TYPE   = 0x1055, /* Secondary Device Type */
+       WLP_ATTR_PORT_DEV       = 0x1056, /* Portable Device */
+       WLP_ATTR_APP_EXT        = 0x1058, /* Application Extension */
+       WLP_ATTR_WLP_VER        = 0x2000, /* WLP Version */
+       WLP_ATTR_WSSID          = 0x2001, /* WSSID */
+       WLP_ATTR_WSS_NAME       = 0x2002, /* WSS Name */
+       WLP_ATTR_WSS_SEC_STAT   = 0x2003, /* WSS Secure Status */
+       WLP_ATTR_WSS_BCAST      = 0x2004, /* WSS Broadcast Address */
+       WLP_ATTR_WSS_M_KEY      = 0x2005, /* WSS Master Key */
+       WLP_ATTR_ACC_ENRL       = 0x2006, /* Accepting Enrollment */
+       WLP_ATTR_WSS_INFO       = 0x2007, /* WSS Information */
+       WLP_ATTR_WSS_SEL_MTHD   = 0x2008, /* WSS Selection Method */
+       WLP_ATTR_ASSC_MTHD_LIST = 0x2009, /* Association Methods List */
+       WLP_ATTR_SEL_ASSC_MTHD  = 0x200A, /* Selected Association Method */
+       WLP_ATTR_ENRL_HASH_COMM = 0x200B, /* Enrollee Hash Commitment */
+       WLP_ATTR_WSS_TAG        = 0x200C, /* WSS Tag */
+       WLP_ATTR_WSS_VIRT       = 0x200D, /* WSS Virtual EUI-48 */
+       WLP_ATTR_WLP_ASSC_ERR   = 0x200E, /* WLP Association Error */
+       WLP_ATTR_VNDR_EXT       = 0x200F, /* Vendor Extension */
+};
+
+/**
+ * WLP Category ID of primary/secondary device
+ * WLP Draft 0.99 [6.6.1.8 Table 12]
+ */
+enum wlp_dev_category_id {
+       WLP_DEV_CAT_COMPUTER = 1,
+       WLP_DEV_CAT_INPUT,
+       WLP_DEV_CAT_PRINT_SCAN_FAX_COPIER,
+       WLP_DEV_CAT_CAMERA,
+       WLP_DEV_CAT_STORAGE,
+       WLP_DEV_CAT_INFRASTRUCTURE,
+       WLP_DEV_CAT_DISPLAY,
+       WLP_DEV_CAT_MULTIM,
+       WLP_DEV_CAT_GAMING,
+       WLP_DEV_CAT_TELEPHONE,
+       WLP_DEV_CAT_OTHER = 65535,
+};
+
+/**
+ * WLP WSS selection method
+ * WLP Draft 0.99 [6.6.1.6 Table 10]
+ */
+enum wlp_wss_sel_mthd {
+       WLP_WSS_ENRL_SELECT = 1,        /* Enrollee selects */
+       WLP_WSS_REG_SELECT,             /* Registrar selects */
+};
+
+/**
+ * WLP association error values
+ * WLP Draft 0.99 [6.6.1.5 Table 9]
+ */
+enum wlp_assc_error {
+       WLP_ASSOC_ERROR_NONE,
+       WLP_ASSOC_ERROR_AUTH,           /* Authenticator Failure */
+       WLP_ASSOC_ERROR_ROGUE,          /* Rogue activity suspected */
+       WLP_ASSOC_ERROR_BUSY,           /* Device busy */
+       WLP_ASSOC_ERROR_LOCK,           /* Setup Locked */
+       WLP_ASSOC_ERROR_NOT_READY,      /* Registrar not ready */
+       WLP_ASSOC_ERROR_INV,            /* Invalid WSS selection */
+       WLP_ASSOC_ERROR_MSG_TIME,       /* Message timeout */
+       WLP_ASSOC_ERROR_ENR_TIME,       /* Enrollment session timeout */
+       WLP_ASSOC_ERROR_PW,             /* Device password invalid */
+       WLP_ASSOC_ERROR_VER,            /* Unsupported version */
+       WLP_ASSOC_ERROR_INT,            /* Internal error */
+       WLP_ASSOC_ERROR_UNDEF,          /* Undefined error */
+       WLP_ASSOC_ERROR_NUM,            /* Numeric comparison failure */
+       WLP_ASSOC_ERROR_WAIT,           /* Waiting for user input */
+};
+
+/**
+ * WLP Parameters
+ * WLP 0.99 [7.7]
+ */
+enum wlp_parameters {
+       WLP_PER_MSG_TIMEOUT = 15,       /* Seconds to wait for response to
+                                          association message. */
+};
+
+/**
+ * WLP IE
+ *
+ * The WLP IE should be included in beacons by all devices.
+ *
+ * The driver can set only a few of the fields in this information element,
+ * most fields are managed by the device self. When the driver needs to set
+ * a field it will only provide values for the fields of interest, the rest
+ * will be filled with zeroes. The fields of interest are:
+ *
+ * Element ID
+ * Length
+ * Capabilities (only to include WSSID Hash list length)
+ * WSSID Hash List fields
+ *
+ * WLP 0.99 [6.7]
+ *
+ * Only the fields that will be used are detailed in this structure, rest
+ * are not detailed or marked as "notused".
+ */
+struct wlp_ie {
+       struct uwb_ie_hdr hdr;
+       __le16 capabilities;
+       __le16 cycle_param;
+       __le16 acw_anchor_addr;
+       u8 wssid_hash_list[];
+} __attribute__((packed));
+
+static inline int wlp_ie_hash_length(struct wlp_ie *ie)
+{
+       return (le16_to_cpu(ie->capabilities) >> 12) & 0xf;
+}
+
+static inline void wlp_ie_set_hash_length(struct wlp_ie *ie, int hash_length)
+{
+       u16 caps = le16_to_cpu(ie->capabilities);
+       caps = (caps & ~(0xf << 12)) | (hash_length << 12);
+       ie->capabilities = cpu_to_le16(caps);
+}
+
+/**
+ * WLP nonce
+ * WLP Draft 0.99 [6.6.1 Table 6]
+ *
+ * A 128-bit random number often used (E-SNonce1, E-SNonce2, Enrollee
+ * Nonce, Registrar Nonce, R-SNonce1, R-SNonce2). It is passed to HW so
+ * it is packed.
+ */
+struct wlp_nonce {
+       u8 data[16];
+} __attribute__((packed));
+
+/**
+ * WLP UUID
+ * WLP Draft 0.99 [6.6.1 Table 6]
+ *
+ * Universally Unique Identifier (UUID) encoded as an octet string in the
+ * order the octets are shown in string representation in RFC4122. A UUID
+ * is often used (UUID-E, UUID-R, WSSID). It is passed to HW so it is packed.
+ */
+struct wlp_uuid {
+       u8 data[16];
+} __attribute__((packed));
+
+
+/**
+ * Primary and secondary device type attributes
+ * WLP Draft 0.99 [6.6.1.8]
+ */
+struct wlp_dev_type {
+       enum wlp_dev_category_id category:16;
+       u8 OUI[3];
+       u8 OUIsubdiv;
+       __le16 subID;
+} __attribute__((packed));
+
+/**
+ * WLP frame header
+ * WLP Draft 0.99 [6.2]
+ */
+struct wlp_frame_hdr {
+       __le16 mux_hdr;                 /* WLP_PROTOCOL_ID */
+       enum wlp_frame_type type:8;
+} __attribute__((packed));
+
+/**
+ * WLP attribute field header
+ * WLP Draft 0.99 [6.6.1]
+ *
+ * Header of each attribute found in an association frame
+ */
+struct wlp_attr_hdr {
+       __le16 type;
+       __le16 length;
+} __attribute__((packed));
+
+/**
+ * Device information commonly used together
+ *
+ * Each of these device information elements has a specified range in which it
+ * should fit (WLP 0.99 [Table 6]). This range provided in the spec does not
+ * include the termination null '\0' character (when used in the
+ * association protocol the attribute fields are accompanied
+ * with a "length" field so the full range from the spec can be used for
+ * the value). We thus allocate an extra byte to be able to store a string
+ * of max length with a terminating '\0'.
+ */
+struct wlp_device_info {
+       char name[33];
+       char model_name[33];
+       char manufacturer[65];
+       char model_nr[33];
+       char serial[33];
+       struct wlp_dev_type prim_dev_type;
+};
+
+/**
+ * Macros for the WLP attributes
+ *
+ * There are quite a few attributes (total is 43). The attribute layout can be
+ * in one of three categories: one value, an array, an enum forced to 8 bits.
+ * These macros help with their definitions.
+ */
+#define wlp_attr(type, name)                                           \
+struct wlp_attr_##name {                                               \
+       struct wlp_attr_hdr hdr;                                        \
+       type name;                                                      \
+} __attribute__((packed));
+
+#define wlp_attr_array(type, name)                                     \
+struct wlp_attr_##name {                                               \
+       struct wlp_attr_hdr hdr;                                        \
+       type name[];                                                    \
+} __attribute__((packed));
+
+/**
+ * WLP association attribute fields
+ * WLP Draft 0.99 [6.6.1 Table 6]
+ *
+ * Attributes appear in same order as the Table in the spec
+ * FIXME Does not define all attributes yet
+ */
+
+/* Device name: Friendly name of sending device */
+wlp_attr_array(u8, dev_name)
+
+/* Enrollee Nonce: Random number generated by enrollee for an enrollment
+ * session */
+wlp_attr(struct wlp_nonce, enonce)
+
+/* Manufacturer name: Name of manufacturer of the sending device */
+wlp_attr_array(u8, manufacturer)
+
+/* WLP Message Type */
+wlp_attr(u8, msg_type)
+
+/* WLP Model name: Model name of sending device */
+wlp_attr_array(u8, model_name)
+
+/* WLP Model number: Model number of sending device */
+wlp_attr_array(u8, model_nr)
+
+/* Registrar Nonce: Random number generated by registrar for an enrollment
+ * session */
+wlp_attr(struct wlp_nonce, rnonce)
+
+/* Serial number of device */
+wlp_attr_array(u8, serial)
+
+/* UUID of enrollee */
+wlp_attr(struct wlp_uuid, uuid_e)
+
+/* UUID of registrar */
+wlp_attr(struct wlp_uuid, uuid_r)
+
+/* WLP Primary device type */
+wlp_attr(struct wlp_dev_type, prim_dev_type)
+
+/* WLP Secondary device type */
+wlp_attr(struct wlp_dev_type, sec_dev_type)
+
+/* WLP protocol version */
+wlp_attr(u8, version)
+
+/* WLP service set identifier */
+wlp_attr(struct wlp_uuid, wssid)
+
+/* WLP WSS name */
+wlp_attr_array(u8, wss_name)
+
+/* WLP WSS Secure Status */
+wlp_attr(u8, wss_sec_status)
+
+/* WSS Broadcast Address */
+wlp_attr(struct uwb_mac_addr, wss_bcast)
+
+/* WLP Accepting Enrollment */
+wlp_attr(u8, accept_enrl)
+
+/**
+ * WSS information attributes
+ * WLP Draft 0.99 [6.6.3 Table 15]
+ */
+struct wlp_wss_info {
+       struct wlp_attr_wssid wssid;
+       struct wlp_attr_wss_name name;
+       struct wlp_attr_accept_enrl accept;
+       struct wlp_attr_wss_sec_status sec_stat;
+       struct wlp_attr_wss_bcast bcast;
+} __attribute__((packed));
+
+/* WLP WSS Information */
+wlp_attr_array(struct wlp_wss_info, wss_info)
+
+/* WLP WSS Selection method */
+wlp_attr(u8, wss_sel_mthd)
+
+/* WLP WSS tag */
+wlp_attr(u8, wss_tag)
+
+/* WSS Virtual Address */
+wlp_attr(struct uwb_mac_addr, wss_virt)
+
+/* WLP association error */
+wlp_attr(u8, wlp_assc_err)
+
+/**
+ * WLP standard and abbreviated frames
+ *
+ * WLP Draft 0.99 [6.3] and [6.4]
+ *
+ * The difference between the WLP standard frame and the WLP
+ * abbreviated frame is that the standard frame includes the src
+ * and dest addresses from the Ethernet header, the abbreviated frame does
+ * not.
+ * The src/dest (as well as the type/length and client data) are already
+ * defined as part of the Ethernet header, we do not do this here.
+ * From this perspective the standard and abbreviated frames appear the
+ * same - they will be treated differently though.
+ *
+ * The size of this header is also captured in WLP_DATA_HLEN to enable
+ * interfaces to prepare their headroom.
+ */
+struct wlp_frame_std_abbrv_hdr {
+       struct wlp_frame_hdr hdr;
+       u8 tag;
+} __attribute__((packed));
+
+/**
+ * WLP association frames
+ *
+ * WLP Draft 0.99 [6.6]
+ */
+struct wlp_frame_assoc {
+       struct wlp_frame_hdr hdr;
+       enum wlp_assoc_type type:8;
+       struct wlp_attr_version version;
+       struct wlp_attr_msg_type msg_type;
+       u8 attr[];
+} __attribute__((packed));
+
+/* Ethernet to dev address mapping */
+struct wlp_eda {
+       spinlock_t lock;
+       struct list_head cache; /* Eth<->Dev Addr cache */
+};
+
+/**
+ * WSS information temporary storage
+ *
+ * This information is only stored temporarily during discovery. It should
+ * not be stored unless the device is enrolled in the advertised WSS. This
+ * is done mainly because we follow the letter of the spec in this regard.
+ * See WLP 0.99 [7.2.3].
+ * When the device does become enrolled in a WSS the WSS information will
+ * be stored as part of the more comprehensive struct wlp_wss.
+ */
+struct wlp_wss_tmp_info {
+       char name[WLP_WSS_NAME_SIZE];
+       u8 accept_enroll;
+       u8 sec_status;
+       struct uwb_mac_addr bcast;
+};
+
+struct wlp_wssid_e {
+       struct list_head node;
+       struct wlp_uuid wssid;
+       struct wlp_wss_tmp_info *info;
+};
+
+/**
+ * A cache entry of WLP neighborhood
+ *
+ * @node: head of list is wlp->neighbors
+ * @wssid: list of wssids of this neighbor, element is wlp_wssid_e
+ * @info:  temporary storage for information learned during discovery. This
+ *         storage is used together with the wssid_e temporary storage
+ *         during discovery.
+ */
+struct wlp_neighbor_e {
+       struct list_head node;
+       struct wlp_uuid uuid;
+       struct uwb_dev *uwb_dev;
+       struct list_head wssid; /* Elements are wlp_wssid_e */
+       struct wlp_device_info *info;
+};
+
+struct wlp;
+/**
+ * Information for an association session in progress.
+ *
+ * @exp_message: The type of the expected message. Both this message and a
+ *               F0 message (which can be sent in response to any
+ *               association frame) will be accepted as a valid message for
+ *               this session.
+ * @cb:          The function that will be called upon receipt of this
+ *               message.
+ * @cb_priv:     Private data of callback
+ * @data:        Data used in association process (always a sk_buff?)
+ * @neighbor:    Address of neighbor with which association session is in
+ *               progress.
+ */
+struct wlp_session {
+       enum wlp_assoc_type exp_message;
+       void (*cb)(struct wlp *);
+       void *cb_priv;
+       void *data;
+       struct uwb_dev_addr neighbor_addr;
+};
+
+/**
+ * WLP Service Set
+ *
+ * @mutex: used to protect entire WSS structure.
+ *
+ * @name: The WSS name is set to 65 bytes, 1 byte larger than the maximum
+ *        allowed by the WLP spec. This is to have a null terminated string
+ *        for display to the user. A maximum of 64 bytes will still be used
+ *        when placing the WSS name field in association frames.
+ *
+ * @accept_enroll: Accepting enrollment: Set to one if registrar is
+ *                 accepting enrollment in WSS, or zero otherwise.
+ *
+ * Global and local information for each WSS in which we are enrolled.
+ * WLP 0.99 Section 7.2.1 and Section 7.2.2
+ */
+struct wlp_wss {
+       struct mutex mutex;
+       struct kobject kobj;
+       /* Global properties. */
+       struct wlp_uuid wssid;
+       u8 hash;
+       char name[WLP_WSS_NAME_SIZE];
+       struct uwb_mac_addr bcast;
+       u8 secure_status:1;
+       u8 master_key[16];
+       /* Local properties. */
+       u8 tag;
+       struct uwb_mac_addr virtual_addr;
+       /* Extra */
+       u8 accept_enroll:1;
+       enum wlp_wss_state state;
+};
+
+/**
+ * WLP main structure
+ * @mutex: protect changes to WLP structure. We only allow changes to the
+ *         uuid, so currently this mutex only protects this field.
+ */
+struct wlp {
+       struct mutex mutex;
+       struct uwb_rc *rc;              /* UWB radio controller */
+       struct uwb_pal pal;
+       struct wlp_eda eda;
+       struct wlp_uuid uuid;
+       struct wlp_session *session;
+       struct wlp_wss wss;
+       struct mutex nbmutex; /* Neighbor mutex protects neighbors list */
+       struct list_head neighbors; /* Elements are wlp_neighbor_e */
+       struct uwb_notifs_handler uwb_notifs_handler;
+       struct wlp_device_info *dev_info;
+       void (*fill_device_info)(struct wlp *wlp, struct wlp_device_info *info);
+       int (*xmit_frame)(struct wlp *, struct sk_buff *,
+                         struct uwb_dev_addr *);
+       void (*stop_queue)(struct wlp *);
+       void (*start_queue)(struct wlp *);
+};
+
+/* sysfs */
+
+
+struct wlp_wss_attribute {
+       struct attribute attr;
+       ssize_t (*show)(struct wlp_wss *wss, char *buf);
+       ssize_t (*store)(struct wlp_wss *wss, const char *buf, size_t count);
+};
+
+#define WSS_ATTR(_name, _mode, _show, _store) \
+static struct wlp_wss_attribute wss_attr_##_name = __ATTR(_name, _mode,        \
+                                                         _show, _store)
+
+extern int wlp_setup(struct wlp *, struct uwb_rc *);
+extern void wlp_remove(struct wlp *);
+extern ssize_t wlp_neighborhood_show(struct wlp *, char *);
+extern int wlp_wss_setup(struct net_device *, struct wlp_wss *);
+extern void wlp_wss_remove(struct wlp_wss *);
+extern ssize_t wlp_wss_activate_show(struct wlp_wss *, char *);
+extern ssize_t wlp_wss_activate_store(struct wlp_wss *, const char *, size_t);
+extern ssize_t wlp_eda_show(struct wlp *, char *);
+extern ssize_t wlp_eda_store(struct wlp *, const char *, size_t);
+extern ssize_t wlp_uuid_show(struct wlp *, char *);
+extern ssize_t wlp_uuid_store(struct wlp *, const char *, size_t);
+extern ssize_t wlp_dev_name_show(struct wlp *, char *);
+extern ssize_t wlp_dev_name_store(struct wlp *, const char *, size_t);
+extern ssize_t wlp_dev_manufacturer_show(struct wlp *, char *);
+extern ssize_t wlp_dev_manufacturer_store(struct wlp *, const char *, size_t);
+extern ssize_t wlp_dev_model_name_show(struct wlp *, char *);
+extern ssize_t wlp_dev_model_name_store(struct wlp *, const char *, size_t);
+extern ssize_t wlp_dev_model_nr_show(struct wlp *, char *);
+extern ssize_t wlp_dev_model_nr_store(struct wlp *, const char *, size_t);
+extern ssize_t wlp_dev_serial_show(struct wlp *, char *);
+extern ssize_t wlp_dev_serial_store(struct wlp *, const char *, size_t);
+extern ssize_t wlp_dev_prim_category_show(struct wlp *, char *);
+extern ssize_t wlp_dev_prim_category_store(struct wlp *, const char *,
+                                          size_t);
+extern ssize_t wlp_dev_prim_OUI_show(struct wlp *, char *);
+extern ssize_t wlp_dev_prim_OUI_store(struct wlp *, const char *, size_t);
+extern ssize_t wlp_dev_prim_OUI_sub_show(struct wlp *, char *);
+extern ssize_t wlp_dev_prim_OUI_sub_store(struct wlp *, const char *,
+                                         size_t);
+extern ssize_t wlp_dev_prim_subcat_show(struct wlp *, char *);
+extern ssize_t wlp_dev_prim_subcat_store(struct wlp *, const char *,
+                                        size_t);
+extern int wlp_receive_frame(struct device *, struct wlp *, struct sk_buff *,
+                            struct uwb_dev_addr *);
+extern int wlp_prepare_tx_frame(struct device *, struct wlp *,
+                              struct sk_buff *, struct uwb_dev_addr *);
+void wlp_reset_all(struct wlp *wlp);
+
+/**
+ * Initialize WSS
+ */
+static inline
+void wlp_wss_init(struct wlp_wss *wss)
+{
+       mutex_init(&wss->mutex);
+}
+
+static inline
+void wlp_init(struct wlp *wlp)
+{
+       INIT_LIST_HEAD(&wlp->neighbors);
+       mutex_init(&wlp->mutex);
+       mutex_init(&wlp->nbmutex);
+       wlp_wss_init(&wlp->wss);
+}
+
+
+#endif /* #ifndef __LINUX__WLP_H_ */
index 06fb57c86de07007dad10f4bbeb3b1adcc0742c4..c2006bfeea417e72f838ad840c5f8eff32421358 100644 (file)
@@ -1007,3 +1007,25 @@ int bitmap_allocate_region(unsigned long *bitmap, int pos, int order)
        return 0;
 }
 EXPORT_SYMBOL(bitmap_allocate_region);
+
+/**
+ * bitmap_copy_le - copy a bitmap, putting the bits into little-endian order.
+ * @dst:   destination buffer
+ * @src:   bitmap to copy
+ * @nbits: number of bits in the bitmap
+ *
+ * Require nbits % BITS_PER_LONG == 0.
+ */
+void bitmap_copy_le(void *dst, const unsigned long *src, int nbits)
+{
+       unsigned long *d = dst;
+       int i;
+
+       for (i = 0; i < nbits/BITS_PER_LONG; i++) {
+               if (BITS_PER_LONG == 64)
+                       d[i] = cpu_to_le64(src[i]);
+               else
+                       d[i] = cpu_to_le32(src[i]);
+       }
+}
+EXPORT_SYMBOL(bitmap_copy_le);