add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
obj-$(CONFIG_UWB_WLP) := wlp.o
wlp-objs := \
driver.o \
eda.o \
messages.o \
sysfs.o \
txrx.o \
wlp-lc.o \
wss-lc.o

View File

@@ -0,0 +1,43 @@
/*
* WiMedia Logical Link Control Protocol (WLP)
*
* Copyright (C) 2007 Intel Corporation
* Reinette Chatre <reinette.chatre@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.
*
*
* Life cycle of WLP substack
*
* FIXME: Docs
*/
#include <linux/module.h>
static int __init wlp_subsys_init(void)
{
return 0;
}
module_init(wlp_subsys_init);
static void __exit wlp_subsys_exit(void)
{
return;
}
module_exit(wlp_subsys_exit);
MODULE_AUTHOR("Reinette Chatre <reinette.chatre@intel.com>");
MODULE_DESCRIPTION("WiMedia Logical Link Control Protocol (WLP)");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,414 @@
/*
* WUSB Wire Adapter: WLP interface
* Ethernet to device address cache
*
* 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.
*
*
* We need to be able to map ethernet addresses to device addresses
* and back because there is not explicit relationship between the eth
* addresses used in the ETH frames and the device addresses (no, it
* would not have been simpler to force as ETH address the MBOA MAC
* address...no, not at all :).
*
* A device has one MBOA MAC address and one device address. It is possible
* for a device to have more than one virtual MAC address (although a
* virtual address can be the same as the MBOA MAC address). The device
* address is guaranteed to be unique among the devices in the extended
* beacon group (see ECMA 17.1.1). We thus use the device address as index
* to this cache. We do allow searching based on virtual address as this
* is how Ethernet frames will be addressed.
*
* We need to support virtual EUI-48. Although, right now the virtual
* EUI-48 will always be the same as the MAC SAP address. The EDA cache
* entry thus contains a MAC SAP address as well as the virtual address
* (used to map the network stack address to a neighbor). When we move
* to support more than one virtual MAC on a host then this organization
* will have to change. Perhaps a neighbor has a list of WSSs, each with a
* tag and virtual EUI-48.
*
* On data transmission
* it is used to determine if the neighbor is connected and what WSS it
* belongs to. With this we know what tag to add to the WLP frame. Storing
* the WSS in the EDA cache may be overkill because we only support one
* WSS. Hopefully we will support more than one WSS at some point.
* On data reception it is used to determine the WSS based on
* the tag and address of the transmitting neighbor.
*/
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/wlp.h>
#include "wlp-internal.h"
/* FIXME: cache is not purged, only on device close */
/* FIXME: does not scale, change to dynamic array */
/*
* Initialize the EDA cache
*
* @returns 0 if ok, < 0 errno code on error
*
* Call when the interface is being brought up
*
* NOTE: Keep it as a separate function as the implementation will
* change and be more complex.
*/
void wlp_eda_init(struct wlp_eda *eda)
{
INIT_LIST_HEAD(&eda->cache);
spin_lock_init(&eda->lock);
}
/*
* Release the EDA cache
*
* @returns 0 if ok, < 0 errno code on error
*
* Called when the interface is brought down
*/
void wlp_eda_release(struct wlp_eda *eda)
{
unsigned long flags;
struct wlp_eda_node *itr, *next;
spin_lock_irqsave(&eda->lock, flags);
list_for_each_entry_safe(itr, next, &eda->cache, list_node) {
list_del(&itr->list_node);
kfree(itr);
}
spin_unlock_irqrestore(&eda->lock, flags);
}
/*
* Add an address mapping
*
* @returns 0 if ok, < 0 errno code on error
*
* An address mapping is initially created when the neighbor device is seen
* for the first time (it is "onair"). At this time the neighbor is not
* connected or associated with a WSS so we only populate the Ethernet and
* Device address fields.
*
*/
int wlp_eda_create_node(struct wlp_eda *eda,
const unsigned char eth_addr[ETH_ALEN],
const struct uwb_dev_addr *dev_addr)
{
int result = 0;
struct wlp_eda_node *itr;
unsigned long flags;
BUG_ON(dev_addr == NULL || eth_addr == NULL);
spin_lock_irqsave(&eda->lock, flags);
list_for_each_entry(itr, &eda->cache, list_node) {
if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
printk(KERN_ERR "EDA cache already contains entry "
"for neighbor %02x:%02x\n",
dev_addr->data[1], dev_addr->data[0]);
result = -EEXIST;
goto out_unlock;
}
}
itr = kzalloc(sizeof(*itr), GFP_ATOMIC);
if (itr != NULL) {
memcpy(itr->eth_addr, eth_addr, sizeof(itr->eth_addr));
itr->dev_addr = *dev_addr;
list_add(&itr->list_node, &eda->cache);
} else
result = -ENOMEM;
out_unlock:
spin_unlock_irqrestore(&eda->lock, flags);
return result;
}
/*
* Remove entry from EDA cache
*
* This is done when the device goes off air.
*/
void wlp_eda_rm_node(struct wlp_eda *eda, const struct uwb_dev_addr *dev_addr)
{
struct wlp_eda_node *itr, *next;
unsigned long flags;
spin_lock_irqsave(&eda->lock, flags);
list_for_each_entry_safe(itr, next, &eda->cache, list_node) {
if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
list_del(&itr->list_node);
kfree(itr);
break;
}
}
spin_unlock_irqrestore(&eda->lock, flags);
}
/*
* Update an address mapping
*
* @returns 0 if ok, < 0 errno code on error
*/
int wlp_eda_update_node(struct wlp_eda *eda,
const struct uwb_dev_addr *dev_addr,
struct wlp_wss *wss,
const unsigned char virt_addr[ETH_ALEN],
const u8 tag, const enum wlp_wss_connect state)
{
int result = -ENOENT;
struct wlp_eda_node *itr;
unsigned long flags;
spin_lock_irqsave(&eda->lock, flags);
list_for_each_entry(itr, &eda->cache, list_node) {
if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
/* Found it, update it */
itr->wss = wss;
memcpy(itr->virt_addr, virt_addr,
sizeof(itr->virt_addr));
itr->tag = tag;
itr->state = state;
result = 0;
goto out_unlock;
}
}
/* Not found */
out_unlock:
spin_unlock_irqrestore(&eda->lock, flags);
return result;
}
/*
* Update only state field of an address mapping
*
* @returns 0 if ok, < 0 errno code on error
*/
int wlp_eda_update_node_state(struct wlp_eda *eda,
const struct uwb_dev_addr *dev_addr,
const enum wlp_wss_connect state)
{
int result = -ENOENT;
struct wlp_eda_node *itr;
unsigned long flags;
spin_lock_irqsave(&eda->lock, flags);
list_for_each_entry(itr, &eda->cache, list_node) {
if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
/* Found it, update it */
itr->state = state;
result = 0;
goto out_unlock;
}
}
/* Not found */
out_unlock:
spin_unlock_irqrestore(&eda->lock, flags);
return result;
}
/*
* Return contents of EDA cache entry
*
* @dev_addr: index to EDA cache
* @eda_entry: pointer to where contents of EDA cache will be copied
*/
int wlp_copy_eda_node(struct wlp_eda *eda, struct uwb_dev_addr *dev_addr,
struct wlp_eda_node *eda_entry)
{
int result = -ENOENT;
struct wlp_eda_node *itr;
unsigned long flags;
spin_lock_irqsave(&eda->lock, flags);
list_for_each_entry(itr, &eda->cache, list_node) {
if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
*eda_entry = *itr;
result = 0;
goto out_unlock;
}
}
/* Not found */
out_unlock:
spin_unlock_irqrestore(&eda->lock, flags);
return result;
}
/*
* Execute function for every element in the cache
*
* @function: function to execute on element of cache (must be atomic)
* @priv: private data of function
* @returns: result of first function that failed, or last function
* executed if no function failed.
*
* Stop executing when function returns error for any element in cache.
*
* IMPORTANT: We are using a spinlock here: the function executed on each
* element has to be atomic.
*/
int wlp_eda_for_each(struct wlp_eda *eda, wlp_eda_for_each_f function,
void *priv)
{
int result = 0;
struct wlp *wlp = container_of(eda, struct wlp, eda);
struct wlp_eda_node *entry;
unsigned long flags;
spin_lock_irqsave(&eda->lock, flags);
list_for_each_entry(entry, &eda->cache, list_node) {
result = (*function)(wlp, entry, priv);
if (result < 0)
break;
}
spin_unlock_irqrestore(&eda->lock, flags);
return result;
}
/*
* Execute function for single element in the cache (return dev addr)
*
* @virt_addr: index into EDA cache used to determine which element to
* execute the function on
* @dev_addr: device address of element in cache will be returned using
* @dev_addr
* @function: function to execute on element of cache (must be atomic)
* @priv: private data of function
* @returns: result of function
*
* IMPORTANT: We are using a spinlock here: the function executed on the
* element has to be atomic.
*/
int wlp_eda_for_virtual(struct wlp_eda *eda,
const unsigned char virt_addr[ETH_ALEN],
struct uwb_dev_addr *dev_addr,
wlp_eda_for_each_f function,
void *priv)
{
int result = 0;
struct wlp *wlp = container_of(eda, struct wlp, eda);
struct wlp_eda_node *itr;
unsigned long flags;
int found = 0;
spin_lock_irqsave(&eda->lock, flags);
list_for_each_entry(itr, &eda->cache, list_node) {
if (!memcmp(itr->virt_addr, virt_addr,
sizeof(itr->virt_addr))) {
result = (*function)(wlp, itr, priv);
*dev_addr = itr->dev_addr;
found = 1;
break;
}
}
if (!found)
result = -ENODEV;
spin_unlock_irqrestore(&eda->lock, flags);
return result;
}
static const char *__wlp_wss_connect_state[] = { "WLP_WSS_UNCONNECTED",
"WLP_WSS_CONNECTED",
"WLP_WSS_CONNECT_FAILED",
};
static const char *wlp_wss_connect_state_str(unsigned id)
{
if (id >= ARRAY_SIZE(__wlp_wss_connect_state))
return "unknown WSS connection state";
return __wlp_wss_connect_state[id];
}
/*
* View EDA cache from user space
*
* A debugging feature to give user visibility into the EDA cache. Also
* used to display members of WSS to user (called from wlp_wss_members_show())
*/
ssize_t wlp_eda_show(struct wlp *wlp, char *buf)
{
ssize_t result = 0;
struct wlp_eda_node *entry;
unsigned long flags;
struct wlp_eda *eda = &wlp->eda;
spin_lock_irqsave(&eda->lock, flags);
result = scnprintf(buf, PAGE_SIZE, "#eth_addr dev_addr wss_ptr "
"tag state virt_addr\n");
list_for_each_entry(entry, &eda->cache, list_node) {
result += scnprintf(buf + result, PAGE_SIZE - result,
"%pM %02x:%02x %p 0x%02x %s %pM\n",
entry->eth_addr,
entry->dev_addr.data[1],
entry->dev_addr.data[0], entry->wss,
entry->tag,
wlp_wss_connect_state_str(entry->state),
entry->virt_addr);
if (result >= PAGE_SIZE)
break;
}
spin_unlock_irqrestore(&eda->lock, flags);
return result;
}
EXPORT_SYMBOL_GPL(wlp_eda_show);
/*
* Add new EDA cache entry based on user input in sysfs
*
* Should only be used for debugging.
*
* The WSS is assumed to be the only WSS supported. This needs to be
* redesigned when we support more than one WSS.
*/
ssize_t wlp_eda_store(struct wlp *wlp, const char *buf, size_t size)
{
ssize_t result;
struct wlp_eda *eda = &wlp->eda;
u8 eth_addr[6];
struct uwb_dev_addr dev_addr;
u8 tag;
unsigned state;
result = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx "
"%02hhx:%02hhx %02hhx %u\n",
&eth_addr[0], &eth_addr[1],
&eth_addr[2], &eth_addr[3],
&eth_addr[4], &eth_addr[5],
&dev_addr.data[1], &dev_addr.data[0], &tag, &state);
switch (result) {
case 6: /* no dev addr specified -- remove entry NOT IMPLEMENTED */
/*result = wlp_eda_rm(eda, eth_addr, &dev_addr);*/
result = -ENOSYS;
break;
case 10:
state = state >= 1 ? 1 : 0;
result = wlp_eda_create_node(eda, eth_addr, &dev_addr);
if (result < 0 && result != -EEXIST)
goto error;
/* Set virtual addr to be same as MAC */
result = wlp_eda_update_node(eda, &dev_addr, &wlp->wss,
eth_addr, tag, state);
if (result < 0)
goto error;
break;
default: /* bad format */
result = -EINVAL;
}
error:
return result < 0 ? result : size;
}
EXPORT_SYMBOL_GPL(wlp_eda_store);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,709 @@
/*
* WiMedia Logical Link Control Protocol (WLP)
* sysfs functions
*
* Copyright (C) 2007 Intel Corporation
* Reinette Chatre <reinette.chatre@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
*
*/
#include <linux/wlp.h>
#include "wlp-internal.h"
static
size_t wlp_wss_wssid_e_print(char *buf, size_t bufsize,
struct wlp_wssid_e *wssid_e)
{
size_t used = 0;
used += scnprintf(buf, bufsize, " WSS: ");
used += wlp_wss_uuid_print(buf + used, bufsize - used,
&wssid_e->wssid);
if (wssid_e->info != NULL) {
used += scnprintf(buf + used, bufsize - used, " ");
used += uwb_mac_addr_print(buf + used, bufsize - used,
&wssid_e->info->bcast);
used += scnprintf(buf + used, bufsize - used, " %u %u %s\n",
wssid_e->info->accept_enroll,
wssid_e->info->sec_status,
wssid_e->info->name);
}
return used;
}
/**
* Print out information learned from neighbor discovery
*
* Some fields being printed may not be included in the device discovery
* information (it is not mandatory). We are thus careful how the
* information is printed to ensure it is clear to the user what field is
* being referenced.
* The information being printed is for one time use - temporary storage is
* cleaned after it is printed.
*
* Ideally sysfs output should be on one line. The information printed here
* contain a few strings so it will be hard to parse if they are all
* printed on the same line - without agreeing on a standard field
* separator.
*/
static
ssize_t wlp_wss_neighborhood_print_remove(struct wlp *wlp, char *buf,
size_t bufsize)
{
size_t used = 0;
struct wlp_neighbor_e *neighb;
struct wlp_wssid_e *wssid_e;
mutex_lock(&wlp->nbmutex);
used = scnprintf(buf, bufsize, "#Neighbor information\n"
"#uuid dev_addr\n"
"# Device Name:\n# Model Name:\n# Manufacturer:\n"
"# Model Nr:\n# Serial:\n"
"# Pri Dev type: CategoryID OUI OUISubdiv "
"SubcategoryID\n"
"# WSS: WSSID WSS_name accept_enroll sec_status "
"bcast\n"
"# WSS: WSSID WSS_name accept_enroll sec_status "
"bcast\n\n");
list_for_each_entry(neighb, &wlp->neighbors, node) {
if (bufsize - used <= 0)
goto out;
used += wlp_wss_uuid_print(buf + used, bufsize - used,
&neighb->uuid);
buf[used++] = ' ';
used += uwb_dev_addr_print(buf + used, bufsize - used,
&neighb->uwb_dev->dev_addr);
if (neighb->info != NULL)
used += scnprintf(buf + used, bufsize - used,
"\n Device Name: %s\n"
" Model Name: %s\n"
" Manufacturer:%s \n"
" Model Nr: %s\n"
" Serial: %s\n"
" Pri Dev type: "
"%u %02x:%02x:%02x %u %u\n",
neighb->info->name,
neighb->info->model_name,
neighb->info->manufacturer,
neighb->info->model_nr,
neighb->info->serial,
neighb->info->prim_dev_type.category,
neighb->info->prim_dev_type.OUI[0],
neighb->info->prim_dev_type.OUI[1],
neighb->info->prim_dev_type.OUI[2],
neighb->info->prim_dev_type.OUIsubdiv,
neighb->info->prim_dev_type.subID);
list_for_each_entry(wssid_e, &neighb->wssid, node) {
used += wlp_wss_wssid_e_print(buf + used,
bufsize - used,
wssid_e);
}
buf[used++] = '\n';
wlp_remove_neighbor_tmp_info(neighb);
}
out:
mutex_unlock(&wlp->nbmutex);
return used;
}
/**
* Show properties of all WSS in neighborhood.
*
* Will trigger a complete discovery of WSS activated by this device and
* its neighbors.
*/
ssize_t wlp_neighborhood_show(struct wlp *wlp, char *buf)
{
wlp_discover(wlp);
return wlp_wss_neighborhood_print_remove(wlp, buf, PAGE_SIZE);
}
EXPORT_SYMBOL_GPL(wlp_neighborhood_show);
static
ssize_t __wlp_wss_properties_show(struct wlp_wss *wss, char *buf,
size_t bufsize)
{
ssize_t result;
result = wlp_wss_uuid_print(buf, bufsize, &wss->wssid);
result += scnprintf(buf + result, bufsize - result, " ");
result += uwb_mac_addr_print(buf + result, bufsize - result,
&wss->bcast);
result += scnprintf(buf + result, bufsize - result,
" 0x%02x %u ", wss->hash, wss->secure_status);
result += wlp_wss_key_print(buf + result, bufsize - result,
wss->master_key);
result += scnprintf(buf + result, bufsize - result, " 0x%02x ",
wss->tag);
result += uwb_mac_addr_print(buf + result, bufsize - result,
&wss->virtual_addr);
result += scnprintf(buf + result, bufsize - result, " %s", wss->name);
result += scnprintf(buf + result, bufsize - result,
"\n\n#WSSID\n#WSS broadcast address\n"
"#WSS hash\n#WSS secure status\n"
"#WSS master key\n#WSS local tag\n"
"#WSS local virtual EUI-48\n#WSS name\n");
return result;
}
/**
* Show which WSS is activated.
*/
ssize_t wlp_wss_activate_show(struct wlp_wss *wss, char *buf)
{
int result = 0;
if (mutex_lock_interruptible(&wss->mutex))
goto out;
if (wss->state >= WLP_WSS_STATE_ACTIVE)
result = __wlp_wss_properties_show(wss, buf, PAGE_SIZE);
else
result = scnprintf(buf, PAGE_SIZE, "No local WSS active.\n");
result += scnprintf(buf + result, PAGE_SIZE - result,
"\n\n"
"# echo WSSID SECURE_STATUS ACCEPT_ENROLLMENT "
"NAME #create new WSS\n"
"# echo WSSID [DEV ADDR] #enroll in and activate "
"existing WSS, can request registrar\n"
"#\n"
"# WSSID is a 16 byte hex array. Eg. 12 A3 3B ... \n"
"# SECURE_STATUS 0 - unsecure, 1 - secure (default)\n"
"# ACCEPT_ENROLLMENT 0 - no, 1 - yes (default)\n"
"# NAME is the text string identifying the WSS\n"
"# DEV ADDR is the device address of neighbor "
"that should be registrar. Eg. 32:AB\n");
mutex_unlock(&wss->mutex);
out:
return result;
}
EXPORT_SYMBOL_GPL(wlp_wss_activate_show);
/**
* Create/activate a new WSS or enroll/activate in neighboring WSS
*
* The user can provide the WSSID of a WSS in which it wants to enroll.
* Only the WSSID is necessary if the WSS have been discovered before. If
* the WSS has not been discovered before, or the user wants to use a
* particular neighbor as its registrar, then the user can also provide a
* device address or the neighbor that will be used as registrar.
*
* A new WSS is created when the user provides a WSSID, secure status, and
* WSS name.
*/
ssize_t wlp_wss_activate_store(struct wlp_wss *wss,
const char *buf, size_t size)
{
ssize_t result = -EINVAL;
struct wlp_uuid wssid;
struct uwb_dev_addr dev;
struct uwb_dev_addr bcast = {.data = {0xff, 0xff} };
char name[65];
unsigned sec_status, accept;
memset(name, 0, sizeof(name));
result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx "
"%02hhx %02hhx %02hhx %02hhx "
"%02hhx %02hhx %02hhx %02hhx "
"%02hhx %02hhx %02hhx %02hhx "
"%02hhx:%02hhx",
&wssid.data[0] , &wssid.data[1],
&wssid.data[2] , &wssid.data[3],
&wssid.data[4] , &wssid.data[5],
&wssid.data[6] , &wssid.data[7],
&wssid.data[8] , &wssid.data[9],
&wssid.data[10], &wssid.data[11],
&wssid.data[12], &wssid.data[13],
&wssid.data[14], &wssid.data[15],
&dev.data[1], &dev.data[0]);
if (result == 16 || result == 17) {
result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx "
"%02hhx %02hhx %02hhx %02hhx "
"%02hhx %02hhx %02hhx %02hhx "
"%02hhx %02hhx %02hhx %02hhx "
"%u %u %64c",
&wssid.data[0] , &wssid.data[1],
&wssid.data[2] , &wssid.data[3],
&wssid.data[4] , &wssid.data[5],
&wssid.data[6] , &wssid.data[7],
&wssid.data[8] , &wssid.data[9],
&wssid.data[10], &wssid.data[11],
&wssid.data[12], &wssid.data[13],
&wssid.data[14], &wssid.data[15],
&sec_status, &accept, name);
if (result == 16)
result = wlp_wss_enroll_activate(wss, &wssid, &bcast);
else if (result == 19) {
sec_status = sec_status == 0 ? 0 : 1;
accept = accept == 0 ? 0 : 1;
/* We read name using %c, so the newline needs to be
* removed */
if (strlen(name) != sizeof(name) - 1)
name[strlen(name) - 1] = '\0';
result = wlp_wss_create_activate(wss, &wssid, name,
sec_status, accept);
} else
result = -EINVAL;
} else if (result == 18)
result = wlp_wss_enroll_activate(wss, &wssid, &dev);
else
result = -EINVAL;
return result < 0 ? result : size;
}
EXPORT_SYMBOL_GPL(wlp_wss_activate_store);
/**
* Show the UUID of this host
*/
ssize_t wlp_uuid_show(struct wlp *wlp, char *buf)
{
ssize_t result = 0;
mutex_lock(&wlp->mutex);
result = wlp_wss_uuid_print(buf, PAGE_SIZE, &wlp->uuid);
buf[result++] = '\n';
mutex_unlock(&wlp->mutex);
return result;
}
EXPORT_SYMBOL_GPL(wlp_uuid_show);
/**
* Store a new UUID for this host
*
* According to the spec this should be encoded as an octet string in the
* order the octets are shown in string representation in RFC 4122 (WLP
* 0.99 [Table 6])
*
* We do not check value provided by user.
*/
ssize_t wlp_uuid_store(struct wlp *wlp, const char *buf, size_t size)
{
ssize_t result;
struct wlp_uuid uuid;
mutex_lock(&wlp->mutex);
result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx "
"%02hhx %02hhx %02hhx %02hhx "
"%02hhx %02hhx %02hhx %02hhx "
"%02hhx %02hhx %02hhx %02hhx ",
&uuid.data[0] , &uuid.data[1],
&uuid.data[2] , &uuid.data[3],
&uuid.data[4] , &uuid.data[5],
&uuid.data[6] , &uuid.data[7],
&uuid.data[8] , &uuid.data[9],
&uuid.data[10], &uuid.data[11],
&uuid.data[12], &uuid.data[13],
&uuid.data[14], &uuid.data[15]);
if (result != 16) {
result = -EINVAL;
goto error;
}
wlp->uuid = uuid;
error:
mutex_unlock(&wlp->mutex);
return result < 0 ? result : size;
}
EXPORT_SYMBOL_GPL(wlp_uuid_store);
/**
* Show contents of members of device information structure
*/
#define wlp_dev_info_show(type) \
ssize_t wlp_dev_##type##_show(struct wlp *wlp, char *buf) \
{ \
ssize_t result = 0; \
mutex_lock(&wlp->mutex); \
if (wlp->dev_info == NULL) { \
result = __wlp_setup_device_info(wlp); \
if (result < 0) \
goto out; \
} \
result = scnprintf(buf, PAGE_SIZE, "%s\n", wlp->dev_info->type);\
out: \
mutex_unlock(&wlp->mutex); \
return result; \
} \
EXPORT_SYMBOL_GPL(wlp_dev_##type##_show);
wlp_dev_info_show(name)
wlp_dev_info_show(model_name)
wlp_dev_info_show(model_nr)
wlp_dev_info_show(manufacturer)
wlp_dev_info_show(serial)
/**
* Store contents of members of device information structure
*/
#define wlp_dev_info_store(type, len) \
ssize_t wlp_dev_##type##_store(struct wlp *wlp, const char *buf, size_t size)\
{ \
ssize_t result; \
char format[10]; \
mutex_lock(&wlp->mutex); \
if (wlp->dev_info == NULL) { \
result = __wlp_alloc_device_info(wlp); \
if (result < 0) \
goto out; \
} \
memset(wlp->dev_info->type, 0, sizeof(wlp->dev_info->type)); \
sprintf(format, "%%%uc", len); \
result = sscanf(buf, format, wlp->dev_info->type); \
out: \
mutex_unlock(&wlp->mutex); \
return result < 0 ? result : size; \
} \
EXPORT_SYMBOL_GPL(wlp_dev_##type##_store);
wlp_dev_info_store(name, 32)
wlp_dev_info_store(manufacturer, 64)
wlp_dev_info_store(model_name, 32)
wlp_dev_info_store(model_nr, 32)
wlp_dev_info_store(serial, 32)
static
const char *__wlp_dev_category[] = {
[WLP_DEV_CAT_COMPUTER] = "Computer",
[WLP_DEV_CAT_INPUT] = "Input device",
[WLP_DEV_CAT_PRINT_SCAN_FAX_COPIER] = "Printer, scanner, FAX, or "
"Copier",
[WLP_DEV_CAT_CAMERA] = "Camera",
[WLP_DEV_CAT_STORAGE] = "Storage Network",
[WLP_DEV_CAT_INFRASTRUCTURE] = "Infrastructure",
[WLP_DEV_CAT_DISPLAY] = "Display",
[WLP_DEV_CAT_MULTIM] = "Multimedia device",
[WLP_DEV_CAT_GAMING] = "Gaming device",
[WLP_DEV_CAT_TELEPHONE] = "Telephone",
[WLP_DEV_CAT_OTHER] = "Other",
};
static
const char *wlp_dev_category_str(unsigned cat)
{
if ((cat >= WLP_DEV_CAT_COMPUTER && cat <= WLP_DEV_CAT_TELEPHONE)
|| cat == WLP_DEV_CAT_OTHER)
return __wlp_dev_category[cat];
return "unknown category";
}
ssize_t wlp_dev_prim_category_show(struct wlp *wlp, char *buf)
{
ssize_t result = 0;
mutex_lock(&wlp->mutex);
if (wlp->dev_info == NULL) {
result = __wlp_setup_device_info(wlp);
if (result < 0)
goto out;
}
result = scnprintf(buf, PAGE_SIZE, "%s\n",
wlp_dev_category_str(wlp->dev_info->prim_dev_type.category));
out:
mutex_unlock(&wlp->mutex);
return result;
}
EXPORT_SYMBOL_GPL(wlp_dev_prim_category_show);
ssize_t wlp_dev_prim_category_store(struct wlp *wlp, const char *buf,
size_t size)
{
ssize_t result;
u16 cat;
mutex_lock(&wlp->mutex);
if (wlp->dev_info == NULL) {
result = __wlp_alloc_device_info(wlp);
if (result < 0)
goto out;
}
result = sscanf(buf, "%hu", &cat);
if ((cat >= WLP_DEV_CAT_COMPUTER && cat <= WLP_DEV_CAT_TELEPHONE)
|| cat == WLP_DEV_CAT_OTHER)
wlp->dev_info->prim_dev_type.category = cat;
else
result = -EINVAL;
out:
mutex_unlock(&wlp->mutex);
return result < 0 ? result : size;
}
EXPORT_SYMBOL_GPL(wlp_dev_prim_category_store);
ssize_t wlp_dev_prim_OUI_show(struct wlp *wlp, char *buf)
{
ssize_t result = 0;
mutex_lock(&wlp->mutex);
if (wlp->dev_info == NULL) {
result = __wlp_setup_device_info(wlp);
if (result < 0)
goto out;
}
result = scnprintf(buf, PAGE_SIZE, "%02x:%02x:%02x\n",
wlp->dev_info->prim_dev_type.OUI[0],
wlp->dev_info->prim_dev_type.OUI[1],
wlp->dev_info->prim_dev_type.OUI[2]);
out:
mutex_unlock(&wlp->mutex);
return result;
}
EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_show);
ssize_t wlp_dev_prim_OUI_store(struct wlp *wlp, const char *buf, size_t size)
{
ssize_t result;
u8 OUI[3];
mutex_lock(&wlp->mutex);
if (wlp->dev_info == NULL) {
result = __wlp_alloc_device_info(wlp);
if (result < 0)
goto out;
}
result = sscanf(buf, "%hhx:%hhx:%hhx",
&OUI[0], &OUI[1], &OUI[2]);
if (result != 3) {
result = -EINVAL;
goto out;
} else
memcpy(wlp->dev_info->prim_dev_type.OUI, OUI, sizeof(OUI));
out:
mutex_unlock(&wlp->mutex);
return result < 0 ? result : size;
}
EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_store);
ssize_t wlp_dev_prim_OUI_sub_show(struct wlp *wlp, char *buf)
{
ssize_t result = 0;
mutex_lock(&wlp->mutex);
if (wlp->dev_info == NULL) {
result = __wlp_setup_device_info(wlp);
if (result < 0)
goto out;
}
result = scnprintf(buf, PAGE_SIZE, "%u\n",
wlp->dev_info->prim_dev_type.OUIsubdiv);
out:
mutex_unlock(&wlp->mutex);
return result;
}
EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_sub_show);
ssize_t wlp_dev_prim_OUI_sub_store(struct wlp *wlp, const char *buf,
size_t size)
{
ssize_t result;
unsigned sub;
u8 max_sub = ~0;
mutex_lock(&wlp->mutex);
if (wlp->dev_info == NULL) {
result = __wlp_alloc_device_info(wlp);
if (result < 0)
goto out;
}
result = sscanf(buf, "%u", &sub);
if (sub <= max_sub)
wlp->dev_info->prim_dev_type.OUIsubdiv = sub;
else
result = -EINVAL;
out:
mutex_unlock(&wlp->mutex);
return result < 0 ? result : size;
}
EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_sub_store);
ssize_t wlp_dev_prim_subcat_show(struct wlp *wlp, char *buf)
{
ssize_t result = 0;
mutex_lock(&wlp->mutex);
if (wlp->dev_info == NULL) {
result = __wlp_setup_device_info(wlp);
if (result < 0)
goto out;
}
result = scnprintf(buf, PAGE_SIZE, "%u\n",
wlp->dev_info->prim_dev_type.subID);
out:
mutex_unlock(&wlp->mutex);
return result;
}
EXPORT_SYMBOL_GPL(wlp_dev_prim_subcat_show);
ssize_t wlp_dev_prim_subcat_store(struct wlp *wlp, const char *buf,
size_t size)
{
ssize_t result;
unsigned sub;
__le16 max_sub = ~0;
mutex_lock(&wlp->mutex);
if (wlp->dev_info == NULL) {
result = __wlp_alloc_device_info(wlp);
if (result < 0)
goto out;
}
result = sscanf(buf, "%u", &sub);
if (sub <= max_sub)
wlp->dev_info->prim_dev_type.subID = sub;
else
result = -EINVAL;
out:
mutex_unlock(&wlp->mutex);
return result < 0 ? result : size;
}
EXPORT_SYMBOL_GPL(wlp_dev_prim_subcat_store);
/**
* Subsystem implementation for interaction with individual WSS via sysfs
*
* Followed instructions for subsystem in Documentation/filesystems/sysfs.txt
*/
#define kobj_to_wlp_wss(obj) container_of(obj, struct wlp_wss, kobj)
#define attr_to_wlp_wss_attr(_attr) \
container_of(_attr, struct wlp_wss_attribute, attr)
/**
* Sysfs subsystem: forward read calls
*
* Sysfs operation for forwarding read call to the show method of the
* attribute owner
*/
static
ssize_t wlp_wss_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct wlp_wss_attribute *wss_attr = attr_to_wlp_wss_attr(attr);
struct wlp_wss *wss = kobj_to_wlp_wss(kobj);
ssize_t ret = -EIO;
if (wss_attr->show)
ret = wss_attr->show(wss, buf);
return ret;
}
/**
* Sysfs subsystem: forward write calls
*
* Sysfs operation for forwarding write call to the store method of the
* attribute owner
*/
static
ssize_t wlp_wss_attr_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t count)
{
struct wlp_wss_attribute *wss_attr = attr_to_wlp_wss_attr(attr);
struct wlp_wss *wss = kobj_to_wlp_wss(kobj);
ssize_t ret = -EIO;
if (wss_attr->store)
ret = wss_attr->store(wss, buf, count);
return ret;
}
static
struct sysfs_ops wss_sysfs_ops = {
.show = wlp_wss_attr_show,
.store = wlp_wss_attr_store,
};
struct kobj_type wss_ktype = {
.release = wlp_wss_release,
.sysfs_ops = &wss_sysfs_ops,
};
/**
* Sysfs files for individual WSS
*/
/**
* Print static properties of this WSS
*
* The name of a WSS may not be null teminated. It's max size is 64 bytes
* so we copy it to a larger array just to make sure we print sane data.
*/
static ssize_t wlp_wss_properties_show(struct wlp_wss *wss, char *buf)
{
int result = 0;
if (mutex_lock_interruptible(&wss->mutex))
goto out;
result = __wlp_wss_properties_show(wss, buf, PAGE_SIZE);
mutex_unlock(&wss->mutex);
out:
return result;
}
WSS_ATTR(properties, S_IRUGO, wlp_wss_properties_show, NULL);
/**
* Print all connected members of this WSS
* The EDA cache contains all members of WSS neighborhood.
*/
static ssize_t wlp_wss_members_show(struct wlp_wss *wss, char *buf)
{
struct wlp *wlp = container_of(wss, struct wlp, wss);
return wlp_eda_show(wlp, buf);
}
WSS_ATTR(members, S_IRUGO, wlp_wss_members_show, NULL);
static
const char *__wlp_strstate[] = {
"none",
"partially enrolled",
"enrolled",
"active",
"connected",
};
static const char *wlp_wss_strstate(unsigned state)
{
if (state >= ARRAY_SIZE(__wlp_strstate))
return "unknown state";
return __wlp_strstate[state];
}
/*
* Print current state of this WSS
*/
static ssize_t wlp_wss_state_show(struct wlp_wss *wss, char *buf)
{
int result = 0;
if (mutex_lock_interruptible(&wss->mutex))
goto out;
result = scnprintf(buf, PAGE_SIZE, "%s\n",
wlp_wss_strstate(wss->state));
mutex_unlock(&wss->mutex);
out:
return result;
}
WSS_ATTR(state, S_IRUGO, wlp_wss_state_show, NULL);
static
struct attribute *wss_attrs[] = {
&wss_attr_properties.attr,
&wss_attr_members.attr,
&wss_attr_state.attr,
NULL,
};
struct attribute_group wss_attr_group = {
.name = NULL, /* we want them in the same directory */
.attrs = wss_attrs,
};

