uwb: add the UWB stack (reservation manager)

DRP and reservation management.

Signed-off-by: David Vrabel <david.vrabel@csr.com>
diff --git a/drivers/uwb/drp-avail.c b/drivers/uwb/drp-avail.c
new file mode 100644
index 0000000..3febd855
--- /dev/null
+++ b/drivers/uwb/drp-avail.c
@@ -0,0 +1,288 @@
+/*
+ * Ultra Wide Band
+ * DRP availability management
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Reinette Chatre <reinette.chatre@intel.com>
+ * Copyright (C) 2008 Cambridge Silicon Radio Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Manage DRP Availability (the MAS available for DRP
+ * reservations). Thus:
+ *
+ * - Handle DRP Availability Change notifications
+ *
+ * - Allow the reservation manager to indicate MAS reserved/released
+ *   by local (owned by/targeted at the radio controller)
+ *   reservations.
+ *
+ * - Based on the two sources above, generate a DRP Availability IE to
+ *   be included in the beacon.
+ *
+ * See also the documentation for struct uwb_drp_avail.
+ */
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/bitmap.h>
+#include "uwb-internal.h"
+
+/**
+ * uwb_drp_avail_init - initialize an RC's MAS availability
+ *
+ * All MAS are available initially.  The RC will inform use which
+ * slots are used for the BP (it may change in size).
+ */
+void uwb_drp_avail_init(struct uwb_rc *rc)
+{
+	bitmap_fill(rc->drp_avail.global, UWB_NUM_MAS);
+	bitmap_fill(rc->drp_avail.local, UWB_NUM_MAS);
+	bitmap_fill(rc->drp_avail.pending, UWB_NUM_MAS);
+}
+
+/*
+ * Determine MAS available for new local reservations.
+ *
+ * avail = global & local & pending
+ */
+static void uwb_drp_available(struct uwb_rc *rc, struct uwb_mas_bm *avail)
+{
+	bitmap_and(avail->bm, rc->drp_avail.global, rc->drp_avail.local, UWB_NUM_MAS);
+	bitmap_and(avail->bm, avail->bm, rc->drp_avail.pending, UWB_NUM_MAS);
+}
+
+/**
+ * uwb_drp_avail_reserve_pending - reserve MAS for a new reservation
+ * @rc: the radio controller
+ * @mas: the MAS to reserve
+ *
+ * Returns 0 on success, or -EBUSY if the MAS requested aren't available.
+ */
+int uwb_drp_avail_reserve_pending(struct uwb_rc *rc, struct uwb_mas_bm *mas)
+{
+	struct uwb_mas_bm avail;
+
+	uwb_drp_available(rc, &avail);
+	if (!bitmap_subset(mas->bm, avail.bm, UWB_NUM_MAS))
+		return -EBUSY;
+
+	bitmap_andnot(rc->drp_avail.pending, rc->drp_avail.pending, mas->bm, UWB_NUM_MAS);
+	return 0;
+}
+
+/**
+ * uwb_drp_avail_reserve - reserve MAS for an established reservation
+ * @rc: the radio controller
+ * @mas: the MAS to reserve
+ */
+void uwb_drp_avail_reserve(struct uwb_rc *rc, struct uwb_mas_bm *mas)
+{
+	bitmap_or(rc->drp_avail.pending, rc->drp_avail.pending, mas->bm, UWB_NUM_MAS);
+	bitmap_andnot(rc->drp_avail.local, rc->drp_avail.local, mas->bm, UWB_NUM_MAS);
+	rc->drp_avail.ie_valid = false;
+}
+
+/**
+ * uwb_drp_avail_release - release MAS from a pending or established reservation
+ * @rc: the radio controller
+ * @mas: the MAS to release
+ */
+void uwb_drp_avail_release(struct uwb_rc *rc, struct uwb_mas_bm *mas)
+{
+	bitmap_or(rc->drp_avail.local, rc->drp_avail.local, mas->bm, UWB_NUM_MAS);
+	bitmap_or(rc->drp_avail.pending, rc->drp_avail.pending, mas->bm, UWB_NUM_MAS);
+	rc->drp_avail.ie_valid = false;
+}
+
+/**
+ * uwb_drp_avail_ie_update - update the DRP Availability IE
+ * @rc: the radio controller
+ *
+ * avail = global & local
+ */
+void uwb_drp_avail_ie_update(struct uwb_rc *rc)
+{
+	struct uwb_mas_bm avail;
+
+	bitmap_and(avail.bm, rc->drp_avail.global, rc->drp_avail.local, UWB_NUM_MAS);
+
+	rc->drp_avail.ie.hdr.element_id = UWB_IE_DRP_AVAILABILITY;
+	rc->drp_avail.ie.hdr.length = UWB_NUM_MAS / 8;
+	uwb_mas_bm_copy_le(rc->drp_avail.ie.bmp, &avail);
+	rc->drp_avail.ie_valid = true;
+}
+
+/**
+ * Create an unsigned long from a buffer containing a byte stream.
+ *
+ * @array: pointer to buffer
+ * @itr:   index of buffer from where we start
+ * @len:   the buffer's remaining size may not be exact multiple of
+ *         sizeof(unsigned long), @len is the length of buffer that needs
+ *         to be converted. This will be sizeof(unsigned long) or smaller
+ *         (BUG if not). If it is smaller then we will pad the remaining
+ *         space of the result with zeroes.
+ */
+static
+unsigned long get_val(u8 *array, size_t itr, size_t len)
+{
+	unsigned long val = 0;
+	size_t top = itr + len;
+
+	BUG_ON(len > sizeof(val));
+
+	while (itr < top) {
+		val <<= 8;
+		val |= array[top - 1];
+		top--;
+	}
+	val <<= 8 * (sizeof(val) - len); /* padding */
+	return val;
+}
+
+/**
+ * Initialize bitmap from data buffer.
+ *
+ * The bitmap to be converted could come from a IE, for example a
+ * DRP Availability IE.
+ * From ECMA-368 1.0 [16.8.7]: "
+ * octets: 1            1               N * (0 to 32)
+ *         Element ID   Length (=N)     DRP Availability Bitmap
+ *
+ * The DRP Availability Bitmap field is up to 256 bits long, one
+ * bit for each MAS in the superframe, where the least-significant
+ * bit of the field corresponds to the first MAS in the superframe
+ * and successive bits correspond to successive MASs."
+ *
+ * The DRP Availability bitmap is in octets from 0 to 32, so octet
+ * 32 contains bits for MAS 1-8, etc. If the bitmap is smaller than 32
+ * octets, the bits in octets not included at the end of the bitmap are
+ * treated as zero. In this case (when the bitmap is smaller than 32
+ * octets) the MAS represented range from MAS 1 to MAS (size of bitmap)
+ * with the last octet still containing bits for MAS 1-8, etc.
+ *
+ * For example:
+ * F00F0102 03040506 0708090A 0B0C0D0E 0F010203
+ * ^^^^
+ * ||||
+ * ||||
+ * |||\LSB of byte is MAS 9
+ * ||\MSB of byte is MAS 16
+ * |\LSB of first byte is MAS 1
+ * \ MSB of byte is MAS 8
+ *
+ * An example of this encoding can be found in ECMA-368 Annex-D [Table D.11]
+ *
+ * The resulting bitmap will have the following mapping:
+ *	bit position 0 == MAS 1
+ *	bit position 1 == MAS 2
+ *	...
+ *	bit position (UWB_NUM_MAS - 1) == MAS UWB_NUM_MAS
+ *
+ * @bmp_itr:	pointer to bitmap (can be declared with DECLARE_BITMAP)
+ * @buffer:	pointer to buffer containing bitmap data in big endian
+ *              format (MSB first)
+ * @buffer_size:number of bytes with which bitmap should be initialized
+ */
+static
+void buffer_to_bmp(unsigned long *bmp_itr, void *_buffer,
+		   size_t buffer_size)
+{
+	u8 *buffer = _buffer;
+	size_t itr, len;
+	unsigned long val;
+
+	itr = 0;
+	while (itr < buffer_size) {
+		len = buffer_size - itr >= sizeof(val) ?
+			sizeof(val) : buffer_size - itr;
+		val = get_val(buffer, itr, len);
+		bmp_itr[itr / sizeof(val)] = val;
+		itr += sizeof(val);
+	}
+}
+
+
+/**
+ * Extract DRP Availability bitmap from the notification.
+ *
+ * The notification that comes in contains a bitmap of (UWB_NUM_MAS / 8) bytes
+ * We convert that to our internal representation.
+ */
+static
+int uwbd_evt_get_drp_avail(struct uwb_event *evt, unsigned long *bmp)
+{
+	struct device *dev = &evt->rc->uwb_dev.dev;
+	struct uwb_rc_evt_drp_avail *drp_evt;
+	int result = -EINVAL;
+
+	/* Is there enough data to decode the event? */
+	if (evt->notif.size < sizeof(*drp_evt)) {
+		dev_err(dev, "DRP Availability Change: Not enough "
+			"data to decode event [%zu bytes, %zu "
+			"needed]\n", evt->notif.size, sizeof(*drp_evt));
+		goto error;
+	}
+	drp_evt = container_of(evt->notif.rceb, struct uwb_rc_evt_drp_avail, rceb);
+	buffer_to_bmp(bmp, drp_evt->bmp, UWB_NUM_MAS/8);
+	result = 0;
+error:
+	return result;
+}
+
+
+/**
+ * Process an incoming DRP Availability notification.
+ *
+ * @evt:	Event information (packs the actual event data, which
+ *              radio controller it came to, etc).
+ *
+ * @returns:    0 on success (so uwbd() frees the event buffer), < 0
+ *              on error.
+ *
+ * According to ECMA-368 1.0 [16.8.7], bits set to ONE indicate that
+ * the MAS slot is available, bits set to ZERO indicate that the slot
+ * is busy.
+ *
+ * So we clear available slots, we set used slots :)
+ *
+ * The notification only marks non-availability based on the BP and
+ * received DRP IEs that are not for this radio controller.  A copy of
+ * this bitmap is needed to generate the real availability (which
+ * includes local and pending reservations).
+ *
+ * The DRP Availability IE that this radio controller emits will need
+ * to be updated.
+ */
+int uwbd_evt_handle_rc_drp_avail(struct uwb_event *evt)
+{
+	int result;
+	struct uwb_rc *rc = evt->rc;
+	DECLARE_BITMAP(bmp, UWB_NUM_MAS);
+
+	result = uwbd_evt_get_drp_avail(evt, bmp);
+	if (result < 0)
+		return result;
+
+	mutex_lock(&rc->rsvs_mutex);
+	bitmap_copy(rc->drp_avail.global, bmp, UWB_NUM_MAS);
+	rc->drp_avail.ie_valid = false;
+	mutex_unlock(&rc->rsvs_mutex);
+
+	uwb_rsv_sched_update(rc);
+
+	return 0;
+}
diff --git a/drivers/uwb/drp-ie.c b/drivers/uwb/drp-ie.c
new file mode 100644
index 0000000..882724c
--- /dev/null
+++ b/drivers/uwb/drp-ie.c
@@ -0,0 +1,232 @@
+/*
+ * UWB DRP IE management.
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Copyright (C) 2008 Cambridge Silicon Radio Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/random.h>
+#include <linux/uwb.h>
+
+#include "uwb-internal.h"
+
+/*
+ * Allocate a DRP IE.
+ *
+ * To save having to free/allocate a DRP IE when its MAS changes,
+ * enough memory is allocated for the maxiumum number of DRP
+ * allocation fields.  This gives an overhead per reservation of up to
+ * (UWB_NUM_ZONES - 1) * 4 = 60 octets.
+ */
+static struct uwb_ie_drp *uwb_drp_ie_alloc(void)
+{
+	struct uwb_ie_drp *drp_ie;
+	unsigned tiebreaker;
+
+	drp_ie = kzalloc(sizeof(struct uwb_ie_drp) +
+			UWB_NUM_ZONES * sizeof(struct uwb_drp_alloc),
+			GFP_KERNEL);
+	if (drp_ie) {
+		drp_ie->hdr.element_id = UWB_IE_DRP;
+
+		get_random_bytes(&tiebreaker, sizeof(unsigned));
+		uwb_ie_drp_set_tiebreaker(drp_ie, tiebreaker & 1);
+	}
+	return drp_ie;
+}
+
+
+/*
+ * Fill a DRP IE's allocation fields from a MAS bitmap.
+ */
+static void uwb_drp_ie_from_bm(struct uwb_ie_drp *drp_ie,
+			       struct uwb_mas_bm *mas)
+{
+	int z, i, num_fields = 0, next = 0;
+	struct uwb_drp_alloc *zones;
+	__le16 current_bmp;
+	DECLARE_BITMAP(tmp_bmp, UWB_NUM_MAS);
+	DECLARE_BITMAP(tmp_mas_bm, UWB_MAS_PER_ZONE);
+
+	zones = drp_ie->allocs;
+
+	bitmap_copy(tmp_bmp, mas->bm, UWB_NUM_MAS);
+
+	/* Determine unique MAS bitmaps in zones from bitmap. */
+	for (z = 0; z < UWB_NUM_ZONES; z++) {
+		bitmap_copy(tmp_mas_bm, tmp_bmp, UWB_MAS_PER_ZONE);
+		if (bitmap_weight(tmp_mas_bm, UWB_MAS_PER_ZONE) > 0) {
+			bool found = false;
+			current_bmp = (__le16) *tmp_mas_bm;
+			for (i = 0; i < next; i++) {
+				if (current_bmp == zones[i].mas_bm) {
+					zones[i].zone_bm |= 1 << z;
+					found = true;
+					break;
+				}
+			}
+			if (!found)  {
+				num_fields++;
+				zones[next].zone_bm = 1 << z;
+				zones[next].mas_bm = current_bmp;
+				next++;
+			}
+		}
+		bitmap_shift_right(tmp_bmp, tmp_bmp, UWB_MAS_PER_ZONE, UWB_NUM_MAS);
+	}
+
+	/* Store in format ready for transmission (le16). */
+	for (i = 0; i < num_fields; i++) {
+		drp_ie->allocs[i].zone_bm = cpu_to_le16(zones[i].zone_bm);
+		drp_ie->allocs[i].mas_bm = cpu_to_le16(zones[i].mas_bm);
+	}
+
+	drp_ie->hdr.length = sizeof(struct uwb_ie_drp) - sizeof(struct uwb_ie_hdr)
+		+ num_fields * sizeof(struct uwb_drp_alloc);
+}
+
+/**
+ * uwb_drp_ie_update - update a reservation's DRP IE
+ * @rsv: the reservation
+ */
+int uwb_drp_ie_update(struct uwb_rsv *rsv)
+{
+	struct device *dev = &rsv->rc->uwb_dev.dev;
+	struct uwb_ie_drp *drp_ie;
+	int reason_code, status;
+
+	switch (rsv->state) {
+	case UWB_RSV_STATE_NONE:
+		kfree(rsv->drp_ie);
+		rsv->drp_ie = NULL;
+		return 0;
+	case UWB_RSV_STATE_O_INITIATED:
+		reason_code = UWB_DRP_REASON_ACCEPTED;
+		status = 0;
+		break;
+	case UWB_RSV_STATE_O_PENDING:
+		reason_code = UWB_DRP_REASON_ACCEPTED;
+		status = 0;
+		break;
+	case UWB_RSV_STATE_O_MODIFIED:
+		reason_code = UWB_DRP_REASON_MODIFIED;
+		status = 1;
+		break;
+	case UWB_RSV_STATE_O_ESTABLISHED:
+		reason_code = UWB_DRP_REASON_ACCEPTED;
+		status = 1;
+		break;
+	case UWB_RSV_STATE_T_ACCEPTED:
+		reason_code = UWB_DRP_REASON_ACCEPTED;
+		status = 1;
+		break;
+	case UWB_RSV_STATE_T_DENIED:
+		reason_code = UWB_DRP_REASON_DENIED;
+		status = 0;
+		break;
+	default:
+		dev_dbg(dev, "rsv with unhandled state (%d)\n", rsv->state);
+		return -EINVAL;
+	}
+
+	if (rsv->drp_ie == NULL) {
+		rsv->drp_ie = uwb_drp_ie_alloc();
+		if (rsv->drp_ie == NULL)
+			return -ENOMEM;
+	}
+	drp_ie = rsv->drp_ie;
+
+	uwb_ie_drp_set_owner(drp_ie,        uwb_rsv_is_owner(rsv));
+	uwb_ie_drp_set_status(drp_ie,       status);
+	uwb_ie_drp_set_reason_code(drp_ie,  reason_code);
+	uwb_ie_drp_set_stream_index(drp_ie, rsv->stream);
+	uwb_ie_drp_set_type(drp_ie,         rsv->type);
+
+	if (uwb_rsv_is_owner(rsv)) {
+		switch (rsv->target.type) {
+		case UWB_RSV_TARGET_DEV:
+			drp_ie->dev_addr = rsv->target.dev->dev_addr;
+			break;
+		case UWB_RSV_TARGET_DEVADDR:
+			drp_ie->dev_addr = rsv->target.devaddr;
+			break;
+		}
+	} else
+		drp_ie->dev_addr = rsv->owner->dev_addr;
+
+	uwb_drp_ie_from_bm(drp_ie, &rsv->mas);
+
+	rsv->ie_valid = true;
+	return 0;
+}
+
+/*
+ * Set MAS bits from given MAS bitmap in a single zone of large bitmap.
+ *
+ * We are given a zone id and the MAS bitmap of bits that need to be set in
+ * this zone. Note that this zone may already have bits set and this only
+ * adds settings - we cannot simply assign the MAS bitmap contents to the
+ * zone contents. We iterate over the the bits (MAS) in the zone and set the
+ * bits that are set in the given MAS bitmap.
+ */
+static
+void uwb_drp_ie_single_zone_to_bm(struct uwb_mas_bm *bm, u8 zone, u16 mas_bm)
+{
+	int mas;
+	u16 mas_mask;
+
+	for (mas = 0; mas < UWB_MAS_PER_ZONE; mas++) {
+		mas_mask = 1 << mas;
+		if (mas_bm & mas_mask)
+			set_bit(zone * UWB_NUM_ZONES + mas, bm->bm);
+	}
+}
+
+/**
+ * uwb_drp_ie_zones_to_bm - convert DRP allocation fields to a bitmap
+ * @mas:    MAS bitmap that will be populated to correspond to the
+ *          allocation fields in the DRP IE
+ * @drp_ie: the DRP IE that contains the allocation fields.
+ *
+ * The input format is an array of MAS allocation fields (16 bit Zone
+ * bitmap, 16 bit MAS bitmap) as described in [ECMA-368] section
+ * 16.8.6. The output is a full 256 bit MAS bitmap.
+ *
+ * We go over all the allocation fields, for each allocation field we
+ * know which zones are impacted. We iterate over all the zones
+ * impacted and call a function that will set the correct MAS bits in
+ * each zone.
+ */
+void uwb_drp_ie_to_bm(struct uwb_mas_bm *bm, const struct uwb_ie_drp *drp_ie)
+{
+	int numallocs = (drp_ie->hdr.length - 4) / 4;
+	const struct uwb_drp_alloc *alloc;
+	int cnt;
+	u16 zone_bm, mas_bm;
+	u8 zone;
+	u16 zone_mask;
+
+	for (cnt = 0; cnt < numallocs; cnt++) {
+		alloc = &drp_ie->allocs[cnt];
+		zone_bm = le16_to_cpu(alloc->zone_bm);
+		mas_bm = le16_to_cpu(alloc->mas_bm);
+		for (zone = 0; zone < UWB_NUM_ZONES; zone++)   {
+			zone_mask = 1 << zone;
+			if (zone_bm & zone_mask)
+				uwb_drp_ie_single_zone_to_bm(bm, zone, mas_bm);
+		}
+	}
+}
diff --git a/drivers/uwb/drp.c b/drivers/uwb/drp.c
new file mode 100644
index 0000000..c0b1e5e
--- /dev/null
+++ b/drivers/uwb/drp.c
@@ -0,0 +1,461 @@
+/*
+ * Ultra Wide Band
+ * Dynamic Reservation Protocol handling
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ * Copyright (C) 2008 Cambridge Silicon Radio Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/delay.h>
+#include "uwb-internal.h"
+
+/**
+ * Construct and send the SET DRP IE
+ *
+ * @rc:         UWB Host controller
+ * @returns:    >= 0 number of bytes still available in the beacon
+ *              < 0 errno code on error.
+ *
+ * See WUSB[8.6.2.7]: The host must set all the DRP IEs that it wants the
+ * device to include in its beacon at the same time. We thus have to
+ * traverse all reservations and include the DRP IEs of all PENDING
+ * and NEGOTIATED reservations in a SET DRP command for transmission.
+ *
+ * A DRP Availability IE is appended.
+ *
+ * rc->uwb_dev.mutex is held
+ *
+ * FIXME We currently ignore the returned value indicating the remaining space
+ * in beacon. This could be used to deny reservation requests earlier if
+ * determined that they would cause the beacon space to be exceeded.
+ */
+static
+int uwb_rc_gen_send_drp_ie(struct uwb_rc *rc)
+{
+	int result;
+	struct device *dev = &rc->uwb_dev.dev;
+	struct uwb_rc_cmd_set_drp_ie *cmd;
+	struct uwb_rc_evt_set_drp_ie reply;
+	struct uwb_rsv *rsv;
+	int num_bytes = 0;
+	u8 *IEDataptr;
+
+	result = -ENOMEM;
+	/* First traverse all reservations to determine memory needed. */
+	list_for_each_entry(rsv, &rc->reservations, rc_node) {
+		if (rsv->drp_ie != NULL)
+			num_bytes += rsv->drp_ie->hdr.length + 2;
+	}
+	num_bytes += sizeof(rc->drp_avail.ie);
+	cmd = kzalloc(sizeof(*cmd) + num_bytes, GFP_KERNEL);
+	if (cmd == NULL)
+		goto error;
+	cmd->rccb.bCommandType = UWB_RC_CET_GENERAL;
+	cmd->rccb.wCommand = cpu_to_le16(UWB_RC_CMD_SET_DRP_IE);
+	cmd->wIELength = num_bytes;
+	IEDataptr = (u8 *)&cmd->IEData[0];
+
+	/* Next traverse all reservations to place IEs in allocated memory. */
+	list_for_each_entry(rsv, &rc->reservations, rc_node) {
+		if (rsv->drp_ie != NULL) {
+			memcpy(IEDataptr, rsv->drp_ie,
+			       rsv->drp_ie->hdr.length + 2);
+			IEDataptr += rsv->drp_ie->hdr.length + 2;
+		}
+	}
+	memcpy(IEDataptr, &rc->drp_avail.ie, sizeof(rc->drp_avail.ie));
+
+	reply.rceb.bEventType = UWB_RC_CET_GENERAL;
+	reply.rceb.wEvent = UWB_RC_CMD_SET_DRP_IE;
+	result = uwb_rc_cmd(rc, "SET-DRP-IE", &cmd->rccb,
+			sizeof(*cmd) + num_bytes, &reply.rceb,
+			sizeof(reply));
+	if (result < 0)
+		goto error_cmd;
+	result = le16_to_cpu(reply.wRemainingSpace);
+	if (reply.bResultCode != UWB_RC_RES_SUCCESS) {
+		dev_err(&rc->uwb_dev.dev, "SET-DRP-IE: command execution "
+				"failed: %s (%d). RemainingSpace in beacon "
+				"= %d\n", uwb_rc_strerror(reply.bResultCode),
+				reply.bResultCode, result);
+		result = -EIO;
+	} else {
+		dev_dbg(dev, "SET-DRP-IE sent. RemainingSpace in beacon "
+			     "= %d.\n", result);
+		result = 0;
+	}
+error_cmd:
+	kfree(cmd);
+error:
+	return result;
+
+}
+/**
+ * Send all DRP IEs associated with this host
+ *
+ * @returns:    >= 0 number of bytes still available in the beacon
+ *              < 0 errno code on error.
+ *
+ * As per the protocol we obtain the host controller device lock to access
+ * bandwidth structures.
+ */
+int uwb_rc_send_all_drp_ie(struct uwb_rc *rc)
+{
+	int result;
+
+	mutex_lock(&rc->uwb_dev.mutex);
+	result = uwb_rc_gen_send_drp_ie(rc);
+	mutex_unlock(&rc->uwb_dev.mutex);
+	return result;
+}
+
+void uwb_drp_handle_timeout(struct uwb_rsv *rsv)
+{
+	struct device *dev = &rsv->rc->uwb_dev.dev;
+
+	dev_dbg(dev, "reservation timeout in state %s (%d)\n",
+		uwb_rsv_state_str(rsv->state), rsv->state);
+
+	switch (rsv->state) {
+	case UWB_RSV_STATE_O_INITIATED:
+		if (rsv->is_multicast) {
+			uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED);
+			return;
+		}
+		break;
+	case UWB_RSV_STATE_O_ESTABLISHED:
+		if (rsv->is_multicast)
+			return;
+		break;
+	default:
+		break;
+	}
+	uwb_rsv_remove(rsv);
+}
+
+/*
+ * Based on the DRP IE, transition a target reservation to a new
+ * state.
+ */
+static void uwb_drp_process_target(struct uwb_rc *rc, struct uwb_rsv *rsv,
+				   struct uwb_ie_drp *drp_ie)
+{
+	struct device *dev = &rc->uwb_dev.dev;
+	int status;
+	enum uwb_drp_reason reason_code;
+
+	status = uwb_ie_drp_status(drp_ie);
+	reason_code = uwb_ie_drp_reason_code(drp_ie);
+
+	if (status) {
+		switch (reason_code) {
+		case UWB_DRP_REASON_ACCEPTED:
+			uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_ACCEPTED);
+			break;
+		case UWB_DRP_REASON_MODIFIED:
+			dev_err(dev, "FIXME: unhandled reason code (%d/%d)\n",
+				reason_code, status);
+			break;
+		default:
+			dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n",
+				 reason_code, status);
+		}
+	} else {
+		switch (reason_code) {
+		case UWB_DRP_REASON_ACCEPTED:
+			/* New reservations are handled in uwb_rsv_find(). */
+			break;
+		case UWB_DRP_REASON_DENIED:
+			uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE);
+			break;
+		case UWB_DRP_REASON_CONFLICT:
+		case UWB_DRP_REASON_MODIFIED:
+			dev_err(dev, "FIXME: unhandled reason code (%d/%d)\n",
+				reason_code, status);
+			break;
+		default:
+			dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n",
+				 reason_code, status);
+		}
+	}
+}
+
+/*
+ * Based on the DRP IE, transition an owner reservation to a new
+ * state.
+ */
+static void uwb_drp_process_owner(struct uwb_rc *rc, struct uwb_rsv *rsv,
+				  struct uwb_ie_drp *drp_ie)
+{
+	struct device *dev = &rc->uwb_dev.dev;
+	int status;
+	enum uwb_drp_reason reason_code;
+
+	status = uwb_ie_drp_status(drp_ie);
+	reason_code = uwb_ie_drp_reason_code(drp_ie);
+
+	if (status) {
+		switch (reason_code) {
+		case UWB_DRP_REASON_ACCEPTED:
+			uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED);
+			break;
+		case UWB_DRP_REASON_MODIFIED:
+			dev_err(dev, "FIXME: unhandled reason code (%d/%d)\n",
+				reason_code, status);
+			break;
+		default:
+			dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n",
+				 reason_code, status);
+		}
+	} else {
+		switch (reason_code) {
+		case UWB_DRP_REASON_PENDING:
+			uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_PENDING);
+			break;
+		case UWB_DRP_REASON_DENIED:
+			uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE);
+			break;
+		case UWB_DRP_REASON_CONFLICT:
+		case UWB_DRP_REASON_MODIFIED:
+			dev_err(dev, "FIXME: unhandled reason code (%d/%d)\n",
+				reason_code, status);
+			break;
+		default:
+			dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n",
+				 reason_code, status);
+		}
+	}
+}
+
+/*
+ * Process a received DRP IE, it's either for a reservation owned by
+ * the RC or targeted at it (or it's for a WUSB cluster reservation).
+ */
+static void uwb_drp_process(struct uwb_rc *rc, struct uwb_dev *src,
+		     struct uwb_ie_drp *drp_ie)
+{
+	struct uwb_rsv *rsv;
+
+	rsv = uwb_rsv_find(rc, src, drp_ie);
+	if (!rsv) {
+		/*
+		 * No reservation? It's either for a recently
+		 * terminated reservation; or the DRP IE couldn't be
+		 * processed (e.g., an invalid IE or out of memory).
+		 */
+		return;
+	}
+
+	/*
+	 * Do nothing with DRP IEs for reservations that have been
+	 * terminated.
+	 */
+	if (rsv->state == UWB_RSV_STATE_NONE) {
+		uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE);
+		return;
+	}
+
+	if (uwb_ie_drp_owner(drp_ie))
+		uwb_drp_process_target(rc, rsv, drp_ie);
+	else
+		uwb_drp_process_owner(rc, rsv, drp_ie);
+}
+
+
+/*
+ * Process all the DRP IEs (both DRP IEs and the DRP Availability IE)
+ * from a device.
+ */
+static
+void uwb_drp_process_all(struct uwb_rc *rc, struct uwb_rc_evt_drp *drp_evt,
+			 size_t ielen, struct uwb_dev *src_dev)
+{
+	struct device *dev = &rc->uwb_dev.dev;
+	struct uwb_ie_hdr *ie_hdr;
+	void *ptr;
+
+	ptr = drp_evt->ie_data;
+	for (;;) {
+		ie_hdr = uwb_ie_next(&ptr, &ielen);
+		if (!ie_hdr)
+			break;
+
+		switch (ie_hdr->element_id) {
+		case UWB_IE_DRP_AVAILABILITY:
+			/* FIXME: does something need to be done with this? */
+			break;
+		case UWB_IE_DRP:
+			uwb_drp_process(rc, src_dev, (struct uwb_ie_drp *)ie_hdr);
+			break;
+		default:
+			dev_warn(dev, "unexpected IE in DRP notification\n");
+			break;
+		}
+	}
+
+	if (ielen > 0)
+		dev_warn(dev, "%d octets remaining in DRP notification\n",
+			 (int)ielen);
+}
+
+
+/*
+ * Go through all the DRP IEs and find the ones that conflict with our
+ * reservations.
+ *
+ * FIXME: must resolve the conflict according the the rules in
+ * [ECMA-368].
+ */
+static
+void uwb_drp_process_conflict_all(struct uwb_rc *rc, struct uwb_rc_evt_drp *drp_evt,
+				  size_t ielen, struct uwb_dev *src_dev)
+{
+	struct device *dev = &rc->uwb_dev.dev;
+	struct uwb_ie_hdr *ie_hdr;
+	struct uwb_ie_drp *drp_ie;
+	void *ptr;
+
+	ptr = drp_evt->ie_data;
+	for (;;) {
+		ie_hdr = uwb_ie_next(&ptr, &ielen);
+		if (!ie_hdr)
+			break;
+
+		drp_ie = container_of(ie_hdr, struct uwb_ie_drp, hdr);
+
+		/* FIXME: check if this DRP IE conflicts. */
+	}
+
+	if (ielen > 0)
+		dev_warn(dev, "%d octets remaining in DRP notification\n",
+			 (int)ielen);
+}
+
+
+/*
+ * Terminate all reservations owned by, or targeted at, 'uwb_dev'.
+ */
+static void uwb_drp_terminate_all(struct uwb_rc *rc, struct uwb_dev *uwb_dev)
+{
+	struct uwb_rsv *rsv;
+
+	list_for_each_entry(rsv, &rc->reservations, rc_node) {
+		if (rsv->owner == uwb_dev
+		    || (rsv->target.type == UWB_RSV_TARGET_DEV && rsv->target.dev == uwb_dev))
+			uwb_rsv_remove(rsv);
+	}
+}
+
+
+/**
+ * uwbd_evt_handle_rc_drp - handle a DRP_IE event
+ * @evt: the DRP_IE event from the radio controller
+ *
+ * This processes DRP notifications from the radio controller, either
+ * initiating a new reservation or transitioning an existing
+ * reservation into a different state.
+ *
+ * DRP notifications can occur for three different reasons:
+ *
+ * - UWB_DRP_NOTIF_DRP_IE_RECVD: one or more DRP IEs with the RC as
+ *   the target or source have been recieved.
+ *
+ *   These DRP IEs could be new or for an existing reservation.
+ *
+ *   If the DRP IE for an existing reservation ceases to be to
+ *   recieved for at least mMaxLostBeacons, the reservation should be
+ *   considered to be terminated.  Note that the TERMINATE reason (see
+ *   below) may not always be signalled (e.g., the remote device has
+ *   two or more reservations established with the RC).
+ *
+ * - UWB_DRP_NOTIF_CONFLICT: DRP IEs from any device in the beacon
+ *   group conflict with the RC's reservations.
+ *
+ * - UWB_DRP_NOTIF_TERMINATE: DRP IEs are no longer being received
+ *   from a device (i.e., it's terminated all reservations).
+ *
+ * Only the software state of the reservations is changed; the setting
+ * of the radio controller's DRP IEs is done after all the events in
+ * an event buffer are processed.  This saves waiting multiple times
+ * for the SET_DRP_IE command to complete.
+ */
+int uwbd_evt_handle_rc_drp(struct uwb_event *evt)
+{
+	struct device *dev = &evt->rc->uwb_dev.dev;
+	struct uwb_rc *rc = evt->rc;
+	struct uwb_rc_evt_drp *drp_evt;
+	size_t ielength, bytes_left;
+	struct uwb_dev_addr src_addr;
+	struct uwb_dev *src_dev;
+	int reason;
+
+	/* Is there enough data to decode the event (and any IEs in
+	   its payload)? */
+	if (evt->notif.size < sizeof(*drp_evt)) {
+		dev_err(dev, "DRP event: Not enough data to decode event "
+			"[%zu bytes left, %zu needed]\n",
+			evt->notif.size, sizeof(*drp_evt));
+		return 0;
+	}
+	bytes_left = evt->notif.size - sizeof(*drp_evt);
+	drp_evt = container_of(evt->notif.rceb, struct uwb_rc_evt_drp, rceb);
+	ielength = le16_to_cpu(drp_evt->ie_length);
+	if (bytes_left != ielength) {
+		dev_err(dev, "DRP event: Not enough data in payload [%zu"
+			"bytes left, %zu declared in the event]\n",
+			bytes_left, ielength);
+		return 0;
+	}
+
+	memcpy(src_addr.data, &drp_evt->src_addr, sizeof(src_addr));
+	src_dev = uwb_dev_get_by_devaddr(rc, &src_addr);
+	if (!src_dev) {
+		/*
+		 * A DRP notification from an unrecognized device.
+		 *
+		 * This is probably from a WUSB device that doesn't
+		 * have an EUI-48 and therefore doesn't show up in the
+		 * UWB device database.  It's safe to simply ignore
+		 * these.
+		 */
+		return 0;
+	}
+
+	mutex_lock(&rc->rsvs_mutex);
+
+	reason = uwb_rc_evt_drp_reason(drp_evt);
+
+	switch (reason) {
+	case UWB_DRP_NOTIF_DRP_IE_RCVD:
+		uwb_drp_process_all(rc, drp_evt, ielength, src_dev);
+		break;
+	case UWB_DRP_NOTIF_CONFLICT:
+		uwb_drp_process_conflict_all(rc, drp_evt, ielength, src_dev);
+		break;
+	case UWB_DRP_NOTIF_TERMINATE:
+		uwb_drp_terminate_all(rc, src_dev);
+		break;
+	default:
+		dev_warn(dev, "ignored DRP event with reason code: %d\n", reason);
+		break;
+	}
+
+	mutex_unlock(&rc->rsvs_mutex);
+
+	uwb_dev_put(src_dev);
+	return 0;
+}
diff --git a/drivers/uwb/rsv.c b/drivers/uwb/rsv.c
new file mode 100644
index 0000000..bae16204
--- /dev/null
+++ b/drivers/uwb/rsv.c
@@ -0,0 +1,680 @@
+/*
+ * UWB reservation management.
+ *
+ * Copyright (C) 2008 Cambridge Silicon Radio Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/uwb.h>
+
+#include "uwb-internal.h"
+
+static void uwb_rsv_timer(unsigned long arg);
+
+static const char *rsv_states[] = {
+	[UWB_RSV_STATE_NONE]          = "none",
+	[UWB_RSV_STATE_O_INITIATED]   = "initiated",
+	[UWB_RSV_STATE_O_PENDING]     = "pending",
+	[UWB_RSV_STATE_O_MODIFIED]    = "modified",
+	[UWB_RSV_STATE_O_ESTABLISHED] = "established",
+	[UWB_RSV_STATE_T_ACCEPTED]    = "accepted",
+	[UWB_RSV_STATE_T_DENIED]      = "denied",
+	[UWB_RSV_STATE_T_PENDING]     = "pending",
+};
+
+static const char *rsv_types[] = {
+	[UWB_DRP_TYPE_ALIEN_BP] = "alien-bp",
+	[UWB_DRP_TYPE_HARD]     = "hard",
+	[UWB_DRP_TYPE_SOFT]     = "soft",
+	[UWB_DRP_TYPE_PRIVATE]  = "private",
+	[UWB_DRP_TYPE_PCA]      = "pca",
+};
+
+/**
+ * uwb_rsv_state_str - return a string for a reservation state
+ * @state: the reservation state.
+ */
+const char *uwb_rsv_state_str(enum uwb_rsv_state state)
+{
+	if (state < UWB_RSV_STATE_NONE || state >= UWB_RSV_STATE_LAST)
+		return "unknown";
+	return rsv_states[state];
+}
+EXPORT_SYMBOL_GPL(uwb_rsv_state_str);
+
+/**
+ * uwb_rsv_type_str - return a string for a reservation type
+ * @type: the reservation type
+ */
+const char *uwb_rsv_type_str(enum uwb_drp_type type)
+{
+	if (type < UWB_DRP_TYPE_ALIEN_BP || type > UWB_DRP_TYPE_PCA)
+		return "invalid";
+	return rsv_types[type];
+}
+EXPORT_SYMBOL_GPL(uwb_rsv_type_str);
+
+static void uwb_rsv_dump(struct uwb_rsv *rsv)
+{
+	struct device *dev = &rsv->rc->uwb_dev.dev;
+	struct uwb_dev_addr devaddr;
+	char owner[UWB_ADDR_STRSIZE], target[UWB_ADDR_STRSIZE];
+
+	uwb_dev_addr_print(owner, sizeof(owner), &rsv->owner->dev_addr);
+	if (rsv->target.type == UWB_RSV_TARGET_DEV)
+		devaddr = rsv->target.dev->dev_addr;
+	else
+		devaddr = rsv->target.devaddr;
+	uwb_dev_addr_print(target, sizeof(target), &devaddr);
+
+	dev_dbg(dev, "rsv %s -> %s: %s\n", owner, target, uwb_rsv_state_str(rsv->state));
+}
+
+/*
+ * Get a free stream index for a reservation.
+ *
+ * If the target is a DevAddr (e.g., a WUSB cluster reservation) then
+ * the stream is allocated from a pool of per-RC stream indexes,
+ * otherwise a unique stream index for the target is selected.
+ */
+static int uwb_rsv_get_stream(struct uwb_rsv *rsv)
+{
+	struct uwb_rc *rc = rsv->rc;
+	unsigned long *streams_bm;
+	int stream;
+
+	switch (rsv->target.type) {
+	case UWB_RSV_TARGET_DEV:
+		streams_bm = rsv->target.dev->streams;
+		break;
+	case UWB_RSV_TARGET_DEVADDR:
+		streams_bm = rc->uwb_dev.streams;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	stream = find_first_zero_bit(streams_bm, UWB_NUM_STREAMS);
+	if (stream >= UWB_NUM_STREAMS)
+		return -EBUSY;
+
+	rsv->stream = stream;
+	set_bit(stream, streams_bm);
+
+	return 0;
+}
+
+static void uwb_rsv_put_stream(struct uwb_rsv *rsv)
+{
+	struct uwb_rc *rc = rsv->rc;
+	unsigned long *streams_bm;
+
+	switch (rsv->target.type) {
+	case UWB_RSV_TARGET_DEV:
+		streams_bm = rsv->target.dev->streams;
+		break;
+	case UWB_RSV_TARGET_DEVADDR:
+		streams_bm = rc->uwb_dev.streams;
+		break;
+	default:
+		return;
+	}
+
+	clear_bit(rsv->stream, streams_bm);
+}
+
+/*
+ * Generate a MAS allocation with a single row component.
+ */
+static void uwb_rsv_gen_alloc_row(struct uwb_mas_bm *mas,
+				  int first_mas, int mas_per_zone,
+				  int zs, int ze)
+{
+	struct uwb_mas_bm col;
+	int z;
+
+	bitmap_zero(mas->bm, UWB_NUM_MAS);
+	bitmap_zero(col.bm, UWB_NUM_MAS);
+	bitmap_fill(col.bm, mas_per_zone);
+	bitmap_shift_left(col.bm, col.bm, first_mas + zs * UWB_MAS_PER_ZONE, UWB_NUM_MAS);
+
+	for (z = zs; z <= ze; z++) {
+		bitmap_or(mas->bm, mas->bm, col.bm, UWB_NUM_MAS);
+		bitmap_shift_left(col.bm, col.bm, UWB_MAS_PER_ZONE, UWB_NUM_MAS);
+	}
+}
+
+/*
+ * Allocate some MAS for this reservation based on current local
+ * availability, the reservation parameters (max_mas, min_mas,
+ * sparsity), and the WiMedia rules for MAS allocations.
+ *
+ * Returns -EBUSY is insufficient free MAS are available.
+ *
+ * FIXME: to simplify this, only safe reservations with a single row
+ * component in zones 1 to 15 are tried (zone 0 is skipped to avoid
+ * problems with the MAS reserved for the BP).
+ *
+ * [ECMA-368] section B.2.
+ */
+static int uwb_rsv_alloc_mas(struct uwb_rsv *rsv)
+{
+	static const int safe_mas_in_row[UWB_NUM_ZONES] = {
+		8, 7, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 2, 1,
+	};
+	int n, r;
+	struct uwb_mas_bm mas;
+	bool found = false;
+
+	/*
+	 * Search all valid safe allocations until either: too few MAS
+	 * are available; or the smallest allocation with sufficient
+	 * MAS is found.
+	 *
+	 * The top of the zones are preferred, so space for larger
+	 * allocations is available in the bottom of the zone (e.g., a
+	 * 15 MAS allocation should start in row 14 leaving space for
+	 * a 120 MAS allocation at row 0).
+	 */
+	for (n = safe_mas_in_row[0]; n >= 1; n--) {
+		int num_mas;
+
+		num_mas = n * (UWB_NUM_ZONES - 1);
+		if (num_mas < rsv->min_mas)
+			break;
+		if (found && num_mas < rsv->max_mas)
+			break;
+
+		for (r = UWB_MAS_PER_ZONE-1;  r >= 0; r--) {
+			if (safe_mas_in_row[r] < n)
+				continue;
+			uwb_rsv_gen_alloc_row(&mas, r, n, 1, UWB_NUM_ZONES);
+			if (uwb_drp_avail_reserve_pending(rsv->rc, &mas) == 0) {
+				found = true;
+				break;
+			}
+		}
+	}
+
+	if (!found)
+		return -EBUSY;
+
+	bitmap_copy(rsv->mas.bm, mas.bm, UWB_NUM_MAS);
+	return 0;
+}
+
+static void uwb_rsv_stroke_timer(struct uwb_rsv *rsv)
+{
+	int sframes = UWB_MAX_LOST_BEACONS;
+
+	/*
+	 * Multicast reservations can become established within 1
+	 * super frame and should not be terminated if no response is
+	 * received.
+	 */
+	if (rsv->is_multicast) {
+		if (rsv->state == UWB_RSV_STATE_O_INITIATED)
+			sframes = 1;
+		if (rsv->state == UWB_RSV_STATE_O_ESTABLISHED)
+			sframes = 0;
+	}
+
+	rsv->expired = false;
+	if (sframes > 0) {
+		/*
+		 * Add an additional 2 superframes to account for the
+		 * time to send the SET DRP IE command.
+		 */
+		unsigned timeout_us = (sframes + 2) * UWB_SUPERFRAME_LENGTH_US;
+		mod_timer(&rsv->timer, jiffies + usecs_to_jiffies(timeout_us));
+	} else
+		del_timer(&rsv->timer);
+}
+
+/*
+ * Update a reservations state, and schedule an update of the
+ * transmitted DRP IEs.
+ */
+static void uwb_rsv_state_update(struct uwb_rsv *rsv,
+				 enum uwb_rsv_state new_state)
+{
+	rsv->state = new_state;
+	rsv->ie_valid = false;
+
+	uwb_rsv_dump(rsv);
+
+	uwb_rsv_stroke_timer(rsv);
+	uwb_rsv_sched_update(rsv->rc);
+}
+
+static void uwb_rsv_callback(struct uwb_rsv *rsv)
+{
+	if (rsv->callback)
+		rsv->callback(rsv);
+}
+
+void uwb_rsv_set_state(struct uwb_rsv *rsv, enum uwb_rsv_state new_state)
+{
+	if (rsv->state == new_state) {
+		switch (rsv->state) {
+		case UWB_RSV_STATE_O_ESTABLISHED:
+		case UWB_RSV_STATE_T_ACCEPTED:
+		case UWB_RSV_STATE_NONE:
+			uwb_rsv_stroke_timer(rsv);
+			break;
+		default:
+			/* Expecting a state transition so leave timer
+			   as-is. */
+			break;
+		}
+		return;
+	}
+
+	switch (new_state) {
+	case UWB_RSV_STATE_NONE:
+		uwb_drp_avail_release(rsv->rc, &rsv->mas);
+		uwb_rsv_put_stream(rsv);
+		uwb_rsv_state_update(rsv, UWB_RSV_STATE_NONE);
+		uwb_rsv_callback(rsv);
+		break;
+	case UWB_RSV_STATE_O_INITIATED:
+		uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_INITIATED);
+		break;
+	case UWB_RSV_STATE_O_PENDING:
+		uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_PENDING);
+		break;
+	case UWB_RSV_STATE_O_ESTABLISHED:
+		uwb_drp_avail_reserve(rsv->rc, &rsv->mas);
+		uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_ESTABLISHED);
+		uwb_rsv_callback(rsv);
+		break;
+	case UWB_RSV_STATE_T_ACCEPTED:
+		uwb_drp_avail_reserve(rsv->rc, &rsv->mas);
+		uwb_rsv_state_update(rsv, UWB_RSV_STATE_T_ACCEPTED);
+		uwb_rsv_callback(rsv);
+		break;
+	case UWB_RSV_STATE_T_DENIED:
+		uwb_rsv_state_update(rsv, UWB_RSV_STATE_T_DENIED);
+		break;
+	default:
+		dev_err(&rsv->rc->uwb_dev.dev, "unhandled state: %s (%d)\n",
+			uwb_rsv_state_str(new_state), new_state);
+	}
+}
+
+static struct uwb_rsv *uwb_rsv_alloc(struct uwb_rc *rc)
+{
+	struct uwb_rsv *rsv;
+
+	rsv = kzalloc(sizeof(struct uwb_rsv), GFP_KERNEL);
+	if (!rsv)
+		return NULL;
+
+	INIT_LIST_HEAD(&rsv->rc_node);
+	INIT_LIST_HEAD(&rsv->pal_node);
+	init_timer(&rsv->timer);
+	rsv->timer.function = uwb_rsv_timer;
+	rsv->timer.data     = (unsigned long)rsv;
+
+	rsv->rc = rc;
+
+	return rsv;
+}
+
+static void uwb_rsv_free(struct uwb_rsv *rsv)
+{
+	uwb_dev_put(rsv->owner);
+	if (rsv->target.type == UWB_RSV_TARGET_DEV)
+		uwb_dev_put(rsv->target.dev);
+	kfree(rsv);
+}
+
+/**
+ * uwb_rsv_create - allocate and initialize a UWB reservation structure
+ * @rc: the radio controller
+ * @cb: callback to use when the reservation completes or terminates
+ * @pal_priv: data private to the PAL to be passed in the callback
+ *
+ * The callback is called when the state of the reservation changes from:
+ *
+ *   - pending to accepted
+ *   - pending to denined
+ *   - accepted to terminated
+ *   - pending to terminated
+ */
+struct uwb_rsv *uwb_rsv_create(struct uwb_rc *rc, uwb_rsv_cb_f cb, void *pal_priv)
+{
+	struct uwb_rsv *rsv;
+
+	rsv = uwb_rsv_alloc(rc);
+	if (!rsv)
+		return NULL;
+
+	rsv->callback = cb;
+	rsv->pal_priv = pal_priv;
+
+	return rsv;
+}
+EXPORT_SYMBOL_GPL(uwb_rsv_create);
+
+void uwb_rsv_remove(struct uwb_rsv *rsv)
+{
+	if (rsv->state != UWB_RSV_STATE_NONE)
+		uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE);
+	del_timer_sync(&rsv->timer);
+	list_del(&rsv->rc_node);
+	uwb_rsv_free(rsv);
+}
+
+/**
+ * uwb_rsv_destroy - free a UWB reservation structure
+ * @rsv: the reservation to free
+ *
+ * The reservation will be terminated if it is pending or established.
+ */
+void uwb_rsv_destroy(struct uwb_rsv *rsv)
+{
+	struct uwb_rc *rc = rsv->rc;
+
+	mutex_lock(&rc->rsvs_mutex);
+	uwb_rsv_remove(rsv);
+	mutex_unlock(&rc->rsvs_mutex);
+}
+EXPORT_SYMBOL_GPL(uwb_rsv_destroy);
+
+/**
+ * usb_rsv_establish - start a reservation establishment
+ * @rsv: the reservation
+ *
+ * The PAL should fill in @rsv's owner, target, type, max_mas,
+ * min_mas, sparsity and is_multicast fields.  If the target is a
+ * uwb_dev it must be referenced.
+ *
+ * The reservation's callback will be called when the reservation is
+ * accepted, denied or times out.
+ */
+int uwb_rsv_establish(struct uwb_rsv *rsv)
+{
+	struct uwb_rc *rc = rsv->rc;
+	int ret;
+
+	mutex_lock(&rc->rsvs_mutex);
+
+	ret = uwb_rsv_get_stream(rsv);
+	if (ret)
+		goto out;
+
+	ret = uwb_rsv_alloc_mas(rsv);
+	if (ret) {
+		uwb_rsv_put_stream(rsv);
+		goto out;
+	}
+
+	list_add_tail(&rsv->rc_node, &rc->reservations);
+	rsv->owner = &rc->uwb_dev;
+	uwb_dev_get(rsv->owner);
+	uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_INITIATED);
+out:
+	mutex_unlock(&rc->rsvs_mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(uwb_rsv_establish);
+
+/**
+ * uwb_rsv_modify - modify an already established reservation
+ * @rsv: the reservation to modify
+ * @max_mas: new maximum MAS to reserve
+ * @min_mas: new minimum MAS to reserve
+ * @sparsity: new sparsity to use
+ *
+ * FIXME: implement this once there are PALs that use it.
+ */
+int uwb_rsv_modify(struct uwb_rsv *rsv, int max_mas, int min_mas, int sparsity)
+{
+	return -ENOSYS;
+}
+EXPORT_SYMBOL_GPL(uwb_rsv_modify);
+
+/**
+ * uwb_rsv_terminate - terminate an established reservation
+ * @rsv: the reservation to terminate
+ *
+ * A reservation is terminated by removing the DRP IE from the beacon,
+ * the other end will consider the reservation to be terminated when
+ * it does not see the DRP IE for at least mMaxLostBeacons.
+ *
+ * If applicable, the reference to the target uwb_dev will be released.
+ */
+void uwb_rsv_terminate(struct uwb_rsv *rsv)
+{
+	struct uwb_rc *rc = rsv->rc;
+
+	mutex_lock(&rc->rsvs_mutex);
+
+	uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE);
+
+	mutex_unlock(&rc->rsvs_mutex);
+}
+EXPORT_SYMBOL_GPL(uwb_rsv_terminate);
+
+/**
+ * uwb_rsv_accept - accept a new reservation from a peer
+ * @rsv:      the reservation
+ * @cb:       call back for reservation changes
+ * @pal_priv: data to be passed in the above call back
+ *
+ * Reservation requests from peers are denied unless a PAL accepts it
+ * by calling this function.
+ */
+void uwb_rsv_accept(struct uwb_rsv *rsv, uwb_rsv_cb_f cb, void *pal_priv)
+{
+	rsv->callback = cb;
+	rsv->pal_priv = pal_priv;
+	rsv->state    = UWB_RSV_STATE_T_ACCEPTED;
+}
+EXPORT_SYMBOL_GPL(uwb_rsv_accept);
+
+/*
+ * Is a received DRP IE for this reservation?
+ */
+static bool uwb_rsv_match(struct uwb_rsv *rsv, struct uwb_dev *src,
+			  struct uwb_ie_drp *drp_ie)
+{
+	struct uwb_dev_addr *rsv_src;
+	int stream;
+
+	stream = uwb_ie_drp_stream_index(drp_ie);
+
+	if (rsv->stream != stream)
+		return false;
+
+	switch (rsv->target.type) {
+	case UWB_RSV_TARGET_DEVADDR:
+		return rsv->stream == stream;
+	case UWB_RSV_TARGET_DEV:
+		if (uwb_ie_drp_owner(drp_ie))
+			rsv_src = &rsv->owner->dev_addr;
+		else
+			rsv_src = &rsv->target.dev->dev_addr;
+		return uwb_dev_addr_cmp(&src->dev_addr, rsv_src) == 0;
+	}
+	return false;
+}
+
+static struct uwb_rsv *uwb_rsv_new_target(struct uwb_rc *rc,
+					  struct uwb_dev *src,
+					  struct uwb_ie_drp *drp_ie)
+{
+	struct uwb_rsv *rsv;
+	struct uwb_pal *pal;
+	enum uwb_rsv_state state;
+
+	rsv = uwb_rsv_alloc(rc);
+	if (!rsv)
+		return NULL;
+
+	rsv->rc          = rc;
+	rsv->owner       = src;
+	uwb_dev_get(rsv->owner);
+	rsv->target.type = UWB_RSV_TARGET_DEV;
+	rsv->target.dev  = &rc->uwb_dev;
+	rsv->type        = uwb_ie_drp_type(drp_ie);
+	rsv->stream      = uwb_ie_drp_stream_index(drp_ie);
+	set_bit(rsv->stream, rsv->owner->streams);
+	uwb_drp_ie_to_bm(&rsv->mas, drp_ie);
+
+	/*
+	 * See if any PALs are interested in this reservation. If not,
+	 * deny the request.
+	 */
+	rsv->state = UWB_RSV_STATE_T_DENIED;
+	spin_lock(&rc->pal_lock);
+	list_for_each_entry(pal, &rc->pals, node) {
+		if (pal->new_rsv)
+			pal->new_rsv(rsv);
+		if (rsv->state == UWB_RSV_STATE_T_ACCEPTED)
+			break;
+	}
+	spin_unlock(&rc->pal_lock);
+
+	list_add_tail(&rsv->rc_node, &rc->reservations);
+	state = rsv->state;
+	rsv->state = UWB_RSV_STATE_NONE;
+	uwb_rsv_set_state(rsv, state);
+
+	return rsv;
+}
+
+/**
+ * uwb_rsv_find - find a reservation for a received DRP IE.
+ * @rc: the radio controller
+ * @src: source of the DRP IE
+ * @drp_ie: the DRP IE
+ *
+ * If the reservation cannot be found and the DRP IE is from a peer
+ * attempting to establish a new reservation, create a new reservation
+ * and add it to the list.
+ */
+struct uwb_rsv *uwb_rsv_find(struct uwb_rc *rc, struct uwb_dev *src,
+			     struct uwb_ie_drp *drp_ie)
+{
+	struct uwb_rsv *rsv;
+
+	list_for_each_entry(rsv, &rc->reservations, rc_node) {
+		if (uwb_rsv_match(rsv, src, drp_ie))
+			return rsv;
+	}
+
+	if (uwb_ie_drp_owner(drp_ie))
+		return uwb_rsv_new_target(rc, src, drp_ie);
+
+	return NULL;
+}
+
+/*
+ * Go through all the reservations and check for timeouts and (if
+ * necessary) update their DRP IEs.
+ *
+ * FIXME: look at building the SET_DRP_IE command here rather than
+ * having to rescan the list in uwb_rc_send_all_drp_ie().
+ */
+static bool uwb_rsv_update_all(struct uwb_rc *rc)
+{
+	struct uwb_rsv *rsv, *t;
+	bool ie_updated = false;
+
+	list_for_each_entry_safe(rsv, t, &rc->reservations, rc_node) {
+		if (rsv->expired)
+			uwb_drp_handle_timeout(rsv);
+		if (!rsv->ie_valid) {
+			uwb_drp_ie_update(rsv);
+			ie_updated = true;
+		}
+	}
+
+	return ie_updated;
+}
+
+void uwb_rsv_sched_update(struct uwb_rc *rc)
+{
+	queue_work(rc->rsv_workq, &rc->rsv_update_work);
+}
+
+/*
+ * Update DRP IEs and, if necessary, the DRP Availability IE and send
+ * the updated IEs to the radio controller.
+ */
+static void uwb_rsv_update_work(struct work_struct *work)
+{
+	struct uwb_rc *rc = container_of(work, struct uwb_rc, rsv_update_work);
+	bool ie_updated;
+
+	mutex_lock(&rc->rsvs_mutex);
+
+	ie_updated = uwb_rsv_update_all(rc);
+
+	if (!rc->drp_avail.ie_valid) {
+		uwb_drp_avail_ie_update(rc);
+		ie_updated = true;
+	}
+
+	if (ie_updated)
+		uwb_rc_send_all_drp_ie(rc);
+
+	mutex_unlock(&rc->rsvs_mutex);
+}
+
+static void uwb_rsv_timer(unsigned long arg)
+{
+	struct uwb_rsv *rsv = (struct uwb_rsv *)arg;
+
+	rsv->expired = true;
+	uwb_rsv_sched_update(rsv->rc);
+}
+
+void uwb_rsv_init(struct uwb_rc *rc)
+{
+	INIT_LIST_HEAD(&rc->reservations);
+	mutex_init(&rc->rsvs_mutex);
+	INIT_WORK(&rc->rsv_update_work, uwb_rsv_update_work);
+
+	bitmap_complement(rc->uwb_dev.streams, rc->uwb_dev.streams, UWB_NUM_STREAMS);
+}
+
+int uwb_rsv_setup(struct uwb_rc *rc)
+{
+	char name[16];
+
+	snprintf(name, sizeof(name), "%s_rsvd", dev_name(&rc->uwb_dev.dev));
+	rc->rsv_workq = create_singlethread_workqueue(name);
+	if (rc->rsv_workq == NULL)
+		return -ENOMEM;
+
+	return 0;
+}
+
+void uwb_rsv_cleanup(struct uwb_rc *rc)
+{
+	struct uwb_rsv *rsv, *t;
+
+	mutex_lock(&rc->rsvs_mutex);
+	list_for_each_entry_safe(rsv, t, &rc->reservations, rc_node) {
+		uwb_rsv_remove(rsv);
+	}
+	mutex_unlock(&rc->rsvs_mutex);
+
+	cancel_work_sync(&rc->rsv_update_work);
+	destroy_workqueue(rc->rsv_workq);
+}