1
0
Fork 0
remarkable-linux/drivers/uwb/lc-rc.c

584 lines
13 KiB
C
Raw Normal View History

/*
* Ultra Wide Band
* Life cycle of radio controllers
*
* Copyright (C) 2005-2006 Intel Corporation
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
*
* FIXME: docs
*
* A UWB radio controller is also a UWB device, so it embeds one...
*
* List of RCs comes from the 'struct class uwb_rc_class'.
*/
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/random.h>
#include <linux/kdev_t.h>
#include <linux/etherdevice.h>
#include <linux/usb.h>
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h percpu.h is included by sched.h and module.h and thus ends up being included when building most .c files. percpu.h includes slab.h which in turn includes gfp.h making everything defined by the two files universally available and complicating inclusion dependencies. percpu.h -> slab.h dependency is about to be removed. Prepare for this change by updating users of gfp and slab facilities include those headers directly instead of assuming availability. As this conversion needs to touch large number of source files, the following script is used as the basis of conversion. http://userweb.kernel.org/~tj/misc/slabh-sweep.py The script does the followings. * Scan files for gfp and slab usages and update includes such that only the necessary includes are there. ie. if only gfp is used, gfp.h, if slab is used, slab.h. * When the script inserts a new include, it looks at the include blocks and try to put the new include such that its order conforms to its surrounding. It's put in the include block which contains core kernel includes, in the same order that the rest are ordered - alphabetical, Christmas tree, rev-Xmas-tree or at the end if there doesn't seem to be any matching order. * If the script can't find a place to put a new include (mostly because the file doesn't have fitting include block), it prints out an error message indicating which .h file needs to be added to the file. The conversion was done in the following steps. 1. The initial automatic conversion of all .c files updated slightly over 4000 files, deleting around 700 includes and adding ~480 gfp.h and ~3000 slab.h inclusions. The script emitted errors for ~400 files. 2. Each error was manually checked. Some didn't need the inclusion, some needed manual addition while adding it to implementation .h or embedding .c file was more appropriate for others. This step added inclusions to around 150 files. 3. The script was run again and the output was compared to the edits from #2 to make sure no file was left behind. 4. Several build tests were done and a couple of problems were fixed. e.g. lib/decompress_*.c used malloc/free() wrappers around slab APIs requiring slab.h to be added manually. 5. The script was run on all .h files but without automatically editing them as sprinkling gfp.h and slab.h inclusions around .h files could easily lead to inclusion dependency hell. Most gfp.h inclusion directives were ignored as stuff from gfp.h was usually wildly available and often used in preprocessor macros. Each slab.h inclusion directive was examined and added manually as necessary. 6. percpu.h was updated not to include slab.h. 7. Build test were done on the following configurations and failures were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my distributed build env didn't work with gcov compiles) and a few more options had to be turned off depending on archs to make things build (like ipr on powerpc/64 which failed due to missing writeq). * x86 and x86_64 UP and SMP allmodconfig and a custom test config. * powerpc and powerpc64 SMP allmodconfig * sparc and sparc64 SMP allmodconfig * ia64 SMP allmodconfig * s390 SMP allmodconfig * alpha SMP allmodconfig * um on x86_64 SMP allmodconfig 8. percpu.h modifications were reverted so that it could be applied as a separate patch and serve as bisection point. Given the fact that I had only a couple of failures from tests on step 6, I'm fairly confident about the coverage of this conversion patch. If there is a breakage, it's likely to be something in one of the arch headers which should be easily discoverable easily on most builds of the specific arch. Signed-off-by: Tejun Heo <tj@kernel.org> Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 02:04:11 -06:00
#include <linux/slab.h>
#include <linux/export.h>
#include "uwb-internal.h"
static int uwb_rc_index_match(struct device *dev, const void *data)
{
const int *index = data;
struct uwb_rc *rc = dev_get_drvdata(dev);
if (rc->index == *index)
return 1;
return 0;
}
static struct uwb_rc *uwb_rc_find_by_index(int index)
{
struct device *dev;
struct uwb_rc *rc = NULL;
dev = class_find_device(&uwb_rc_class, NULL, &index, uwb_rc_index_match);
if (dev) {
rc = dev_get_drvdata(dev);
put_device(dev);
}
return rc;
}
static int uwb_rc_new_index(void)
{
int index = 0;
for (;;) {
if (!uwb_rc_find_by_index(index))
return index;
if (++index < 0)
index = 0;
}
}
/**
* Release the backing device of a uwb_rc that has been dynamically allocated.
*/
static void uwb_rc_sys_release(struct device *dev)
{
struct uwb_dev *uwb_dev = container_of(dev, struct uwb_dev, dev);
struct uwb_rc *rc = container_of(uwb_dev, struct uwb_rc, uwb_dev);
uwb_rc_ie_release(rc);
kfree(rc);
}
void uwb_rc_init(struct uwb_rc *rc)
{
struct uwb_dev *uwb_dev = &rc->uwb_dev;
uwb_dev_init(uwb_dev);
rc->uwb_dev.dev.class = &uwb_rc_class;
rc->uwb_dev.dev.release = uwb_rc_sys_release;
uwb_rc_neh_create(rc);
rc->beaconing = -1;
rc->scan_type = UWB_SCAN_DISABLED;
INIT_LIST_HEAD(&rc->notifs_chain.list);
mutex_init(&rc->notifs_chain.mutex);
INIT_LIST_HEAD(&rc->uwb_beca.list);
mutex_init(&rc->uwb_beca.mutex);
uwb_drp_avail_init(rc);
uwb_rc_ie_init(rc);
uwb_rsv_init(rc);
uwb_rc_pal_init(rc);
}
EXPORT_SYMBOL_GPL(uwb_rc_init);
struct uwb_rc *uwb_rc_alloc(void)
{
struct uwb_rc *rc;
rc = kzalloc(sizeof(*rc), GFP_KERNEL);
if (rc == NULL)
return NULL;
uwb_rc_init(rc);
return rc;
}
EXPORT_SYMBOL_GPL(uwb_rc_alloc);
/*
* Show the ASIE that is broadcast in the UWB beacon by this uwb_rc device.
*/
static ssize_t ASIE_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_ie_hdr *ie;
void *ptr;
size_t len;
int result = 0;
/* init empty buffer. */
result = scnprintf(buf, PAGE_SIZE, "\n");
mutex_lock(&rc->ies_mutex);
/* walk IEData looking for an ASIE. */
ptr = rc->ies->IEData;
len = le16_to_cpu(rc->ies->wIELength);
for (;;) {
ie = uwb_ie_next(&ptr, &len);
if (!ie)
break;
if (ie->element_id == UWB_APP_SPEC_IE) {
result = uwb_ie_dump_hex(ie,
ie->length + sizeof(struct uwb_ie_hdr),
buf, PAGE_SIZE);
break;
}
}
mutex_unlock(&rc->ies_mutex);
return result;
}
/*
* Update the ASIE that is broadcast in the UWB beacon by this uwb_rc device.
*/
static ssize_t ASIE_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;
char ie_buf[255];
int result, ie_len = 0;
const char *cur_ptr = buf;
struct uwb_ie_hdr *ie;
/* empty string means clear the ASIE. */
if (strlen(buf) <= 1) {
uwb_rc_ie_rm(rc, UWB_APP_SPEC_IE);
return size;
}
/* if non-empty string, convert string of hex chars to binary. */
while (ie_len < sizeof(ie_buf)) {
int char_count;
if (sscanf(cur_ptr, " %02hhX %n",
&(ie_buf[ie_len]), &char_count) > 0) {
++ie_len;
/* skip chars read from cur_ptr. */
cur_ptr += char_count;
} else {
break;
}
}
/* validate IE length and type. */
if (ie_len < sizeof(struct uwb_ie_hdr)) {
dev_err(dev, "%s: Invalid ASIE size %d.\n", __func__, ie_len);
return -EINVAL;
}
ie = (struct uwb_ie_hdr *)ie_buf;
if (ie->element_id != UWB_APP_SPEC_IE) {
dev_err(dev, "%s: Invalid IE element type size = 0x%02X.\n",
__func__, ie->element_id);
return -EINVAL;
}
/* bounds check length field from user. */
if (ie->length > (ie_len - sizeof(struct uwb_ie_hdr)))
ie->length = ie_len - sizeof(struct uwb_ie_hdr);
/*
* Valid ASIE received. Remove current ASIE then add the new one using
* uwb_rc_ie_add.
*/
uwb_rc_ie_rm(rc, UWB_APP_SPEC_IE);
result = uwb_rc_ie_add(rc, ie, ie->length + sizeof(struct uwb_ie_hdr));
return result >= 0 ? size : result;
}
static DEVICE_ATTR_RW(ASIE);
static struct attribute *rc_attrs[] = {
&dev_attr_mac_address.attr,
&dev_attr_scan.attr,
&dev_attr_beacon.attr,
&dev_attr_ASIE.attr,
NULL,
};
static const struct attribute_group rc_attr_group = {
.attrs = rc_attrs,
};
/*
* Registration of sysfs specific stuff
*/
static int uwb_rc_sys_add(struct uwb_rc *rc)
{
return sysfs_create_group(&rc->uwb_dev.dev.kobj, &rc_attr_group);
}
static void __uwb_rc_sys_rm(struct uwb_rc *rc)
{
sysfs_remove_group(&rc->uwb_dev.dev.kobj, &rc_attr_group);
}
/**
* uwb_rc_mac_addr_setup - get an RC's EUI-48 address or set it
* @rc: the radio controller.
*
* If the EUI-48 address is 00:00:00:00:00:00 or FF:FF:FF:FF:FF:FF
* then a random locally administered EUI-48 is generated and set on
* the device. The probability of address collisions is sufficiently
* unlikely (1/2^40 = 9.1e-13) that they're not checked for.
*/
static
int uwb_rc_mac_addr_setup(struct uwb_rc *rc)
{
int result;
struct device *dev = &rc->uwb_dev.dev;
struct uwb_dev *uwb_dev = &rc->uwb_dev;
char devname[UWB_ADDR_STRSIZE];
struct uwb_mac_addr addr;
result = uwb_rc_mac_addr_get(rc, &addr);
if (result < 0) {
dev_err(dev, "cannot retrieve UWB EUI-48 address: %d\n", result);
return result;
}
if (uwb_mac_addr_unset(&addr) || uwb_mac_addr_bcast(&addr)) {
addr.data[0] = 0x02; /* locally administered and unicast */
get_random_bytes(&addr.data[1], sizeof(addr.data)-1);
result = uwb_rc_mac_addr_set(rc, &addr);
if (result < 0) {
uwb_mac_addr_print(devname, sizeof(devname), &addr);
dev_err(dev, "cannot set EUI-48 address %s: %d\n",
devname, result);
return result;
}
}
uwb_dev->mac_addr = addr;
return 0;
}
static int uwb_rc_setup(struct uwb_rc *rc)
{
int result;
struct device *dev = &rc->uwb_dev.dev;
result = uwb_radio_setup(rc);
if (result < 0) {
dev_err(dev, "cannot setup UWB radio: %d\n", result);
goto error;
}
result = uwb_rc_mac_addr_setup(rc);
if (result < 0) {
dev_err(dev, "cannot setup UWB MAC address: %d\n", result);
goto error;
}
result = uwb_rc_dev_addr_assign(rc);
if (result < 0) {
dev_err(dev, "cannot assign UWB DevAddr: %d\n", result);
goto error;
}
result = uwb_rc_ie_setup(rc);
if (result < 0) {
dev_err(dev, "cannot setup IE subsystem: %d\n", result);
goto error_ie_setup;
}
result = uwb_rsv_setup(rc);
if (result < 0) {
dev_err(dev, "cannot setup reservation subsystem: %d\n", result);
goto error_rsv_setup;
}
uwb_dbg_add_rc(rc);
return 0;
error_rsv_setup:
uwb_rc_ie_release(rc);
error_ie_setup:
error:
return result;
}
/**
* Register a new UWB radio controller
*
* Did you call uwb_rc_init() on your rc?
*
* We assume that this is being called with a > 0 refcount on
* it [through ops->{get|put}_device(). We'll take our own, though.
*
* @parent_dev is our real device, the one that provides the actual UWB device
*/
int uwb_rc_add(struct uwb_rc *rc, struct device *parent_dev, void *priv)
{
int result;
struct device *dev;
char macbuf[UWB_ADDR_STRSIZE], devbuf[UWB_ADDR_STRSIZE];
rc->index = uwb_rc_new_index();
dev = &rc->uwb_dev.dev;
dev_set_name(dev, "uwb%d", rc->index);
rc->priv = priv;
init_waitqueue_head(&rc->uwbd.wq);
INIT_LIST_HEAD(&rc->uwbd.event_list);
spin_lock_init(&rc->uwbd.event_list_lock);
uwbd_start(rc);
result = rc->start(rc);
if (result < 0)
goto error_rc_start;
result = uwb_rc_setup(rc);
if (result < 0) {
dev_err(dev, "cannot setup UWB radio controller: %d\n", result);
goto error_rc_setup;
}
result = uwb_dev_add(&rc->uwb_dev, parent_dev, rc);
if (result < 0 && result != -EADDRNOTAVAIL)
goto error_dev_add;
result = uwb_rc_sys_add(rc);
if (result < 0) {
dev_err(parent_dev, "cannot register UWB radio controller "
"dev attributes: %d\n", result);
goto error_sys_add;
}
uwb_mac_addr_print(macbuf, sizeof(macbuf), &rc->uwb_dev.mac_addr);
uwb_dev_addr_print(devbuf, sizeof(devbuf), &rc->uwb_dev.dev_addr);
dev_info(dev,
"new uwb radio controller (mac %s dev %s) on %s %s\n",
macbuf, devbuf, parent_dev->bus->name, dev_name(parent_dev));
rc->ready = 1;
return 0;
error_sys_add:
uwb_dev_rm(&rc->uwb_dev);
error_dev_add:
error_rc_setup:
rc->stop(rc);
error_rc_start:
uwbd_stop(rc);
return result;
}
EXPORT_SYMBOL_GPL(uwb_rc_add);
static int uwb_dev_offair_helper(struct device *dev, void *priv)
{
struct uwb_dev *uwb_dev = to_uwb_dev(dev);
return __uwb_dev_offair(uwb_dev, uwb_dev->rc);
}
/*
* Remove a Radio Controller; stop beaconing/scanning, disconnect all children
*/
void uwb_rc_rm(struct uwb_rc *rc)
{
rc->ready = 0;
uwb_dbg_del_rc(rc);
uwb_rsv_remove_all(rc);
uwb_radio_shutdown(rc);
rc->stop(rc);
uwbd_stop(rc);
uwb_rc_neh_destroy(rc);
uwb_dev_lock(&rc->uwb_dev);
rc->priv = NULL;
rc->cmd = NULL;
uwb_dev_unlock(&rc->uwb_dev);
mutex_lock(&rc->uwb_beca.mutex);
uwb_dev_for_each(rc, uwb_dev_offair_helper, NULL);
__uwb_rc_sys_rm(rc);
mutex_unlock(&rc->uwb_beca.mutex);
uwb_rsv_cleanup(rc);
uwb_beca_release(rc);
uwb_dev_rm(&rc->uwb_dev);
}
EXPORT_SYMBOL_GPL(uwb_rc_rm);
static int find_rc_try_get(struct device *dev, const void *data)
{
const struct uwb_rc *target_rc = data;
struct uwb_rc *rc = dev_get_drvdata(dev);
if (rc == NULL) {
WARN_ON(1);
return 0;
}
if (rc == target_rc) {
if (rc->ready == 0)
return 0;
else
return 1;
}
return 0;
}
/**
* Given a radio controller descriptor, validate and refcount it
*
* @returns NULL if the rc does not exist or is quiescing; the ptr to
* it otherwise.
*/
struct uwb_rc *__uwb_rc_try_get(struct uwb_rc *target_rc)
{
struct device *dev;
struct uwb_rc *rc = NULL;
dev = class_find_device(&uwb_rc_class, NULL, target_rc,
find_rc_try_get);
if (dev) {
rc = dev_get_drvdata(dev);
__uwb_rc_get(rc);
put_device(dev);
}
return rc;
}
EXPORT_SYMBOL_GPL(__uwb_rc_try_get);
/*
* RC get for external refcount acquirers...
*
* Increments the refcount of the device and it's backend modules
*/
static inline struct uwb_rc *uwb_rc_get(struct uwb_rc *rc)
{
if (rc->ready == 0)
return NULL;
uwb_dev_get(&rc->uwb_dev);
return rc;
}
static int find_rc_grandpa(struct device *dev, const void *data)
{
const struct device *grandpa_dev = data;
struct uwb_rc *rc = dev_get_drvdata(dev);
if (rc->uwb_dev.dev.parent->parent == grandpa_dev) {
rc = uwb_rc_get(rc);
return 1;
}
return 0;
}
/**
* Locate and refcount a radio controller given a common grand-parent
*
* @grandpa_dev Pointer to the 'grandparent' device structure.
* @returns NULL If the rc does not exist or is quiescing; the ptr to
* it otherwise, properly referenced.
*
* The Radio Control interface (or the UWB Radio Controller) is always
* an interface of a device. The parent is the interface, the
* grandparent is the device that encapsulates the interface.
*
* There is no need to lock around as the "grandpa" would be
* refcounted by the target, and to remove the referemes, the
* uwb_rc_class->sem would have to be taken--we hold it, ergo we
* should be safe.
*/
struct uwb_rc *uwb_rc_get_by_grandpa(const struct device *grandpa_dev)
{
struct device *dev;
struct uwb_rc *rc = NULL;
dev = class_find_device(&uwb_rc_class, NULL, grandpa_dev,
find_rc_grandpa);
if (dev) {
rc = dev_get_drvdata(dev);
put_device(dev);
}
return rc;
}
EXPORT_SYMBOL_GPL(uwb_rc_get_by_grandpa);
/**
* Find a radio controller by device address
*
* @returns the pointer to the radio controller, properly referenced
*/
static int find_rc_dev(struct device *dev, const void *data)
{
const struct uwb_dev_addr *addr = data;
struct uwb_rc *rc = dev_get_drvdata(dev);
if (rc == NULL) {
WARN_ON(1);
return 0;
}
if (!uwb_dev_addr_cmp(&rc->uwb_dev.dev_addr, addr)) {
rc = uwb_rc_get(rc);
return 1;
}
return 0;
}
struct uwb_rc *uwb_rc_get_by_dev(const struct uwb_dev_addr *addr)
{
struct device *dev;
struct uwb_rc *rc = NULL;
dev = class_find_device(&uwb_rc_class, NULL, addr, find_rc_dev);
if (dev) {
rc = dev_get_drvdata(dev);
put_device(dev);
}
return rc;
}
EXPORT_SYMBOL_GPL(uwb_rc_get_by_dev);
/**
* Drop a reference on a radio controller
*
* This is the version that should be done by entities external to the
* UWB Radio Control stack (ie: clients of the API).
*/
void uwb_rc_put(struct uwb_rc *rc)
{
__uwb_rc_put(rc);
}
EXPORT_SYMBOL_GPL(uwb_rc_put);