View File

@@ -0,0 +1,353 @@
/*
* WiMedia Logical Link Control Protocol (WLP)
* Message exchange infrastructure
*
* Copyright (C) 2007 Intel Corporation
* Reinette Chatre <reinette.chatre@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
*
*/
#include <linux/etherdevice.h>
#include <linux/wlp.h>
#include "wlp-internal.h"
/*
* Direct incoming association msg to correct parsing routine
*
* We only expect D1, E1, C1, C3 messages as new. All other incoming
* association messages should form part of an established session that is
* handled elsewhere.
* The handling of these messages often require calling sleeping functions
* - this cannot be done in interrupt context. We use the kernel's
* workqueue to handle these messages.
*/
static
void wlp_direct_assoc_frame(struct wlp *wlp, struct sk_buff *skb,
struct uwb_dev_addr *src)
{
struct device *dev = &wlp->rc->uwb_dev.dev;
struct wlp_frame_assoc *assoc = (void *) skb->data;
struct wlp_assoc_frame_ctx *frame_ctx;
frame_ctx = kmalloc(sizeof(*frame_ctx), GFP_ATOMIC);
if (frame_ctx == NULL) {
dev_err(dev, "WLP: Unable to allocate memory for association "
"frame handling.\n");
kfree_skb(skb);
return;
}
frame_ctx->wlp = wlp;
frame_ctx->skb = skb;
frame_ctx->src = *src;
switch (assoc->type) {
case WLP_ASSOC_D1:
INIT_WORK(&frame_ctx->ws, wlp_handle_d1_frame);
schedule_work(&frame_ctx->ws);
break;
case WLP_ASSOC_E1:
kfree_skb(skb); /* Temporary until we handle it */
kfree(frame_ctx); /* Temporary until we handle it */
break;
case WLP_ASSOC_C1:
INIT_WORK(&frame_ctx->ws, wlp_handle_c1_frame);
schedule_work(&frame_ctx->ws);
break;
case WLP_ASSOC_C3:
INIT_WORK(&frame_ctx->ws, wlp_handle_c3_frame);
schedule_work(&frame_ctx->ws);
break;
default:
dev_err(dev, "Received unexpected association frame. "
"Type = %d \n", assoc->type);
kfree_skb(skb);
kfree(frame_ctx);
break;
}
}
/*
* Process incoming association frame
*
* Although it could be possible to deal with some incoming association
* messages without creating a new session we are keeping things simple. We
* do not accept new association messages if there is a session in progress
* and the messages do not belong to that session.
*
* If an association message arrives that causes the creation of a session
* (WLP_ASSOC_E1) while we are in the process of creating a session then we
* rely on the neighbor mutex to protect the data. That is, the new session
* will not be started until the previous is completed.
*/
static
void wlp_receive_assoc_frame(struct wlp *wlp, struct sk_buff *skb,
struct uwb_dev_addr *src)
{
struct device *dev = &wlp->rc->uwb_dev.dev;
struct wlp_frame_assoc *assoc = (void *) skb->data;
struct wlp_session *session = wlp->session;
u8 version;
if (wlp_get_version(wlp, &assoc->version, &version,
sizeof(assoc->version)) < 0)
goto error;
if (version != WLP_VERSION) {
dev_err(dev, "Unsupported WLP version in association "
"message.\n");
goto error;
}
if (session != NULL) {
/* Function that created this session is still holding the
* &wlp->mutex to protect this session. */
if (assoc->type == session->exp_message ||
assoc->type == WLP_ASSOC_F0) {
if (!memcmp(&session->neighbor_addr, src,
sizeof(*src))) {
session->data = skb;
(session->cb)(wlp);
} else {
dev_err(dev, "Received expected message from "
"unexpected source. Expected message "
"%d or F0 from %02x:%02x, but received "
"it from %02x:%02x. Dropping.\n",
session->exp_message,
session->neighbor_addr.data[1],
session->neighbor_addr.data[0],
src->data[1], src->data[0]);
goto error;
}
} else {
dev_err(dev, "Association already in progress. "
"Dropping.\n");
goto error;
}
} else {
wlp_direct_assoc_frame(wlp, skb, src);
}
return;
error:
kfree_skb(skb);
}
/*
* Verify incoming frame is from connected neighbor, prep to pass to WLP client
*
* Verification proceeds according to WLP 0.99 [7.3.1]. The source address
* is used to determine which neighbor is sending the frame and the WSS tag
* is used to know to which WSS the frame belongs (we only support one WSS
* so this test is straight forward).
* With the WSS found we need to ensure that we are connected before
* allowing the exchange of data frames.
*/
static
int wlp_verify_prep_rx_frame(struct wlp *wlp, struct sk_buff *skb,
struct uwb_dev_addr *src)
{
struct device *dev = &wlp->rc->uwb_dev.dev;
int result = -EINVAL;
struct wlp_eda_node eda_entry;
struct wlp_frame_std_abbrv_hdr *hdr = (void *) skb->data;
/*verify*/
result = wlp_copy_eda_node(&wlp->eda, src, &eda_entry);
if (result < 0) {
if (printk_ratelimit())
dev_err(dev, "WLP: Incoming frame is from unknown "
"neighbor %02x:%02x.\n", src->data[1],
src->data[0]);
goto out;
}
if (hdr->tag != eda_entry.tag) {
if (printk_ratelimit())
dev_err(dev, "WLP: Tag of incoming frame from "
"%02x:%02x does not match expected tag. "
"Received 0x%02x, expected 0x%02x. \n",
src->data[1], src->data[0], hdr->tag,
eda_entry.tag);
result = -EINVAL;
goto out;
}
if (eda_entry.state != WLP_WSS_CONNECTED) {
if (printk_ratelimit())
dev_err(dev, "WLP: Incoming frame from "
"%02x:%02x does is not from connected WSS.\n",
src->data[1], src->data[0]);
result = -EINVAL;
goto out;
}
/*prep*/
skb_pull(skb, sizeof(*hdr));
out:
return result;
}
/*
* Receive a WLP frame from device
*
* @returns: 1 if calling function should free the skb
* 0 if it successfully handled skb and freed it
* 0 if error occured, will free skb in this case
*/
int wlp_receive_frame(struct device *dev, struct wlp *wlp, struct sk_buff *skb,
struct uwb_dev_addr *src)
{
unsigned len = skb->len;
void *ptr = skb->data;
struct wlp_frame_hdr *hdr;
int result = 0;
if (len < sizeof(*hdr)) {
dev_err(dev, "Not enough data to parse WLP header.\n");
result = -EINVAL;
goto out;
}
hdr = ptr;
if (le16_to_cpu(hdr->mux_hdr) != WLP_PROTOCOL_ID) {
dev_err(dev, "Not a WLP frame type.\n");
result = -EINVAL;
goto out;
}
switch (hdr->type) {
case WLP_FRAME_STANDARD:
if (len < sizeof(struct wlp_frame_std_abbrv_hdr)) {
dev_err(dev, "Not enough data to parse Standard "
"WLP header.\n");
goto out;
}
result = wlp_verify_prep_rx_frame(wlp, skb, src);
if (result < 0) {
if (printk_ratelimit())
dev_err(dev, "WLP: Verification of frame "
"from neighbor %02x:%02x failed.\n",
src->data[1], src->data[0]);
goto out;
}
result = 1;
break;
case WLP_FRAME_ABBREVIATED:
dev_err(dev, "Abbreviated frame received. FIXME?\n");
kfree_skb(skb);
break;
case WLP_FRAME_CONTROL:
dev_err(dev, "Control frame received. FIXME?\n");
kfree_skb(skb);
break;
case WLP_FRAME_ASSOCIATION:
if (len < sizeof(struct wlp_frame_assoc)) {
dev_err(dev, "Not enough data to parse Association "
"WLP header.\n");
goto out;
}
wlp_receive_assoc_frame(wlp, skb, src);
break;
default:
dev_err(dev, "Invalid frame received.\n");
result = -EINVAL;
break;
}
out:
if (result < 0) {
kfree_skb(skb);
result = 0;
}
return result;
}
EXPORT_SYMBOL_GPL(wlp_receive_frame);
/*
* Verify frame from network stack, prepare for further transmission
*
* @skb: the socket buffer that needs to be prepared for transmission (it
* is in need of a WLP header). If this is a broadcast frame we take
* over the entire transmission.
* If it is a unicast the WSS connection should already be established
* and transmission will be done by the calling function.
* @dst: On return this will contain the device address to which the
* frame is destined.
* @returns: 0 on success no tx : WLP header sucessfully applied to skb buffer,
* calling function can proceed with tx
* 1 on success with tx : WLP will take over transmission of this
* frame
* <0 on error
*
* The network stack (WLP client) is attempting to transmit a frame. We can
* only transmit data if a local WSS is at least active (connection will be
* done here if this is a broadcast frame and neighbor also has the WSS
* active).
*
* The frame can be either broadcast or unicast. Broadcast in a WSS is
* supported via multicast, but we don't support multicast yet (until
* devices start to support MAB IEs). If a broadcast frame needs to be
* transmitted it is treated as a unicast frame to each neighbor. In this
* case the WLP takes over transmission of the skb and returns 1
* to the caller to indicate so. Also, in this case, if a neighbor has the
* same WSS activated but is not connected then the WSS connection will be
* done at this time. The neighbor's virtual address will be learned at
* this time.
*
* The destination address in a unicast frame is the virtual address of the
* neighbor. This address only becomes known when a WSS connection is
* established. We thus rely on a broadcast frame to trigger the setup of
* WSS connections to all neighbors before we are able to send unicast
* frames to them. This seems reasonable as IP would usually use ARP first
* before any unicast frames are sent.
*
* If we are already connected to the neighbor (neighbor's virtual address
* is known) we just prepare the WLP header and the caller will continue to
* send the frame.
*
* A failure in this function usually indicates something that cannot be
* fixed automatically. So, if this function fails (@return < 0) the calling
* function should not retry to send the frame as it will very likely keep
* failing.
*
*/
int wlp_prepare_tx_frame(struct device *dev, struct wlp *wlp,
struct sk_buff *skb, struct uwb_dev_addr *dst)
{
int result = -EINVAL;
struct ethhdr *eth_hdr = (void *) skb->data;
if (is_multicast_ether_addr(eth_hdr->h_dest)) {
result = wlp_eda_for_each(&wlp->eda, wlp_wss_send_copy, skb);
if (result < 0) {
if (printk_ratelimit())
dev_err(dev, "Unable to handle broadcast "
"frame from WLP client.\n");
goto out;
}
dev_kfree_skb_irq(skb);
result = 1;
/* Frame will be transmitted by WLP. */
} else {
result = wlp_eda_for_virtual(&wlp->eda, eth_hdr->h_dest, dst,
wlp_wss_prep_hdr, skb);
if (unlikely(result < 0)) {
if (printk_ratelimit())
dev_err(dev, "Unable to prepare "
"skb for transmission. \n");
goto out;
}
}
out:
return result;
}
EXPORT_SYMBOL_GPL(wlp_prepare_tx_frame);

View File

@@ -0,0 +1,224 @@
/*
* WiMedia Logical Link Control Protocol (WLP)
* Internal API
*
* Copyright (C) 2007 Intel Corporation
* Reinette Chatre <reinette.chatre@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.
*
*/
#ifndef __WLP_INTERNAL_H__
#define __WLP_INTERNAL_H__
/**
* State of WSS connection
*
* A device needs to connect to a neighbor in an activated WSS before data
* can be transmitted. The spec also distinguishes between a new connection
* attempt and a connection attempt after previous connection attempts. The
* state WLP_WSS_CONNECT_FAILED is used for this scenario. See WLP 0.99
* [7.2.6]
*/
enum wlp_wss_connect {
WLP_WSS_UNCONNECTED = 0,
WLP_WSS_CONNECTED,
WLP_WSS_CONNECT_FAILED,
};
extern struct kobj_type wss_ktype;
extern struct attribute_group wss_attr_group;
/* This should be changed to a dynamic array where entries are sorted
* by eth_addr and search is done in a binary form
*
* Although thinking twice about it: this technologie's maximum reach
* is 10 meters...unless you want to pack too much stuff in around
* your radio controller/WLP device, the list will probably not be
* too big.
*
* In any case, there is probably some data structure in the kernel
* than we could reused for that already.
*
* The below structure is really just good while we support one WSS per
* host.
*/
struct wlp_eda_node {
struct list_head list_node;
unsigned char eth_addr[ETH_ALEN];
struct uwb_dev_addr dev_addr;
struct wlp_wss *wss;
unsigned char virt_addr[ETH_ALEN];
u8 tag;
enum wlp_wss_connect state;
};
typedef int (*wlp_eda_for_each_f)(struct wlp *, struct wlp_eda_node *, void *);
extern void wlp_eda_init(struct wlp_eda *);
extern void wlp_eda_release(struct wlp_eda *);
extern int wlp_eda_create_node(struct wlp_eda *,
const unsigned char eth_addr[ETH_ALEN],
const struct uwb_dev_addr *);
extern void wlp_eda_rm_node(struct wlp_eda *, const struct uwb_dev_addr *);
extern int wlp_eda_update_node(struct wlp_eda *,
const struct uwb_dev_addr *,
struct wlp_wss *,
const unsigned char virt_addr[ETH_ALEN],
const u8, const enum wlp_wss_connect);
extern int wlp_eda_update_node_state(struct wlp_eda *,
const struct uwb_dev_addr *,
const enum wlp_wss_connect);
extern int wlp_copy_eda_node(struct wlp_eda *, struct uwb_dev_addr *,
struct wlp_eda_node *);
extern int wlp_eda_for_each(struct wlp_eda *, wlp_eda_for_each_f , void *);
extern int wlp_eda_for_virtual(struct wlp_eda *,
const unsigned char eth_addr[ETH_ALEN],
struct uwb_dev_addr *,
wlp_eda_for_each_f , void *);
extern void wlp_remove_neighbor_tmp_info(struct wlp_neighbor_e *);
extern size_t wlp_wss_key_print(char *, size_t, u8 *);
/* Function called when no more references to WSS exists */
extern void wlp_wss_release(struct kobject *);
extern void wlp_wss_reset(struct wlp_wss *);
extern int wlp_wss_create_activate(struct wlp_wss *, struct wlp_uuid *,
char *, unsigned, unsigned);
extern int wlp_wss_enroll_activate(struct wlp_wss *, struct wlp_uuid *,
struct uwb_dev_addr *);
extern ssize_t wlp_discover(struct wlp *);
extern int wlp_enroll_neighbor(struct wlp *, struct wlp_neighbor_e *,
struct wlp_wss *, struct wlp_uuid *);
extern int wlp_wss_is_active(struct wlp *, struct wlp_wss *,
struct uwb_dev_addr *);
struct wlp_assoc_conn_ctx {
struct work_struct ws;
struct wlp *wlp;
struct sk_buff *skb;
struct wlp_eda_node eda_entry;
};
extern int wlp_wss_connect_prep(struct wlp *, struct wlp_eda_node *, void *);
extern int wlp_wss_send_copy(struct wlp *, struct wlp_eda_node *, void *);
/* Message handling */
struct wlp_assoc_frame_ctx {
struct work_struct ws;
struct wlp *wlp;
struct sk_buff *skb;
struct uwb_dev_addr src;
};
extern int wlp_wss_prep_hdr(struct wlp *, struct wlp_eda_node *, void *);
extern void wlp_handle_d1_frame(struct work_struct *);
extern int wlp_parse_d2_frame_to_cache(struct wlp *, struct sk_buff *,
struct wlp_neighbor_e *);
extern int wlp_parse_d2_frame_to_enroll(struct wlp_wss *, struct sk_buff *,
struct wlp_neighbor_e *,
struct wlp_uuid *);
extern void wlp_handle_c1_frame(struct work_struct *);
extern void wlp_handle_c3_frame(struct work_struct *);
extern int wlp_parse_c3c4_frame(struct wlp *, struct sk_buff *,
struct wlp_uuid *, u8 *,
struct uwb_mac_addr *);
extern int wlp_parse_f0(struct wlp *, struct sk_buff *);
extern int wlp_send_assoc_frame(struct wlp *, struct wlp_wss *,
struct uwb_dev_addr *, enum wlp_assoc_type);
extern ssize_t wlp_get_version(struct wlp *, struct wlp_attr_version *,
u8 *, ssize_t);
extern ssize_t wlp_get_wssid(struct wlp *, struct wlp_attr_wssid *,
struct wlp_uuid *, ssize_t);
extern int __wlp_alloc_device_info(struct wlp *);
extern int __wlp_setup_device_info(struct wlp *);
extern struct wlp_wss_attribute wss_attribute_properties;
extern struct wlp_wss_attribute wss_attribute_members;
extern struct wlp_wss_attribute wss_attribute_state;
static inline
size_t wlp_wss_uuid_print(char *buf, size_t bufsize, struct wlp_uuid *uuid)
{
size_t result;
result = scnprintf(buf, bufsize,
"%02x:%02x:%02x:%02x:%02x:%02x:"
"%02x:%02x:%02x:%02x:%02x:%02x:"
"%02x:%02x:%02x:%02x",
uuid->data[0], uuid->data[1],
uuid->data[2], uuid->data[3],
uuid->data[4], uuid->data[5],
uuid->data[6], uuid->data[7],
uuid->data[8], uuid->data[9],
uuid->data[10], uuid->data[11],
uuid->data[12], uuid->data[13],
uuid->data[14], uuid->data[15]);
return result;
}
/**
* FIXME: How should a nonce be displayed?
*/
static inline
size_t wlp_wss_nonce_print(char *buf, size_t bufsize, struct wlp_nonce *nonce)
{
size_t result;
result = scnprintf(buf, bufsize,
"%02x %02x %02x %02x %02x %02x "
"%02x %02x %02x %02x %02x %02x "
"%02x %02x %02x %02x",
nonce->data[0], nonce->data[1],
nonce->data[2], nonce->data[3],
nonce->data[4], nonce->data[5],
nonce->data[6], nonce->data[7],
nonce->data[8], nonce->data[9],
nonce->data[10], nonce->data[11],
nonce->data[12], nonce->data[13],
nonce->data[14], nonce->data[15]);
return result;
}
static inline
void wlp_session_cb(struct wlp *wlp)
{
struct completion *completion = wlp->session->cb_priv;
complete(completion);
}
static inline
int wlp_uuid_is_set(struct wlp_uuid *uuid)
{
struct wlp_uuid zero_uuid = { .data = { 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00} };
if (!memcmp(uuid, &zero_uuid, sizeof(*uuid)))
return 0;
return 1;
}
#endif /* __WLP_INTERNAL_H__ */

