提交 8cc13a09 编写于 作者: D David Vrabel 提交者: David Vrabel

uwb: add the UWB stack (reservation manager)

DRP and reservation management.
Signed-off-by: NDavid Vrabel <david.vrabel@csr.com>
上级 22d203ec
master alk-4.19.24 alk-4.19.30 alk-4.19.34 alk-4.19.36 alk-4.19.43 alk-4.19.48 alk-4.19.57 ck-4.19.67 ck-4.19.81 ck-4.19.91 github/fork/deepanshu1422/fix-typo-in-comment github/fork/haosdent/fix-typo linux-next v4.19.91 v4.19.90 v4.19.89 v4.19.88 v4.19.87 v4.19.86 v4.19.85 v4.19.84 v4.19.83 v4.19.82 v4.19.81 v4.19.80 v4.19.79 v4.19.78 v4.19.77 v4.19.76 v4.19.75 v4.19.74 v4.19.73 v4.19.72 v4.19.71 v4.19.70 v4.19.69 v4.19.68 v4.19.67 v4.19.66 v4.19.65 v4.19.64 v4.19.63 v4.19.62 v4.19.61 v4.19.60 v4.19.59 v4.19.58 v4.19.57 v4.19.56 v4.19.55 v4.19.54 v4.19.53 v4.19.52 v4.19.51 v4.19.50 v4.19.49 v4.19.48 v4.19.47 v4.19.46 v4.19.45 v4.19.44 v4.19.43 v4.19.42 v4.19.41 v4.19.40 v4.19.39 v4.19.38 v4.19.37 v4.19.36 v4.19.35 v4.19.34 v4.19.33 v4.19.32 v4.19.31 v4.19.30 v4.19.29 v4.19.28 v4.19.27 v4.19.26 v4.19.25 v4.19.24 v4.19.23 v4.19.22 v4.19.21 v4.19.20 v4.19.19 v4.19.18 v4.19.17 v4.19.16 v4.19.15 v4.19.14 v4.19.13 v4.19.12 v4.19.11 v4.19.10 v4.19.9 v4.19.8 v4.19.7 v4.19.6 v4.19.5 v4.19.4 v4.19.3 v4.19.2 v4.19.1 v4.19 v4.19-rc8 v4.19-rc7 v4.19-rc6 v4.19-rc5 v4.19-rc4 v4.19-rc3 v4.19-rc2 v4.19-rc1 ck-release-21 ck-release-20 ck-release-19.2 ck-release-19.1 ck-release-19 ck-release-18 ck-release-17.2 ck-release-17.1 ck-release-17 ck-release-16 ck-release-15.1 ck-release-15 ck-release-14 ck-release-13.2 ck-release-13 ck-release-12 ck-release-11 ck-release-10 ck-release-9 ck-release-7 alk-release-15 alk-release-14 alk-release-13.2 alk-release-13 alk-release-12 alk-release-11 alk-release-10 alk-release-9 alk-release-7
无相关合并请求
/*
* 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;
}
/*
* 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);
}
}
}
/*
* 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;
}
/*
* 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);
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册
反馈
建议
客服 返回
顶部