uwb: add basic radio manager
authorDavid Vrabel <david.vrabel@csr.com>
Mon, 17 Nov 2008 15:53:42 +0000 (15:53 +0000)
committerDavid Vrabel <david.vrabel@csr.com>
Wed, 19 Nov 2008 14:46:33 +0000 (14:46 +0000)
The UWB radio manager coordinates the use of the radio between the
PALs that may be using it.  PALs request use of the radio with
uwb_radio_start() and the radio manager will start beaconing if its
not already doing so.  When the last PAL has called uwb_radio_stop()
beaconing will be stopped.

In the future, the radio manager will have a more sophisticated channel
selection algorithm, probably following the Channel Selection Policy
from the WiMedia Alliance when it is finalized.  For now, channel 9
(BG1, TFC1) is selected.

The user may override the channel selected by the radio manager and may
force the radio to stop beaconing.

The WUSB Host Controller PAL makes use of this and there are two new
debug PAL commands that can be used for testing.

Signed-off-by: David Vrabel <david.vrabel@csr.com>
21 files changed:
Documentation/ABI/testing/sysfs-class-uwb_rc
Documentation/usb/wusb-cbaf
drivers/usb/host/hwa-hc.c
drivers/usb/host/whci/hcd.c
drivers/usb/wusbcore/devconnect.c
drivers/usb/wusbcore/mmc.c
drivers/usb/wusbcore/pal.c
drivers/usb/wusbcore/wusbhc.h
drivers/uwb/Makefile
drivers/uwb/beacon.c
drivers/uwb/drp.c
drivers/uwb/lc-rc.c
drivers/uwb/pal.c
drivers/uwb/radio.c [new file with mode: 0644]
drivers/uwb/reset.c
drivers/uwb/rsv.c
drivers/uwb/uwb-debug.c
drivers/uwb/uwb-internal.h
drivers/uwb/wlp/wlp-lc.c
include/linux/uwb.h
include/linux/uwb/debug-cmd.h

index a0d18dbeb7a9ea8d02565ebd0c2040c72e224668..6a5fd072849d7d7b402bd0a141a21dc5ece97fe3 100644 (file)
@@ -32,14 +32,16 @@ Contact:        linux-usb@vger.kernel.org
 Description:
                 Write:
 
-                <channel> [<bpst offset>]
+                <channel>
 
-                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.
+                to force a specific channel to be used when beaconing,
+                or, if <channel> is -1, to prohibit beaconing.  If
+                <channel> is 0, then the default channel selection
+                algorithm will be used.  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.
+                Reading returns the currently active channel, or -1 if
+                the radio controller is not beaconing.
 
 What:           /sys/class/uwb_rc/uwbN/scan
 Date:           July 2008
index 2e78b70f3adccf8bc5f16c13990d042ec8c2b03f..426ddaaef96f423812579908992cc464b15044bf 100644 (file)
@@ -80,12 +80,6 @@ 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
@@ -95,9 +89,6 @@ case $1 in
           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)
index 2827353e97e15f31239e0e9df3b354f581b07595..2a4d36fa70b023971a6025ea82dc1cfbf4be2e3b 100644 (file)
@@ -221,7 +221,6 @@ static void hwahc_op_stop(struct usb_hcd *usb_hcd)
 
        d_fnstart(4, dev, "(hwahc %p)\n", hwahc);
        mutex_lock(&wusbhc->mutex);
-       wusbhc_stop(wusbhc);
        wusb_cluster_id_put(wusbhc->cluster_id);
        mutex_unlock(&wusbhc->mutex);
        d_fnend(4, dev, "(hwahc %p) = %d\n", hwahc, result);
index de1e07271b81740629476b26e107e435095ba473..f599f89d3be13d553d02e166393c541707d5a58b 100644 (file)
@@ -91,8 +91,6 @@ static void whc_stop(struct usb_hcd *usb_hcd)
 
        mutex_lock(&wusbhc->mutex);
 
-       wusbhc_stop(wusbhc);
-
        /* stop HC */
        le_writel(0, whc->base + WUSBINTR);
        whc_write_wusbcmd(whc, WUSBCMD_RUN, 0);
index c01c7a80744c8c51b083d8a66789d5220f34fee3..08a1ec903867cd1cc40454693ea59ca62977050d 100644 (file)
@@ -1124,8 +1124,7 @@ void wusbhc_devconnect_destroy(struct wusbhc *wusbhc)
  * 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)
+int wusbhc_devconnect_start(struct wusbhc *wusbhc)
 {
        struct device *dev = wusbhc->dev;
        struct wuie_host_info *hi;
@@ -1138,7 +1137,7 @@ int wusbhc_devconnect_start(struct wusbhc *wusbhc,
        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;
+       hi->CHID              = wusbhc->chid;
        result = wusbhc_mmcie_set(wusbhc, 0, 0, &hi->hdr);
        if (result < 0) {
                dev_err(dev, "Cannot add Host Info MMCIE: %d\n", result);
index af2aee0fdffa872c1839cb1aa373de7760ce15d8..5463ecebafdfdf5500f8c2cb116a7772c0cf3e6b 100644 (file)
@@ -162,12 +162,11 @@ 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 wusbhc_start(struct wusbhc *wusbhc)
 {
        int result;
        struct device *dev = wusbhc->dev;
@@ -181,7 +180,7 @@ int wusbhc_start(struct wusbhc *wusbhc, const struct wusb_ckhdid *chid)
                goto error_rsv_establish;
        }
 
-       result = wusbhc_devconnect_start(wusbhc, chid);
+       result = wusbhc_devconnect_start(wusbhc);
        if (result < 0) {
                dev_err(dev, "error enabling device connections: %d\n", result);
                goto error_devconnect_start;
@@ -218,34 +217,6 @@ 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
@@ -264,29 +235,6 @@ void wusbhc_stop(struct wusbhc *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
  *
@@ -302,16 +250,19 @@ int wusbhc_chid_set(struct wusbhc *wusbhc, const struct wusb_ckhdid *chid)
                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);
+       if (chid) {
+               if (wusbhc->active) {
+                       mutex_unlock(&wusbhc->mutex);
+                       return -EBUSY;
+               }
+               wusbhc->chid = *chid;
        }
        mutex_unlock(&wusbhc->mutex);
+
+       if (chid)
+               result = uwb_radio_start(&wusbhc->pal);
+       else
+               uwb_radio_stop(&wusbhc->pal);
        return result;
 }
 EXPORT_SYMBOL_GPL(wusbhc_chid_set);
index 7cc51e9905cf81e2234c0435d88630e9b1f558be..d0b172c5ecc77219f885bda14fabd2991b6688d1 100644 (file)
  */
 #include "wusbhc.h"
 
+static void wusbhc_channel_changed(struct uwb_pal *pal, int channel)
+{
+       struct wusbhc *wusbhc = container_of(pal, struct wusbhc, pal);
+
+       if (channel < 0)
+               wusbhc_stop(wusbhc);
+       else
+               wusbhc_start(wusbhc);
+}
+
 /**
  * wusbhc_pal_register - register the WUSB HC as a UWB PAL
  * @wusbhc: the WUSB HC
@@ -28,8 +38,10 @@ int wusbhc_pal_register(struct wusbhc *wusbhc)
 
        wusbhc->pal.name   = "wusbhc";
        wusbhc->pal.device = wusbhc->usb_hcd.self.controller;
+       wusbhc->pal.rc     = wusbhc->uwb_rc;
+       wusbhc->pal.channel_changed = wusbhc_channel_changed;
 
-       return uwb_pal_register(wusbhc->uwb_rc, &wusbhc->pal);
+       return uwb_pal_register(&wusbhc->pal);
 }
 
 /**
@@ -38,5 +50,5 @@ int wusbhc_pal_register(struct wusbhc *wusbhc)
  */
 void wusbhc_pal_unregister(struct wusbhc *wusbhc)
 {
-       uwb_pal_unregister(wusbhc->uwb_rc, &wusbhc->pal);
+       uwb_pal_unregister(&wusbhc->pal);
 }
index 8fef934ad2f3f1f04765fb08cb679927202f6ae9..797c2453a35bf829ec2fef965d5ac42a37cd33af 100644 (file)
@@ -252,7 +252,8 @@ struct wusbhc {
        struct uwb_pal pal;
 
        unsigned trust_timeout;                 /* in jiffies */
-       struct wuie_host_info *wuie_host_info;  /* Includes CHID */
+       struct wusb_ckhdid chid;
+       struct wuie_host_info *wuie_host_info;
 
        struct mutex mutex;                     /* locks everything else */
        u16 cluster_id;                         /* Wireless USB Cluster ID */
@@ -376,15 +377,14 @@ 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);
+int wusbhc_start(struct wusbhc *wusbhc);
 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 int wusbhc_devconnect_start(struct wusbhc *wusbhc);
 extern void wusbhc_devconnect_stop(struct wusbhc *wusbhc);
 extern void wusbhc_handle_dn(struct wusbhc *, u8 srcaddr,
                             struct wusb_dn_hdr *dn_hdr, size_t size);
index 2b99c3e61671f92843415a3838f233c6efb98e71..ce21a95da04a9cbb092475e8c9d9f7208197ea0a 100644 (file)
@@ -18,6 +18,7 @@ uwb-objs :=           \
        lc-rc.o         \
        neh.o           \
        pal.o           \
+       radio.o         \
        reset.o         \
        rsv.o           \
        scan.o          \
index d9f2a8acc593cf8064a6ae13af55cb23a912b5a7..247956098afac2c7558149e6e0c034d71bd64586 100644 (file)
@@ -119,7 +119,6 @@ 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)
@@ -128,7 +127,7 @@ int uwb_rc_beacon(struct uwb_rc *rc, int channel, unsigned bpst_offset)
                /* channel >= 0...dah */
                result = uwb_rc_start_beacon(rc, bpst_offset, channel);
                if (result < 0)
-                       goto out_up;
+                       return result;
                if (le16_to_cpu(rc->ies->wIELength) > 0) {
                        result = uwb_rc_set_ie(rc, rc->ies);
                        if (result < 0) {
@@ -137,19 +136,14 @@ int uwb_rc_beacon(struct uwb_rc *rc, int channel, unsigned bpst_offset)
                                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);
+       if (result >= 0) {
+               rc->beaconing = channel;
+               uwb_notify(rc, NULL, uwb_bg_joined(rc) ? UWB_NOTIF_BG_JOIN : UWB_NOTIF_BG_LEAVE);
+       }
        return result;
 }
 
@@ -618,9 +612,6 @@ static ssize_t uwb_rc_beacon_show(struct device *dev,
 
 /*
  * 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,
@@ -629,12 +620,11 @@ static ssize_t uwb_rc_beacon_store(struct device *dev,
        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);
+       result = sscanf(buf, "%d", &channel);
        if (result >= 1)
-               result = uwb_rc_beacon(rc, channel, bpst_offset);
+               result = uwb_radio_force_channel(rc, channel);
 
        return result < 0 ? result : size;
 }
index c0b1e5e2bd08a7245427516e1e95e57aaeb70470..fe328146adb7844f3ab0ba5efd5e7505d9b2b965 100644 (file)
  *
  * A DRP Availability IE is appended.
  *
- * rc->uwb_dev.mutex is held
+ * rc->rsvs_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 uwb_rc_send_all_drp_ie(struct uwb_rc *rc)
 {
        int result;
        struct device *dev = &rc->uwb_dev.dev;
@@ -102,25 +101,6 @@ 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)
index f00633d334dda8962693d241f0b3661512c66108..9cf21e6bb624e036b6f698bc975f7e66080ca16e 100644 (file)
@@ -189,9 +189,9 @@ static int uwb_rc_setup(struct uwb_rc *rc)
        int result;
        struct device *dev = &rc->uwb_dev.dev;
 
-       result = uwb_rc_reset(rc);
+       result = uwb_radio_setup(rc);
        if (result < 0) {
-               dev_err(dev, "cannot reset UWB radio: %d\n", result);
+               dev_err(dev, "cannot setup UWB radio: %d\n", result);
                goto error;
        }
        result = uwb_rc_mac_addr_setup(rc);
@@ -311,12 +311,7 @@ void uwb_rc_rm(struct uwb_rc *rc)
 
        uwb_dbg_del_rc(rc);
        uwb_rsv_remove_all(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);
+       uwb_radio_shutdown(rc);
 
        rc->stop(rc);
 
index 1afb38eacb9a295d55e6356b0ab3b3f8692ce28a..605765124f5b7430ffe4544646bcc4f2de56aa5a 100644 (file)
@@ -32,13 +32,13 @@ 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 uwb_pal_register(struct uwb_pal *pal)
 {
+       struct uwb_rc *rc = pal->rc;
        int ret;
 
        if (pal->device) {
@@ -54,9 +54,9 @@ int uwb_pal_register(struct uwb_rc *rc, struct uwb_pal *pal)
                }
        }
 
-       spin_lock(&rc->pal_lock);
+       mutex_lock(&rc->uwb_dev.mutex);
        list_add(&pal->node, &rc->pals);
-       spin_unlock(&rc->pal_lock);
+       mutex_unlock(&rc->uwb_dev.mutex);
 
        return 0;
 }
@@ -64,14 +64,17 @@ 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)
+void uwb_pal_unregister(struct uwb_pal *pal)
 {
-       spin_lock(&rc->pal_lock);
+       struct uwb_rc *rc = pal->rc;
+
+       uwb_radio_stop(pal);
+
+       mutex_lock(&rc->uwb_dev.mutex);
        list_del(&pal->node);
-       spin_unlock(&rc->pal_lock);
+       mutex_unlock(&rc->uwb_dev.mutex);
 
        if (pal->device) {
                sysfs_remove_link(&rc->uwb_dev.dev.kobj, pal->name);
@@ -86,6 +89,5 @@ EXPORT_SYMBOL_GPL(uwb_pal_unregister);
  */
 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/radio.c b/drivers/uwb/radio.c
new file mode 100644 (file)
index 0000000..f0d5549
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * UWB radio (channel) 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/kernel.h>
+#include <linux/uwb.h>
+
+#include "uwb-internal.h"
+
+
+static int uwb_radio_select_channel(struct uwb_rc *rc)
+{
+       /*
+        * Default to channel 9 (BG1, TFC1) unless the user has
+        * selected a specific channel or there are no active PALs.
+        */
+       if (rc->active_pals == 0)
+               return -1;
+       if (rc->beaconing_forced)
+               return rc->beaconing_forced;
+       return 9;
+}
+
+
+/*
+ * Notify all active PALs that the channel has changed.
+ */
+static void uwb_radio_channel_changed(struct uwb_rc *rc, int channel)
+{
+       struct uwb_pal *pal;
+
+       list_for_each_entry(pal, &rc->pals, node) {
+               if (pal->channel && channel != pal->channel) {
+                       pal->channel = channel;
+                       if (pal->channel_changed)
+                               pal->channel_changed(pal, pal->channel);
+               }
+       }
+}
+
+/*
+ * Change to a new channel and notify any active PALs of the new
+ * channel.
+ *
+ * When stopping the radio, PALs need to be notified first so they can
+ * terminate any active reservations.
+ */
+static int uwb_radio_change_channel(struct uwb_rc *rc, int channel)
+{
+       int ret = 0;
+
+       if (channel == -1)
+               uwb_radio_channel_changed(rc, channel);
+
+       if (channel != rc->beaconing) {
+               if (rc->beaconing != -1 && channel != -1) {
+                       /*
+                        * FIXME: should signal the channel change
+                        * with a Channel Change IE.
+                        */
+                       ret = uwb_radio_change_channel(rc, -1);
+                       if (ret < 0)
+                               return ret;
+               }
+               ret = uwb_rc_beacon(rc, channel, 0);
+       }
+
+       if (channel != -1)
+               uwb_radio_channel_changed(rc, rc->beaconing);
+
+       return ret;
+}
+
+/**
+ * uwb_radio_start - request that the radio be started
+ * @pal: the PAL making the request.
+ *
+ * If the radio is not already active, aa suitable channel is selected
+ * and beacons are started.
+ */
+int uwb_radio_start(struct uwb_pal *pal)
+{
+       struct uwb_rc *rc = pal->rc;
+       int ret = 0;
+
+       mutex_lock(&rc->uwb_dev.mutex);
+
+       if (!pal->channel) {
+               pal->channel = -1;
+               rc->active_pals++;
+               ret = uwb_radio_change_channel(rc, uwb_radio_select_channel(rc));
+       }
+
+       mutex_unlock(&rc->uwb_dev.mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(uwb_radio_start);
+
+/**
+ * uwb_radio_stop - request tha the radio be stopped.
+ * @pal: the PAL making the request.
+ *
+ * Stops the radio if no other PAL is making use of it.
+ */
+void uwb_radio_stop(struct uwb_pal *pal)
+{
+       struct uwb_rc *rc = pal->rc;
+
+       mutex_lock(&rc->uwb_dev.mutex);
+
+       if (pal->channel) {
+               rc->active_pals--;
+               uwb_radio_change_channel(rc, uwb_radio_select_channel(rc));
+               pal->channel = 0;
+       }
+
+       mutex_unlock(&rc->uwb_dev.mutex);
+}
+EXPORT_SYMBOL_GPL(uwb_radio_stop);
+
+/*
+ * uwb_radio_force_channel - force a specific channel to be used
+ * @rc: the radio controller.
+ * @channel: the channel to use; -1 to force the radio to stop; 0 to
+ *   use the default channel selection algorithm.
+ */
+int uwb_radio_force_channel(struct uwb_rc *rc, int channel)
+{
+       int ret = 0;
+
+       mutex_lock(&rc->uwb_dev.mutex);
+
+       rc->beaconing_forced = channel;
+       ret = uwb_radio_change_channel(rc, uwb_radio_select_channel(rc));
+
+       mutex_unlock(&rc->uwb_dev.mutex);
+       return ret;
+}
+
+/*
+ * uwb_radio_setup - setup the radio manager
+ * @rc: the radio controller.
+ *
+ * The radio controller is reset to ensure it's in a known state
+ * before it's used.
+ */
+int uwb_radio_setup(struct uwb_rc *rc)
+{
+       return uwb_rc_reset(rc);
+}
+
+/*
+ * uwb_radio_reset_state - reset any radio manager state
+ * @rc: the radio controller.
+ *
+ * All internal radio manager state is reset to values corresponding
+ * to a reset radio controller.
+ */
+void uwb_radio_reset_state(struct uwb_rc *rc)
+{
+       struct uwb_pal *pal;
+
+       mutex_lock(&rc->uwb_dev.mutex);
+
+       list_for_each_entry(pal, &rc->pals, node) {
+               if (pal->channel) {
+                       pal->channel = -1;
+                       if (pal->channel_changed)
+                               pal->channel_changed(pal, -1);
+               }
+       }
+
+       rc->beaconing = -1;
+       rc->scanning = -1;
+
+       mutex_unlock(&rc->uwb_dev.mutex);
+}
+
+/*
+ * uwb_radio_shutdown - shutdown the radio manager
+ * @rc: the radio controller.
+ *
+ * The radio controller is reset.
+ */
+void uwb_radio_shutdown(struct uwb_rc *rc)
+{
+       uwb_radio_reset_state(rc);
+       uwb_rc_reset(rc);
+}
index e39b32099af3c9ccff508cb27fb7cea8e8ada2ad..ce8283cc8098b693d18f3f7d89244ebc53e5f693 100644 (file)
@@ -365,11 +365,7 @@ void uwb_rc_pre_reset(struct uwb_rc *rc)
        rc->stop(rc);
        uwbd_flush(rc);
 
-       mutex_lock(&rc->uwb_dev.mutex);
-       rc->beaconing = -1;
-       rc->scanning = -1;
-       mutex_unlock(&rc->uwb_dev.mutex);
-
+       uwb_radio_reset_state(rc);
        uwb_rsv_remove_all(rc);
 }
 EXPORT_SYMBOL_GPL(uwb_rc_pre_reset);
index 935d5b536db78eb2c40e9ddcc6add5c2d4c70de1..1cd84f927540207b4d8a1d7630edb13d2e2a5c40 100644 (file)
@@ -555,14 +555,14 @@ static struct uwb_rsv *uwb_rsv_new_target(struct uwb_rc *rc,
         * deny the request.
         */
        rsv->state = UWB_RSV_STATE_T_DENIED;
-       spin_lock(&rc->pal_lock);
+       mutex_lock(&rc->uwb_dev.mutex);
        list_for_each_entry(pal, &rc->pals, node) {
                if (pal->new_rsv)
                        pal->new_rsv(pal, rsv);
                if (rsv->state == UWB_RSV_STATE_T_ACCEPTED)
                        break;
        }
-       spin_unlock(&rc->pal_lock);
+       mutex_unlock(&rc->uwb_dev.mutex);
 
        list_add_tail(&rsv->rc_node, &rc->reservations);
        state = rsv->state;
index 217ebaac128d72aedaa31b82d9e7c31e4573f87a..0e58071a232d14fb55fdc16a9cf95e2aceb7cf3b 100644 (file)
@@ -192,7 +192,7 @@ static ssize_t command_write(struct file *file, const char __user *buf,
 {
        struct uwb_rc *rc = file->private_data;
        struct uwb_dbg_cmd cmd;
-       int ret;
+       int ret = 0;
 
        if (len != sizeof(struct uwb_dbg_cmd))
                return -EINVAL;
@@ -213,6 +213,12 @@ static ssize_t command_write(struct file *file, const char __user *buf,
        case UWB_DBG_CMD_IE_RM:
                ret = cmd_ie_rm(rc, &cmd.ie_rm);
                break;
+       case UWB_DBG_CMD_RADIO_START:
+               ret = uwb_radio_start(&rc->dbg->pal);
+               break;
+       case UWB_DBG_CMD_RADIO_STOP:
+               uwb_radio_stop(&rc->dbg->pal);
+               break;
        default:
                return -EINVAL;
        }
@@ -306,6 +312,17 @@ static struct file_operations drp_avail_fops = {
        .owner   = THIS_MODULE,
 };
 
+static void uwb_dbg_channel_changed(struct uwb_pal *pal, int channel)
+{
+       struct uwb_dbg *dbg = container_of(pal, struct uwb_dbg, pal);
+       struct device *dev = &pal->rc->uwb_dev.dev;
+
+       if (channel > 0)
+               dev_info(dev, "debug: channel %d started\n", channel);
+       else
+               dev_info(dev, "debug: channel stopped\n");
+}
+
 static void uwb_dbg_new_rsv(struct uwb_pal *pal, struct uwb_rsv *rsv)
 {
        struct uwb_dbg *dbg = container_of(pal, struct uwb_dbg, pal);
@@ -329,8 +346,11 @@ void uwb_dbg_add_rc(struct uwb_rc *rc)
        INIT_LIST_HEAD(&rc->dbg->rsvs);
 
        uwb_pal_init(&rc->dbg->pal);
+       rc->dbg->pal.rc = rc;
+       rc->dbg->pal.channel_changed = uwb_dbg_channel_changed;
        rc->dbg->pal.new_rsv = uwb_dbg_new_rsv;
-       uwb_pal_register(rc, &rc->dbg->pal);
+       uwb_pal_register(&rc->dbg->pal);
+
        if (root_dir) {
                rc->dbg->root_d = debugfs_create_dir(dev_name(&rc->uwb_dev.dev),
                                                     root_dir);
@@ -364,7 +384,7 @@ void uwb_dbg_del_rc(struct uwb_rc *rc)
                uwb_rsv_terminate(rsv);
        }
 
-       uwb_pal_unregister(rc, &rc->dbg->pal);
+       uwb_pal_unregister(&rc->dbg->pal);
 
        if (root_dir) {
                debugfs_remove(rc->dbg->drp_avail_f);
index af95541dabcd9a0aea50dde0f8beda6785881708..9c0cdb4ded0c1e72655b6966b178f54c7b6edaf5 100644 (file)
@@ -238,6 +238,11 @@ struct uwb_dev *uwb_dev_get_by_devaddr(struct uwb_rc *rc,
 struct uwb_dev *uwb_dev_get_by_macaddr(struct uwb_rc *rc,
                                       const struct uwb_mac_addr *macaddr);
 
+int uwb_radio_setup(struct uwb_rc *rc);
+void uwb_radio_reset_state(struct uwb_rc *rc);
+void uwb_radio_shutdown(struct uwb_rc *rc);
+int uwb_radio_force_channel(struct uwb_rc *rc, int channel);
+
 /* -- UWB Sysfs representation */
 extern struct class uwb_rc_class;
 extern struct device_attribute dev_attr_mac_address;
index 0799402e73fbeace0af949b8f51a93ad4e67813b..7e5eb49b03b83301664e328ffee357dd8bd644bc 100644 (file)
@@ -543,7 +543,8 @@ int wlp_setup(struct wlp *wlp, struct uwb_rc *rc)
        uwb_notifs_register(rc, &wlp->uwb_notifs_handler);
 
        uwb_pal_init(&wlp->pal);
-       result = uwb_pal_register(rc, &wlp->pal);
+       wlp->pal.rc = rc;
+       result = uwb_pal_register(&wlp->pal);
        if (result < 0)
                uwb_notifs_deregister(wlp->rc, &wlp->uwb_notifs_handler);
 
@@ -557,7 +558,7 @@ 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_pal_unregister(&wlp->pal);
        uwb_notifs_deregister(wlp->rc, &wlp->uwb_notifs_handler);
        wlp_eda_release(&wlp->eda);
        mutex_lock(&wlp->mutex);
index effd97998fd18c90becd42de411345858f52106b..7d3ebf046f9a51e2c3877d8d88047b70ad7bc524 100644 (file)
@@ -355,6 +355,7 @@ struct uwb_rc {
        u8 ctx_roll;
 
        int beaconing;                  /* Beaconing state [channel number] */
+       int beaconing_forced;
        int scanning;
        enum uwb_scan_type scan_type:3;
        unsigned ready:1;
@@ -373,8 +374,8 @@ struct uwb_rc {
        struct uwb_rc_cmd_set_ie *ies;
        size_t ies_capacity;
 
-       spinlock_t pal_lock;
        struct list_head pals;
+       int active_pals;
 
        struct uwb_dbg *dbg;
 };
@@ -382,11 +383,17 @@ struct uwb_rc {
 
 /**
  * struct uwb_pal - a UWB PAL
- * @name:    descriptive name for this PAL (wushc, wlp, etc.).
+ * @name:    descriptive name for this PAL (wusbhc, wlp, etc.).
  * @device:  a device for the PAL.  Used to link the PAL and the radio
  *           controller in sysfs.
+ * @rc:      the radio controller the PAL uses.
+ * @channel_changed: called when the channel used by the radio changes.
+ *           A channel of -1 means the channel has been stopped.
  * @new_rsv: called when a peer requests a reservation (may be NULL if
  *           the PAL cannot accept reservation requests).
+ * @channel: channel being used by the PAL; 0 if the PAL isn't using
+ *           the radio; -1 if the PAL wishes to use the radio but
+ *           cannot.
  *
  * A Protocol Adaptation Layer (PAL) is a user of the WiMedia UWB
  * radio platform (e.g., WUSB, WLP or Bluetooth UWB AMP).
@@ -405,12 +412,20 @@ struct uwb_pal {
        struct list_head node;
        const char *name;
        struct device *device;
+       struct uwb_rc *rc;
+
+       void (*channel_changed)(struct uwb_pal *pal, int channel);
        void (*new_rsv)(struct uwb_pal *pal, struct uwb_rsv *rsv);
+
+       int channel;
 };
 
 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);
+int uwb_pal_register(struct uwb_pal *pal);
+void uwb_pal_unregister(struct uwb_pal *pal);
+
+int uwb_radio_start(struct uwb_pal *pal);
+void uwb_radio_stop(struct uwb_pal *pal);
 
 /*
  * General public API
index 6a16566f0221f671d9d15b36edd478cfeb594597..07efbe17db533f997944d24a70494ae979082faa 100644 (file)
@@ -34,6 +34,8 @@ enum uwb_dbg_cmd_type {
        UWB_DBG_CMD_RSV_TERMINATE = 2,
        UWB_DBG_CMD_IE_ADD = 3,
        UWB_DBG_CMD_IE_RM = 4,
+       UWB_DBG_CMD_RADIO_START = 5,
+       UWB_DBG_CMD_RADIO_STOP = 6,
 };
 
 struct uwb_dbg_cmd_rsv_establish {