View File

@@ -0,0 +1,559 @@
/*
* WiMedia Logical Link Control Protocol (WLP)
*
* Copyright (C) 2005-2006 Intel Corporation
* Reinette Chatre <reinette.chatre@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
*/
#include <linux/wlp.h>
#include "wlp-internal.h"
static
void wlp_neighbor_init(struct wlp_neighbor_e *neighbor)
{
INIT_LIST_HEAD(&neighbor->wssid);
}
/**
* Create area for device information storage
*
* wlp->mutex must be held
*/
int __wlp_alloc_device_info(struct wlp *wlp)
{
struct device *dev = &wlp->rc->uwb_dev.dev;
BUG_ON(wlp->dev_info != NULL);
wlp->dev_info = kzalloc(sizeof(struct wlp_device_info), GFP_KERNEL);
if (wlp->dev_info == NULL) {
dev_err(dev, "WLP: Unable to allocate memory for "
"device information.\n");
return -ENOMEM;
}
return 0;
}
/**
* Fill in device information using function provided by driver
*
* wlp->mutex must be held
*/
static
void __wlp_fill_device_info(struct wlp *wlp)
{
wlp->fill_device_info(wlp, wlp->dev_info);
}
/**
* Setup device information
*
* Allocate area for device information and populate it.
*
* wlp->mutex must be held
*/
int __wlp_setup_device_info(struct wlp *wlp)
{
int result;
struct device *dev = &wlp->rc->uwb_dev.dev;
result = __wlp_alloc_device_info(wlp);
if (result < 0) {
dev_err(dev, "WLP: Unable to allocate area for "
"device information.\n");
return result;
}
__wlp_fill_device_info(wlp);
return 0;
}
/**
* Remove information about neighbor stored temporarily
*
* Information learned during discovey should only be stored when the
* device enrolls in the neighbor's WSS. We do need to store this
* information temporarily in order to present it to the user.
*
* We are only interested in keeping neighbor WSS information if that
* neighbor is accepting enrollment.
*
* should be called with wlp->nbmutex held
*/
void wlp_remove_neighbor_tmp_info(struct wlp_neighbor_e *neighbor)
{
struct wlp_wssid_e *wssid_e, *next;
u8 keep;
if (!list_empty(&neighbor->wssid)) {
list_for_each_entry_safe(wssid_e, next, &neighbor->wssid,
node) {
if (wssid_e->info != NULL) {
keep = wssid_e->info->accept_enroll;
kfree(wssid_e->info);
wssid_e->info = NULL;
if (!keep) {
list_del(&wssid_e->node);
kfree(wssid_e);
}
}
}
}
if (neighbor->info != NULL) {
kfree(neighbor->info);
neighbor->info = NULL;
}
}
/*
* Populate WLP neighborhood cache with neighbor information
*
* A new neighbor is found. If it is discoverable then we add it to the
* neighborhood cache.
*
*/
static
int wlp_add_neighbor(struct wlp *wlp, struct uwb_dev *dev)
{
int result = 0;
int discoverable;
struct wlp_neighbor_e *neighbor;
/*
* FIXME:
* Use contents of WLP IE found in beacon cache to determine if
* neighbor is discoverable.
* The device does not support WLP IE yet so this still needs to be
* done. Until then we assume all devices are discoverable.
*/
discoverable = 1; /* will be changed when FIXME disappears */
if (discoverable) {
/* Add neighbor to cache for discovery */
neighbor = kzalloc(sizeof(*neighbor), GFP_KERNEL);
if (neighbor == NULL) {
dev_err(&dev->dev, "Unable to create memory for "
"new neighbor. \n");
result = -ENOMEM;
goto error_no_mem;
}
wlp_neighbor_init(neighbor);
uwb_dev_get(dev);
neighbor->uwb_dev = dev;
list_add(&neighbor->node, &wlp->neighbors);
}
error_no_mem:
return result;
}
/**
* Remove one neighbor from cache
*/
static
void __wlp_neighbor_release(struct wlp_neighbor_e *neighbor)
{
struct wlp_wssid_e *wssid_e, *next_wssid_e;
list_for_each_entry_safe(wssid_e, next_wssid_e,
&neighbor->wssid, node) {
list_del(&wssid_e->node);
kfree(wssid_e);
}
uwb_dev_put(neighbor->uwb_dev);
list_del(&neighbor->node);
kfree(neighbor);
}
/**
* Clear entire neighborhood cache.
*/
static
void __wlp_neighbors_release(struct wlp *wlp)
{
struct wlp_neighbor_e *neighbor, *next;
if (list_empty(&wlp->neighbors))
return;
list_for_each_entry_safe(neighbor, next, &wlp->neighbors, node) {
__wlp_neighbor_release(neighbor);
}
}
static
void wlp_neighbors_release(struct wlp *wlp)
{
mutex_lock(&wlp->nbmutex);
__wlp_neighbors_release(wlp);
mutex_unlock(&wlp->nbmutex);
}
/**
* Send D1 message to neighbor, receive D2 message
*
* @neighbor: neighbor to which D1 message will be sent
* @wss: if not NULL, it is an enrollment request for this WSS
* @wssid: if wss not NULL, this is the wssid of the WSS in which we
* want to enroll
*
* A D1/D2 exchange is done for one of two reasons: discovery or
* enrollment. If done for discovery the D1 message is sent to the neighbor
* and the contents of the D2 response is stored in a temporary cache.
* If done for enrollment the @wss and @wssid are provided also. In this
* case the D1 message is sent to the neighbor, the D2 response is parsed
* for enrollment of the WSS with wssid.
*
* &wss->mutex is held
*/
static
int wlp_d1d2_exchange(struct wlp *wlp, struct wlp_neighbor_e *neighbor,
struct wlp_wss *wss, struct wlp_uuid *wssid)
{
int result;
struct device *dev = &wlp->rc->uwb_dev.dev;
DECLARE_COMPLETION_ONSTACK(completion);
struct wlp_session session;
struct sk_buff *skb;
struct wlp_frame_assoc *resp;
struct uwb_dev_addr *dev_addr = &neighbor->uwb_dev->dev_addr;
mutex_lock(&wlp->mutex);
if (!wlp_uuid_is_set(&wlp->uuid)) {
dev_err(dev, "WLP: UUID is not set. Set via sysfs to "
"proceed.\n");
result = -ENXIO;
goto out;
}
/* Send D1 association frame */
result = wlp_send_assoc_frame(wlp, wss, dev_addr, WLP_ASSOC_D1);
if (result < 0) {
dev_err(dev, "Unable to send D1 frame to neighbor "
"%02x:%02x (%d)\n", dev_addr->data[1],
dev_addr->data[0], result);
goto out;
}
/* Create session, wait for response */
session.exp_message = WLP_ASSOC_D2;
session.cb = wlp_session_cb;
session.cb_priv = &completion;
session.neighbor_addr = *dev_addr;
BUG_ON(wlp->session != NULL);
wlp->session = &session;
/* Wait for D2/F0 frame */
result = wait_for_completion_interruptible_timeout(&completion,
WLP_PER_MSG_TIMEOUT * HZ);
if (result == 0) {
result = -ETIMEDOUT;
dev_err(dev, "Timeout while sending D1 to neighbor "
"%02x:%02x.\n", dev_addr->data[1],
dev_addr->data[0]);
goto error_session;
}
if (result < 0) {
dev_err(dev, "Unable to discover/enroll neighbor %02x:%02x.\n",
dev_addr->data[1], dev_addr->data[0]);
goto error_session;
}
/* Parse message in session->data: it will be either D2 or F0 */
skb = session.data;
resp = (void *) skb->data;
if (resp->type == WLP_ASSOC_F0) {
result = wlp_parse_f0(wlp, skb);
if (result < 0)
dev_err(dev, "WLP: Unable to parse F0 from neighbor "
"%02x:%02x.\n", dev_addr->data[1],
dev_addr->data[0]);
result = -EINVAL;
goto error_resp_parse;
}
if (wss == NULL) {
/* Discovery */
result = wlp_parse_d2_frame_to_cache(wlp, skb, neighbor);
if (result < 0) {
dev_err(dev, "WLP: Unable to parse D2 message from "
"neighbor %02x:%02x for discovery.\n",
dev_addr->data[1], dev_addr->data[0]);
goto error_resp_parse;
}
} else {
/* Enrollment */
result = wlp_parse_d2_frame_to_enroll(wss, skb, neighbor,
wssid);
if (result < 0) {
dev_err(dev, "WLP: Unable to parse D2 message from "
"neighbor %02x:%02x for enrollment.\n",
dev_addr->data[1], dev_addr->data[0]);
goto error_resp_parse;
}
}
error_resp_parse:
kfree_skb(skb);
error_session:
wlp->session = NULL;
out:
mutex_unlock(&wlp->mutex);
return result;
}
/**
* Enroll into WSS of provided WSSID by using neighbor as registrar
*
* &wss->mutex is held
*/
int wlp_enroll_neighbor(struct wlp *wlp, struct wlp_neighbor_e *neighbor,
struct wlp_wss *wss, struct wlp_uuid *wssid)
{
int result = 0;
struct device *dev = &wlp->rc->uwb_dev.dev;
char buf[WLP_WSS_UUID_STRSIZE];
struct uwb_dev_addr *dev_addr = &neighbor->uwb_dev->dev_addr;
wlp_wss_uuid_print(buf, sizeof(buf), wssid);
result = wlp_d1d2_exchange(wlp, neighbor, wss, wssid);
if (result < 0) {
dev_err(dev, "WLP: D1/D2 message exchange for enrollment "
"failed. result = %d \n", result);
goto out;
}
if (wss->state != WLP_WSS_STATE_PART_ENROLLED) {
dev_err(dev, "WLP: Unable to enroll into WSS %s using "
"neighbor %02x:%02x. \n", buf,
dev_addr->data[1], dev_addr->data[0]);
result = -EINVAL;
goto out;
}
if (wss->secure_status == WLP_WSS_SECURE) {
dev_err(dev, "FIXME: need to complete secure enrollment.\n");
result = -EINVAL;
goto error;
} else {
wss->state = WLP_WSS_STATE_ENROLLED;
dev_dbg(dev, "WLP: Success Enrollment into unsecure WSS "
"%s using neighbor %02x:%02x. \n",
buf, dev_addr->data[1], dev_addr->data[0]);
}
out:
return result;
error:
wlp_wss_reset(wss);
return result;
}
/**
* Discover WSS information of neighbor's active WSS
*/
static
int wlp_discover_neighbor(struct wlp *wlp,
struct wlp_neighbor_e *neighbor)
{
return wlp_d1d2_exchange(wlp, neighbor, NULL, NULL);
}
/**
* Each neighbor in the neighborhood cache is discoverable. Discover it.
*
* Discovery is done through sending of D1 association frame and parsing
* the D2 association frame response. Only wssid from D2 will be included
* in neighbor cache, rest is just displayed to user and forgotten.
*
* The discovery is not done in parallel. This is simple and enables us to
* maintain only one association context.
*
* The discovery of one neighbor does not affect the other, but if the
* discovery of a neighbor fails it is removed from the neighborhood cache.
*/
static
int wlp_discover_all_neighbors(struct wlp *wlp)
{
int result = 0;
struct device *dev = &wlp->rc->uwb_dev.dev;
struct wlp_neighbor_e *neighbor, *next;
list_for_each_entry_safe(neighbor, next, &wlp->neighbors, node) {
result = wlp_discover_neighbor(wlp, neighbor);
if (result < 0) {
dev_err(dev, "WLP: Unable to discover neighbor "
"%02x:%02x, removing from neighborhood. \n",
neighbor->uwb_dev->dev_addr.data[1],
neighbor->uwb_dev->dev_addr.data[0]);
__wlp_neighbor_release(neighbor);
}
}
return result;
}
static int wlp_add_neighbor_helper(struct device *dev, void *priv)
{
struct wlp *wlp = priv;
struct uwb_dev *uwb_dev = to_uwb_dev(dev);
return wlp_add_neighbor(wlp, uwb_dev);
}
/**
* Discover WLP neighborhood
*
* Will send D1 association frame to all devices in beacon group that have
* discoverable bit set in WLP IE. D2 frames will be received, information
* displayed to user in @buf. Partial information (from D2 association
* frame) will be cached to assist with future association
* requests.
*
* The discovery of the WLP neighborhood is triggered by the user. This
* should occur infrequently and we thus free current cache and re-allocate
* memory if needed.
*
* If one neighbor fails during initial discovery (determining if it is a
* neighbor or not), we fail all - note that interaction with neighbor has
* not occured at this point so if a failure occurs we know something went wrong
* locally. We thus undo everything.
*/
ssize_t wlp_discover(struct wlp *wlp)
{
int result = 0;
struct device *dev = &wlp->rc->uwb_dev.dev;
mutex_lock(&wlp->nbmutex);
/* Clear current neighborhood cache. */
__wlp_neighbors_release(wlp);
/* Determine which devices in neighborhood. Repopulate cache. */
result = uwb_dev_for_each(wlp->rc, wlp_add_neighbor_helper, wlp);
if (result < 0) {
/* May have partial neighbor information, release all. */
__wlp_neighbors_release(wlp);
goto error_dev_for_each;
}
/* Discover the properties of devices in neighborhood. */
result = wlp_discover_all_neighbors(wlp);
/* In case of failure we still print our partial results. */
if (result < 0) {
dev_err(dev, "Unable to fully discover neighborhood. \n");
result = 0;
}
error_dev_for_each:
mutex_unlock(&wlp->nbmutex);
return result;
}
/**
* Handle events from UWB stack
*
* We handle events conservatively. If a neighbor goes off the air we
* remove it from the neighborhood. If an association process is in
* progress this function will block waiting for the nbmutex to become
* free. The association process will thus be allowed to complete before it
* is removed.
*/
static
void wlp_uwb_notifs_cb(void *_wlp, struct uwb_dev *uwb_dev,
enum uwb_notifs event)
{
struct wlp *wlp = _wlp;
struct device *dev = &wlp->rc->uwb_dev.dev;
struct wlp_neighbor_e *neighbor, *next;
int result;
switch (event) {
case UWB_NOTIF_ONAIR:
result = wlp_eda_create_node(&wlp->eda,
uwb_dev->mac_addr.data,
&uwb_dev->dev_addr);
if (result < 0)
dev_err(dev, "WLP: Unable to add new neighbor "
"%02x:%02x to EDA cache.\n",
uwb_dev->dev_addr.data[1],
uwb_dev->dev_addr.data[0]);
break;
case UWB_NOTIF_OFFAIR:
wlp_eda_rm_node(&wlp->eda, &uwb_dev->dev_addr);
mutex_lock(&wlp->nbmutex);
list_for_each_entry_safe(neighbor, next, &wlp->neighbors, node) {
if (neighbor->uwb_dev == uwb_dev)
__wlp_neighbor_release(neighbor);
}
mutex_unlock(&wlp->nbmutex);
break;
default:
dev_err(dev, "don't know how to handle event %d from uwb\n",
event);
}
}
static void wlp_channel_changed(struct uwb_pal *pal, int channel)
{
struct wlp *wlp = container_of(pal, struct wlp, pal);
if (channel < 0)
netif_carrier_off(wlp->ndev);
else
netif_carrier_on(wlp->ndev);
}
int wlp_setup(struct wlp *wlp, struct uwb_rc *rc, struct net_device *ndev)
{
int result;
BUG_ON(wlp->fill_device_info == NULL);
BUG_ON(wlp->xmit_frame == NULL);
BUG_ON(wlp->stop_queue == NULL);
BUG_ON(wlp->start_queue == NULL);
wlp->rc = rc;
wlp->ndev = ndev;
wlp_eda_init(&wlp->eda);/* Set up address cache */
wlp->uwb_notifs_handler.cb = wlp_uwb_notifs_cb;
wlp->uwb_notifs_handler.data = wlp;
uwb_notifs_register(rc, &wlp->uwb_notifs_handler);
uwb_pal_init(&wlp->pal);
wlp->pal.rc = rc;
wlp->pal.channel_changed = wlp_channel_changed;
result = uwb_pal_register(&wlp->pal);
if (result < 0)
uwb_notifs_deregister(wlp->rc, &wlp->uwb_notifs_handler);
return result;
}
EXPORT_SYMBOL_GPL(wlp_setup);
void wlp_remove(struct wlp *wlp)
{
wlp_neighbors_release(wlp);
uwb_pal_unregister(&wlp->pal);
uwb_notifs_deregister(wlp->rc, &wlp->uwb_notifs_handler);
wlp_eda_release(&wlp->eda);
mutex_lock(&wlp->mutex);
if (wlp->dev_info != NULL)
kfree(wlp->dev_info);
mutex_unlock(&wlp->mutex);
wlp->rc = NULL;
}
EXPORT_SYMBOL_GPL(wlp_remove);
/**
* wlp_reset_all - reset the WLP hardware
* @wlp: the WLP device to reset.
*
* This schedules a full hardware reset of the WLP device. The radio
* controller and any other PALs will also be reset.
*/
void wlp_reset_all(struct wlp *wlp)
{
uwb_rc_reset_all(wlp->rc);
}
EXPORT_SYMBOL_GPL(wlp_reset_all);

View File

@@ -0,0 +1,961 @@
/*
* WiMedia Logical Link Control Protocol (WLP)
*
* Copyright (C) 2007 Intel Corporation
* Reinette Chatre <reinette.chatre@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.
*
*
* Implementation of the WLP association protocol.
*
* FIXME: Docs
*
* A UWB network interface will configure a WSS through wlp_wss_setup() after
* the interface has been assigned a MAC address, typically after
* "ifconfig" has been called. When the interface goes down it should call
* wlp_wss_remove().
*
* When the WSS is ready for use the user interacts via sysfs to create,
* discover, and activate WSS.
*
* wlp_wss_enroll_activate()
*
* wlp_wss_create_activate()
* wlp_wss_set_wssid_hash()
* wlp_wss_comp_wssid_hash()
* wlp_wss_sel_bcast_addr()
* wlp_wss_sysfs_add()
*
* Called when no more references to WSS exist:
* wlp_wss_release()
* wlp_wss_reset()
*/
#include <linux/etherdevice.h> /* for is_valid_ether_addr */
#include <linux/skbuff.h>
#include <linux/wlp.h>
#include "wlp-internal.h"
size_t wlp_wss_key_print(char *buf, size_t bufsize, u8 *key)
{
size_t result;
result = scnprintf(buf, bufsize,
"%02x %02x %02x %02x %02x %02x "
"%02x %02x %02x %02x %02x %02x "
"%02x %02x %02x %02x",
key[0], key[1], key[2], key[3],
key[4], key[5], key[6], key[7],
key[8], key[9], key[10], key[11],
key[12], key[13], key[14], key[15]);
return result;
}
/**
* Compute WSSID hash
* WLP Draft 0.99 [7.2.1]
*
* The WSSID hash for a WSSID is the result of an octet-wise exclusive-OR
* of all octets in the WSSID.
*/
static
u8 wlp_wss_comp_wssid_hash(struct wlp_uuid *wssid)
{
return wssid->data[0] ^ wssid->data[1] ^ wssid->data[2]
^ wssid->data[3] ^ wssid->data[4] ^ wssid->data[5]
^ wssid->data[6] ^ wssid->data[7] ^ wssid->data[8]
^ wssid->data[9] ^ wssid->data[10] ^ wssid->data[11]
^ wssid->data[12] ^ wssid->data[13] ^ wssid->data[14]
^ wssid->data[15];
}
/**
* Select a multicast EUI-48 for the WSS broadcast address.
* WLP Draft 0.99 [7.2.1]
*
* Selected based on the WiMedia Alliance OUI, 00-13-88, within the WLP
* range, [01-13-88-00-01-00, 01-13-88-00-01-FF] inclusive.
*
* This address is currently hardcoded.
* FIXME?
*/
static
struct uwb_mac_addr wlp_wss_sel_bcast_addr(struct wlp_wss *wss)
{
struct uwb_mac_addr bcast = {
.data = { 0x01, 0x13, 0x88, 0x00, 0x01, 0x00 }
};
return bcast;
}
/**
* Clear the contents of the WSS structure - all except kobj, mutex, virtual
*
* We do not want to reinitialize - the internal kobj should not change as
* it still points to the parent received during setup. The mutex should
* remain also. We thus just reset values individually.
* The virutal address assigned to WSS will remain the same for the
* lifetime of the WSS. We only reset the fields that can change during its
* lifetime.
*/
void wlp_wss_reset(struct wlp_wss *wss)
{
memset(&wss->wssid, 0, sizeof(wss->wssid));
wss->hash = 0;
memset(&wss->name[0], 0, sizeof(wss->name));
memset(&wss->bcast, 0, sizeof(wss->bcast));
wss->secure_status = WLP_WSS_UNSECURE;
memset(&wss->master_key[0], 0, sizeof(wss->master_key));
wss->tag = 0;
wss->state = WLP_WSS_STATE_NONE;
}
/**
* Create sysfs infrastructure for WSS
*
* The WSS is configured to have the interface as parent (see wlp_wss_setup())
* a new sysfs directory that includes wssid as its name is created in the
* interface's sysfs directory. The group of files interacting with WSS are
* created also.
*/
static
int wlp_wss_sysfs_add(struct wlp_wss *wss, char *wssid_str)
{
struct wlp *wlp = container_of(wss, struct wlp, wss);
struct device *dev = &wlp->rc->uwb_dev.dev;
int result;
result = kobject_set_name(&wss->kobj, "wss-%s", wssid_str);
if (result < 0)
return result;
wss->kobj.ktype = &wss_ktype;
result = kobject_init_and_add(&wss->kobj,
&wss_ktype, wss->kobj.parent, "wlp");
if (result < 0) {
dev_err(dev, "WLP: Cannot register WSS kobject.\n");
goto error_kobject_register;
}
result = sysfs_create_group(&wss->kobj, &wss_attr_group);
if (result < 0) {
dev_err(dev, "WLP: Cannot register WSS attributes: %d\n",
result);
goto error_sysfs_create_group;
}
return 0;
error_sysfs_create_group:
kobject_put(&wss->kobj); /* will free name if needed */
return result;
error_kobject_register:
kfree(wss->kobj.name);
wss->kobj.name = NULL;
wss->kobj.ktype = NULL;
return result;
}
/**
* Release WSS
*
* No more references exist to this WSS. We should undo everything that was
* done in wlp_wss_create_activate() except removing the group. The group
* is not removed because an object can be unregistered before the group is
* created. We also undo any additional operations on the WSS after this
* (addition of members).
*
* If memory was allocated for the kobject's name then it will
* be freed by the kobject system during this time.
*
* The EDA cache is removed and reinitilized when the WSS is removed. We
* thus loose knowledge of members of this WSS at that time and need not do
* it here.
*/
void wlp_wss_release(struct kobject *kobj)
{
struct wlp_wss *wss = container_of(kobj, struct wlp_wss, kobj);
wlp_wss_reset(wss);
}
/**
* Enroll into a WSS using provided neighbor as registrar
*
* First search the neighborhood information to learn which neighbor is
* referred to, next proceed with enrollment.
*
* &wss->mutex is held
*/
static
int wlp_wss_enroll_target(struct wlp_wss *wss, struct wlp_uuid *wssid,
struct uwb_dev_addr *dest)
{
struct wlp *wlp = container_of(wss, struct wlp, wss);
struct device *dev = &wlp->rc->uwb_dev.dev;
struct wlp_neighbor_e *neighbor;
int result = -ENXIO;
struct uwb_dev_addr *dev_addr;
mutex_lock(&wlp->nbmutex);
list_for_each_entry(neighbor, &wlp->neighbors, node) {
dev_addr = &neighbor->uwb_dev->dev_addr;
if (!memcmp(dest, dev_addr, sizeof(*dest))) {
result = wlp_enroll_neighbor(wlp, neighbor, wss, wssid);
break;
}
}
if (result == -ENXIO)
dev_err(dev, "WLP: Cannot find neighbor %02x:%02x. \n",
dest->data[1], dest->data[0]);
mutex_unlock(&wlp->nbmutex);
return result;
}
/**
* Enroll into a WSS previously discovered
*
* User provides WSSID of WSS, search for neighbor that has this WSS
* activated and attempt to enroll.
*
* &wss->mutex is held
*/
static
int wlp_wss_enroll_discovered(struct wlp_wss *wss, struct wlp_uuid *wssid)
{
struct wlp *wlp = container_of(wss, struct wlp, wss);
struct device *dev = &wlp->rc->uwb_dev.dev;
struct wlp_neighbor_e *neighbor;
struct wlp_wssid_e *wssid_e;
char buf[WLP_WSS_UUID_STRSIZE];
int result = -ENXIO;
mutex_lock(&wlp->nbmutex);
list_for_each_entry(neighbor, &wlp->neighbors, node) {
list_for_each_entry(wssid_e, &neighbor->wssid, node) {
if (!memcmp(wssid, &wssid_e->wssid, sizeof(*wssid))) {
result = wlp_enroll_neighbor(wlp, neighbor,
wss, wssid);
if (result == 0) /* enrollment success */
goto out;
break;
}
}
}
out:
if (result == -ENXIO) {
wlp_wss_uuid_print(buf, sizeof(buf), wssid);
dev_err(dev, "WLP: Cannot find WSSID %s in cache. \n", buf);
}
mutex_unlock(&wlp->nbmutex);
return result;
}
/**
* Enroll into WSS with provided WSSID, registrar may be provided
*
* @wss: out WSS that will be enrolled
* @wssid: wssid of neighboring WSS that we want to enroll in
* @devaddr: registrar can be specified, will be broadcast (ff:ff) if any
* neighbor can be used as registrar.
*
* &wss->mutex is held
*/
static
int wlp_wss_enroll(struct wlp_wss *wss, struct wlp_uuid *wssid,
struct uwb_dev_addr *devaddr)
{
int result;
struct wlp *wlp = container_of(wss, struct wlp, wss);
struct device *dev = &wlp->rc->uwb_dev.dev;
char buf[WLP_WSS_UUID_STRSIZE];
struct uwb_dev_addr bcast = {.data = {0xff, 0xff} };
wlp_wss_uuid_print(buf, sizeof(buf), wssid);
if (wss->state != WLP_WSS_STATE_NONE) {
dev_err(dev, "WLP: Already enrolled in WSS %s.\n", buf);
result = -EEXIST;
goto error;
}
if (!memcmp(&bcast, devaddr, sizeof(bcast)))
result = wlp_wss_enroll_discovered(wss, wssid);
else
result = wlp_wss_enroll_target(wss, wssid, devaddr);
if (result < 0) {
dev_err(dev, "WLP: Unable to enroll into WSS %s, result %d \n",
buf, result);
goto error;
}
dev_dbg(dev, "Successfully enrolled into WSS %s \n", buf);
result = wlp_wss_sysfs_add(wss, buf);
if (result < 0) {
dev_err(dev, "WLP: Unable to set up sysfs for WSS kobject.\n");
wlp_wss_reset(wss);
}
error:
return result;
}
/**
* Activate given WSS
*
* Prior to activation a WSS must be enrolled. To activate a WSS a device
* includes the WSS hash in the WLP IE in its beacon in each superframe.
* WLP 0.99 [7.2.5].
*
* The WSS tag is also computed at this time. We only support one activated
* WSS so we can use the hash as a tag - there will never be a conflict.
*
* We currently only support one activated WSS so only one WSS hash is
* included in the WLP IE.
*/
static
int wlp_wss_activate(struct wlp_wss *wss)
{
struct wlp *wlp = container_of(wss, struct wlp, wss);
struct device *dev = &wlp->rc->uwb_dev.dev;
struct uwb_rc *uwb_rc = wlp->rc;
int result;
struct {
struct wlp_ie wlp_ie;
u8 hash; /* only include one hash */
} ie_data;
BUG_ON(wss->state != WLP_WSS_STATE_ENROLLED);
wss->hash = wlp_wss_comp_wssid_hash(&wss->wssid);
wss->tag = wss->hash;
memset(&ie_data, 0, sizeof(ie_data));
ie_data.wlp_ie.hdr.element_id = UWB_IE_WLP;
ie_data.wlp_ie.hdr.length = sizeof(ie_data) - sizeof(struct uwb_ie_hdr);
wlp_ie_set_hash_length(&ie_data.wlp_ie, sizeof(ie_data.hash));
ie_data.hash = wss->hash;
result = uwb_rc_ie_add(uwb_rc, &ie_data.wlp_ie.hdr,
sizeof(ie_data));
if (result < 0) {
dev_err(dev, "WLP: Unable to add WLP IE to beacon. "
"result = %d.\n", result);
goto error_wlp_ie;
}
wss->state = WLP_WSS_STATE_ACTIVE;
result = 0;
error_wlp_ie:
return result;
}
/**
* Enroll in and activate WSS identified by provided WSSID
*
* The neighborhood cache should contain a list of all neighbors and the
* WSS they have activated. Based on that cache we search which neighbor we
* can perform the association process with. The user also has option to
* specify which neighbor it prefers as registrar.
* Successful enrollment is followed by activation.
* Successful activation will create the sysfs directory containing
* specific information regarding this WSS.
*/
int wlp_wss_enroll_activate(struct wlp_wss *wss, struct wlp_uuid *wssid,
struct uwb_dev_addr *devaddr)
{
struct wlp *wlp = container_of(wss, struct wlp, wss);
struct device *dev = &wlp->rc->uwb_dev.dev;
int result = 0;
char buf[WLP_WSS_UUID_STRSIZE];
mutex_lock(&wss->mutex);
result = wlp_wss_enroll(wss, wssid, devaddr);
if (result < 0) {
wlp_wss_uuid_print(buf, sizeof(buf), &wss->wssid);
dev_err(dev, "WLP: Enrollment into WSS %s failed.\n", buf);
goto error_enroll;
}
result = wlp_wss_activate(wss);
if (result < 0) {
dev_err(dev, "WLP: Unable to activate WSS. Undoing enrollment "
"result = %d \n", result);
/* Undo enrollment */
wlp_wss_reset(wss);
goto error_activate;
}
error_activate:
error_enroll:
mutex_unlock(&wss->mutex);
return result;
}
/**
* Create, enroll, and activate a new WSS
*
* @wssid: new wssid provided by user
* @name: WSS name requested by used.
* @sec_status: security status requested by user
*
* A user requested the creation of a new WSS. All operations are done
* locally. The new WSS will be stored locally, the hash will be included
* in the WLP IE, and the sysfs infrastructure for this WSS will be
* created.
*/
int wlp_wss_create_activate(struct wlp_wss *wss, struct wlp_uuid *wssid,
char *name, unsigned sec_status, unsigned accept)
{
struct wlp *wlp = container_of(wss, struct wlp, wss);
struct device *dev = &wlp->rc->uwb_dev.dev;
int result = 0;
char buf[WLP_WSS_UUID_STRSIZE];
result = wlp_wss_uuid_print(buf, sizeof(buf), wssid);
if (!mutex_trylock(&wss->mutex)) {
dev_err(dev, "WLP: WLP association session in progress.\n");
return -EBUSY;
}
if (wss->state != WLP_WSS_STATE_NONE) {
dev_err(dev, "WLP: WSS already exists. Not creating new.\n");
result = -EEXIST;
goto out;
}
if (wss->kobj.parent == NULL) {
dev_err(dev, "WLP: WSS parent not ready. Is network interface "
"up?\n");
result = -ENXIO;
goto out;
}
if (sec_status == WLP_WSS_SECURE) {
dev_err(dev, "WLP: FIXME Creation of secure WSS not "
"supported yet.\n");
result = -EINVAL;
goto out;
}
wss->wssid = *wssid;
memcpy(wss->name, name, sizeof(wss->name));
wss->bcast = wlp_wss_sel_bcast_addr(wss);
wss->secure_status = sec_status;
wss->accept_enroll = accept;
/*wss->virtual_addr is initialized in call to wlp_wss_setup*/
/* sysfs infrastructure */
result = wlp_wss_sysfs_add(wss, buf);
if (result < 0) {
dev_err(dev, "Cannot set up sysfs for WSS kobject.\n");
wlp_wss_reset(wss);
goto out;
} else
result = 0;
wss->state = WLP_WSS_STATE_ENROLLED;
result = wlp_wss_activate(wss);
if (result < 0) {
dev_err(dev, "WLP: Unable to activate WSS. Undoing "
"enrollment\n");
wlp_wss_reset(wss);
goto out;
}
result = 0;
out:
mutex_unlock(&wss->mutex);
return result;
}
/**
* Determine if neighbor has WSS activated
*
* @returns: 1 if neighbor has WSS activated, zero otherwise
*
* This can be done in two ways:
* - send a C1 frame, parse C2/F0 response
* - examine the WLP IE sent by the neighbor
*
* The WLP IE is not fully supported in hardware so we use the C1/C2 frame
* exchange to determine if a WSS is activated. Using the WLP IE should be
* faster and should be used when it becomes possible.
*/
int wlp_wss_is_active(struct wlp *wlp, struct wlp_wss *wss,
struct uwb_dev_addr *dev_addr)
{
int result = 0;
struct device *dev = &wlp->rc->uwb_dev.dev;
DECLARE_COMPLETION_ONSTACK(completion);
struct wlp_session session;
struct sk_buff *skb;
struct wlp_frame_assoc *resp;
struct wlp_uuid wssid;
mutex_lock(&wlp->mutex);
/* Send C1 association frame */
result = wlp_send_assoc_frame(wlp, wss, dev_addr, WLP_ASSOC_C1);
if (result < 0) {
dev_err(dev, "Unable to send C1 frame to neighbor "
"%02x:%02x (%d)\n", dev_addr->data[1],
dev_addr->data[0], result);
result = 0;
goto out;
}
/* Create session, wait for response */
session.exp_message = WLP_ASSOC_C2;
session.cb = wlp_session_cb;
session.cb_priv = &completion;
session.neighbor_addr = *dev_addr;
BUG_ON(wlp->session != NULL);
wlp->session = &session;
/* Wait for C2/F0 frame */
result = wait_for_completion_interruptible_timeout(&completion,
WLP_PER_MSG_TIMEOUT * HZ);
if (result == 0) {
dev_err(dev, "Timeout while sending C1 to neighbor "
"%02x:%02x.\n", dev_addr->data[1],
dev_addr->data[0]);
goto out;
}
if (result < 0) {
dev_err(dev, "Unable to send C1 to neighbor %02x:%02x.\n",
dev_addr->data[1], dev_addr->data[0]);
result = 0;
goto out;
}
/* Parse message in session->data: it will be either C2 or F0 */
skb = session.data;
resp = (void *) skb->data;
if (resp->type == WLP_ASSOC_F0) {
result = wlp_parse_f0(wlp, skb);
if (result < 0)
dev_err(dev, "WLP: unable to parse incoming F0 "
"frame from neighbor %02x:%02x.\n",
dev_addr->data[1], dev_addr->data[0]);
result = 0;
goto error_resp_parse;
}
/* WLP version and message type fields have already been parsed */
result = wlp_get_wssid(wlp, (void *)resp + sizeof(*resp), &wssid,
skb->len - sizeof(*resp));
if (result < 0) {
dev_err(dev, "WLP: unable to obtain WSSID from C2 frame.\n");
result = 0;
goto error_resp_parse;
}
if (!memcmp(&wssid, &wss->wssid, sizeof(wssid)))
result = 1;
else {
dev_err(dev, "WLP: Received a C2 frame without matching "
"WSSID.\n");
result = 0;
}
error_resp_parse:
kfree_skb(skb);
out:
wlp->session = NULL;
mutex_unlock(&wlp->mutex);
return result;
}
/**
* Activate connection with neighbor by updating EDA cache
*
* @wss: local WSS to which neighbor wants to connect
* @dev_addr: neighbor's address
* @wssid: neighbor's WSSID - must be same as our WSS's WSSID
* @tag: neighbor's WSS tag used to identify frames transmitted by it
* @virt_addr: neighbor's virtual EUI-48
*/
static
int wlp_wss_activate_connection(struct wlp *wlp, struct wlp_wss *wss,
struct uwb_dev_addr *dev_addr,
struct wlp_uuid *wssid, u8 *tag,
struct uwb_mac_addr *virt_addr)
{
struct device *dev = &wlp->rc->uwb_dev.dev;
int result = 0;
if (!memcmp(wssid, &wss->wssid, sizeof(*wssid))) {
/* Update EDA cache */
result = wlp_eda_update_node(&wlp->eda, dev_addr, wss,
(void *) virt_addr->data, *tag,
WLP_WSS_CONNECTED);
if (result < 0)
dev_err(dev, "WLP: Unable to update EDA cache "
"with new connected neighbor information.\n");
} else {
dev_err(dev, "WLP: Neighbor does not have matching WSSID.\n");
result = -EINVAL;
}
return result;
}
/**
* Connect to WSS neighbor
*
* Use C3/C4 exchange to determine if neighbor has WSS activated and
* retrieve the WSS tag and virtual EUI-48 of the neighbor.
*/
static
int wlp_wss_connect_neighbor(struct wlp *wlp, struct wlp_wss *wss,
struct uwb_dev_addr *dev_addr)
{
int result;
struct device *dev = &wlp->rc->uwb_dev.dev;
struct wlp_uuid wssid;
u8 tag;
struct uwb_mac_addr virt_addr;
DECLARE_COMPLETION_ONSTACK(completion);
struct wlp_session session;
struct wlp_frame_assoc *resp;
struct sk_buff *skb;
mutex_lock(&wlp->mutex);
/* Send C3 association frame */
result = wlp_send_assoc_frame(wlp, wss, dev_addr, WLP_ASSOC_C3);
if (result < 0) {
dev_err(dev, "Unable to send C3 frame to neighbor "
"%02x:%02x (%d)\n", dev_addr->data[1],
dev_addr->data[0], result);
goto out;
}
/* Create session, wait for response */
session.exp_message = WLP_ASSOC_C4;
session.cb = wlp_session_cb;
session.cb_priv = &completion;
session.neighbor_addr = *dev_addr;
BUG_ON(wlp->session != NULL);
wlp->session = &session;
/* Wait for C4/F0 frame */
result = wait_for_completion_interruptible_timeout(&completion,
WLP_PER_MSG_TIMEOUT * HZ);
if (result == 0) {
dev_err(dev, "Timeout while sending C3 to neighbor "
"%02x:%02x.\n", dev_addr->data[1],
dev_addr->data[0]);
result = -ETIMEDOUT;
goto out;
}
if (result < 0) {
dev_err(dev, "Unable to send C3 to neighbor %02x:%02x.\n",
dev_addr->data[1], dev_addr->data[0]);
goto out;
}
/* Parse message in session->data: it will be either C4 or F0 */
skb = session.data;
resp = (void *) skb->data;
if (resp->type == WLP_ASSOC_F0) {
result = wlp_parse_f0(wlp, skb);
if (result < 0)
dev_err(dev, "WLP: unable to parse incoming F0 "
"frame from neighbor %02x:%02x.\n",
dev_addr->data[1], dev_addr->data[0]);
result = -EINVAL;
goto error_resp_parse;
}
result = wlp_parse_c3c4_frame(wlp, skb, &wssid, &tag, &virt_addr);
if (result < 0) {
dev_err(dev, "WLP: Unable to parse C4 frame from neighbor.\n");
goto error_resp_parse;
}
result = wlp_wss_activate_connection(wlp, wss, dev_addr, &wssid, &tag,
&virt_addr);
if (result < 0) {
dev_err(dev, "WLP: Unable to activate connection to "
"neighbor %02x:%02x.\n", dev_addr->data[1],
dev_addr->data[0]);
goto error_resp_parse;
}
error_resp_parse:
kfree_skb(skb);
out:
/* Record that we unsuccessfully tried to connect to this neighbor */
if (result < 0)
wlp_eda_update_node_state(&wlp->eda, dev_addr,
WLP_WSS_CONNECT_FAILED);
wlp->session = NULL;
mutex_unlock(&wlp->mutex);
return result;
}
/**
* Connect to neighbor with common WSS, send pending frame
*
* This function is scheduled when a frame is destined to a neighbor with
* which we do not have a connection. A copy of the EDA cache entry is
* provided - not the actual cache entry (because it is protected by a
* spinlock).
*
* First determine if neighbor has the same WSS activated, connect if it
* does. The C3/C4 exchange is dual purpose to determine if neighbor has
* WSS activated and proceed with the connection.
*
* The frame that triggered the connection setup is sent after connection
* setup.
*
* network queue is stopped - we need to restart when done
*
*/
static
void wlp_wss_connect_send(struct work_struct *ws)
{
struct wlp_assoc_conn_ctx *conn_ctx = container_of(ws,
struct wlp_assoc_conn_ctx,
ws);
struct wlp *wlp = conn_ctx->wlp;
struct sk_buff *skb = conn_ctx->skb;
struct wlp_eda_node *eda_entry = &conn_ctx->eda_entry;
struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr;
struct wlp_wss *wss = &wlp->wss;
int result;
struct device *dev = &wlp->rc->uwb_dev.dev;
mutex_lock(&wss->mutex);
if (wss->state < WLP_WSS_STATE_ACTIVE) {
if (printk_ratelimit())
dev_err(dev, "WLP: Attempting to connect with "
"WSS that is not active or connected.\n");
dev_kfree_skb(skb);
goto out;
}
/* Establish connection - send C3 rcv C4 */
result = wlp_wss_connect_neighbor(wlp, wss, dev_addr);
if (result < 0) {
if (printk_ratelimit())
dev_err(dev, "WLP: Unable to establish connection "
"with neighbor %02x:%02x.\n",
dev_addr->data[1], dev_addr->data[0]);
dev_kfree_skb(skb);
goto out;
}
/* EDA entry changed, update the local copy being used */
result = wlp_copy_eda_node(&wlp->eda, dev_addr, eda_entry);
if (result < 0) {
if (printk_ratelimit())
dev_err(dev, "WLP: Cannot find EDA entry for "
"neighbor %02x:%02x \n",
dev_addr->data[1], dev_addr->data[0]);
}
result = wlp_wss_prep_hdr(wlp, eda_entry, skb);
if (result < 0) {
if (printk_ratelimit())
dev_err(dev, "WLP: Unable to prepare frame header for "
"transmission (neighbor %02x:%02x). \n",
dev_addr->data[1], dev_addr->data[0]);
dev_kfree_skb(skb);
goto out;
}
BUG_ON(wlp->xmit_frame == NULL);
result = wlp->xmit_frame(wlp, skb, dev_addr);
if (result < 0) {
if (printk_ratelimit())
dev_err(dev, "WLP: Unable to transmit frame: %d\n",
result);
if (result == -ENXIO)
dev_err(dev, "WLP: Is network interface up? \n");
/* We could try again ... */
dev_kfree_skb(skb);/*we need to free if tx fails */
}
out:
kfree(conn_ctx);
BUG_ON(wlp->start_queue == NULL);
wlp->start_queue(wlp);
mutex_unlock(&wss->mutex);
}
/**
* Add WLP header to outgoing skb
*
* @eda_entry: pointer to neighbor's entry in the EDA cache
* @_skb: skb containing data destined to the neighbor
*/
int wlp_wss_prep_hdr(struct wlp *wlp, struct wlp_eda_node *eda_entry,
void *_skb)
{
struct device *dev = &wlp->rc->uwb_dev.dev;
int result = 0;
unsigned char *eth_addr = eda_entry->eth_addr;
struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr;
struct sk_buff *skb = _skb;
struct wlp_frame_std_abbrv_hdr *std_hdr;
if (eda_entry->state == WLP_WSS_CONNECTED) {
/* Add WLP header */
BUG_ON(skb_headroom(skb) < sizeof(*std_hdr));
std_hdr = (void *) __skb_push(skb, sizeof(*std_hdr));
std_hdr->hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID);
std_hdr->hdr.type = WLP_FRAME_STANDARD;
std_hdr->tag = eda_entry->wss->tag;
} else {
if (printk_ratelimit())
dev_err(dev, "WLP: Destination neighbor (Ethernet: "
"%02x:%02x:%02x:%02x:%02x:%02x, Dev: "
"%02x:%02x) is not connected. \n", eth_addr[0],
eth_addr[1], eth_addr[2], eth_addr[3],
eth_addr[4], eth_addr[5], dev_addr->data[1],
dev_addr->data[0]);
result = -EINVAL;
}
return result;
}
/**
* Prepare skb for neighbor: connect if not already and prep WLP header
*
* This function is called in interrupt context, but it needs to sleep. We
* temporarily stop the net queue to establish the WLP connection.
* Setup of the WLP connection and restart of queue is scheduled
* on the default work queue.
*
* run with eda->lock held (spinlock)
*/
int wlp_wss_connect_prep(struct wlp *wlp, struct wlp_eda_node *eda_entry,
void *_skb)
{
int result = 0;
struct device *dev = &wlp->rc->uwb_dev.dev;
struct sk_buff *skb = _skb;
struct wlp_assoc_conn_ctx *conn_ctx;
if (eda_entry->state == WLP_WSS_UNCONNECTED) {
/* We don't want any more packets while we set up connection */
BUG_ON(wlp->stop_queue == NULL);
wlp->stop_queue(wlp);
conn_ctx = kmalloc(sizeof(*conn_ctx), GFP_ATOMIC);
if (conn_ctx == NULL) {
if (printk_ratelimit())
dev_err(dev, "WLP: Unable to allocate memory "
"for connection handling.\n");
result = -ENOMEM;
goto out;
}
conn_ctx->wlp = wlp;
conn_ctx->skb = skb;
conn_ctx->eda_entry = *eda_entry;
INIT_WORK(&conn_ctx->ws, wlp_wss_connect_send);
schedule_work(&conn_ctx->ws);
result = 1;
} else if (eda_entry->state == WLP_WSS_CONNECT_FAILED) {
/* Previous connection attempts failed, don't retry - see
* conditions for connection in WLP 0.99 [7.6.2] */
if (printk_ratelimit())
dev_err(dev, "Could not connect to neighbor "
"previously. Not retrying. \n");
result = -ENONET;
goto out;
} else /* eda_entry->state == WLP_WSS_CONNECTED */
result = wlp_wss_prep_hdr(wlp, eda_entry, skb);
out:
return result;
}
/**
* Emulate broadcast: copy skb, send copy to neighbor (connect if not already)
*
* We need to copy skbs in the case where we emulate broadcast through
* unicast. We copy instead of clone because we are modifying the data of
* the frame after copying ... clones share data so we cannot emulate
* broadcast using clones.
*
* run with eda->lock held (spinlock)
*/
int wlp_wss_send_copy(struct wlp *wlp, struct wlp_eda_node *eda_entry,
void *_skb)
{
int result = -ENOMEM;
struct device *dev = &wlp->rc->uwb_dev.dev;
struct sk_buff *skb = _skb;
struct sk_buff *copy;
struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr;
copy = skb_copy(skb, GFP_ATOMIC);
if (copy == NULL) {
if (printk_ratelimit())
dev_err(dev, "WLP: Unable to copy skb for "
"transmission.\n");
goto out;
}
result = wlp_wss_connect_prep(wlp, eda_entry, copy);
if (result < 0) {
if (printk_ratelimit())
dev_err(dev, "WLP: Unable to connect/send skb "
"to neighbor.\n");
dev_kfree_skb_irq(copy);
goto out;
} else if (result == 1)
/* Frame will be transmitted separately */
goto out;
BUG_ON(wlp->xmit_frame == NULL);
result = wlp->xmit_frame(wlp, copy, dev_addr);
if (result < 0) {
if (printk_ratelimit())
dev_err(dev, "WLP: Unable to transmit frame: %d\n",
result);
if ((result == -ENXIO) && printk_ratelimit())
dev_err(dev, "WLP: Is network interface up? \n");
/* We could try again ... */
dev_kfree_skb_irq(copy);/*we need to free if tx fails */
}
out:
return result;
}
/**
* Setup WSS
*
* Should be called by network driver after the interface has been given a
* MAC address.
*/
int wlp_wss_setup(struct net_device *net_dev, struct wlp_wss *wss)
{
struct wlp *wlp = container_of(wss, struct wlp, wss);
struct device *dev = &wlp->rc->uwb_dev.dev;
int result = 0;
mutex_lock(&wss->mutex);
wss->kobj.parent = &net_dev->dev.kobj;
if (!is_valid_ether_addr(net_dev->dev_addr)) {
dev_err(dev, "WLP: Invalid MAC address. Cannot use for"
"virtual.\n");
result = -EINVAL;
goto out;
}
memcpy(wss->virtual_addr.data, net_dev->dev_addr,
sizeof(wss->virtual_addr.data));
out:
mutex_unlock(&wss->mutex);
return result;
}
EXPORT_SYMBOL_GPL(wlp_wss_setup);
/**
* Remove WSS
*
* Called by client that configured WSS through wlp_wss_setup(). This
* function is called when client no longer needs WSS, eg. client shuts
* down.
*
* We remove the WLP IE from the beacon before initiating local cleanup.
*/
void wlp_wss_remove(struct wlp_wss *wss)
{
struct wlp *wlp = container_of(wss, struct wlp, wss);
mutex_lock(&wss->mutex);
if (wss->state == WLP_WSS_STATE_ACTIVE)
uwb_rc_ie_rm(wlp->rc, UWB_IE_WLP);
if (wss->state != WLP_WSS_STATE_NONE) {
sysfs_remove_group(&wss->kobj, &wss_attr_group);
kobject_put(&wss->kobj);
}
wss->kobj.parent = NULL;
memset(&wss->virtual_addr, 0, sizeof(wss->virtual_addr));
/* Cleanup EDA cache */
wlp_eda_release(&wlp->eda);
wlp_eda_init(&wlp->eda);
mutex_unlock(&wss->mutex);
}
EXPORT_SYMBOL_GPL(wlp_wss_remove);