Skip to content
Snippets Groups Projects
Commit caa6772d authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman
Browse files

Staging: remove wusbcore and UWB from the kernel tree.

It's been over 6 months, and no one has noticed that these drivers are
deleted, probably because no one actually has this hardware.  As no one
has volunteered to maintain the code, let's drop it for good.

Link: https://lore.kernel.org/r/20200210231417.GA1736729@kroah.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 4fb8b5aa
No related branches found
No related tags found
No related merge requests found
Showing
with 0 additions and 5347 deletions
......@@ -3919,11 +3919,6 @@ F: certs/
F: scripts/sign-file.c
F: scripts/extract-cert.c
 
CERTIFIED WIRELESS USB (WUSB) SUBSYSTEM:
L: devel@driverdev.osuosl.org
S: Obsolete
F: drivers/staging/wusbcore/
CFAG12864B LCD DRIVER
M: Miguel Ojeda Sandonis <miguel.ojeda.sandonis@gmail.com>
S: Maintained
......@@ -17094,11 +17089,6 @@ S: Maintained
F: drivers/usb/common/ulpi.c
F: include/linux/ulpi/
 
ULTRA-WIDEBAND (UWB) SUBSYSTEM:
L: devel@driverdev.osuosl.org
S: Obsolete
F: drivers/staging/uwb/
UNICODE SUBSYSTEM:
M: Gabriel Krisman Bertazi <krisman@collabora.com>
L: linux-fsdevel@vger.kernel.org
......
......@@ -112,9 +112,6 @@ source "drivers/staging/fieldbus/Kconfig"
source "drivers/staging/kpc2000/Kconfig"
source "drivers/staging/wusbcore/Kconfig"
source "drivers/staging/uwb/Kconfig"
source "drivers/staging/exfat/Kconfig"
source "drivers/staging/qlge/Kconfig"
......
......@@ -46,8 +46,6 @@ obj-$(CONFIG_STAGING_GASKET_FRAMEWORK) += gasket/
obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/
obj-$(CONFIG_FIELDBUS_DEV) += fieldbus/
obj-$(CONFIG_KPC2000) += kpc2000/
obj-$(CONFIG_UWB) += uwb/
obj-$(CONFIG_USB_WUSB) += wusbcore/
obj-$(CONFIG_STAGING_EXFAT_FS) += exfat/
obj-$(CONFIG_QLGE) += qlge/
obj-$(CONFIG_NET_VENDOR_HP) += hp/
......
# SPDX-License-Identifier: GPL-2.0-only
#
# UWB device configuration
#
menuconfig UWB
tristate "Ultra Wideband devices"
default n
select GENERIC_NET_UTILS
help
UWB is a high-bandwidth, low-power, point-to-point radio
technology using a wide spectrum (3.1-10.6GHz). It is
optimized for in-room use (480Mbps at 2 meters, 110Mbps at
10m). It serves as the transport layer for other protocols,
such as Wireless USB (WUSB).
The topology is peer to peer; however, higher level
protocols (such as WUSB) might impose a master/slave
relationship.
Say Y here if your computer has UWB radio controllers (USB or PCI)
based. You will need to enable the radio controllers
below. It is ok to select all of them, no harm done.
For more help check the UWB and WUSB related files in
<file:Documentation/usb/>.
To compile the UWB stack as a module, choose M here.
if UWB
config UWB_HWA
tristate "UWB Radio Control driver for WUSB-compliant USB dongles (HWA)"
depends on USB
help
This driver enables the radio controller for HWA USB
devices. HWA stands for Host Wire Adapter, and it is a UWB
Radio Controller connected to your system via USB. Most of
them come with a Wireless USB host controller also.
To compile this driver select Y (built in) or M (module). It
is safe to select any even if you do not have the hardware.
config UWB_WHCI
tristate "UWB Radio Control driver for WHCI-compliant cards"
depends on PCI
help
This driver enables the radio controller for WHCI cards.
WHCI is a specification developed by Intel
(http://www.intel.com/technology/comms/wusb/whci.htm) much
in the spirit of USB's EHCI, but for UWB and Wireless USB
radio/host controllers connected via memory mapping (eg:
PCI). Most of these cards come also with a Wireless USB host
controller.
To compile this driver select Y (built in) or M (module). It
is safe to select any even if you do not have the hardware.
config UWB_I1480U
tristate "Support for Intel Wireless UWB Link 1480 HWA"
depends on UWB_HWA
select FW_LOADER
help
This driver enables support for the i1480 when connected via
USB. It consists of a firmware uploader that will enable it
to behave as an HWA device.
To compile this driver select Y (built in) or M (module). It
is safe to select any even if you do not have the hardware.
endif # UWB
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_UWB) += uwb.o
obj-$(CONFIG_UWB_WHCI) += umc.o whci.o whc-rc.o
obj-$(CONFIG_UWB_HWA) += hwa-rc.o
obj-$(CONFIG_UWB_I1480U) += i1480/
uwb-objs := \
address.o \
allocator.o \
beacon.o \
driver.o \
drp.o \
drp-avail.o \
drp-ie.o \
est.o \
ie.o \
ie-rcv.o \
lc-dev.o \
lc-rc.o \
neh.o \
pal.o \
radio.o \
reset.o \
rsv.o \
scan.o \
uwb-debug.o \
uwbd.o
umc-objs := \
umc-bus.o \
umc-dev.o \
umc-drv.o
TODO: Remove in late 2019 unless there are users
There seems to not be any real wireless USB devices anywhere in the wild
anymore. It turned out to be a failed technology :(
This will be removed from the tree if no one objects.
Greg Kroah-Hartman <gregkh@linuxfoundation.org>
// SPDX-License-Identifier: GPL-2.0-only
/*
* Ultra Wide Band
* Address management
*
* Copyright (C) 2005-2006 Intel Corporation
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
*
* FIXME: docs
*/
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/random.h>
#include <linux/etherdevice.h>
#include "uwb-internal.h"
/** Device Address Management command */
struct uwb_rc_cmd_dev_addr_mgmt {
struct uwb_rccb rccb;
u8 bmOperationType;
u8 baAddr[6];
} __attribute__((packed));
/**
* Low level command for setting/getting UWB radio's addresses
*
* @hwarc: HWA Radio Control interface instance
* @bmOperationType:
* Set/get, MAC/DEV (see WUSB1.0[8.6.2.2])
* @baAddr: address buffer--assumed to have enough data to hold
* the address type requested.
* @reply: Pointer to reply buffer (can be stack allocated)
* @returns: 0 if ok, < 0 errno code on error.
*
* @cmd has to be allocated because USB cannot grok USB or vmalloc
* buffers depending on your combination of host architecture.
*/
static
int uwb_rc_dev_addr_mgmt(struct uwb_rc *rc,
u8 bmOperationType, const u8 *baAddr,
struct uwb_rc_evt_dev_addr_mgmt *reply)
{
int result;
struct uwb_rc_cmd_dev_addr_mgmt *cmd;
result = -ENOMEM;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (cmd == NULL)
goto error_kzalloc;
cmd->rccb.bCommandType = UWB_RC_CET_GENERAL;
cmd->rccb.wCommand = cpu_to_le16(UWB_RC_CMD_DEV_ADDR_MGMT);
cmd->bmOperationType = bmOperationType;
if (baAddr) {
size_t size = 0;
switch (bmOperationType >> 1) {
case 0: size = 2; break;
case 1: size = 6; break;
default: BUG();
}
memcpy(cmd->baAddr, baAddr, size);
}
reply->rceb.bEventType = UWB_RC_CET_GENERAL;
reply->rceb.wEvent = UWB_RC_CMD_DEV_ADDR_MGMT;
result = uwb_rc_cmd(rc, "DEV-ADDR-MGMT",
&cmd->rccb, sizeof(*cmd),
&reply->rceb, sizeof(*reply));
if (result < 0)
goto error_cmd;
if (result < sizeof(*reply)) {
dev_err(&rc->uwb_dev.dev,
"DEV-ADDR-MGMT: not enough data replied: "
"%d vs %zu bytes needed\n", result, sizeof(*reply));
result = -ENOMSG;
} else if (reply->bResultCode != UWB_RC_RES_SUCCESS) {
dev_err(&rc->uwb_dev.dev,
"DEV-ADDR-MGMT: command execution failed: %s (%d)\n",
uwb_rc_strerror(reply->bResultCode),
reply->bResultCode);
result = -EIO;
} else
result = 0;
error_cmd:
kfree(cmd);
error_kzalloc:
return result;
}
/**
* Set the UWB RC MAC or device address.
*
* @rc: UWB Radio Controller
* @_addr: Pointer to address to write [assumed to be either a
* 'struct uwb_mac_addr *' or a 'struct uwb_dev_addr *'].
* @type: Type of address to set (UWB_ADDR_DEV or UWB_ADDR_MAC).
* @returns: 0 if ok, < 0 errno code on error.
*
* Some anal retentivity here: even if both 'struct
* uwb_{dev,mac}_addr' have the actual byte array in the same offset
* and I could just pass _addr to hwarc_cmd_dev_addr_mgmt(), I prefer
* to use some syntatic sugar in case someday we decide to change the
* format of the structs. The compiler will optimize it out anyway.
*/
static int uwb_rc_addr_set(struct uwb_rc *rc,
const void *_addr, enum uwb_addr_type type)
{
int result;
u8 bmOperationType = 0x1; /* Set address */
const struct uwb_dev_addr *dev_addr = _addr;
const struct uwb_mac_addr *mac_addr = _addr;
struct uwb_rc_evt_dev_addr_mgmt reply;
const u8 *baAddr;
result = -EINVAL;
switch (type) {
case UWB_ADDR_DEV:
baAddr = dev_addr->data;
break;
case UWB_ADDR_MAC:
baAddr = mac_addr->data;
bmOperationType |= 0x2;
break;
default:
return result;
}
return uwb_rc_dev_addr_mgmt(rc, bmOperationType, baAddr, &reply);
}
/**
* Get the UWB radio's MAC or device address.
*
* @rc: UWB Radio Controller
* @_addr: Where to write the address data [assumed to be either a
* 'struct uwb_mac_addr *' or a 'struct uwb_dev_addr *'].
* @type: Type of address to get (UWB_ADDR_DEV or UWB_ADDR_MAC).
* @returns: 0 if ok (and *_addr set), < 0 errno code on error.
*
* See comment in uwb_rc_addr_set() about anal retentivity in the
* type handling of the address variables.
*/
static int uwb_rc_addr_get(struct uwb_rc *rc,
void *_addr, enum uwb_addr_type type)
{
int result;
u8 bmOperationType = 0x0; /* Get address */
struct uwb_rc_evt_dev_addr_mgmt evt;
struct uwb_dev_addr *dev_addr = _addr;
struct uwb_mac_addr *mac_addr = _addr;
u8 *baAddr;
result = -EINVAL;
switch (type) {
case UWB_ADDR_DEV:
baAddr = dev_addr->data;
break;
case UWB_ADDR_MAC:
bmOperationType |= 0x2;
baAddr = mac_addr->data;
break;
default:
return result;
}
result = uwb_rc_dev_addr_mgmt(rc, bmOperationType, baAddr, &evt);
if (result == 0)
switch (type) {
case UWB_ADDR_DEV:
memcpy(&dev_addr->data, evt.baAddr,
sizeof(dev_addr->data));
break;
case UWB_ADDR_MAC:
memcpy(&mac_addr->data, evt.baAddr,
sizeof(mac_addr->data));
break;
default: /* shut gcc up */
BUG();
}
return result;
}
/** Get @rc's MAC address to @addr */
int uwb_rc_mac_addr_get(struct uwb_rc *rc,
struct uwb_mac_addr *addr) {
return uwb_rc_addr_get(rc, addr, UWB_ADDR_MAC);
}
EXPORT_SYMBOL_GPL(uwb_rc_mac_addr_get);
/** Get @rc's device address to @addr */
int uwb_rc_dev_addr_get(struct uwb_rc *rc,
struct uwb_dev_addr *addr) {
return uwb_rc_addr_get(rc, addr, UWB_ADDR_DEV);
}
EXPORT_SYMBOL_GPL(uwb_rc_dev_addr_get);
/** Set @rc's address to @addr */
int uwb_rc_mac_addr_set(struct uwb_rc *rc,
const struct uwb_mac_addr *addr)
{
int result = -EINVAL;
mutex_lock(&rc->uwb_dev.mutex);
result = uwb_rc_addr_set(rc, addr, UWB_ADDR_MAC);
mutex_unlock(&rc->uwb_dev.mutex);
return result;
}
/** Set @rc's address to @addr */
int uwb_rc_dev_addr_set(struct uwb_rc *rc,
const struct uwb_dev_addr *addr)
{
int result = -EINVAL;
mutex_lock(&rc->uwb_dev.mutex);
result = uwb_rc_addr_set(rc, addr, UWB_ADDR_DEV);
rc->uwb_dev.dev_addr = *addr;
mutex_unlock(&rc->uwb_dev.mutex);
return result;
}
/* Returns !0 if given address is already assigned to device. */
int __uwb_mac_addr_assigned_check(struct device *dev, void *_addr)
{
struct uwb_dev *uwb_dev = to_uwb_dev(dev);
struct uwb_mac_addr *addr = _addr;
if (!uwb_mac_addr_cmp(addr, &uwb_dev->mac_addr))
return !0;
return 0;
}
/* Returns !0 if given address is already assigned to device. */
int __uwb_dev_addr_assigned_check(struct device *dev, void *_addr)
{
struct uwb_dev *uwb_dev = to_uwb_dev(dev);
struct uwb_dev_addr *addr = _addr;
if (!uwb_dev_addr_cmp(addr, &uwb_dev->dev_addr))
return !0;
return 0;
}
/**
* uwb_dev_addr_assign - assigned a generated DevAddr to a radio controller
* @rc: the (local) radio controller device requiring a new DevAddr
*
* A new DevAddr is required when:
* - first setting up a radio controller
* - if the hardware reports a DevAddr conflict
*
* The DevAddr is randomly generated in the generated DevAddr range
* [0x100, 0xfeff]. The number of devices in a beacon group is limited
* by mMaxBPLength (96) so this address space will never be exhausted.
*
* [ECMA-368] 17.1.1, 17.16.
*/
int uwb_rc_dev_addr_assign(struct uwb_rc *rc)
{
struct uwb_dev_addr new_addr;
do {
get_random_bytes(new_addr.data, sizeof(new_addr.data));
} while (new_addr.data[0] == 0x00 || new_addr.data[0] == 0xff
|| __uwb_dev_addr_assigned(rc, &new_addr));
return uwb_rc_dev_addr_set(rc, &new_addr);
}
/**
* uwbd_evt_handle_rc_dev_addr_conflict - handle a DEV_ADDR_CONFLICT event
* @evt: the DEV_ADDR_CONFLICT notification from the radio controller
*
* A new (non-conflicting) DevAddr is assigned to the radio controller.
*
* [ECMA-368] 17.1.1.1.
*/
int uwbd_evt_handle_rc_dev_addr_conflict(struct uwb_event *evt)
{
struct uwb_rc *rc = evt->rc;
return uwb_rc_dev_addr_assign(rc);
}
/*
* Print the 48-bit EUI MAC address of the radio controller when
* reading /sys/class/uwb_rc/XX/mac_address
*/
static ssize_t uwb_rc_mac_addr_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct uwb_dev *uwb_dev = to_uwb_dev(dev);
struct uwb_rc *rc = uwb_dev->rc;
struct uwb_mac_addr addr;
ssize_t result;
mutex_lock(&rc->uwb_dev.mutex);
result = uwb_rc_addr_get(rc, &addr, UWB_ADDR_MAC);
mutex_unlock(&rc->uwb_dev.mutex);
if (result >= 0) {
result = uwb_mac_addr_print(buf, UWB_ADDR_STRSIZE, &addr);
buf[result++] = '\n';
}
return result;
}
/*
* Parse a 48 bit address written to /sys/class/uwb_rc/XX/mac_address
* and if correct, set it.
*/
static ssize_t uwb_rc_mac_addr_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct uwb_dev *uwb_dev = to_uwb_dev(dev);
struct uwb_rc *rc = uwb_dev->rc;
struct uwb_mac_addr addr;
ssize_t result;
if (!mac_pton(buf, addr.data))
return -EINVAL;
if (is_multicast_ether_addr(addr.data)) {
dev_err(&rc->uwb_dev.dev, "refusing to set multicast "
"MAC address %s\n", buf);
return -EINVAL;
}
result = uwb_rc_mac_addr_set(rc, &addr);
if (result == 0)
rc->uwb_dev.mac_addr = addr;
return result < 0 ? result : size;
}
DEVICE_ATTR(mac_address, S_IRUGO | S_IWUSR, uwb_rc_mac_addr_show, uwb_rc_mac_addr_store);
/** Print @addr to @buf, @return bytes written */
size_t __uwb_addr_print(char *buf, size_t buf_size, const unsigned char *addr,
int type)
{
size_t result;
if (type)
result = scnprintf(buf, buf_size, "%pM", addr);
else
result = scnprintf(buf, buf_size, "%02x:%02x",
addr[1], addr[0]);
return result;
}
EXPORT_SYMBOL_GPL(__uwb_addr_print);
// SPDX-License-Identifier: GPL-2.0-only
/*
* UWB reservation management.
*
* Copyright (C) 2008 Cambridge Silicon Radio Ltd.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include "uwb.h"
#include "uwb-internal.h"
static void uwb_rsv_fill_column_alloc(struct uwb_rsv_alloc_info *ai)
{
int col, mas, safe_mas, unsafe_mas;
unsigned char *bm = ai->bm;
struct uwb_rsv_col_info *ci = ai->ci;
unsigned char c;
for (col = ci->csi.start_col; col < UWB_NUM_ZONES; col += ci->csi.interval) {
safe_mas = ci->csi.safe_mas_per_col;
unsafe_mas = ci->csi.unsafe_mas_per_col;
for (mas = 0; mas < UWB_MAS_PER_ZONE; mas++ ) {
if (bm[col * UWB_MAS_PER_ZONE + mas] == 0) {
if (safe_mas > 0) {
safe_mas--;
c = UWB_RSV_MAS_SAFE;
} else if (unsafe_mas > 0) {
unsafe_mas--;
c = UWB_RSV_MAS_UNSAFE;
} else {
break;
}
bm[col * UWB_MAS_PER_ZONE + mas] = c;
}
}
}
}
static void uwb_rsv_fill_row_alloc(struct uwb_rsv_alloc_info *ai)
{
int mas, col, rows;
unsigned char *bm = ai->bm;
struct uwb_rsv_row_info *ri = &ai->ri;
unsigned char c;
rows = 1;
c = UWB_RSV_MAS_SAFE;
for (mas = UWB_MAS_PER_ZONE - 1; mas >= 0; mas--) {
if (ri->avail[mas] == 1) {
if (rows > ri->used_rows) {
break;
} else if (rows > 7) {
c = UWB_RSV_MAS_UNSAFE;
}
for (col = 0; col < UWB_NUM_ZONES; col++) {
if (bm[col * UWB_NUM_ZONES + mas] != UWB_RSV_MAS_NOT_AVAIL) {
bm[col * UWB_NUM_ZONES + mas] = c;
if(c == UWB_RSV_MAS_SAFE)
ai->safe_allocated_mases++;
else
ai->unsafe_allocated_mases++;
}
}
rows++;
}
}
ai->total_allocated_mases = ai->safe_allocated_mases + ai->unsafe_allocated_mases;
}
/*
* Find the best column set for a given availability, interval, num safe mas and
* num unsafe mas.
*
* The different sets are tried in order as shown below, depending on the interval.
*
* interval = 16
* deep = 0
* set 1 -> { 8 }
* deep = 1
* set 1 -> { 4 }
* set 2 -> { 12 }
* deep = 2
* set 1 -> { 2 }
* set 2 -> { 6 }
* set 3 -> { 10 }
* set 4 -> { 14 }
* deep = 3
* set 1 -> { 1 }
* set 2 -> { 3 }
* set 3 -> { 5 }
* set 4 -> { 7 }
* set 5 -> { 9 }
* set 6 -> { 11 }
* set 7 -> { 13 }
* set 8 -> { 15 }
*
* interval = 8
* deep = 0
* set 1 -> { 4 12 }
* deep = 1
* set 1 -> { 2 10 }
* set 2 -> { 6 14 }
* deep = 2
* set 1 -> { 1 9 }
* set 2 -> { 3 11 }
* set 3 -> { 5 13 }
* set 4 -> { 7 15 }
*
* interval = 4
* deep = 0
* set 1 -> { 2 6 10 14 }
* deep = 1
* set 1 -> { 1 5 9 13 }
* set 2 -> { 3 7 11 15 }
*
* interval = 2
* deep = 0
* set 1 -> { 1 3 5 7 9 11 13 15 }
*/
static int uwb_rsv_find_best_column_set(struct uwb_rsv_alloc_info *ai, int interval,
int num_safe_mas, int num_unsafe_mas)
{
struct uwb_rsv_col_info *ci = ai->ci;
struct uwb_rsv_col_set_info *csi = &ci->csi;
struct uwb_rsv_col_set_info tmp_csi;
int deep, set, col, start_col_deep, col_start_set;
int start_col, max_mas_in_set, lowest_max_mas_in_deep;
int n_mas;
int found = UWB_RSV_ALLOC_NOT_FOUND;
tmp_csi.start_col = 0;
start_col_deep = interval;
n_mas = num_unsafe_mas + num_safe_mas;
for (deep = 0; ((interval >> deep) & 0x1) == 0; deep++) {
start_col_deep /= 2;
col_start_set = 0;
lowest_max_mas_in_deep = UWB_MAS_PER_ZONE;
for (set = 1; set <= (1 << deep); set++) {
max_mas_in_set = 0;
start_col = start_col_deep + col_start_set;
for (col = start_col; col < UWB_NUM_ZONES; col += interval) {
if (ci[col].max_avail_safe >= num_safe_mas &&
ci[col].max_avail_unsafe >= n_mas) {
if (ci[col].highest_mas[n_mas] > max_mas_in_set)
max_mas_in_set = ci[col].highest_mas[n_mas];
} else {
max_mas_in_set = 0;
break;
}
}
if ((lowest_max_mas_in_deep > max_mas_in_set) && max_mas_in_set) {
lowest_max_mas_in_deep = max_mas_in_set;
tmp_csi.start_col = start_col;
}
col_start_set += (interval >> deep);
}
if (lowest_max_mas_in_deep < 8) {
csi->start_col = tmp_csi.start_col;
found = UWB_RSV_ALLOC_FOUND;
break;
} else if ((lowest_max_mas_in_deep > 8) &&
(lowest_max_mas_in_deep != UWB_MAS_PER_ZONE) &&
(found == UWB_RSV_ALLOC_NOT_FOUND)) {
csi->start_col = tmp_csi.start_col;
found = UWB_RSV_ALLOC_FOUND;
}
}
if (found == UWB_RSV_ALLOC_FOUND) {
csi->interval = interval;
csi->safe_mas_per_col = num_safe_mas;
csi->unsafe_mas_per_col = num_unsafe_mas;
ai->safe_allocated_mases = (UWB_NUM_ZONES / interval) * num_safe_mas;
ai->unsafe_allocated_mases = (UWB_NUM_ZONES / interval) * num_unsafe_mas;
ai->total_allocated_mases = ai->safe_allocated_mases + ai->unsafe_allocated_mases;
ai->interval = interval;
}
return found;
}
static void get_row_descriptors(struct uwb_rsv_alloc_info *ai)
{
unsigned char *bm = ai->bm;
struct uwb_rsv_row_info *ri = &ai->ri;
int col, mas;
ri->free_rows = 16;
for (mas = 0; mas < UWB_MAS_PER_ZONE; mas ++) {
ri->avail[mas] = 1;
for (col = 1; col < UWB_NUM_ZONES; col++) {
if (bm[col * UWB_NUM_ZONES + mas] == UWB_RSV_MAS_NOT_AVAIL) {
ri->free_rows--;
ri->avail[mas]=0;
break;
}
}
}
}
static void uwb_rsv_fill_column_info(unsigned char *bm, int column, struct uwb_rsv_col_info *rci)
{
int mas;
int block_count = 0, start_block = 0;
int previous_avail = 0;
int available = 0;
int safe_mas_in_row[UWB_MAS_PER_ZONE] = {
8, 7, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 2, 1,
};
rci->max_avail_safe = 0;
for (mas = 0; mas < UWB_MAS_PER_ZONE; mas ++) {
if (!bm[column * UWB_NUM_ZONES + mas]) {
available++;
rci->max_avail_unsafe = available;
rci->highest_mas[available] = mas;
if (previous_avail) {
block_count++;
if ((block_count > safe_mas_in_row[start_block]) &&
(!rci->max_avail_safe))
rci->max_avail_safe = available - 1;
} else {
previous_avail = 1;
start_block = mas;
block_count = 1;
}
} else {
previous_avail = 0;
}
}
if (!rci->max_avail_safe)
rci->max_avail_safe = rci->max_avail_unsafe;
}
static void get_column_descriptors(struct uwb_rsv_alloc_info *ai)
{
unsigned char *bm = ai->bm;
struct uwb_rsv_col_info *ci = ai->ci;
int col;
for (col = 1; col < UWB_NUM_ZONES; col++) {
uwb_rsv_fill_column_info(bm, col, &ci[col]);
}
}
static int uwb_rsv_find_best_row_alloc(struct uwb_rsv_alloc_info *ai)
{
int n_rows;
int max_rows = ai->max_mas / UWB_USABLE_MAS_PER_ROW;
int min_rows = ai->min_mas / UWB_USABLE_MAS_PER_ROW;
if (ai->min_mas % UWB_USABLE_MAS_PER_ROW)
min_rows++;
for (n_rows = max_rows; n_rows >= min_rows; n_rows--) {
if (n_rows <= ai->ri.free_rows) {
ai->ri.used_rows = n_rows;
ai->interval = 1; /* row reservation */
uwb_rsv_fill_row_alloc(ai);
return UWB_RSV_ALLOC_FOUND;
}
}
return UWB_RSV_ALLOC_NOT_FOUND;
}
static int uwb_rsv_find_best_col_alloc(struct uwb_rsv_alloc_info *ai, int interval)
{
int n_safe, n_unsafe, n_mas;
int n_column = UWB_NUM_ZONES / interval;
int max_per_zone = ai->max_mas / n_column;
int min_per_zone = ai->min_mas / n_column;
if (ai->min_mas % n_column)
min_per_zone++;
if (min_per_zone > UWB_MAS_PER_ZONE) {
return UWB_RSV_ALLOC_NOT_FOUND;
}
if (max_per_zone > UWB_MAS_PER_ZONE) {
max_per_zone = UWB_MAS_PER_ZONE;
}
for (n_mas = max_per_zone; n_mas >= min_per_zone; n_mas--) {
if (uwb_rsv_find_best_column_set(ai, interval, 0, n_mas) == UWB_RSV_ALLOC_NOT_FOUND)
continue;
for (n_safe = n_mas; n_safe >= 0; n_safe--) {
n_unsafe = n_mas - n_safe;
if (uwb_rsv_find_best_column_set(ai, interval, n_safe, n_unsafe) == UWB_RSV_ALLOC_FOUND) {
uwb_rsv_fill_column_alloc(ai);
return UWB_RSV_ALLOC_FOUND;
}
}
}
return UWB_RSV_ALLOC_NOT_FOUND;
}
int uwb_rsv_find_best_allocation(struct uwb_rsv *rsv, struct uwb_mas_bm *available,
struct uwb_mas_bm *result)
{
struct uwb_rsv_alloc_info *ai;
int interval;
int bit_index;
ai = kzalloc(sizeof(struct uwb_rsv_alloc_info), GFP_KERNEL);
if (!ai)
return UWB_RSV_ALLOC_NOT_FOUND;
ai->min_mas = rsv->min_mas;
ai->max_mas = rsv->max_mas;
ai->max_interval = rsv->max_interval;
/* fill the not available vector from the available bm */
for_each_clear_bit(bit_index, available->bm, UWB_NUM_MAS)
ai->bm[bit_index] = UWB_RSV_MAS_NOT_AVAIL;
if (ai->max_interval == 1) {
get_row_descriptors(ai);
if (uwb_rsv_find_best_row_alloc(ai) == UWB_RSV_ALLOC_FOUND)
goto alloc_found;
else
goto alloc_not_found;
}
get_column_descriptors(ai);
for (interval = 16; interval >= 2; interval>>=1) {
if (interval > ai->max_interval)
continue;
if (uwb_rsv_find_best_col_alloc(ai, interval) == UWB_RSV_ALLOC_FOUND)
goto alloc_found;
}
/* try row reservation if no column is found */
get_row_descriptors(ai);
if (uwb_rsv_find_best_row_alloc(ai) == UWB_RSV_ALLOC_FOUND)
goto alloc_found;
else
goto alloc_not_found;
alloc_found:
bitmap_zero(result->bm, UWB_NUM_MAS);
bitmap_zero(result->unsafe_bm, UWB_NUM_MAS);
/* fill the safe and unsafe bitmaps */
for (bit_index = 0; bit_index < UWB_NUM_MAS; bit_index++) {
if (ai->bm[bit_index] == UWB_RSV_MAS_SAFE)
set_bit(bit_index, result->bm);
else if (ai->bm[bit_index] == UWB_RSV_MAS_UNSAFE)
set_bit(bit_index, result->unsafe_bm);
}
bitmap_or(result->bm, result->bm, result->unsafe_bm, UWB_NUM_MAS);
result->safe = ai->safe_allocated_mases;
result->unsafe = ai->unsafe_allocated_mases;
kfree(ai);
return UWB_RSV_ALLOC_FOUND;
alloc_not_found:
kfree(ai);
return UWB_RSV_ALLOC_NOT_FOUND;
}
// SPDX-License-Identifier: GPL-2.0-only
/*
* Ultra Wide Band
* Beacon management
*
* Copyright (C) 2005-2006 Intel Corporation
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
*
* FIXME: docs
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/kdev_t.h>
#include <linux/slab.h>
#include "uwb-internal.h"
/* Start Beaconing command structure */
struct uwb_rc_cmd_start_beacon {
struct uwb_rccb rccb;
__le16 wBPSTOffset;
u8 bChannelNumber;
} __attribute__((packed));
static int uwb_rc_start_beacon(struct uwb_rc *rc, u16 bpst_offset, u8 channel)
{
int result;
struct uwb_rc_cmd_start_beacon *cmd;
struct uwb_rc_evt_confirm reply;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (cmd == NULL)
return -ENOMEM;
cmd->rccb.bCommandType = UWB_RC_CET_GENERAL;
cmd->rccb.wCommand = cpu_to_le16(UWB_RC_CMD_START_BEACON);
cmd->wBPSTOffset = cpu_to_le16(bpst_offset);
cmd->bChannelNumber = channel;
reply.rceb.bEventType = UWB_RC_CET_GENERAL;
reply.rceb.wEvent = UWB_RC_CMD_START_BEACON;
result = uwb_rc_cmd(rc, "START-BEACON", &cmd->rccb, sizeof(*cmd),
&reply.rceb, sizeof(reply));
if (result < 0)
goto error_cmd;
if (reply.bResultCode != UWB_RC_RES_SUCCESS) {
dev_err(&rc->uwb_dev.dev,
"START-BEACON: command execution failed: %s (%d)\n",
uwb_rc_strerror(reply.bResultCode), reply.bResultCode);
result = -EIO;
}
error_cmd:
kfree(cmd);
return result;
}
static int uwb_rc_stop_beacon(struct uwb_rc *rc)
{
int result;
struct uwb_rccb *cmd;
struct uwb_rc_evt_confirm reply;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (cmd == NULL)
return -ENOMEM;
cmd->bCommandType = UWB_RC_CET_GENERAL;
cmd->wCommand = cpu_to_le16(UWB_RC_CMD_STOP_BEACON);
reply.rceb.bEventType = UWB_RC_CET_GENERAL;
reply.rceb.wEvent = UWB_RC_CMD_STOP_BEACON;
result = uwb_rc_cmd(rc, "STOP-BEACON", cmd, sizeof(*cmd),
&reply.rceb, sizeof(reply));
if (result < 0)
goto error_cmd;
if (reply.bResultCode != UWB_RC_RES_SUCCESS) {
dev_err(&rc->uwb_dev.dev,
"STOP-BEACON: command execution failed: %s (%d)\n",
uwb_rc_strerror(reply.bResultCode), reply.bResultCode);
result = -EIO;
}
error_cmd:
kfree(cmd);
return result;
}
/*
* Start/stop beacons
*
* @rc: UWB Radio Controller to operate on
* @channel: UWB channel on which to beacon (WUSB[table
* 5-12]). If -1, stop beaconing.
* @bpst_offset: Beacon Period Start Time offset; FIXME-do zero
*
* According to WHCI 0.95 [4.13.6] the driver will only receive the RCEB
* of a SET IE command after the device sent the first beacon that includes
* the IEs specified in the SET IE command. So, after we start beaconing we
* check if there is anything in the IE cache and call the SET IE command
* if needed.
*/
int uwb_rc_beacon(struct uwb_rc *rc, int channel, unsigned bpst_offset)
{
int result;
struct device *dev = &rc->uwb_dev.dev;
dev_dbg(dev, "%s: channel = %d\n", __func__, channel);
if (channel < 0)
channel = -1;
if (channel == -1)
result = uwb_rc_stop_beacon(rc);
else {
/* channel >= 0...dah */
result = uwb_rc_start_beacon(rc, bpst_offset, channel);
if (result < 0) {
dev_err(dev, "Cannot start beaconing: %d\n", result);
return result;
}
if (le16_to_cpu(rc->ies->wIELength) > 0) {
result = uwb_rc_set_ie(rc, rc->ies);
if (result < 0) {
dev_err(dev, "Cannot set new IE on device: "
"%d\n", result);
result = uwb_rc_stop_beacon(rc);
channel = -1;
bpst_offset = 0;
}
}
}
if (result >= 0)
rc->beaconing = channel;
return result;
}
/*
* Beacon cache
*
* The purpose of this is to speed up the lookup of becon information
* when a new beacon arrives. The UWB Daemon uses it also to keep a
* tab of which devices are in radio distance and which not. When a
* device's beacon stays present for more than a certain amount of
* time, it is considered a new, usable device. When a beacon ceases
* to be received for a certain amount of time, it is considered that
* the device is gone.
*
* FIXME: use an allocator for the entries
* FIXME: use something faster for search than a list
*/
void uwb_bce_kfree(struct kref *_bce)
{
struct uwb_beca_e *bce = container_of(_bce, struct uwb_beca_e, refcnt);
kfree(bce->be);
kfree(bce);
}
/* Find a beacon by dev addr in the cache */
static
struct uwb_beca_e *__uwb_beca_find_bydev(struct uwb_rc *rc,
const struct uwb_dev_addr *dev_addr)
{
struct uwb_beca_e *bce, *next;
list_for_each_entry_safe(bce, next, &rc->uwb_beca.list, node) {
if (!memcmp(&bce->dev_addr, dev_addr, sizeof(bce->dev_addr)))
goto out;
}
bce = NULL;
out:
return bce;
}
/* Find a beacon by dev addr in the cache */
static
struct uwb_beca_e *__uwb_beca_find_bymac(struct uwb_rc *rc,
const struct uwb_mac_addr *mac_addr)
{
struct uwb_beca_e *bce, *next;
list_for_each_entry_safe(bce, next, &rc->uwb_beca.list, node) {
if (!memcmp(bce->mac_addr, mac_addr->data,
sizeof(struct uwb_mac_addr)))
goto out;
}
bce = NULL;
out:
return bce;
}
/**
* uwb_dev_get_by_devaddr - get a UWB device with a specific DevAddr
* @rc: the radio controller that saw the device
* @devaddr: DevAddr of the UWB device to find
*
* There may be more than one matching device (in the case of a
* DevAddr conflict), but only the first one is returned.
*/
struct uwb_dev *uwb_dev_get_by_devaddr(struct uwb_rc *rc,
const struct uwb_dev_addr *devaddr)
{
struct uwb_dev *found = NULL;
struct uwb_beca_e *bce;
mutex_lock(&rc->uwb_beca.mutex);
bce = __uwb_beca_find_bydev(rc, devaddr);
if (bce)
found = uwb_dev_try_get(rc, bce->uwb_dev);
mutex_unlock(&rc->uwb_beca.mutex);
return found;
}
/**
* uwb_dev_get_by_macaddr - get a UWB device with a specific EUI-48
* @rc: the radio controller that saw the device
* @devaddr: EUI-48 of the UWB device to find
*/
struct uwb_dev *uwb_dev_get_by_macaddr(struct uwb_rc *rc,
const struct uwb_mac_addr *macaddr)
{
struct uwb_dev *found = NULL;
struct uwb_beca_e *bce;
mutex_lock(&rc->uwb_beca.mutex);
bce = __uwb_beca_find_bymac(rc, macaddr);
if (bce)
found = uwb_dev_try_get(rc, bce->uwb_dev);
mutex_unlock(&rc->uwb_beca.mutex);
return found;
}
/* Initialize a beacon cache entry */
static void uwb_beca_e_init(struct uwb_beca_e *bce)
{
mutex_init(&bce->mutex);
kref_init(&bce->refcnt);
stats_init(&bce->lqe_stats);
stats_init(&bce->rssi_stats);
}
/*
* Add a beacon to the cache
*
* @be: Beacon event information
* @bf: Beacon frame (part of b, really)
* @ts_jiffies: Timestamp (in jiffies) when the beacon was received
*/
static
struct uwb_beca_e *__uwb_beca_add(struct uwb_rc *rc,
struct uwb_rc_evt_beacon *be,
struct uwb_beacon_frame *bf,
unsigned long ts_jiffies)
{
struct uwb_beca_e *bce;
bce = kzalloc(sizeof(*bce), GFP_KERNEL);
if (bce == NULL)
return NULL;
uwb_beca_e_init(bce);
bce->ts_jiffies = ts_jiffies;
bce->uwb_dev = NULL;
list_add(&bce->node, &rc->uwb_beca.list);
return bce;
}
/*
* Wipe out beacon entries that became stale
*
* Remove associated devicest too.
*/
void uwb_beca_purge(struct uwb_rc *rc)
{
struct uwb_beca_e *bce, *next;
unsigned long expires;
mutex_lock(&rc->uwb_beca.mutex);
list_for_each_entry_safe(bce, next, &rc->uwb_beca.list, node) {
expires = bce->ts_jiffies + msecs_to_jiffies(beacon_timeout_ms);
if (time_after(jiffies, expires)) {
uwbd_dev_offair(bce);
}
}
mutex_unlock(&rc->uwb_beca.mutex);
}
/* Clean up the whole beacon cache. Called on shutdown */
void uwb_beca_release(struct uwb_rc *rc)
{
struct uwb_beca_e *bce, *next;
mutex_lock(&rc->uwb_beca.mutex);
list_for_each_entry_safe(bce, next, &rc->uwb_beca.list, node) {
list_del(&bce->node);
uwb_bce_put(bce);
}
mutex_unlock(&rc->uwb_beca.mutex);
}
static void uwb_beacon_print(struct uwb_rc *rc, struct uwb_rc_evt_beacon *be,
struct uwb_beacon_frame *bf)
{
char macbuf[UWB_ADDR_STRSIZE];
char devbuf[UWB_ADDR_STRSIZE];
char dstbuf[UWB_ADDR_STRSIZE];
uwb_mac_addr_print(macbuf, sizeof(macbuf), &bf->Device_Identifier);
uwb_dev_addr_print(devbuf, sizeof(devbuf), &bf->hdr.SrcAddr);
uwb_dev_addr_print(dstbuf, sizeof(dstbuf), &bf->hdr.DestAddr);
dev_info(&rc->uwb_dev.dev,
"BEACON from %s to %s (ch%u offset %u slot %u MAC %s)\n",
devbuf, dstbuf, be->bChannelNumber, be->wBPSTOffset,
bf->Beacon_Slot_Number, macbuf);
}
/*
* @bce: beacon cache entry, referenced
*/
ssize_t uwb_bce_print_IEs(struct uwb_dev *uwb_dev, struct uwb_beca_e *bce,
char *buf, size_t size)
{
ssize_t result = 0;
struct uwb_rc_evt_beacon *be;
struct uwb_beacon_frame *bf;
int ies_len;
struct uwb_ie_hdr *ies;
mutex_lock(&bce->mutex);
be = bce->be;
if (be) {
bf = (struct uwb_beacon_frame *)bce->be->BeaconInfo;
ies_len = be->wBeaconInfoLength - sizeof(struct uwb_beacon_frame);
ies = (struct uwb_ie_hdr *)bf->IEData;
result = uwb_ie_dump_hex(ies, ies_len, buf, size);
}
mutex_unlock(&bce->mutex);
return result;
}
/*
* Verify that the beacon event, frame and IEs are ok
*/
static int uwb_verify_beacon(struct uwb_rc *rc, struct uwb_event *evt,
struct uwb_rc_evt_beacon *be)
{
int result = -EINVAL;
struct uwb_beacon_frame *bf;
struct device *dev = &rc->uwb_dev.dev;
/* Is there enough data to decode a beacon frame? */
if (evt->notif.size < sizeof(*be) + sizeof(*bf)) {
dev_err(dev, "BEACON event: Not enough data to decode "
"(%zu vs %zu bytes needed)\n", evt->notif.size,
sizeof(*be) + sizeof(*bf));
goto error;
}
/* FIXME: make sure beacon frame IEs are fine and that the whole thing
* is consistent */
result = 0;
error:
return result;
}
/*
* Handle UWB_RC_EVT_BEACON events
*
* We check the beacon cache to see how the received beacon fares. If
* is there already we refresh the timestamp. If not we create a new
* entry.
*
* According to the WHCI and WUSB specs, only one beacon frame is
* allowed per notification block, so we don't bother about scanning
* for more.
*/
int uwbd_evt_handle_rc_beacon(struct uwb_event *evt)
{
int result = -EINVAL;
struct uwb_rc *rc;
struct uwb_rc_evt_beacon *be;
struct uwb_beacon_frame *bf;
struct uwb_beca_e *bce;
rc = evt->rc;
be = container_of(evt->notif.rceb, struct uwb_rc_evt_beacon, rceb);
result = uwb_verify_beacon(rc, evt, be);
if (result < 0)
return result;
/* FIXME: handle alien beacons. */
if (be->bBeaconType == UWB_RC_BEACON_TYPE_OL_ALIEN ||
be->bBeaconType == UWB_RC_BEACON_TYPE_NOL_ALIEN) {
return -ENOSYS;
}
bf = (struct uwb_beacon_frame *) be->BeaconInfo;
/*
* Drop beacons from devices with a NULL EUI-48 -- they cannot
* be uniquely identified.
*
* It's expected that these will all be WUSB devices and they
* have a WUSB specific connection method so ignoring them
* here shouldn't be a problem.
*/
if (uwb_mac_addr_bcast(&bf->Device_Identifier))
return 0;
mutex_lock(&rc->uwb_beca.mutex);
bce = __uwb_beca_find_bymac(rc, &bf->Device_Identifier);
if (bce == NULL) {
/* Not in there, a new device is pinging */
uwb_beacon_print(evt->rc, be, bf);
bce = __uwb_beca_add(rc, be, bf, evt->ts_jiffies);
if (bce == NULL) {
mutex_unlock(&rc->uwb_beca.mutex);
return -ENOMEM;
}
}
mutex_unlock(&rc->uwb_beca.mutex);
mutex_lock(&bce->mutex);
/* purge old beacon data */
kfree(bce->be);
/* Update commonly used fields */
bce->ts_jiffies = evt->ts_jiffies;
bce->be = be;
bce->dev_addr = bf->hdr.SrcAddr;
bce->mac_addr = &bf->Device_Identifier;
be->wBPSTOffset = le16_to_cpu(be->wBPSTOffset);
be->wBeaconInfoLength = le16_to_cpu(be->wBeaconInfoLength);
stats_add_sample(&bce->lqe_stats, be->bLQI - 7);
stats_add_sample(&bce->rssi_stats, be->bRSSI + 18);
/*
* This might be a beacon from a new device.
*/
if (bce->uwb_dev == NULL)
uwbd_dev_onair(evt->rc, bce);
mutex_unlock(&bce->mutex);
return 1; /* we keep the event data */
}
/*
* Handle UWB_RC_EVT_BEACON_SIZE events
*
* XXXXX
*/
int uwbd_evt_handle_rc_beacon_size(struct uwb_event *evt)
{
int result = -EINVAL;
struct device *dev = &evt->rc->uwb_dev.dev;
struct uwb_rc_evt_beacon_size *bs;
/* Is there enough data to decode the event? */
if (evt->notif.size < sizeof(*bs)) {
dev_err(dev, "BEACON SIZE notification: Not enough data to "
"decode (%zu vs %zu bytes needed)\n",
evt->notif.size, sizeof(*bs));
goto error;
}
bs = container_of(evt->notif.rceb, struct uwb_rc_evt_beacon_size, rceb);
if (0)
dev_info(dev, "Beacon size changed to %u bytes "
"(FIXME: action?)\n", le16_to_cpu(bs->wNewBeaconSize));
else {
/* temporary hack until we do something with this message... */
static unsigned count;
if (++count % 1000 == 0)
dev_info(dev, "Beacon size changed %u times "
"(FIXME: action?)\n", count);
}
result = 0;
error:
return result;
}
/**
* uwbd_evt_handle_rc_bp_slot_change - handle a BP_SLOT_CHANGE event
* @evt: the BP_SLOT_CHANGE notification from the radio controller
*
* If the event indicates that no beacon period slots were available
* then radio controller has transitioned to a non-beaconing state.
* Otherwise, simply save the current beacon slot.
*/
int uwbd_evt_handle_rc_bp_slot_change(struct uwb_event *evt)
{
struct uwb_rc *rc = evt->rc;
struct device *dev = &rc->uwb_dev.dev;
struct uwb_rc_evt_bp_slot_change *bpsc;
if (evt->notif.size < sizeof(*bpsc)) {
dev_err(dev, "BP SLOT CHANGE event: Not enough data\n");
return -EINVAL;
}
bpsc = container_of(evt->notif.rceb, struct uwb_rc_evt_bp_slot_change, rceb);
if (uwb_rc_evt_bp_slot_change_no_slot(bpsc)) {
dev_err(dev, "stopped beaconing: No free slots in BP\n");
mutex_lock(&rc->uwb_dev.mutex);
rc->beaconing = -1;
mutex_unlock(&rc->uwb_dev.mutex);
} else
rc->uwb_dev.beacon_slot = uwb_rc_evt_bp_slot_change_slot_num(bpsc);
return 0;
}
/**
* Handle UWB_RC_EVT_BPOIE_CHANGE events
*
* XXXXX
*/
struct uwb_ie_bpo {
struct uwb_ie_hdr hdr;
u8 bp_length;
u8 data[];
} __attribute__((packed));
int uwbd_evt_handle_rc_bpoie_change(struct uwb_event *evt)
{
int result = -EINVAL;
struct device *dev = &evt->rc->uwb_dev.dev;
struct uwb_rc_evt_bpoie_change *bpoiec;
struct uwb_ie_bpo *bpoie;
static unsigned count; /* FIXME: this is a temp hack */
size_t iesize;
/* Is there enough data to decode it? */
if (evt->notif.size < sizeof(*bpoiec)) {
dev_err(dev, "BPOIEC notification: Not enough data to "
"decode (%zu vs %zu bytes needed)\n",
evt->notif.size, sizeof(*bpoiec));
goto error;
}
bpoiec = container_of(evt->notif.rceb, struct uwb_rc_evt_bpoie_change, rceb);
iesize = le16_to_cpu(bpoiec->wBPOIELength);
if (iesize < sizeof(*bpoie)) {
dev_err(dev, "BPOIEC notification: Not enough IE data to "
"decode (%zu vs %zu bytes needed)\n",
iesize, sizeof(*bpoie));
goto error;
}
if (++count % 1000 == 0) /* Lame placeholder */
dev_info(dev, "BPOIE: %u changes received\n", count);
/*
* FIXME: At this point we should go over all the IEs in the
* bpoiec->BPOIE array and act on each.
*/
result = 0;
error:
return result;
}
/*
* Print beaconing state.
*/
static ssize_t uwb_rc_beacon_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct uwb_dev *uwb_dev = to_uwb_dev(dev);
struct uwb_rc *rc = uwb_dev->rc;
ssize_t result;
mutex_lock(&rc->uwb_dev.mutex);
result = sprintf(buf, "%d\n", rc->beaconing);
mutex_unlock(&rc->uwb_dev.mutex);
return result;
}
/*
* Start beaconing on the specified channel, or stop beaconing.
*/
static ssize_t uwb_rc_beacon_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct uwb_dev *uwb_dev = to_uwb_dev(dev);
struct uwb_rc *rc = uwb_dev->rc;
int channel;
ssize_t result = -EINVAL;
result = sscanf(buf, "%d", &channel);
if (result >= 1)
result = uwb_radio_force_channel(rc, channel);
return result < 0 ? result : size;
}
DEVICE_ATTR(beacon, S_IRUGO | S_IWUSR, uwb_rc_beacon_show, uwb_rc_beacon_store);
// SPDX-License-Identifier: GPL-2.0-only
/*
* Ultra Wide Band
* Driver initialization, etc
*
* Copyright (C) 2005-2006 Intel Corporation
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
*
* FIXME: docs
*
* Life cycle: FIXME: explain
*
* UWB radio controller:
*
* 1. alloc a uwb_rc, zero it
* 2. call uwb_rc_init() on it to set it up + ops (won't do any
* kind of allocation)
* 3. register (now it is owned by the UWB stack--deregister before
* freeing/destroying).
* 4. It lives on it's own now (UWB stack handles)--when it
* disconnects, call unregister()
* 5. free it.
*
* Make sure you have a reference to the uwb_rc before calling
* any of the UWB API functions.
*
* TODO:
*
* 1. Locking and life cycle management is crappy still. All entry
* points to the UWB HCD API assume you have a reference on the
* uwb_rc structure and that it won't go away. They mutex lock it
* before doing anything.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/kdev_t.h>
#include <linux/random.h>
#include "uwb-internal.h"
/* UWB stack attributes (or 'global' constants) */
/**
* If a beacon disappears for longer than this, then we consider the
* device who was represented by that beacon to be gone.
*
* ECMA-368[17.2.3, last para] establishes that a device must not
* consider a device to be its neighbour if he doesn't receive a beacon
* for more than mMaxLostBeacons. mMaxLostBeacons is defined in
* ECMA-368[17.16] as 3; because we can get only one beacon per
* superframe, that'd be 3 * 65ms = 195 ~ 200 ms. Let's give it time
* for jitter and stuff and make it 500 ms.
*/
unsigned long beacon_timeout_ms = 500;
static
ssize_t beacon_timeout_ms_show(struct class *class,
struct class_attribute *attr,
char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%lu\n", beacon_timeout_ms);
}
static
ssize_t beacon_timeout_ms_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t size)
{
unsigned long bt;
ssize_t result;
result = sscanf(buf, "%lu", &bt);
if (result != 1)
return -EINVAL;
beacon_timeout_ms = bt;
return size;
}
static CLASS_ATTR_RW(beacon_timeout_ms);
static struct attribute *uwb_class_attrs[] = {
&class_attr_beacon_timeout_ms.attr,
NULL,
};
ATTRIBUTE_GROUPS(uwb_class);
/** Device model classes */
struct class uwb_rc_class = {
.name = "uwb_rc",
.class_groups = uwb_class_groups,
};
static int __init uwb_subsys_init(void)
{
int result = 0;
result = uwb_est_create();
if (result < 0) {
printk(KERN_ERR "uwb: Can't initialize EST subsystem\n");
goto error_est_init;
}
result = class_register(&uwb_rc_class);
if (result < 0)
goto error_uwb_rc_class_register;
/* Register the UWB bus */
result = bus_register(&uwb_bus_type);
if (result) {
pr_err("%s - registering bus driver failed\n", __func__);
goto exit_bus;
}
uwb_dbg_init();
return 0;
exit_bus:
class_unregister(&uwb_rc_class);
error_uwb_rc_class_register:
uwb_est_destroy();
error_est_init:
return result;
}
module_init(uwb_subsys_init);
static void __exit uwb_subsys_exit(void)
{
uwb_dbg_exit();
bus_unregister(&uwb_bus_type);
class_unregister(&uwb_rc_class);
uwb_est_destroy();
return;
}
module_exit(uwb_subsys_exit);
MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
MODULE_DESCRIPTION("Ultra Wide Band core");
MODULE_LICENSE("GPL");
// SPDX-License-Identifier: GPL-2.0-only
/*
* 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.
*
* 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
*/
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_rsv_handle_drp_avail_change(rc);
}
/**
* 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;
uwb_rsv_handle_drp_avail_change(rc);
mutex_unlock(&rc->rsvs_mutex);
uwb_rsv_sched_update(rc);
return 0;
}
// SPDX-License-Identifier: GPL-2.0-only
/*
* UWB DRP IE management.
*
* Copyright (C) 2005-2006 Intel Corporation
* Copyright (C) 2008 Cambridge Silicon Radio Ltd.
*/
#include <linux/kernel.h>
#include <linux/random.h>
#include <linux/slab.h>
#include "uwb.h"
#include "uwb-internal.h"
/*
* Return the reason code for a reservations's DRP IE.
*/
static int uwb_rsv_reason_code(struct uwb_rsv *rsv)
{
static const int reason_codes[] = {
[UWB_RSV_STATE_O_INITIATED] = UWB_DRP_REASON_ACCEPTED,
[UWB_RSV_STATE_O_PENDING] = UWB_DRP_REASON_ACCEPTED,
[UWB_RSV_STATE_O_MODIFIED] = UWB_DRP_REASON_MODIFIED,
[UWB_RSV_STATE_O_ESTABLISHED] = UWB_DRP_REASON_ACCEPTED,
[UWB_RSV_STATE_O_TO_BE_MOVED] = UWB_DRP_REASON_ACCEPTED,
[UWB_RSV_STATE_O_MOVE_COMBINING] = UWB_DRP_REASON_MODIFIED,
[UWB_RSV_STATE_O_MOVE_REDUCING] = UWB_DRP_REASON_MODIFIED,
[UWB_RSV_STATE_O_MOVE_EXPANDING] = UWB_DRP_REASON_ACCEPTED,
[UWB_RSV_STATE_T_ACCEPTED] = UWB_DRP_REASON_ACCEPTED,
[UWB_RSV_STATE_T_CONFLICT] = UWB_DRP_REASON_CONFLICT,
[UWB_RSV_STATE_T_PENDING] = UWB_DRP_REASON_PENDING,
[UWB_RSV_STATE_T_DENIED] = UWB_DRP_REASON_DENIED,
[UWB_RSV_STATE_T_RESIZED] = UWB_DRP_REASON_ACCEPTED,
[UWB_RSV_STATE_T_EXPANDING_ACCEPTED] = UWB_DRP_REASON_ACCEPTED,
[UWB_RSV_STATE_T_EXPANDING_CONFLICT] = UWB_DRP_REASON_CONFLICT,
[UWB_RSV_STATE_T_EXPANDING_PENDING] = UWB_DRP_REASON_PENDING,
[UWB_RSV_STATE_T_EXPANDING_DENIED] = UWB_DRP_REASON_DENIED,
};
return reason_codes[rsv->state];
}
/*
* Return the reason code for a reservations's companion DRP IE .
*/
static int uwb_rsv_companion_reason_code(struct uwb_rsv *rsv)
{
static const int companion_reason_codes[] = {
[UWB_RSV_STATE_O_MOVE_EXPANDING] = UWB_DRP_REASON_ACCEPTED,
[UWB_RSV_STATE_T_EXPANDING_ACCEPTED] = UWB_DRP_REASON_ACCEPTED,
[UWB_RSV_STATE_T_EXPANDING_CONFLICT] = UWB_DRP_REASON_CONFLICT,
[UWB_RSV_STATE_T_EXPANDING_PENDING] = UWB_DRP_REASON_PENDING,
[UWB_RSV_STATE_T_EXPANDING_DENIED] = UWB_DRP_REASON_DENIED,
};
return companion_reason_codes[rsv->state];
}
/*
* Return the status bit for a reservations's DRP IE.
*/
int uwb_rsv_status(struct uwb_rsv *rsv)
{
static const int statuses[] = {
[UWB_RSV_STATE_O_INITIATED] = 0,
[UWB_RSV_STATE_O_PENDING] = 0,
[UWB_RSV_STATE_O_MODIFIED] = 1,
[UWB_RSV_STATE_O_ESTABLISHED] = 1,
[UWB_RSV_STATE_O_TO_BE_MOVED] = 0,
[UWB_RSV_STATE_O_MOVE_COMBINING] = 1,
[UWB_RSV_STATE_O_MOVE_REDUCING] = 1,
[UWB_RSV_STATE_O_MOVE_EXPANDING] = 1,
[UWB_RSV_STATE_T_ACCEPTED] = 1,
[UWB_RSV_STATE_T_CONFLICT] = 0,
[UWB_RSV_STATE_T_PENDING] = 0,
[UWB_RSV_STATE_T_DENIED] = 0,
[UWB_RSV_STATE_T_RESIZED] = 1,
[UWB_RSV_STATE_T_EXPANDING_ACCEPTED] = 1,
[UWB_RSV_STATE_T_EXPANDING_CONFLICT] = 1,
[UWB_RSV_STATE_T_EXPANDING_PENDING] = 1,
[UWB_RSV_STATE_T_EXPANDING_DENIED] = 1,
};
return statuses[rsv->state];
}
/*
* Return the status bit for a reservations's companion DRP IE .
*/
int uwb_rsv_companion_status(struct uwb_rsv *rsv)
{
static const int companion_statuses[] = {
[UWB_RSV_STATE_O_MOVE_EXPANDING] = 0,
[UWB_RSV_STATE_T_EXPANDING_ACCEPTED] = 1,
[UWB_RSV_STATE_T_EXPANDING_CONFLICT] = 0,
[UWB_RSV_STATE_T_EXPANDING_PENDING] = 0,
[UWB_RSV_STATE_T_EXPANDING_DENIED] = 0,
};
return companion_statuses[rsv->state];
}
/*
* 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;
drp_ie = kzalloc(struct_size(drp_ie, allocs, UWB_NUM_ZONES),
GFP_KERNEL);
if (drp_ie)
drp_ie->hdr.element_id = UWB_IE_DRP;
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 uwb_ie_drp *drp_ie;
struct uwb_rsv_move *mv;
int unsafe;
if (rsv->state == UWB_RSV_STATE_NONE) {
kfree(rsv->drp_ie);
rsv->drp_ie = NULL;
return 0;
}
unsafe = rsv->mas.unsafe ? 1 : 0;
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_unsafe(drp_ie, unsafe);
uwb_ie_drp_set_tiebreaker(drp_ie, rsv->tiebreaker);
uwb_ie_drp_set_owner(drp_ie, uwb_rsv_is_owner(rsv));
uwb_ie_drp_set_status(drp_ie, uwb_rsv_status(rsv));
uwb_ie_drp_set_reason_code(drp_ie, uwb_rsv_reason_code(rsv));
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);
if (uwb_rsv_has_two_drp_ies(rsv)) {
mv = &rsv->mv;
if (mv->companion_drp_ie == NULL) {
mv->companion_drp_ie = uwb_drp_ie_alloc();
if (mv->companion_drp_ie == NULL)
return -ENOMEM;
}
drp_ie = mv->companion_drp_ie;
/* keep all the same configuration of the main drp_ie */
memcpy(drp_ie, rsv->drp_ie, sizeof(struct uwb_ie_drp));
/* FIXME: handle properly the unsafe bit */
uwb_ie_drp_set_unsafe(drp_ie, 1);
uwb_ie_drp_set_status(drp_ie, uwb_rsv_companion_status(rsv));
uwb_ie_drp_set_reason_code(drp_ie, uwb_rsv_companion_reason_code(rsv));
uwb_drp_ie_from_bm(drp_ie, &mv->companion_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;
bitmap_zero(bm->bm, UWB_NUM_MAS);
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);
}
}
}
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0-only
/*
* Ultra Wide Band Radio Control
* Event Size Tables management
*
* Copyright (C) 2005-2006 Intel Corporation
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
*
* FIXME: docs
*
* Infrastructure, code and data tables for guessing the size of
* events received on the notification endpoints of UWB radio
* controllers.
*
* You define a table of events and for each, its size and how to get
* the extra size.
*
* ENTRY POINTS:
*
* uwb_est_{init/destroy}(): To initialize/release the EST subsystem.
*
* uwb_est_[u]register(): To un/register event size tables
* uwb_est_grow()
*
* uwb_est_find_size(): Get the size of an event
* uwb_est_get_size()
*/
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/export.h>
#include "uwb-internal.h"
struct uwb_est {
u16 type_event_high;
u16 vendor, product;
u8 entries;
const struct uwb_est_entry *entry;
};
static struct uwb_est *uwb_est;
static u8 uwb_est_size;
static u8 uwb_est_used;
static DEFINE_RWLOCK(uwb_est_lock);
/**
* WUSB Standard Event Size Table, HWA-RC interface
*
* Sizes for events and notifications type 0 (general), high nibble 0.
*/
static
struct uwb_est_entry uwb_est_00_00xx[] = {
[UWB_RC_EVT_IE_RCV] = {
.size = sizeof(struct uwb_rc_evt_ie_rcv),
.offset = 1 + offsetof(struct uwb_rc_evt_ie_rcv, wIELength),
},
[UWB_RC_EVT_BEACON] = {
.size = sizeof(struct uwb_rc_evt_beacon),
.offset = 1 + offsetof(struct uwb_rc_evt_beacon, wBeaconInfoLength),
},
[UWB_RC_EVT_BEACON_SIZE] = {
.size = sizeof(struct uwb_rc_evt_beacon_size),
},
[UWB_RC_EVT_BPOIE_CHANGE] = {
.size = sizeof(struct uwb_rc_evt_bpoie_change),
.offset = 1 + offsetof(struct uwb_rc_evt_bpoie_change,
wBPOIELength),
},
[UWB_RC_EVT_BP_SLOT_CHANGE] = {
.size = sizeof(struct uwb_rc_evt_bp_slot_change),
},
[UWB_RC_EVT_BP_SWITCH_IE_RCV] = {
.size = sizeof(struct uwb_rc_evt_bp_switch_ie_rcv),
.offset = 1 + offsetof(struct uwb_rc_evt_bp_switch_ie_rcv, wIELength),
},
[UWB_RC_EVT_DEV_ADDR_CONFLICT] = {
.size = sizeof(struct uwb_rc_evt_dev_addr_conflict),
},
[UWB_RC_EVT_DRP_AVAIL] = {
.size = sizeof(struct uwb_rc_evt_drp_avail)
},
[UWB_RC_EVT_DRP] = {
.size = sizeof(struct uwb_rc_evt_drp),
.offset = 1 + offsetof(struct uwb_rc_evt_drp, ie_length),
},
[UWB_RC_EVT_BP_SWITCH_STATUS] = {
.size = sizeof(struct uwb_rc_evt_bp_switch_status),
},
[UWB_RC_EVT_CMD_FRAME_RCV] = {
.size = sizeof(struct uwb_rc_evt_cmd_frame_rcv),
.offset = 1 + offsetof(struct uwb_rc_evt_cmd_frame_rcv, dataLength),
},
[UWB_RC_EVT_CHANNEL_CHANGE_IE_RCV] = {
.size = sizeof(struct uwb_rc_evt_channel_change_ie_rcv),
.offset = 1 + offsetof(struct uwb_rc_evt_channel_change_ie_rcv, wIELength),
},
[UWB_RC_CMD_CHANNEL_CHANGE] = {
.size = sizeof(struct uwb_rc_evt_confirm),
},
[UWB_RC_CMD_DEV_ADDR_MGMT] = {
.size = sizeof(struct uwb_rc_evt_dev_addr_mgmt) },
[UWB_RC_CMD_GET_IE] = {
.size = sizeof(struct uwb_rc_evt_get_ie),
.offset = 1 + offsetof(struct uwb_rc_evt_get_ie, wIELength),
},
[UWB_RC_CMD_RESET] = {
.size = sizeof(struct uwb_rc_evt_confirm),
},
[UWB_RC_CMD_SCAN] = {
.size = sizeof(struct uwb_rc_evt_confirm),
},
[UWB_RC_CMD_SET_BEACON_FILTER] = {
.size = sizeof(struct uwb_rc_evt_confirm),
},
[UWB_RC_CMD_SET_DRP_IE] = {
.size = sizeof(struct uwb_rc_evt_set_drp_ie),
},
[UWB_RC_CMD_SET_IE] = {
.size = sizeof(struct uwb_rc_evt_set_ie),
},
[UWB_RC_CMD_SET_NOTIFICATION_FILTER] = {
.size = sizeof(struct uwb_rc_evt_confirm),
},
[UWB_RC_CMD_SET_TX_POWER] = {
.size = sizeof(struct uwb_rc_evt_confirm),
},
[UWB_RC_CMD_SLEEP] = {
.size = sizeof(struct uwb_rc_evt_confirm),
},
[UWB_RC_CMD_START_BEACON] = {
.size = sizeof(struct uwb_rc_evt_confirm),
},
[UWB_RC_CMD_STOP_BEACON] = {
.size = sizeof(struct uwb_rc_evt_confirm),
},
[UWB_RC_CMD_BP_MERGE] = {
.size = sizeof(struct uwb_rc_evt_confirm),
},
[UWB_RC_CMD_SEND_COMMAND_FRAME] = {
.size = sizeof(struct uwb_rc_evt_confirm),
},
[UWB_RC_CMD_SET_ASIE_NOTIF] = {
.size = sizeof(struct uwb_rc_evt_confirm),
},
};
static
struct uwb_est_entry uwb_est_01_00xx[] = {
[UWB_RC_DAA_ENERGY_DETECTED] = {
.size = sizeof(struct uwb_rc_evt_daa_energy_detected),
},
[UWB_RC_SET_DAA_ENERGY_MASK] = {
.size = sizeof(struct uwb_rc_evt_set_daa_energy_mask),
},
[UWB_RC_SET_NOTIFICATION_FILTER_EX] = {
.size = sizeof(struct uwb_rc_evt_set_notification_filter_ex),
},
};
/**
* Initialize the EST subsystem
*
* Register the standard tables also.
*
* FIXME: tag init
*/
int uwb_est_create(void)
{
int result;
uwb_est_size = 2;
uwb_est_used = 0;
uwb_est = kcalloc(uwb_est_size, sizeof(uwb_est[0]), GFP_KERNEL);
if (uwb_est == NULL)
return -ENOMEM;
result = uwb_est_register(UWB_RC_CET_GENERAL, 0, 0xffff, 0xffff,
uwb_est_00_00xx, ARRAY_SIZE(uwb_est_00_00xx));
if (result < 0)
goto out;
result = uwb_est_register(UWB_RC_CET_EX_TYPE_1, 0, 0xffff, 0xffff,
uwb_est_01_00xx, ARRAY_SIZE(uwb_est_01_00xx));
out:
return result;
}
/** Clean it up */
void uwb_est_destroy(void)
{
kfree(uwb_est);
uwb_est = NULL;
uwb_est_size = uwb_est_used = 0;
}
/**
* Double the capacity of the EST table
*
* @returns 0 if ok, < 0 errno no error.
*/
static
int uwb_est_grow(void)
{
size_t actual_size = uwb_est_size * sizeof(uwb_est[0]);
void *new = kmalloc_array(2, actual_size, GFP_ATOMIC);
if (new == NULL)
return -ENOMEM;
memcpy(new, uwb_est, actual_size);
memset(new + actual_size, 0, actual_size);
kfree(uwb_est);
uwb_est = new;
uwb_est_size *= 2;
return 0;
}
/**
* Register an event size table
*
* Makes room for it if the table is full, and then inserts it in the
* right position (entries are sorted by type, event_high, vendor and
* then product).
*
* @vendor: vendor code for matching against the device (0x0000 and
* 0xffff mean any); use 0x0000 to force all to match without
* checking possible vendor specific ones, 0xfffff to match
* after checking vendor specific ones.
*
* @product: product code from that vendor; same matching rules, use
* 0x0000 for not allowing vendor specific matches, 0xffff
* for allowing.
*
* This arragement just makes the tables sort differenty. Because the
* table is sorted by growing type-event_high-vendor-product, a zero
* vendor will match before than a 0x456a vendor, that will match
* before a 0xfffff vendor.
*
* @returns 0 if ok, < 0 errno on error (-ENOENT if not found).
*/
/* FIXME: add bus type to vendor/product code */
int uwb_est_register(u8 type, u8 event_high, u16 vendor, u16 product,
const struct uwb_est_entry *entry, size_t entries)
{
unsigned long flags;
unsigned itr;
int result = 0;
write_lock_irqsave(&uwb_est_lock, flags);
if (uwb_est_used == uwb_est_size) {
result = uwb_est_grow();
if (result < 0)
goto out;
}
/* Find the right spot to insert it in */
for (itr = 0; itr < uwb_est_used; itr++)
if (uwb_est[itr].type_event_high < type
&& uwb_est[itr].vendor < vendor
&& uwb_est[itr].product < product)
break;
/* Shift others to make room for the new one? */
if (itr < uwb_est_used)
memmove(&uwb_est[itr+1], &uwb_est[itr], uwb_est_used - itr);
uwb_est[itr].type_event_high = type << 8 | event_high;
uwb_est[itr].vendor = vendor;
uwb_est[itr].product = product;
uwb_est[itr].entry = entry;
uwb_est[itr].entries = entries;
uwb_est_used++;
out:
write_unlock_irqrestore(&uwb_est_lock, flags);
return result;
}
EXPORT_SYMBOL_GPL(uwb_est_register);
/**
* Unregister an event size table
*
* This just removes the specified entry and moves the ones after it
* to fill in the gap. This is needed to keep the list sorted; no
* reallocation is done to reduce the size of the table.
*
* We unregister by all the data we used to register instead of by
* pointer to the @entry array because we might have used the same
* table for a bunch of IDs (for example).
*
* @returns 0 if ok, < 0 errno on error (-ENOENT if not found).
*/
int uwb_est_unregister(u8 type, u8 event_high, u16 vendor, u16 product,
const struct uwb_est_entry *entry, size_t entries)
{
unsigned long flags;
unsigned itr;
struct uwb_est est_cmp = {
.type_event_high = type << 8 | event_high,
.vendor = vendor,
.product = product,
.entry = entry,
.entries = entries
};
write_lock_irqsave(&uwb_est_lock, flags);
for (itr = 0; itr < uwb_est_used; itr++)
if (!memcmp(&uwb_est[itr], &est_cmp, sizeof(est_cmp)))
goto found;
write_unlock_irqrestore(&uwb_est_lock, flags);
return -ENOENT;
found:
if (itr < uwb_est_used - 1) /* Not last one? move ones above */
memmove(&uwb_est[itr], &uwb_est[itr+1], uwb_est_used - itr - 1);
uwb_est_used--;
write_unlock_irqrestore(&uwb_est_lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(uwb_est_unregister);
/**
* Get the size of an event from a table
*
* @rceb: pointer to the buffer with the event
* @rceb_size: size of the area pointed to by @rceb in bytes.
* @returns: > 0 Size of the event
* -ENOSPC An area big enough was not provided to look
* ahead into the event's guts and guess the size.
* -EINVAL Unknown event code (wEvent).
*
* This will look at the received RCEB and guess what is the total
* size. For variable sized events, it will look further ahead into
* their length field to see how much data should be read.
*
* Note this size is *not* final--the neh (Notification/Event Handle)
* might specificy an extra size to add.
*/
static
ssize_t uwb_est_get_size(struct uwb_rc *uwb_rc, struct uwb_est *est,
u8 event_low, const struct uwb_rceb *rceb,
size_t rceb_size)
{
unsigned offset;
ssize_t size;
struct device *dev = &uwb_rc->uwb_dev.dev;
const struct uwb_est_entry *entry;
size = -ENOENT;
if (event_low >= est->entries) { /* in range? */
dev_err(dev, "EST %p 0x%04x/%04x/%04x[%u]: event %u out of range\n",
est, est->type_event_high, est->vendor, est->product,
est->entries, event_low);
goto out;
}
size = -ENOENT;
entry = &est->entry[event_low];
if (entry->size == 0 && entry->offset == 0) { /* unknown? */
dev_err(dev, "EST %p 0x%04x/%04x/%04x[%u]: event %u unknown\n",
est, est->type_event_high, est->vendor, est->product,
est->entries, event_low);
goto out;
}
offset = entry->offset; /* extra fries with that? */
if (offset == 0)
size = entry->size;
else {
/* Ops, got an extra size field at 'offset'--read it */
const void *ptr = rceb;
size_t type_size = 0;
offset--;
size = -ENOSPC; /* enough data for more? */
switch (entry->type) {
case UWB_EST_16: type_size = sizeof(__le16); break;
case UWB_EST_8: type_size = sizeof(u8); break;
default: BUG();
}
if (offset + type_size > rceb_size) {
dev_err(dev, "EST %p 0x%04x/%04x/%04x[%u]: "
"not enough data to read extra size\n",
est, est->type_event_high, est->vendor,
est->product, est->entries);
goto out;
}
size = entry->size;
ptr += offset;
switch (entry->type) {
case UWB_EST_16: size += le16_to_cpu(*(__le16 *)ptr); break;
case UWB_EST_8: size += *(u8 *)ptr; break;
default: BUG();
}
}
out:
return size;
}
/**
* Guesses the size of a WA event
*
* @rceb: pointer to the buffer with the event
* @rceb_size: size of the area pointed to by @rceb in bytes.
* @returns: > 0 Size of the event
* -ENOSPC An area big enough was not provided to look
* ahead into the event's guts and guess the size.
* -EINVAL Unknown event code (wEvent).
*
* This will look at the received RCEB and guess what is the total
* size by checking all the tables registered with
* uwb_est_register(). For variable sized events, it will look further
* ahead into their length field to see how much data should be read.
*
* Note this size is *not* final--the neh (Notification/Event Handle)
* might specificy an extra size to add or replace.
*/
ssize_t uwb_est_find_size(struct uwb_rc *rc, const struct uwb_rceb *rceb,
size_t rceb_size)
{
/* FIXME: add vendor/product data */
ssize_t size;
struct device *dev = &rc->uwb_dev.dev;
unsigned long flags;
unsigned itr;
u16 type_event_high, event;
read_lock_irqsave(&uwb_est_lock, flags);
size = -ENOSPC;
if (rceb_size < sizeof(*rceb))
goto out;
event = le16_to_cpu(rceb->wEvent);
type_event_high = rceb->bEventType << 8 | (event & 0xff00) >> 8;
for (itr = 0; itr < uwb_est_used; itr++) {
if (uwb_est[itr].type_event_high != type_event_high)
continue;
size = uwb_est_get_size(rc, &uwb_est[itr],
event & 0x00ff, rceb, rceb_size);
/* try more tables that might handle the same type */
if (size != -ENOENT)
goto out;
}
dev_dbg(dev,
"event 0x%02x/%04x/%02x: no handlers available; RCEB %4ph\n",
(unsigned) rceb->bEventType,
(unsigned) le16_to_cpu(rceb->wEvent),
(unsigned) rceb->bEventContext,
rceb);
size = -ENOENT;
out:
read_unlock_irqrestore(&uwb_est_lock, flags);
return size;
}
EXPORT_SYMBOL_GPL(uwb_est_find_size);
This diff is collapsed.
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_UWB_I1480U) += dfu/ i1480-est.o
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_UWB_I1480U) += i1480-dfu-usb.o
i1480-dfu-usb-objs := \
dfu.o \
mac.o \
phy.o \
usb.o
// SPDX-License-Identifier: GPL-2.0-only
/*
* Intel Wireless UWB Link 1480
* Main driver
*
* Copyright (C) 2005-2006 Intel Corporation
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
*
* Common code for firmware upload used by the USB and PCI version;
* i1480_fw_upload() takes a device descriptor and uses the function
* pointers it provides to upload firmware and prepare the PHY.
*
* As well, provides common functions used by the rest of the code.
*/
#include "i1480-dfu.h"
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/device.h>
#include <linux/random.h>
#include <linux/export.h>
#include "../../uwb.h"
/*
* i1480_rceb_check - Check RCEB for expected field values
* @i1480: pointer to device for which RCEB is being checked
* @rceb: RCEB being checked
* @cmd: which command the RCEB is related to
* @context: expected context
* @expected_type: expected event type
* @expected_event: expected event
*
* If @cmd is NULL, do not print error messages, but still return an error
* code.
*
* Return 0 if @rceb matches the expected values, -EINVAL otherwise.
*/
int i1480_rceb_check(const struct i1480 *i1480, const struct uwb_rceb *rceb,
const char *cmd, u8 context, u8 expected_type,
unsigned expected_event)
{
int result = 0;
struct device *dev = i1480->dev;
if (rceb->bEventContext != context) {
if (cmd)
dev_err(dev, "%s: unexpected context id 0x%02x "
"(expected 0x%02x)\n", cmd,
rceb->bEventContext, context);
result = -EINVAL;
}
if (rceb->bEventType != expected_type) {
if (cmd)
dev_err(dev, "%s: unexpected event type 0x%02x "
"(expected 0x%02x)\n", cmd,
rceb->bEventType, expected_type);
result = -EINVAL;
}
if (le16_to_cpu(rceb->wEvent) != expected_event) {
if (cmd)
dev_err(dev, "%s: unexpected event 0x%04x "
"(expected 0x%04x)\n", cmd,
le16_to_cpu(rceb->wEvent), expected_event);
result = -EINVAL;
}
return result;
}
EXPORT_SYMBOL_GPL(i1480_rceb_check);
/*
* Execute a Radio Control Command
*
* Command data has to be in i1480->cmd_buf.
*
* @returns size of the reply data filled in i1480->evt_buf or < 0 errno
* code on error.
*/
ssize_t i1480_cmd(struct i1480 *i1480, const char *cmd_name, size_t cmd_size,
size_t reply_size)
{
ssize_t result;
struct uwb_rceb *reply = i1480->evt_buf;
struct uwb_rccb *cmd = i1480->cmd_buf;
u16 expected_event = reply->wEvent;
u8 expected_type = reply->bEventType;
u8 context;
init_completion(&i1480->evt_complete);
i1480->evt_result = -EINPROGRESS;
do {
get_random_bytes(&context, 1);
} while (context == 0x00 || context == 0xff);
cmd->bCommandContext = context;
result = i1480->cmd(i1480, cmd_name, cmd_size);
if (result < 0)
goto error;
/* wait for the callback to report a event was received */
result = wait_for_completion_interruptible_timeout(
&i1480->evt_complete, HZ);
if (result == 0) {
result = -ETIMEDOUT;
goto error;
}
if (result < 0)
goto error;
result = i1480->evt_result;
if (result < 0) {
dev_err(i1480->dev, "%s: command reply reception failed: %zd\n",
cmd_name, result);
goto error;
}
/*
* Firmware versions >= 1.4.12224 for IOGear GUWA100U generate a
* spurious notification after firmware is downloaded. So check whether
* the receibed RCEB is such notification before assuming that the
* command has failed.
*/
if (i1480_rceb_check(i1480, i1480->evt_buf, NULL,
0, 0xfd, 0x0022) == 0) {
/* Now wait for the actual RCEB for this command. */
result = i1480->wait_init_done(i1480);
if (result < 0)
goto error;
result = i1480->evt_result;
}
if (result != reply_size) {
dev_err(i1480->dev, "%s returned only %zu bytes, %zu expected\n",
cmd_name, result, reply_size);
result = -EINVAL;
goto error;
}
/* Verify we got the right event in response */
result = i1480_rceb_check(i1480, i1480->evt_buf, cmd_name, context,
expected_type, expected_event);
error:
return result;
}
EXPORT_SYMBOL_GPL(i1480_cmd);
static
int i1480_print_state(struct i1480 *i1480)
{
int result;
u32 *buf = (u32 *) i1480->cmd_buf;
result = i1480->read(i1480, 0x80080000, 2 * sizeof(*buf));
if (result < 0) {
dev_err(i1480->dev, "cannot read U & L states: %d\n", result);
goto error;
}
dev_info(i1480->dev, "state U 0x%08x, L 0x%08x\n", buf[0], buf[1]);
error:
return result;
}
/*
* PCI probe, firmware uploader
*
* _mac_fw_upload() will call rc_setup(), which needs an rc_release().
*/
int i1480_fw_upload(struct i1480 *i1480)
{
int result;
result = i1480_pre_fw_upload(i1480); /* PHY pre fw */
if (result < 0 && result != -ENOENT) {
i1480_print_state(i1480);
goto error;
}
result = i1480_mac_fw_upload(i1480); /* MAC fw */
if (result < 0) {
if (result == -ENOENT)
dev_err(i1480->dev, "Cannot locate MAC FW file '%s'\n",
i1480->mac_fw_name);
else
i1480_print_state(i1480);
goto error;
}
result = i1480_phy_fw_upload(i1480); /* PHY fw */
if (result < 0 && result != -ENOENT) {
i1480_print_state(i1480);
goto error_rc_release;
}
/*
* FIXME: find some reliable way to check whether firmware is running
* properly. Maybe use some standard request that has no side effects?
*/
dev_info(i1480->dev, "firmware uploaded successfully\n");
error_rc_release:
if (i1480->rc_release)
i1480->rc_release(i1480);
result = 0;
error:
return result;
}
EXPORT_SYMBOL_GPL(i1480_fw_upload);
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* i1480 Device Firmware Upload
*
* Copyright (C) 2005-2006 Intel Corporation
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
*
* This driver is the firmware uploader for the Intel Wireless UWB
* Link 1480 device (both in the USB and PCI incarnations).
*
* The process is quite simple: we stop the device, write the firmware
* to its memory and then restart it. Wait for the device to let us
* know it is done booting firmware. Ready.
*
* We might have to upload before or after a phy firmware (which might
* be done in two methods, using a normal firmware image or through
* the MPI port).
*
* Because USB and PCI use common methods, we just make ops out of the
* common operations (read, write, wait_init_done and cmd) and
* implement them in usb.c and pci.c.
*
* The flow is (some parts omitted):
*
* i1480_{usb,pci}_probe() On enumerate/discovery
* i1480_fw_upload()
* i1480_pre_fw_upload()
* __mac_fw_upload()
* fw_hdrs_load()
* mac_fw_hdrs_push()
* i1480->write() [i1480_{usb,pci}_write()]
* i1480_fw_cmp()
* i1480->read() [i1480_{usb,pci}_read()]
* i1480_mac_fw_upload()
* __mac_fw_upload()
* i1480->setup(()
* i1480->wait_init_done()
* i1480_cmd_reset()
* i1480->cmd() [i1480_{usb,pci}_cmd()]
* ...
* i1480_phy_fw_upload()
* request_firmware()
* i1480_mpi_write()
* i1480->cmd() [i1480_{usb,pci}_cmd()]
*
* Once the probe function enumerates the device and uploads the
* firmware, we just exit with -ENODEV, as we don't really want to
* attach to the device.
*/
#ifndef __i1480_DFU_H__
#define __i1480_DFU_H__
#include <linux/types.h>
#include <linux/completion.h>
#include "../../include/spec.h"
#define i1480_FW_UPLOAD_MODE_MASK (cpu_to_le32(0x00000018))
#if i1480_FW > 0x00000302
#define i1480_RCEB_EXTENDED
#endif
struct uwb_rccb;
struct uwb_rceb;
/*
* Common firmware upload handlers
*
* Normally you embed this struct in another one specific to your hw.
*
* @write Write to device's memory from buffer.
* @read Read from device's memory to i1480->evt_buf.
* @setup Setup device after basic firmware is uploaded
* @wait_init_done
* Wait for the device to send a notification saying init
* is done.
* @cmd FOP for issuing the command to the hardware. The
* command data is contained in i1480->cmd_buf and the size
* is supplied as an argument. The command replied is put
* in i1480->evt_buf and the size in i1480->evt_result (or if
* an error, a < 0 errno code).
*
* @cmd_buf Memory buffer used to send commands to the device.
* Allocated by the upper layers i1480_fw_upload().
* Size has to be @buf_size.
* @evt_buf Memory buffer used to place the async notifications
* received by the hw. Allocated by the upper layers
* i1480_fw_upload().
* Size has to be @buf_size.
* @cmd_complete
* Low level driver uses this to notify code waiting afor
* an event that the event has arrived and data is in
* i1480->evt_buf (and size/result in i1480->evt_result).
* @hw_rev
* Use this value to activate dfu code to support new revisions
* of hardware. i1480_init() sets this to a default value.
* It should be updated by the USB and PCI code.
*/
struct i1480 {
struct device *dev;
int (*write)(struct i1480 *, u32 addr, const void *, size_t);
int (*read)(struct i1480 *, u32 addr, size_t);
int (*rc_setup)(struct i1480 *);
void (*rc_release)(struct i1480 *);
int (*wait_init_done)(struct i1480 *);
int (*cmd)(struct i1480 *, const char *cmd_name, size_t cmd_size);
const char *pre_fw_name;
const char *mac_fw_name;
const char *mac_fw_name_deprecate; /* FIXME: Will go away */
const char *phy_fw_name;
u8 hw_rev;
size_t buf_size; /* size of both evt_buf and cmd_buf */
void *evt_buf, *cmd_buf;
ssize_t evt_result;
struct completion evt_complete;
};
static inline
void i1480_init(struct i1480 *i1480)
{
i1480->hw_rev = 1;
init_completion(&i1480->evt_complete);
}
extern int i1480_fw_upload(struct i1480 *);
extern int i1480_pre_fw_upload(struct i1480 *);
extern int i1480_mac_fw_upload(struct i1480 *);
extern int i1480_phy_fw_upload(struct i1480 *);
extern ssize_t i1480_cmd(struct i1480 *, const char *, size_t, size_t);
extern int i1480_rceb_check(const struct i1480 *,
const struct uwb_rceb *, const char *, u8,
u8, unsigned);
enum {
/* Vendor specific command type */
i1480_CET_VS1 = 0xfd,
/* i1480 commands */
i1480_CMD_SET_IP_MAS = 0x000e,
i1480_CMD_GET_MAC_PHY_INFO = 0x0003,
i1480_CMD_MPI_WRITE = 0x000f,
i1480_CMD_MPI_READ = 0x0010,
/* i1480 events */
#if i1480_FW > 0x00000302
i1480_EVT_CONFIRM = 0x0002,
i1480_EVT_RM_INIT_DONE = 0x0101,
i1480_EVT_DEV_ADD = 0x0103,
i1480_EVT_DEV_RM = 0x0104,
i1480_EVT_DEV_ID_CHANGE = 0x0105,
i1480_EVT_GET_MAC_PHY_INFO = i1480_CMD_GET_MAC_PHY_INFO,
#else
i1480_EVT_CONFIRM = 0x0002,
i1480_EVT_RM_INIT_DONE = 0x0101,
i1480_EVT_DEV_ADD = 0x0103,
i1480_EVT_DEV_RM = 0x0104,
i1480_EVT_DEV_ID_CHANGE = 0x0105,
i1480_EVT_GET_MAC_PHY_INFO = i1480_EVT_CONFIRM,
#endif
};
struct i1480_evt_confirm {
struct uwb_rceb rceb;
#ifdef i1480_RCEB_EXTENDED
__le16 wParamLength;
#endif
u8 bResultCode;
} __attribute__((packed));
struct i1480_rceb {
struct uwb_rceb rceb;
#ifdef i1480_RCEB_EXTENDED
__le16 wParamLength;
#endif
} __attribute__((packed));
/**
* Get MAC & PHY Information confirm event structure
*
* Confirm event returned by the command.
*/
struct i1480_evt_confirm_GMPI {
#if i1480_FW > 0x00000302
struct uwb_rceb rceb;
__le16 wParamLength;
__le16 status;
u8 mac_addr[6]; /* EUI-64 bit IEEE address [still 8 bytes?] */
u8 dev_addr[2];
__le16 mac_fw_rev; /* major = v >> 8; minor = v & 0xff */
u8 hw_rev;
u8 phy_vendor;
u8 phy_rev; /* major v = >> 8; minor = v & 0xff */
__le16 mac_caps;
u8 phy_caps[3];
u8 key_stores;
__le16 mcast_addr_stores;
u8 sec_mode_supported;
#else
struct uwb_rceb rceb;
u8 status;
u8 mac_addr[8]; /* EUI-64 bit IEEE address [still 8 bytes?] */
u8 dev_addr[2];
__le16 mac_fw_rev; /* major = v >> 8; minor = v & 0xff */
__le16 phy_fw_rev; /* major v = >> 8; minor = v & 0xff */
__le16 mac_caps;
u8 phy_caps;
u8 key_stores;
__le16 mcast_addr_stores;
u8 sec_mode_supported;
#endif
} __attribute__((packed));
struct i1480_cmd_mpi_write {
struct uwb_rccb rccb;
__le16 size;
u8 data[];
};
struct i1480_cmd_mpi_read {
struct uwb_rccb rccb;
__le16 size;
struct {
u8 page, offset;
} __attribute__((packed)) data[];
} __attribute__((packed));
struct i1480_evt_mpi_read {
struct uwb_rceb rceb;
#ifdef i1480_RCEB_EXTENDED
__le16 wParamLength;
#endif
u8 bResultCode;
__le16 size;
struct {
u8 page, offset, value;
} __attribute__((packed)) data[];
} __attribute__((packed));
#endif /* #ifndef __i1480_DFU_H__ */
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment