forked from Minki/linux
uwb: Remove the WLP subsystem and drivers
The only Wimedia LLC Protocol (WLP) hardware was an Intel i1480 chip with a beta release of firmware that was never commercially available as a product. This hardware and firmware is no longer available as Intel sold their UWB/WLP IP. I also see little prospect of other WLP capable hardware ever being available. Signed-off-by: David Vrabel <david.vrabel@csr.com>
This commit is contained in:
parent
229aebb873
commit
446396bfab
12
MAINTAINERS
12
MAINTAINERS
@ -5995,9 +5995,6 @@ M: David Vrabel <david.vrabel@csr.com>
|
||||
L: linux-usb@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/uwb/
|
||||
X: drivers/uwb/wlp/
|
||||
X: drivers/uwb/i1480/i1480u-wlp/
|
||||
X: drivers/uwb/i1480/i1480-wlp.h
|
||||
F: include/linux/uwb.h
|
||||
F: include/linux/uwb/
|
||||
|
||||
@ -6533,15 +6530,6 @@ F: include/linux/wimax/debug.h
|
||||
F: include/net/wimax.h
|
||||
F: net/wimax/
|
||||
|
||||
WIMEDIA LLC PROTOCOL (WLP) SUBSYSTEM
|
||||
M: David Vrabel <david.vrabel@csr.com>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
F: include/linux/wlp.h
|
||||
F: drivers/uwb/wlp/
|
||||
F: drivers/uwb/i1480/i1480u-wlp/
|
||||
F: drivers/uwb/i1480/i1480-wlp.h
|
||||
|
||||
WISTRON LAPTOP BUTTON DRIVER
|
||||
M: Miloslav Trmac <mitr@volny.cz>
|
||||
S: Maintained
|
||||
|
@ -12,8 +12,7 @@ menuconfig UWB
|
||||
technology using a wide spectrum (3.1-10.6GHz). It is
|
||||
optimized for in-room use (480Mbps at 2 meters, 110Mbps at
|
||||
10m). It serves as the transport layer for other protocols,
|
||||
such as Wireless USB (WUSB), IP (WLP) and upcoming
|
||||
Bluetooth and 1394
|
||||
such as Wireless USB (WUSB).
|
||||
|
||||
The topology is peer to peer; however, higher level
|
||||
protocols (such as WUSB) might impose a master/slave
|
||||
@ -58,13 +57,6 @@ config UWB_WHCI
|
||||
To compile this driver select Y (built in) or M (module). It
|
||||
is safe to select any even if you do not have the hardware.
|
||||
|
||||
config UWB_WLP
|
||||
tristate "Support WiMedia Link Protocol (Ethernet/IP over UWB)"
|
||||
depends on UWB && NET
|
||||
help
|
||||
This is a common library for drivers that implement
|
||||
networking over UWB.
|
||||
|
||||
config UWB_I1480U
|
||||
tristate "Support for Intel Wireless UWB Link 1480 HWA"
|
||||
depends on UWB_HWA
|
||||
@ -77,14 +69,4 @@ config UWB_I1480U
|
||||
To compile this driver select Y (built in) or M (module). It
|
||||
is safe to select any even if you do not have the hardware.
|
||||
|
||||
config UWB_I1480U_WLP
|
||||
tristate "Support for Intel Wireless UWB Link 1480 HWA's WLP interface"
|
||||
depends on UWB_I1480U && UWB_WLP && NET
|
||||
help
|
||||
This driver enables WLP support for the i1480 when connected via
|
||||
USB. WLP is the WiMedia Link Protocol, or IP over UWB.
|
||||
|
||||
To compile this driver select Y (built in) or M (module). It
|
||||
is safe to select any even if you don't have the hardware.
|
||||
|
||||
endif # UWB
|
||||
|
@ -1,5 +1,4 @@
|
||||
obj-$(CONFIG_UWB) += uwb.o
|
||||
obj-$(CONFIG_UWB_WLP) += wlp/
|
||||
obj-$(CONFIG_UWB_WHCI) += umc.o whci.o whc-rc.o
|
||||
obj-$(CONFIG_UWB_HWA) += hwa-rc.o
|
||||
obj-$(CONFIG_UWB_I1480U) += i1480/
|
||||
|
@ -1,2 +1 @@
|
||||
obj-$(CONFIG_UWB_I1480U) += dfu/ i1480-est.o
|
||||
obj-$(CONFIG_UWB_I1480U_WLP) += i1480u-wlp/
|
||||
|
@ -1,200 +0,0 @@
|
||||
/*
|
||||
* Intel 1480 Wireless UWB Link
|
||||
* WLP specific definitions
|
||||
*
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef __i1480_wlp_h__
|
||||
#define __i1480_wlp_h__
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/uwb.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
/* New simplified header format? */
|
||||
#undef WLP_HDR_FMT_2 /* FIXME: rename */
|
||||
|
||||
/**
|
||||
* Values of the Delivery ID & Type field when PCA or DRP
|
||||
*
|
||||
* The Delivery ID & Type field in the WLP TX header indicates whether
|
||||
* the frame is PCA or DRP. This is done based on the high level bit of
|
||||
* this field.
|
||||
* We use this constant to test if the traffic is PCA or DRP as follows:
|
||||
* if (wlp_tx_hdr_delivery_id_type(wlp_tx_hdr) & WLP_DRP)
|
||||
* this is DRP traffic
|
||||
* else
|
||||
* this is PCA traffic
|
||||
*/
|
||||
enum deliver_id_type_bit {
|
||||
WLP_DRP = 8,
|
||||
};
|
||||
|
||||
/**
|
||||
* WLP TX header
|
||||
*
|
||||
* Indicates UWB/WLP-specific transmission parameters for a network
|
||||
* packet.
|
||||
*/
|
||||
struct wlp_tx_hdr {
|
||||
/* dword 0 */
|
||||
struct uwb_dev_addr dstaddr;
|
||||
u8 key_index;
|
||||
u8 mac_params;
|
||||
/* dword 1 */
|
||||
u8 phy_params;
|
||||
#ifndef WLP_HDR_FMT_2
|
||||
u8 reserved;
|
||||
__le16 oui01; /* FIXME: not so sure if __le16 or u8[2] */
|
||||
/* dword 2 */
|
||||
u8 oui2; /* if all LE, it could be merged */
|
||||
__le16 prid;
|
||||
#endif
|
||||
} __attribute__((packed));
|
||||
|
||||
static inline int wlp_tx_hdr_delivery_id_type(const struct wlp_tx_hdr *hdr)
|
||||
{
|
||||
return hdr->mac_params & 0x0f;
|
||||
}
|
||||
|
||||
static inline int wlp_tx_hdr_ack_policy(const struct wlp_tx_hdr *hdr)
|
||||
{
|
||||
return (hdr->mac_params >> 4) & 0x07;
|
||||
}
|
||||
|
||||
static inline int wlp_tx_hdr_rts_cts(const struct wlp_tx_hdr *hdr)
|
||||
{
|
||||
return (hdr->mac_params >> 7) & 0x01;
|
||||
}
|
||||
|
||||
static inline void wlp_tx_hdr_set_delivery_id_type(struct wlp_tx_hdr *hdr, int id)
|
||||
{
|
||||
hdr->mac_params = (hdr->mac_params & ~0x0f) | id;
|
||||
}
|
||||
|
||||
static inline void wlp_tx_hdr_set_ack_policy(struct wlp_tx_hdr *hdr,
|
||||
enum uwb_ack_pol policy)
|
||||
{
|
||||
hdr->mac_params = (hdr->mac_params & ~0x70) | (policy << 4);
|
||||
}
|
||||
|
||||
static inline void wlp_tx_hdr_set_rts_cts(struct wlp_tx_hdr *hdr, int rts_cts)
|
||||
{
|
||||
hdr->mac_params = (hdr->mac_params & ~0x80) | (rts_cts << 7);
|
||||
}
|
||||
|
||||
static inline enum uwb_phy_rate wlp_tx_hdr_phy_rate(const struct wlp_tx_hdr *hdr)
|
||||
{
|
||||
return hdr->phy_params & 0x0f;
|
||||
}
|
||||
|
||||
static inline int wlp_tx_hdr_tx_power(const struct wlp_tx_hdr *hdr)
|
||||
{
|
||||
return (hdr->phy_params >> 4) & 0x0f;
|
||||
}
|
||||
|
||||
static inline void wlp_tx_hdr_set_phy_rate(struct wlp_tx_hdr *hdr, enum uwb_phy_rate rate)
|
||||
{
|
||||
hdr->phy_params = (hdr->phy_params & ~0x0f) | rate;
|
||||
}
|
||||
|
||||
static inline void wlp_tx_hdr_set_tx_power(struct wlp_tx_hdr *hdr, int pwr)
|
||||
{
|
||||
hdr->phy_params = (hdr->phy_params & ~0xf0) | (pwr << 4);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* WLP RX header
|
||||
*
|
||||
* Provides UWB/WLP-specific transmission data for a received
|
||||
* network packet.
|
||||
*/
|
||||
struct wlp_rx_hdr {
|
||||
/* dword 0 */
|
||||
struct uwb_dev_addr dstaddr;
|
||||
struct uwb_dev_addr srcaddr;
|
||||
/* dword 1 */
|
||||
u8 LQI;
|
||||
s8 RSSI;
|
||||
u8 reserved3;
|
||||
#ifndef WLP_HDR_FMT_2
|
||||
u8 oui0;
|
||||
/* dword 2 */
|
||||
__le16 oui12;
|
||||
__le16 prid;
|
||||
#endif
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
/** User configurable options for WLP */
|
||||
struct wlp_options {
|
||||
struct mutex mutex; /* access to user configurable options*/
|
||||
struct wlp_tx_hdr def_tx_hdr; /* default tx hdr */
|
||||
u8 pca_base_priority;
|
||||
u8 bw_alloc; /*index into bw_allocs[] for PCA/DRP reservations*/
|
||||
};
|
||||
|
||||
|
||||
static inline
|
||||
void wlp_options_init(struct wlp_options *options)
|
||||
{
|
||||
mutex_init(&options->mutex);
|
||||
wlp_tx_hdr_set_ack_policy(&options->def_tx_hdr, UWB_ACK_INM);
|
||||
wlp_tx_hdr_set_rts_cts(&options->def_tx_hdr, 1);
|
||||
/* FIXME: default to phy caps */
|
||||
wlp_tx_hdr_set_phy_rate(&options->def_tx_hdr, UWB_PHY_RATE_480);
|
||||
#ifndef WLP_HDR_FMT_2
|
||||
options->def_tx_hdr.prid = cpu_to_le16(0x0000);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* sysfs helpers */
|
||||
|
||||
extern ssize_t uwb_pca_base_priority_store(struct wlp_options *,
|
||||
const char *, size_t);
|
||||
extern ssize_t uwb_pca_base_priority_show(const struct wlp_options *, char *);
|
||||
extern ssize_t uwb_bw_alloc_store(struct wlp_options *, const char *, size_t);
|
||||
extern ssize_t uwb_bw_alloc_show(const struct wlp_options *, char *);
|
||||
extern ssize_t uwb_ack_policy_store(struct wlp_options *,
|
||||
const char *, size_t);
|
||||
extern ssize_t uwb_ack_policy_show(const struct wlp_options *, char *);
|
||||
extern ssize_t uwb_rts_cts_store(struct wlp_options *, const char *, size_t);
|
||||
extern ssize_t uwb_rts_cts_show(const struct wlp_options *, char *);
|
||||
extern ssize_t uwb_phy_rate_store(struct wlp_options *, const char *, size_t);
|
||||
extern ssize_t uwb_phy_rate_show(const struct wlp_options *, char *);
|
||||
|
||||
|
||||
/** Simple bandwidth allocation (temporary and too simple) */
|
||||
struct wlp_bw_allocs {
|
||||
const char *name;
|
||||
struct {
|
||||
u8 mask, stream;
|
||||
} tx, rx;
|
||||
};
|
||||
|
||||
|
||||
#endif /* #ifndef __i1480_wlp_h__ */
|
@ -1,8 +0,0 @@
|
||||
obj-$(CONFIG_UWB_I1480U_WLP) += i1480u-wlp.o
|
||||
|
||||
i1480u-wlp-objs := \
|
||||
lc.o \
|
||||
netdev.o \
|
||||
rx.o \
|
||||
sysfs.o \
|
||||
tx.o
|
@ -1,283 +0,0 @@
|
||||
/*
|
||||
* Intel 1480 Wireless UWB Link USB
|
||||
* Header formats, constants, general internal interfaces
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
* This is not an standard interface.
|
||||
*
|
||||
* FIXME: docs
|
||||
*
|
||||
* i1480u-wlp is pretty simple: two endpoints, one for tx, one for
|
||||
* rx. rx is polled. Network packets (ethernet, whatever) are wrapped
|
||||
* in i1480 TX or RX headers (for sending over the air), and these
|
||||
* packets are wrapped in UNTD headers (for sending to the WLP UWB
|
||||
* controller).
|
||||
*
|
||||
* UNTD packets (UNTD hdr + i1480 hdr + network packet) packets
|
||||
* cannot be bigger than i1480u_MAX_FRG_SIZE. When this happens, the
|
||||
* i1480 packet is broken in chunks/packets:
|
||||
*
|
||||
* UNTD-1st.hdr + i1480.hdr + payload
|
||||
* UNTD-next.hdr + payload
|
||||
* ...
|
||||
* UNTD-last.hdr + payload
|
||||
*
|
||||
* so that each packet is smaller or equal than i1480u_MAX_FRG_SIZE.
|
||||
*
|
||||
* All HW structures and bitmaps are little endian, so we need to play
|
||||
* ugly tricks when defining bitfields. Hoping for the day GCC
|
||||
* implements __attribute__((endian(1234))).
|
||||
*
|
||||
* FIXME: ROADMAP to the whole implementation
|
||||
*/
|
||||
|
||||
#ifndef __i1480u_wlp_h__
|
||||
#define __i1480u_wlp_h__
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/uwb.h> /* struct uwb_rc, struct uwb_notifs_handler */
|
||||
#include <linux/wlp.h>
|
||||
#include "../i1480-wlp.h"
|
||||
|
||||
#undef i1480u_FLOW_CONTROL /* Enable flow control code */
|
||||
|
||||
/**
|
||||
* Basic flow control
|
||||
*/
|
||||
enum {
|
||||
i1480u_TX_INFLIGHT_MAX = 1000,
|
||||
i1480u_TX_INFLIGHT_THRESHOLD = 100,
|
||||
};
|
||||
|
||||
/** Maximum size of a transaction that we can tx/rx */
|
||||
enum {
|
||||
/* Maximum packet size computed as follows: max UNTD header (8) +
|
||||
* i1480 RX header (8) + max Ethernet header and payload (4096) +
|
||||
* Padding added by skb_reserve (2) to make post Ethernet payload
|
||||
* start on 16 byte boundary*/
|
||||
i1480u_MAX_RX_PKT_SIZE = 4114,
|
||||
i1480u_MAX_FRG_SIZE = 512,
|
||||
i1480u_RX_BUFS = 9,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* UNTD packet type
|
||||
*
|
||||
* We need to fragment any payload whose UNTD packet is going to be
|
||||
* bigger than i1480u_MAX_FRG_SIZE.
|
||||
*/
|
||||
enum i1480u_pkt_type {
|
||||
i1480u_PKT_FRAG_1ST = 0x1,
|
||||
i1480u_PKT_FRAG_NXT = 0x0,
|
||||
i1480u_PKT_FRAG_LST = 0x2,
|
||||
i1480u_PKT_FRAG_CMP = 0x3
|
||||
};
|
||||
enum {
|
||||
i1480u_PKT_NONE = 0x4,
|
||||
};
|
||||
|
||||
/** USB Network Transfer Descriptor - common */
|
||||
struct untd_hdr {
|
||||
u8 type;
|
||||
__le16 len;
|
||||
} __attribute__((packed));
|
||||
|
||||
static inline enum i1480u_pkt_type untd_hdr_type(const struct untd_hdr *hdr)
|
||||
{
|
||||
return hdr->type & 0x03;
|
||||
}
|
||||
|
||||
static inline int untd_hdr_rx_tx(const struct untd_hdr *hdr)
|
||||
{
|
||||
return (hdr->type >> 2) & 0x01;
|
||||
}
|
||||
|
||||
static inline void untd_hdr_set_type(struct untd_hdr *hdr, enum i1480u_pkt_type type)
|
||||
{
|
||||
hdr->type = (hdr->type & ~0x03) | type;
|
||||
}
|
||||
|
||||
static inline void untd_hdr_set_rx_tx(struct untd_hdr *hdr, int rx_tx)
|
||||
{
|
||||
hdr->type = (hdr->type & ~0x04) | (rx_tx << 2);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* USB Network Transfer Descriptor - Complete Packet
|
||||
*
|
||||
* This is for a packet that is smaller (header + payload) than
|
||||
* i1480u_MAX_FRG_SIZE.
|
||||
*
|
||||
* @hdr.total_len is the size of the payload; the payload doesn't
|
||||
* count this header nor the padding, but includes the size of i1480
|
||||
* header.
|
||||
*/
|
||||
struct untd_hdr_cmp {
|
||||
struct untd_hdr hdr;
|
||||
u8 padding;
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
/**
|
||||
* USB Network Transfer Descriptor - First fragment
|
||||
*
|
||||
* @hdr.len is the size of the *whole packet* (excluding UNTD
|
||||
* headers); @fragment_len is the size of the payload (excluding UNTD
|
||||
* headers, but including i1480 headers).
|
||||
*/
|
||||
struct untd_hdr_1st {
|
||||
struct untd_hdr hdr;
|
||||
__le16 fragment_len;
|
||||
u8 padding[3];
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
/**
|
||||
* USB Network Transfer Descriptor - Next / Last [Rest]
|
||||
*
|
||||
* @hdr.len is the size of the payload, not including headrs.
|
||||
*/
|
||||
struct untd_hdr_rst {
|
||||
struct untd_hdr hdr;
|
||||
u8 padding;
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
/**
|
||||
* Transmission context
|
||||
*
|
||||
* Wraps all the stuff needed to track a pending/active tx
|
||||
* operation.
|
||||
*/
|
||||
struct i1480u_tx {
|
||||
struct list_head list_node;
|
||||
struct i1480u *i1480u;
|
||||
struct urb *urb;
|
||||
|
||||
struct sk_buff *skb;
|
||||
struct wlp_tx_hdr *wlp_tx_hdr;
|
||||
|
||||
void *buf; /* if NULL, no new buf was used */
|
||||
size_t buf_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* Basic flow control
|
||||
*
|
||||
* We maintain a basic flow control counter. "count" how many TX URBs are
|
||||
* outstanding. Only allow "max"
|
||||
* TX URBs to be outstanding. If this value is reached the queue will be
|
||||
* stopped. The queue will be restarted when there are
|
||||
* "threshold" URBs outstanding.
|
||||
* Maintain a counter of how many time the TX queue needed to be restarted
|
||||
* due to the "max" being exceeded and the "threshold" reached again. The
|
||||
* timestamp "restart_ts" is to keep track from when the counter was last
|
||||
* queried (see sysfs handling of file wlp_tx_inflight).
|
||||
*/
|
||||
struct i1480u_tx_inflight {
|
||||
atomic_t count;
|
||||
unsigned long max;
|
||||
unsigned long threshold;
|
||||
unsigned long restart_ts;
|
||||
atomic_t restart_count;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instance of a i1480u WLP interface
|
||||
*
|
||||
* Keeps references to the USB device that wraps it, as well as it's
|
||||
* interface and associated UWB host controller. As well, it also
|
||||
* keeps a link to the netdevice for integration into the networking
|
||||
* stack.
|
||||
* We maintian separate error history for the tx and rx endpoints because
|
||||
* the implementation does not rely on locking - having one shared
|
||||
* structure between endpoints may cause problems. Adding locking to the
|
||||
* implementation will have higher cost than adding a separate structure.
|
||||
*/
|
||||
struct i1480u {
|
||||
struct usb_device *usb_dev;
|
||||
struct usb_interface *usb_iface;
|
||||
struct net_device *net_dev;
|
||||
|
||||
spinlock_t lock;
|
||||
|
||||
/* RX context handling */
|
||||
struct sk_buff *rx_skb;
|
||||
struct uwb_dev_addr rx_srcaddr;
|
||||
size_t rx_untd_pkt_size;
|
||||
struct i1480u_rx_buf {
|
||||
struct i1480u *i1480u; /* back pointer */
|
||||
struct urb *urb;
|
||||
struct sk_buff *data; /* i1480u_MAX_RX_PKT_SIZE each */
|
||||
} rx_buf[i1480u_RX_BUFS]; /* N bufs */
|
||||
|
||||
spinlock_t tx_list_lock; /* TX context */
|
||||
struct list_head tx_list;
|
||||
u8 tx_stream;
|
||||
|
||||
struct stats lqe_stats, rssi_stats; /* radio statistics */
|
||||
|
||||
/* Options we can set from sysfs */
|
||||
struct wlp_options options;
|
||||
struct uwb_notifs_handler uwb_notifs_handler;
|
||||
struct edc tx_errors;
|
||||
struct edc rx_errors;
|
||||
struct wlp wlp;
|
||||
#ifdef i1480u_FLOW_CONTROL
|
||||
struct urb *notif_urb;
|
||||
struct edc notif_edc; /* error density counter */
|
||||
u8 notif_buffer[1];
|
||||
#endif
|
||||
struct i1480u_tx_inflight tx_inflight;
|
||||
};
|
||||
|
||||
/* Internal interfaces */
|
||||
extern void i1480u_rx_cb(struct urb *urb);
|
||||
extern int i1480u_rx_setup(struct i1480u *);
|
||||
extern void i1480u_rx_release(struct i1480u *);
|
||||
extern void i1480u_tx_release(struct i1480u *);
|
||||
extern int i1480u_xmit_frame(struct wlp *, struct sk_buff *,
|
||||
struct uwb_dev_addr *);
|
||||
extern void i1480u_stop_queue(struct wlp *);
|
||||
extern void i1480u_start_queue(struct wlp *);
|
||||
extern int i1480u_sysfs_setup(struct i1480u *);
|
||||
extern void i1480u_sysfs_release(struct i1480u *);
|
||||
|
||||
/* netdev interface */
|
||||
extern int i1480u_open(struct net_device *);
|
||||
extern int i1480u_stop(struct net_device *);
|
||||
extern netdev_tx_t i1480u_hard_start_xmit(struct sk_buff *,
|
||||
struct net_device *);
|
||||
extern void i1480u_tx_timeout(struct net_device *);
|
||||
extern int i1480u_set_config(struct net_device *, struct ifmap *);
|
||||
extern int i1480u_change_mtu(struct net_device *, int);
|
||||
extern void i1480u_uwb_notifs_cb(void *, struct uwb_dev *, enum uwb_notifs);
|
||||
|
||||
/* bandwidth allocation callback */
|
||||
extern void i1480u_bw_alloc_cb(struct uwb_rsv *);
|
||||
|
||||
/* Sys FS */
|
||||
extern struct attribute_group i1480u_wlp_attr_group;
|
||||
|
||||
#endif /* #ifndef __i1480u_wlp_h__ */
|
@ -1,424 +0,0 @@
|
||||
/*
|
||||
* WUSB Wire Adapter: WLP interface
|
||||
* Driver for the Linux Network stack.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* This implements a very simple network driver for the WLP USB
|
||||
* device that is associated to a UWB (Ultra Wide Band) host.
|
||||
*
|
||||
* This is seen as an interface of a composite device. Once the UWB
|
||||
* host has an association to another WLP capable device, the
|
||||
* networking interface (aka WLP) can start to send packets back and
|
||||
* forth.
|
||||
*
|
||||
* Limitations:
|
||||
*
|
||||
* - Hand cranked; can't ifup the interface until there is an association
|
||||
*
|
||||
* - BW allocation very simplistic [see i1480u_mas_set() and callees].
|
||||
*
|
||||
*
|
||||
* ROADMAP:
|
||||
*
|
||||
* ENTRY POINTS (driver model):
|
||||
*
|
||||
* i1480u_driver_{exit,init}(): initialization of the driver.
|
||||
*
|
||||
* i1480u_probe(): called by the driver code when a device
|
||||
* matching 'i1480u_id_table' is connected.
|
||||
*
|
||||
* This allocs a netdev instance, inits with
|
||||
* i1480u_add(), then registers_netdev().
|
||||
* i1480u_init()
|
||||
* i1480u_add()
|
||||
*
|
||||
* i1480u_disconnect(): device has been disconnected/module
|
||||
* is being removed.
|
||||
* i1480u_rm()
|
||||
*/
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/etherdevice.h>
|
||||
|
||||
#include "i1480u-wlp.h"
|
||||
|
||||
|
||||
|
||||
static inline
|
||||
void i1480u_init(struct i1480u *i1480u)
|
||||
{
|
||||
/* nothing so far... doesn't it suck? */
|
||||
spin_lock_init(&i1480u->lock);
|
||||
INIT_LIST_HEAD(&i1480u->tx_list);
|
||||
spin_lock_init(&i1480u->tx_list_lock);
|
||||
wlp_options_init(&i1480u->options);
|
||||
edc_init(&i1480u->tx_errors);
|
||||
edc_init(&i1480u->rx_errors);
|
||||
#ifdef i1480u_FLOW_CONTROL
|
||||
edc_init(&i1480u->notif_edc);
|
||||
#endif
|
||||
stats_init(&i1480u->lqe_stats);
|
||||
stats_init(&i1480u->rssi_stats);
|
||||
wlp_init(&i1480u->wlp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill WLP device information structure
|
||||
*
|
||||
* The structure will contain a few character arrays, each ending with a
|
||||
* null terminated string. Each string has to fit (excluding terminating
|
||||
* character) into a specified range obtained from the WLP substack.
|
||||
*
|
||||
* It is still not clear exactly how this device information should be
|
||||
* obtained. Until we find out we use the USB device descriptor as backup, some
|
||||
* information elements have intuitive mappings, other not.
|
||||
*/
|
||||
static
|
||||
void i1480u_fill_device_info(struct wlp *wlp, struct wlp_device_info *dev_info)
|
||||
{
|
||||
struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp);
|
||||
struct usb_device *usb_dev = i1480u->usb_dev;
|
||||
/* Treat device name and model name the same */
|
||||
if (usb_dev->descriptor.iProduct) {
|
||||
usb_string(usb_dev, usb_dev->descriptor.iProduct,
|
||||
dev_info->name, sizeof(dev_info->name));
|
||||
usb_string(usb_dev, usb_dev->descriptor.iProduct,
|
||||
dev_info->model_name, sizeof(dev_info->model_name));
|
||||
}
|
||||
if (usb_dev->descriptor.iManufacturer)
|
||||
usb_string(usb_dev, usb_dev->descriptor.iManufacturer,
|
||||
dev_info->manufacturer,
|
||||
sizeof(dev_info->manufacturer));
|
||||
scnprintf(dev_info->model_nr, sizeof(dev_info->model_nr), "%04x",
|
||||
__le16_to_cpu(usb_dev->descriptor.bcdDevice));
|
||||
if (usb_dev->descriptor.iSerialNumber)
|
||||
usb_string(usb_dev, usb_dev->descriptor.iSerialNumber,
|
||||
dev_info->serial, sizeof(dev_info->serial));
|
||||
/* FIXME: where should we obtain category? */
|
||||
dev_info->prim_dev_type.category = cpu_to_le16(WLP_DEV_CAT_OTHER);
|
||||
/* FIXME: Complete OUI and OUIsubdiv attributes */
|
||||
}
|
||||
|
||||
#ifdef i1480u_FLOW_CONTROL
|
||||
/**
|
||||
* Callback for the notification endpoint
|
||||
*
|
||||
* This mostly controls the xon/xoff protocol. In case of hard error,
|
||||
* we stop the queue. If not, we always retry.
|
||||
*/
|
||||
static
|
||||
void i1480u_notif_cb(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
struct i1480u *i1480u = urb->context;
|
||||
struct usb_interface *usb_iface = i1480u->usb_iface;
|
||||
struct device *dev = &usb_iface->dev;
|
||||
int result;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0: /* Got valid data, do xon/xoff */
|
||||
switch (i1480u->notif_buffer[0]) {
|
||||
case 'N':
|
||||
dev_err(dev, "XOFF STOPPING queue at %lu\n", jiffies);
|
||||
netif_stop_queue(i1480u->net_dev);
|
||||
break;
|
||||
case 'A':
|
||||
dev_err(dev, "XON STARTING queue at %lu\n", jiffies);
|
||||
netif_start_queue(i1480u->net_dev);
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "NEP: unknown data 0x%02hhx\n",
|
||||
i1480u->notif_buffer[0]);
|
||||
}
|
||||
break;
|
||||
case -ECONNRESET: /* Controlled situation ... */
|
||||
case -ENOENT: /* we killed the URB... */
|
||||
dev_err(dev, "NEP: URB reset/noent %d\n", urb->status);
|
||||
goto error;
|
||||
case -ESHUTDOWN: /* going away! */
|
||||
dev_err(dev, "NEP: URB down %d\n", urb->status);
|
||||
goto error;
|
||||
default: /* Retry unless it gets ugly */
|
||||
if (edc_inc(&i1480u->notif_edc, EDC_MAX_ERRORS,
|
||||
EDC_ERROR_TIMEFRAME)) {
|
||||
dev_err(dev, "NEP: URB max acceptable errors "
|
||||
"exceeded; resetting device\n");
|
||||
goto error_reset;
|
||||
}
|
||||
dev_err(dev, "NEP: URB error %d\n", urb->status);
|
||||
break;
|
||||
}
|
||||
result = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "NEP: Can't resubmit URB: %d; resetting device\n",
|
||||
result);
|
||||
goto error_reset;
|
||||
}
|
||||
return;
|
||||
|
||||
error_reset:
|
||||
wlp_reset_all(&i1480-wlp);
|
||||
error:
|
||||
netif_stop_queue(i1480u->net_dev);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct net_device_ops i1480u_netdev_ops = {
|
||||
.ndo_open = i1480u_open,
|
||||
.ndo_stop = i1480u_stop,
|
||||
.ndo_start_xmit = i1480u_hard_start_xmit,
|
||||
.ndo_tx_timeout = i1480u_tx_timeout,
|
||||
.ndo_set_config = i1480u_set_config,
|
||||
.ndo_change_mtu = i1480u_change_mtu,
|
||||
};
|
||||
|
||||
static
|
||||
int i1480u_add(struct i1480u *i1480u, struct usb_interface *iface)
|
||||
{
|
||||
int result = -ENODEV;
|
||||
struct wlp *wlp = &i1480u->wlp;
|
||||
struct usb_device *usb_dev = interface_to_usbdev(iface);
|
||||
struct net_device *net_dev = i1480u->net_dev;
|
||||
struct uwb_rc *rc;
|
||||
struct uwb_dev *uwb_dev;
|
||||
#ifdef i1480u_FLOW_CONTROL
|
||||
struct usb_endpoint_descriptor *epd;
|
||||
#endif
|
||||
|
||||
i1480u->usb_dev = usb_get_dev(usb_dev);
|
||||
i1480u->usb_iface = iface;
|
||||
rc = uwb_rc_get_by_grandpa(&i1480u->usb_dev->dev);
|
||||
if (rc == NULL) {
|
||||
dev_err(&iface->dev, "Cannot get associated UWB Radio "
|
||||
"Controller\n");
|
||||
goto out;
|
||||
}
|
||||
wlp->xmit_frame = i1480u_xmit_frame;
|
||||
wlp->fill_device_info = i1480u_fill_device_info;
|
||||
wlp->stop_queue = i1480u_stop_queue;
|
||||
wlp->start_queue = i1480u_start_queue;
|
||||
result = wlp_setup(wlp, rc, net_dev);
|
||||
if (result < 0) {
|
||||
dev_err(&iface->dev, "Cannot setup WLP\n");
|
||||
goto error_wlp_setup;
|
||||
}
|
||||
result = 0;
|
||||
ether_setup(net_dev); /* make it an etherdevice */
|
||||
uwb_dev = &rc->uwb_dev;
|
||||
/* FIXME: hookup address change notifications? */
|
||||
|
||||
memcpy(net_dev->dev_addr, uwb_dev->mac_addr.data,
|
||||
sizeof(net_dev->dev_addr));
|
||||
|
||||
net_dev->hard_header_len = sizeof(struct untd_hdr_cmp)
|
||||
+ sizeof(struct wlp_tx_hdr)
|
||||
+ WLP_DATA_HLEN
|
||||
+ ETH_HLEN;
|
||||
net_dev->mtu = 3500;
|
||||
net_dev->tx_queue_len = 20; /* FIXME: maybe use 1000? */
|
||||
|
||||
/* net_dev->flags &= ~IFF_BROADCAST; FIXME: BUG in firmware */
|
||||
/* FIXME: multicast disabled */
|
||||
net_dev->flags &= ~IFF_MULTICAST;
|
||||
net_dev->features &= ~NETIF_F_SG;
|
||||
net_dev->features &= ~NETIF_F_FRAGLIST;
|
||||
/* All NETIF_F_*_CSUM disabled */
|
||||
net_dev->features |= NETIF_F_HIGHDMA;
|
||||
net_dev->watchdog_timeo = 5*HZ; /* FIXME: a better default? */
|
||||
|
||||
net_dev->netdev_ops = &i1480u_netdev_ops;
|
||||
|
||||
#ifdef i1480u_FLOW_CONTROL
|
||||
/* Notification endpoint setup (submitted when we open the device) */
|
||||
i1480u->notif_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (i1480u->notif_urb == NULL) {
|
||||
dev_err(&iface->dev, "Unable to allocate notification URB\n");
|
||||
result = -ENOMEM;
|
||||
goto error_urb_alloc;
|
||||
}
|
||||
epd = &iface->cur_altsetting->endpoint[0].desc;
|
||||
usb_fill_int_urb(i1480u->notif_urb, usb_dev,
|
||||
usb_rcvintpipe(usb_dev, epd->bEndpointAddress),
|
||||
i1480u->notif_buffer, sizeof(i1480u->notif_buffer),
|
||||
i1480u_notif_cb, i1480u, epd->bInterval);
|
||||
|
||||
#endif
|
||||
|
||||
i1480u->tx_inflight.max = i1480u_TX_INFLIGHT_MAX;
|
||||
i1480u->tx_inflight.threshold = i1480u_TX_INFLIGHT_THRESHOLD;
|
||||
i1480u->tx_inflight.restart_ts = jiffies;
|
||||
usb_set_intfdata(iface, i1480u);
|
||||
return result;
|
||||
|
||||
#ifdef i1480u_FLOW_CONTROL
|
||||
error_urb_alloc:
|
||||
#endif
|
||||
wlp_remove(wlp);
|
||||
error_wlp_setup:
|
||||
uwb_rc_put(rc);
|
||||
out:
|
||||
usb_put_dev(i1480u->usb_dev);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void i1480u_rm(struct i1480u *i1480u)
|
||||
{
|
||||
struct uwb_rc *rc = i1480u->wlp.rc;
|
||||
usb_set_intfdata(i1480u->usb_iface, NULL);
|
||||
#ifdef i1480u_FLOW_CONTROL
|
||||
usb_kill_urb(i1480u->notif_urb);
|
||||
usb_free_urb(i1480u->notif_urb);
|
||||
#endif
|
||||
wlp_remove(&i1480u->wlp);
|
||||
uwb_rc_put(rc);
|
||||
usb_put_dev(i1480u->usb_dev);
|
||||
}
|
||||
|
||||
/** Just setup @net_dev's i1480u private data */
|
||||
static void i1480u_netdev_setup(struct net_device *net_dev)
|
||||
{
|
||||
struct i1480u *i1480u = netdev_priv(net_dev);
|
||||
/* Initialize @i1480u */
|
||||
memset(i1480u, 0, sizeof(*i1480u));
|
||||
i1480u_init(i1480u);
|
||||
}
|
||||
|
||||
/**
|
||||
* Probe a i1480u interface and register it
|
||||
*
|
||||
* @iface: USB interface to link to
|
||||
* @id: USB class/subclass/protocol id
|
||||
* @returns: 0 if ok, < 0 errno code on error.
|
||||
*
|
||||
* Does basic housekeeping stuff and then allocs a netdev with space
|
||||
* for the i1480u data. Initializes, registers in i1480u, registers in
|
||||
* netdev, ready to go.
|
||||
*/
|
||||
static int i1480u_probe(struct usb_interface *iface,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
int result;
|
||||
struct net_device *net_dev;
|
||||
struct device *dev = &iface->dev;
|
||||
struct i1480u *i1480u;
|
||||
|
||||
/* Allocate instance [calls i1480u_netdev_setup() on it] */
|
||||
result = -ENOMEM;
|
||||
net_dev = alloc_netdev(sizeof(*i1480u), "wlp%d", i1480u_netdev_setup);
|
||||
if (net_dev == NULL) {
|
||||
dev_err(dev, "no memory for network device instance\n");
|
||||
goto error_alloc_netdev;
|
||||
}
|
||||
SET_NETDEV_DEV(net_dev, dev);
|
||||
i1480u = netdev_priv(net_dev);
|
||||
i1480u->net_dev = net_dev;
|
||||
result = i1480u_add(i1480u, iface); /* Now setup all the wlp stuff */
|
||||
if (result < 0) {
|
||||
dev_err(dev, "cannot add i1480u device: %d\n", result);
|
||||
goto error_i1480u_add;
|
||||
}
|
||||
result = register_netdev(net_dev); /* Okey dokey, bring it up */
|
||||
if (result < 0) {
|
||||
dev_err(dev, "cannot register network device: %d\n", result);
|
||||
goto error_register_netdev;
|
||||
}
|
||||
i1480u_sysfs_setup(i1480u);
|
||||
if (result < 0)
|
||||
goto error_sysfs_init;
|
||||
return 0;
|
||||
|
||||
error_sysfs_init:
|
||||
unregister_netdev(net_dev);
|
||||
error_register_netdev:
|
||||
i1480u_rm(i1480u);
|
||||
error_i1480u_add:
|
||||
free_netdev(net_dev);
|
||||
error_alloc_netdev:
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Disconect a i1480u from the system.
|
||||
*
|
||||
* i1480u_stop() has been called before, so al the rx and tx contexts
|
||||
* have been taken down already. Make sure the queue is stopped,
|
||||
* unregister netdev and i1480u, free and kill.
|
||||
*/
|
||||
static void i1480u_disconnect(struct usb_interface *iface)
|
||||
{
|
||||
struct i1480u *i1480u;
|
||||
struct net_device *net_dev;
|
||||
|
||||
i1480u = usb_get_intfdata(iface);
|
||||
net_dev = i1480u->net_dev;
|
||||
netif_stop_queue(net_dev);
|
||||
#ifdef i1480u_FLOW_CONTROL
|
||||
usb_kill_urb(i1480u->notif_urb);
|
||||
#endif
|
||||
i1480u_sysfs_release(i1480u);
|
||||
unregister_netdev(net_dev);
|
||||
i1480u_rm(i1480u);
|
||||
free_netdev(net_dev);
|
||||
}
|
||||
|
||||
static struct usb_device_id i1480u_id_table[] = {
|
||||
{
|
||||
.match_flags = USB_DEVICE_ID_MATCH_DEVICE \
|
||||
| USB_DEVICE_ID_MATCH_DEV_INFO \
|
||||
| USB_DEVICE_ID_MATCH_INT_INFO,
|
||||
.idVendor = 0x8086,
|
||||
.idProduct = 0x0c3b,
|
||||
.bDeviceClass = 0xef,
|
||||
.bDeviceSubClass = 0x02,
|
||||
.bDeviceProtocol = 0x02,
|
||||
.bInterfaceClass = 0xff,
|
||||
.bInterfaceSubClass = 0xff,
|
||||
.bInterfaceProtocol = 0xff,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, i1480u_id_table);
|
||||
|
||||
static struct usb_driver i1480u_driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.probe = i1480u_probe,
|
||||
.disconnect = i1480u_disconnect,
|
||||
.id_table = i1480u_id_table,
|
||||
};
|
||||
|
||||
static int __init i1480u_driver_init(void)
|
||||
{
|
||||
return usb_register(&i1480u_driver);
|
||||
}
|
||||
module_init(i1480u_driver_init);
|
||||
|
||||
|
||||
static void __exit i1480u_driver_exit(void)
|
||||
{
|
||||
usb_deregister(&i1480u_driver);
|
||||
}
|
||||
module_exit(i1480u_driver_exit);
|
||||
|
||||
MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
|
||||
MODULE_DESCRIPTION("i1480 Wireless UWB Link WLP networking for USB");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,331 +0,0 @@
|
||||
/*
|
||||
* WUSB Wire Adapter: WLP interface
|
||||
* Driver for the Linux Network stack.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* Implementation of the netdevice linkage (except tx and rx related stuff).
|
||||
*
|
||||
* ROADMAP:
|
||||
*
|
||||
* ENTRY POINTS (Net device):
|
||||
*
|
||||
* i1480u_open(): Called when we ifconfig up the interface;
|
||||
* associates to a UWB host controller, reserves
|
||||
* bandwidth (MAS), sets up RX USB URB and starts
|
||||
* the queue.
|
||||
*
|
||||
* i1480u_stop(): Called when we ifconfig down a interface;
|
||||
* reverses _open().
|
||||
*
|
||||
* i1480u_set_config():
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/etherdevice.h>
|
||||
|
||||
#include "i1480u-wlp.h"
|
||||
|
||||
struct i1480u_cmd_set_ip_mas {
|
||||
struct uwb_rccb rccb;
|
||||
struct uwb_dev_addr addr;
|
||||
u8 stream;
|
||||
u8 owner;
|
||||
u8 type; /* enum uwb_drp_type */
|
||||
u8 baMAS[32];
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
static
|
||||
int i1480u_set_ip_mas(
|
||||
struct uwb_rc *rc,
|
||||
const struct uwb_dev_addr *dstaddr,
|
||||
u8 stream, u8 owner, u8 type, unsigned long *mas)
|
||||
{
|
||||
|
||||
int result;
|
||||
struct i1480u_cmd_set_ip_mas *cmd;
|
||||
struct uwb_rc_evt_confirm reply;
|
||||
|
||||
result = -ENOMEM;
|
||||
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
|
||||
if (cmd == NULL)
|
||||
goto error_kzalloc;
|
||||
cmd->rccb.bCommandType = 0xfd;
|
||||
cmd->rccb.wCommand = cpu_to_le16(0x000e);
|
||||
cmd->addr = *dstaddr;
|
||||
cmd->stream = stream;
|
||||
cmd->owner = owner;
|
||||
cmd->type = type;
|
||||
if (mas == NULL)
|
||||
memset(cmd->baMAS, 0x00, sizeof(cmd->baMAS));
|
||||
else
|
||||
memcpy(cmd->baMAS, mas, sizeof(cmd->baMAS));
|
||||
reply.rceb.bEventType = 0xfd;
|
||||
reply.rceb.wEvent = cpu_to_le16(0x000e);
|
||||
result = uwb_rc_cmd(rc, "SET-IP-MAS", &cmd->rccb, sizeof(*cmd),
|
||||
&reply.rceb, sizeof(reply));
|
||||
if (result < 0)
|
||||
goto error_cmd;
|
||||
if (reply.bResultCode != UWB_RC_RES_FAIL) {
|
||||
dev_err(&rc->uwb_dev.dev,
|
||||
"SET-IP-MAS: command execution failed: %d\n",
|
||||
reply.bResultCode);
|
||||
result = -EIO;
|
||||
}
|
||||
error_cmd:
|
||||
kfree(cmd);
|
||||
error_kzalloc:
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Inform a WLP interface of a MAS reservation
|
||||
*
|
||||
* @rc is assumed refcnted.
|
||||
*/
|
||||
/* FIXME: detect if remote device is WLP capable? */
|
||||
static int i1480u_mas_set_dev(struct uwb_dev *uwb_dev, struct uwb_rc *rc,
|
||||
u8 stream, u8 owner, u8 type, unsigned long *mas)
|
||||
{
|
||||
int result = 0;
|
||||
struct device *dev = &rc->uwb_dev.dev;
|
||||
|
||||
result = i1480u_set_ip_mas(rc, &uwb_dev->dev_addr, stream, owner,
|
||||
type, mas);
|
||||
if (result < 0) {
|
||||
char rcaddrbuf[UWB_ADDR_STRSIZE], devaddrbuf[UWB_ADDR_STRSIZE];
|
||||
uwb_dev_addr_print(rcaddrbuf, sizeof(rcaddrbuf),
|
||||
&rc->uwb_dev.dev_addr);
|
||||
uwb_dev_addr_print(devaddrbuf, sizeof(devaddrbuf),
|
||||
&uwb_dev->dev_addr);
|
||||
dev_err(dev, "Set IP MAS (%s to %s) failed: %d\n",
|
||||
rcaddrbuf, devaddrbuf, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by bandwidth allocator when change occurs in reservation.
|
||||
*
|
||||
* @rsv: The reservation that is being established, modified, or
|
||||
* terminated.
|
||||
*
|
||||
* When a reservation is established, modified, or terminated the upper layer
|
||||
* (WLP here) needs set/update the currently available Media Access Slots
|
||||
* that can be use for IP traffic.
|
||||
*
|
||||
* Our action taken during failure depends on how the reservation is being
|
||||
* changed:
|
||||
* - if reservation is being established we do nothing if we cannot set the
|
||||
* new MAS to be used
|
||||
* - if reservation is being terminated we revert back to PCA whether the
|
||||
* SET IP MAS command succeeds or not.
|
||||
*/
|
||||
void i1480u_bw_alloc_cb(struct uwb_rsv *rsv)
|
||||
{
|
||||
int result = 0;
|
||||
struct i1480u *i1480u = rsv->pal_priv;
|
||||
struct device *dev = &i1480u->usb_iface->dev;
|
||||
struct uwb_dev *target_dev = rsv->target.dev;
|
||||
struct uwb_rc *rc = i1480u->wlp.rc;
|
||||
u8 stream = rsv->stream;
|
||||
int type = rsv->type;
|
||||
int is_owner = rsv->owner == &rc->uwb_dev;
|
||||
unsigned long *bmp = rsv->mas.bm;
|
||||
|
||||
dev_err(dev, "WLP callback called - sending set ip mas\n");
|
||||
/*user cannot change options while setting configuration*/
|
||||
mutex_lock(&i1480u->options.mutex);
|
||||
switch (rsv->state) {
|
||||
case UWB_RSV_STATE_T_ACCEPTED:
|
||||
case UWB_RSV_STATE_O_ESTABLISHED:
|
||||
result = i1480u_mas_set_dev(target_dev, rc, stream, is_owner,
|
||||
type, bmp);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "MAS reservation failed: %d\n", result);
|
||||
goto out;
|
||||
}
|
||||
if (is_owner) {
|
||||
wlp_tx_hdr_set_delivery_id_type(&i1480u->options.def_tx_hdr,
|
||||
WLP_DRP | stream);
|
||||
wlp_tx_hdr_set_rts_cts(&i1480u->options.def_tx_hdr, 0);
|
||||
}
|
||||
break;
|
||||
case UWB_RSV_STATE_NONE:
|
||||
/* revert back to PCA */
|
||||
result = i1480u_mas_set_dev(target_dev, rc, stream, is_owner,
|
||||
type, bmp);
|
||||
if (result < 0)
|
||||
dev_err(dev, "MAS reservation failed: %d\n", result);
|
||||
/* Revert to PCA even though SET IP MAS failed. */
|
||||
wlp_tx_hdr_set_delivery_id_type(&i1480u->options.def_tx_hdr,
|
||||
i1480u->options.pca_base_priority);
|
||||
wlp_tx_hdr_set_rts_cts(&i1480u->options.def_tx_hdr, 1);
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "unexpected WLP reservation state: %s (%d).\n",
|
||||
uwb_rsv_state_str(rsv->state), rsv->state);
|
||||
break;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&i1480u->options.mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Called on 'ifconfig up'
|
||||
*/
|
||||
int i1480u_open(struct net_device *net_dev)
|
||||
{
|
||||
int result;
|
||||
struct i1480u *i1480u = netdev_priv(net_dev);
|
||||
struct wlp *wlp = &i1480u->wlp;
|
||||
struct uwb_rc *rc;
|
||||
struct device *dev = &i1480u->usb_iface->dev;
|
||||
|
||||
rc = wlp->rc;
|
||||
result = i1480u_rx_setup(i1480u); /* Alloc RX stuff */
|
||||
if (result < 0)
|
||||
goto error_rx_setup;
|
||||
|
||||
result = uwb_radio_start(&wlp->pal);
|
||||
if (result < 0)
|
||||
goto error_radio_start;
|
||||
|
||||
netif_wake_queue(net_dev);
|
||||
#ifdef i1480u_FLOW_CONTROL
|
||||
result = usb_submit_urb(i1480u->notif_urb, GFP_KERNEL);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "Can't submit notification URB: %d\n", result);
|
||||
goto error_notif_urb_submit;
|
||||
}
|
||||
#endif
|
||||
/* Interface is up with an address, now we can create WSS */
|
||||
result = wlp_wss_setup(net_dev, &wlp->wss);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "Can't create WSS: %d. \n", result);
|
||||
goto error_wss_setup;
|
||||
}
|
||||
return 0;
|
||||
error_wss_setup:
|
||||
#ifdef i1480u_FLOW_CONTROL
|
||||
usb_kill_urb(i1480u->notif_urb);
|
||||
error_notif_urb_submit:
|
||||
#endif
|
||||
uwb_radio_stop(&wlp->pal);
|
||||
error_radio_start:
|
||||
netif_stop_queue(net_dev);
|
||||
i1480u_rx_release(i1480u);
|
||||
error_rx_setup:
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called on 'ifconfig down'
|
||||
*/
|
||||
int i1480u_stop(struct net_device *net_dev)
|
||||
{
|
||||
struct i1480u *i1480u = netdev_priv(net_dev);
|
||||
struct wlp *wlp = &i1480u->wlp;
|
||||
|
||||
BUG_ON(wlp->rc == NULL);
|
||||
wlp_wss_remove(&wlp->wss);
|
||||
netif_carrier_off(net_dev);
|
||||
#ifdef i1480u_FLOW_CONTROL
|
||||
usb_kill_urb(i1480u->notif_urb);
|
||||
#endif
|
||||
netif_stop_queue(net_dev);
|
||||
uwb_radio_stop(&wlp->pal);
|
||||
i1480u_rx_release(i1480u);
|
||||
i1480u_tx_release(i1480u);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Change the interface config--we probably don't have to do anything.
|
||||
*/
|
||||
int i1480u_set_config(struct net_device *net_dev, struct ifmap *map)
|
||||
{
|
||||
int result;
|
||||
struct i1480u *i1480u = netdev_priv(net_dev);
|
||||
BUG_ON(i1480u->wlp.rc == NULL);
|
||||
result = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the MTU of the interface
|
||||
*/
|
||||
int i1480u_change_mtu(struct net_device *net_dev, int mtu)
|
||||
{
|
||||
static union {
|
||||
struct wlp_tx_hdr tx;
|
||||
struct wlp_rx_hdr rx;
|
||||
} i1480u_all_hdrs;
|
||||
|
||||
if (mtu < ETH_HLEN) /* We encap eth frames */
|
||||
return -ERANGE;
|
||||
if (mtu > 4000 - sizeof(i1480u_all_hdrs))
|
||||
return -ERANGE;
|
||||
net_dev->mtu = mtu;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the network queue
|
||||
*
|
||||
* Enable WLP substack to stop network queue. We also set the flow control
|
||||
* threshold at this time to prevent the flow control from restarting the
|
||||
* queue.
|
||||
*
|
||||
* we are loosing the current threshold value here ... FIXME?
|
||||
*/
|
||||
void i1480u_stop_queue(struct wlp *wlp)
|
||||
{
|
||||
struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp);
|
||||
struct net_device *net_dev = i1480u->net_dev;
|
||||
i1480u->tx_inflight.threshold = 0;
|
||||
netif_stop_queue(net_dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the network queue
|
||||
*
|
||||
* Enable WLP substack to start network queue. Also re-enable the flow
|
||||
* control to manage the queue again.
|
||||
*
|
||||
* We re-enable the flow control by storing the default threshold in the
|
||||
* flow control threshold. This means that if the user modified the
|
||||
* threshold before the queue was stopped and restarted that information
|
||||
* will be lost. FIXME?
|
||||
*/
|
||||
void i1480u_start_queue(struct wlp *wlp)
|
||||
{
|
||||
struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp);
|
||||
struct net_device *net_dev = i1480u->net_dev;
|
||||
i1480u->tx_inflight.threshold = i1480u_TX_INFLIGHT_THRESHOLD;
|
||||
netif_start_queue(net_dev);
|
||||
}
|
@ -1,474 +0,0 @@
|
||||
/*
|
||||
* WUSB Wire Adapter: WLP interface
|
||||
* Driver for the Linux Network stack.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
* i1480u's RX handling is simple. i1480u will send the received
|
||||
* network packets broken up in fragments; 1 to N fragments make a
|
||||
* packet, we assemble them together and deliver the packet with netif_rx().
|
||||
*
|
||||
* Beacuse each USB transfer is a *single* fragment (except when the
|
||||
* transfer contains a first fragment), each URB called thus
|
||||
* back contains one or two fragments. So we queue N URBs, each with its own
|
||||
* fragment buffer. When a URB is done, we process it (adding to the
|
||||
* current skb from the fragment buffer until complete). Once
|
||||
* processed, we requeue the URB. There is always a bunch of URBs
|
||||
* ready to take data, so the intergap should be minimal.
|
||||
*
|
||||
* An URB's transfer buffer is the data field of a socket buffer. This
|
||||
* reduces copying as data can be passed directly to network layer. If a
|
||||
* complete packet or 1st fragment is received the URB's transfer buffer is
|
||||
* taken away from it and used to send data to the network layer. In this
|
||||
* case a new transfer buffer is allocated to the URB before being requeued.
|
||||
* If a "NEXT" or "LAST" fragment is received, the fragment contents is
|
||||
* appended to the RX packet under construction and the transfer buffer
|
||||
* is reused. To be able to use this buffer to assemble complete packets
|
||||
* we set each buffer's size to that of the MAX ethernet packet that can
|
||||
* be received. There is thus room for improvement in memory usage.
|
||||
*
|
||||
* When the max tx fragment size increases, we should be able to read
|
||||
* data into the skbs directly with very simple code.
|
||||
*
|
||||
* ROADMAP:
|
||||
*
|
||||
* ENTRY POINTS:
|
||||
*
|
||||
* i1480u_rx_setup(): setup RX context [from i1480u_open()]
|
||||
*
|
||||
* i1480u_rx_release(): release RX context [from i1480u_stop()]
|
||||
*
|
||||
* i1480u_rx_cb(): called when the RX USB URB receives a
|
||||
* packet. It removes the header and pushes it up
|
||||
* the Linux netdev stack with netif_rx().
|
||||
*
|
||||
* i1480u_rx_buffer()
|
||||
* i1480u_drop() and i1480u_fix()
|
||||
* i1480u_skb_deliver
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include "i1480u-wlp.h"
|
||||
|
||||
/*
|
||||
* Setup the RX context
|
||||
*
|
||||
* Each URB is provided with a transfer_buffer that is the data field
|
||||
* of a new socket buffer.
|
||||
*/
|
||||
int i1480u_rx_setup(struct i1480u *i1480u)
|
||||
{
|
||||
int result, cnt;
|
||||
struct device *dev = &i1480u->usb_iface->dev;
|
||||
struct net_device *net_dev = i1480u->net_dev;
|
||||
struct usb_endpoint_descriptor *epd;
|
||||
struct sk_buff *skb;
|
||||
|
||||
/* Alloc RX stuff */
|
||||
i1480u->rx_skb = NULL; /* not in process of receiving packet */
|
||||
result = -ENOMEM;
|
||||
epd = &i1480u->usb_iface->cur_altsetting->endpoint[1].desc;
|
||||
for (cnt = 0; cnt < i1480u_RX_BUFS; cnt++) {
|
||||
struct i1480u_rx_buf *rx_buf = &i1480u->rx_buf[cnt];
|
||||
rx_buf->i1480u = i1480u;
|
||||
skb = dev_alloc_skb(i1480u_MAX_RX_PKT_SIZE);
|
||||
if (!skb) {
|
||||
dev_err(dev,
|
||||
"RX: cannot allocate RX buffer %d\n", cnt);
|
||||
result = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
skb->dev = net_dev;
|
||||
skb->ip_summed = CHECKSUM_NONE;
|
||||
skb_reserve(skb, 2);
|
||||
rx_buf->data = skb;
|
||||
rx_buf->urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (unlikely(rx_buf->urb == NULL)) {
|
||||
dev_err(dev, "RX: cannot allocate URB %d\n", cnt);
|
||||
result = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
usb_fill_bulk_urb(rx_buf->urb, i1480u->usb_dev,
|
||||
usb_rcvbulkpipe(i1480u->usb_dev, epd->bEndpointAddress),
|
||||
rx_buf->data->data, i1480u_MAX_RX_PKT_SIZE - 2,
|
||||
i1480u_rx_cb, rx_buf);
|
||||
result = usb_submit_urb(rx_buf->urb, GFP_NOIO);
|
||||
if (unlikely(result < 0)) {
|
||||
dev_err(dev, "RX: cannot submit URB %d: %d\n",
|
||||
cnt, result);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
error:
|
||||
i1480u_rx_release(i1480u);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* Release resources associated to the rx context */
|
||||
void i1480u_rx_release(struct i1480u *i1480u)
|
||||
{
|
||||
int cnt;
|
||||
for (cnt = 0; cnt < i1480u_RX_BUFS; cnt++) {
|
||||
if (i1480u->rx_buf[cnt].data)
|
||||
dev_kfree_skb(i1480u->rx_buf[cnt].data);
|
||||
if (i1480u->rx_buf[cnt].urb) {
|
||||
usb_kill_urb(i1480u->rx_buf[cnt].urb);
|
||||
usb_free_urb(i1480u->rx_buf[cnt].urb);
|
||||
}
|
||||
}
|
||||
if (i1480u->rx_skb != NULL)
|
||||
dev_kfree_skb(i1480u->rx_skb);
|
||||
}
|
||||
|
||||
static
|
||||
void i1480u_rx_unlink_urbs(struct i1480u *i1480u)
|
||||
{
|
||||
int cnt;
|
||||
for (cnt = 0; cnt < i1480u_RX_BUFS; cnt++) {
|
||||
if (i1480u->rx_buf[cnt].urb)
|
||||
usb_unlink_urb(i1480u->rx_buf[cnt].urb);
|
||||
}
|
||||
}
|
||||
|
||||
/* Fix an out-of-sequence packet */
|
||||
#define i1480u_fix(i1480u, msg...) \
|
||||
do { \
|
||||
if (printk_ratelimit()) \
|
||||
dev_err(&i1480u->usb_iface->dev, msg); \
|
||||
dev_kfree_skb_irq(i1480u->rx_skb); \
|
||||
i1480u->rx_skb = NULL; \
|
||||
i1480u->rx_untd_pkt_size = 0; \
|
||||
} while (0)
|
||||
|
||||
|
||||
/* Drop an out-of-sequence packet */
|
||||
#define i1480u_drop(i1480u, msg...) \
|
||||
do { \
|
||||
if (printk_ratelimit()) \
|
||||
dev_err(&i1480u->usb_iface->dev, msg); \
|
||||
i1480u->net_dev->stats.rx_dropped++; \
|
||||
} while (0)
|
||||
|
||||
|
||||
|
||||
|
||||
/* Finalizes setting up the SKB and delivers it
|
||||
*
|
||||
* We first pass the incoming frame to WLP substack for verification. It
|
||||
* may also be a WLP association frame in which case WLP will take over the
|
||||
* processing. If WLP does not take it over it will still verify it, if the
|
||||
* frame is invalid the skb will be freed by WLP and we will not continue
|
||||
* parsing.
|
||||
* */
|
||||
static
|
||||
void i1480u_skb_deliver(struct i1480u *i1480u)
|
||||
{
|
||||
int should_parse;
|
||||
struct net_device *net_dev = i1480u->net_dev;
|
||||
struct device *dev = &i1480u->usb_iface->dev;
|
||||
|
||||
should_parse = wlp_receive_frame(dev, &i1480u->wlp, i1480u->rx_skb,
|
||||
&i1480u->rx_srcaddr);
|
||||
if (!should_parse)
|
||||
goto out;
|
||||
i1480u->rx_skb->protocol = eth_type_trans(i1480u->rx_skb, net_dev);
|
||||
net_dev->stats.rx_packets++;
|
||||
net_dev->stats.rx_bytes += i1480u->rx_untd_pkt_size;
|
||||
|
||||
netif_rx(i1480u->rx_skb); /* deliver */
|
||||
out:
|
||||
i1480u->rx_skb = NULL;
|
||||
i1480u->rx_untd_pkt_size = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Process a buffer of data received from the USB RX endpoint
|
||||
*
|
||||
* First fragment arrives with next or last fragment. All other fragments
|
||||
* arrive alone.
|
||||
*
|
||||
* /me hates long functions.
|
||||
*/
|
||||
static
|
||||
void i1480u_rx_buffer(struct i1480u_rx_buf *rx_buf)
|
||||
{
|
||||
unsigned pkt_completed = 0; /* !0 when we got all pkt fragments */
|
||||
size_t untd_hdr_size, untd_frg_size;
|
||||
size_t i1480u_hdr_size;
|
||||
struct wlp_rx_hdr *i1480u_hdr = NULL;
|
||||
|
||||
struct i1480u *i1480u = rx_buf->i1480u;
|
||||
struct sk_buff *skb = rx_buf->data;
|
||||
int size_left = rx_buf->urb->actual_length;
|
||||
void *ptr = rx_buf->urb->transfer_buffer; /* also rx_buf->data->data */
|
||||
struct untd_hdr *untd_hdr;
|
||||
|
||||
struct net_device *net_dev = i1480u->net_dev;
|
||||
struct device *dev = &i1480u->usb_iface->dev;
|
||||
struct sk_buff *new_skb;
|
||||
|
||||
#if 0
|
||||
dev_fnstart(dev,
|
||||
"(i1480u %p ptr %p size_left %zu)\n", i1480u, ptr, size_left);
|
||||
dev_err(dev, "RX packet, %zu bytes\n", size_left);
|
||||
dump_bytes(dev, ptr, size_left);
|
||||
#endif
|
||||
i1480u_hdr_size = sizeof(struct wlp_rx_hdr);
|
||||
|
||||
while (size_left > 0) {
|
||||
if (pkt_completed) {
|
||||
i1480u_drop(i1480u, "RX: fragment follows completed"
|
||||
"packet in same buffer. Dropping\n");
|
||||
break;
|
||||
}
|
||||
untd_hdr = ptr;
|
||||
if (size_left < sizeof(*untd_hdr)) { /* Check the UNTD header */
|
||||
i1480u_drop(i1480u, "RX: short UNTD header! Dropping\n");
|
||||
goto out;
|
||||
}
|
||||
if (unlikely(untd_hdr_rx_tx(untd_hdr) == 0)) { /* Paranoia: TX set? */
|
||||
i1480u_drop(i1480u, "RX: TX bit set! Dropping\n");
|
||||
goto out;
|
||||
}
|
||||
switch (untd_hdr_type(untd_hdr)) { /* Check the UNTD header type */
|
||||
case i1480u_PKT_FRAG_1ST: {
|
||||
struct untd_hdr_1st *untd_hdr_1st = (void *) untd_hdr;
|
||||
dev_dbg(dev, "1st fragment\n");
|
||||
untd_hdr_size = sizeof(struct untd_hdr_1st);
|
||||
if (i1480u->rx_skb != NULL)
|
||||
i1480u_fix(i1480u, "RX: 1st fragment out of "
|
||||
"sequence! Fixing\n");
|
||||
if (size_left < untd_hdr_size + i1480u_hdr_size) {
|
||||
i1480u_drop(i1480u, "RX: short 1st fragment! "
|
||||
"Dropping\n");
|
||||
goto out;
|
||||
}
|
||||
i1480u->rx_untd_pkt_size = le16_to_cpu(untd_hdr->len)
|
||||
- i1480u_hdr_size;
|
||||
untd_frg_size = le16_to_cpu(untd_hdr_1st->fragment_len);
|
||||
if (size_left < untd_hdr_size + untd_frg_size) {
|
||||
i1480u_drop(i1480u,
|
||||
"RX: short payload! Dropping\n");
|
||||
goto out;
|
||||
}
|
||||
i1480u->rx_skb = skb;
|
||||
i1480u_hdr = (void *) untd_hdr_1st + untd_hdr_size;
|
||||
i1480u->rx_srcaddr = i1480u_hdr->srcaddr;
|
||||
skb_put(i1480u->rx_skb, untd_hdr_size + untd_frg_size);
|
||||
skb_pull(i1480u->rx_skb, untd_hdr_size + i1480u_hdr_size);
|
||||
stats_add_sample(&i1480u->lqe_stats, (s8) i1480u_hdr->LQI - 7);
|
||||
stats_add_sample(&i1480u->rssi_stats, i1480u_hdr->RSSI + 18);
|
||||
rx_buf->data = NULL; /* need to create new buffer */
|
||||
break;
|
||||
}
|
||||
case i1480u_PKT_FRAG_NXT: {
|
||||
dev_dbg(dev, "nxt fragment\n");
|
||||
untd_hdr_size = sizeof(struct untd_hdr_rst);
|
||||
if (i1480u->rx_skb == NULL) {
|
||||
i1480u_drop(i1480u, "RX: next fragment out of "
|
||||
"sequence! Dropping\n");
|
||||
goto out;
|
||||
}
|
||||
if (size_left < untd_hdr_size) {
|
||||
i1480u_drop(i1480u, "RX: short NXT fragment! "
|
||||
"Dropping\n");
|
||||
goto out;
|
||||
}
|
||||
untd_frg_size = le16_to_cpu(untd_hdr->len);
|
||||
if (size_left < untd_hdr_size + untd_frg_size) {
|
||||
i1480u_drop(i1480u,
|
||||
"RX: short payload! Dropping\n");
|
||||
goto out;
|
||||
}
|
||||
memmove(skb_put(i1480u->rx_skb, untd_frg_size),
|
||||
ptr + untd_hdr_size, untd_frg_size);
|
||||
break;
|
||||
}
|
||||
case i1480u_PKT_FRAG_LST: {
|
||||
dev_dbg(dev, "Lst fragment\n");
|
||||
untd_hdr_size = sizeof(struct untd_hdr_rst);
|
||||
if (i1480u->rx_skb == NULL) {
|
||||
i1480u_drop(i1480u, "RX: last fragment out of "
|
||||
"sequence! Dropping\n");
|
||||
goto out;
|
||||
}
|
||||
if (size_left < untd_hdr_size) {
|
||||
i1480u_drop(i1480u, "RX: short LST fragment! "
|
||||
"Dropping\n");
|
||||
goto out;
|
||||
}
|
||||
untd_frg_size = le16_to_cpu(untd_hdr->len);
|
||||
if (size_left < untd_frg_size + untd_hdr_size) {
|
||||
i1480u_drop(i1480u,
|
||||
"RX: short payload! Dropping\n");
|
||||
goto out;
|
||||
}
|
||||
memmove(skb_put(i1480u->rx_skb, untd_frg_size),
|
||||
ptr + untd_hdr_size, untd_frg_size);
|
||||
pkt_completed = 1;
|
||||
break;
|
||||
}
|
||||
case i1480u_PKT_FRAG_CMP: {
|
||||
dev_dbg(dev, "cmp fragment\n");
|
||||
untd_hdr_size = sizeof(struct untd_hdr_cmp);
|
||||
if (i1480u->rx_skb != NULL)
|
||||
i1480u_fix(i1480u, "RX: fix out-of-sequence CMP"
|
||||
" fragment!\n");
|
||||
if (size_left < untd_hdr_size + i1480u_hdr_size) {
|
||||
i1480u_drop(i1480u, "RX: short CMP fragment! "
|
||||
"Dropping\n");
|
||||
goto out;
|
||||
}
|
||||
i1480u->rx_untd_pkt_size = le16_to_cpu(untd_hdr->len);
|
||||
untd_frg_size = i1480u->rx_untd_pkt_size;
|
||||
if (size_left < i1480u->rx_untd_pkt_size + untd_hdr_size) {
|
||||
i1480u_drop(i1480u,
|
||||
"RX: short payload! Dropping\n");
|
||||
goto out;
|
||||
}
|
||||
i1480u->rx_skb = skb;
|
||||
i1480u_hdr = (void *) untd_hdr + untd_hdr_size;
|
||||
i1480u->rx_srcaddr = i1480u_hdr->srcaddr;
|
||||
stats_add_sample(&i1480u->lqe_stats, (s8) i1480u_hdr->LQI - 7);
|
||||
stats_add_sample(&i1480u->rssi_stats, i1480u_hdr->RSSI + 18);
|
||||
skb_put(i1480u->rx_skb, untd_hdr_size + i1480u->rx_untd_pkt_size);
|
||||
skb_pull(i1480u->rx_skb, untd_hdr_size + i1480u_hdr_size);
|
||||
rx_buf->data = NULL; /* for hand off skb to network stack */
|
||||
pkt_completed = 1;
|
||||
i1480u->rx_untd_pkt_size -= i1480u_hdr_size; /* accurate stat */
|
||||
break;
|
||||
}
|
||||
default:
|
||||
i1480u_drop(i1480u, "RX: unknown packet type %u! "
|
||||
"Dropping\n", untd_hdr_type(untd_hdr));
|
||||
goto out;
|
||||
}
|
||||
size_left -= untd_hdr_size + untd_frg_size;
|
||||
if (size_left > 0)
|
||||
ptr += untd_hdr_size + untd_frg_size;
|
||||
}
|
||||
if (pkt_completed)
|
||||
i1480u_skb_deliver(i1480u);
|
||||
out:
|
||||
/* recreate needed RX buffers*/
|
||||
if (rx_buf->data == NULL) {
|
||||
/* buffer is being used to receive packet, create new */
|
||||
new_skb = dev_alloc_skb(i1480u_MAX_RX_PKT_SIZE);
|
||||
if (!new_skb) {
|
||||
if (printk_ratelimit())
|
||||
dev_err(dev,
|
||||
"RX: cannot allocate RX buffer\n");
|
||||
} else {
|
||||
new_skb->dev = net_dev;
|
||||
new_skb->ip_summed = CHECKSUM_NONE;
|
||||
skb_reserve(new_skb, 2);
|
||||
rx_buf->data = new_skb;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Called when an RX URB has finished receiving or has found some kind
|
||||
* of error condition.
|
||||
*
|
||||
* LIMITATIONS:
|
||||
*
|
||||
* - We read USB-transfers, each transfer contains a SINGLE fragment
|
||||
* (can contain a complete packet, or a 1st, next, or last fragment
|
||||
* of a packet).
|
||||
* Looks like a transfer can contain more than one fragment (07/18/06)
|
||||
*
|
||||
* - Each transfer buffer is the size of the maximum packet size (minus
|
||||
* headroom), i1480u_MAX_PKT_SIZE - 2
|
||||
*
|
||||
* - We always read the full USB-transfer, no partials.
|
||||
*
|
||||
* - Each transfer is read directly into a skb. This skb will be used to
|
||||
* send data to the upper layers if it is the first fragment or a complete
|
||||
* packet. In the other cases the data will be copied from the skb to
|
||||
* another skb that is being prepared for the upper layers from a prev
|
||||
* first fragment.
|
||||
*
|
||||
* It is simply too much of a pain. Gosh, there should be a unified
|
||||
* SG infrastructure for *everything* [so that I could declare a SG
|
||||
* buffer, pass it to USB for receiving, append some space to it if
|
||||
* I wish, receive more until I have the whole chunk, adapt
|
||||
* pointers on each fragment to remove hardware headers and then
|
||||
* attach that to an skbuff and netif_rx()].
|
||||
*/
|
||||
void i1480u_rx_cb(struct urb *urb)
|
||||
{
|
||||
int result;
|
||||
int do_parse_buffer = 1;
|
||||
struct i1480u_rx_buf *rx_buf = urb->context;
|
||||
struct i1480u *i1480u = rx_buf->i1480u;
|
||||
struct device *dev = &i1480u->usb_iface->dev;
|
||||
unsigned long flags;
|
||||
u8 rx_buf_idx = rx_buf - i1480u->rx_buf;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
break;
|
||||
case -ECONNRESET: /* Not an error, but a controlled situation; */
|
||||
case -ENOENT: /* (we killed the URB)...so, no broadcast */
|
||||
case -ESHUTDOWN: /* going away! */
|
||||
dev_err(dev, "RX URB[%u]: goind down %d\n",
|
||||
rx_buf_idx, urb->status);
|
||||
goto error;
|
||||
default:
|
||||
dev_err(dev, "RX URB[%u]: unknown status %d\n",
|
||||
rx_buf_idx, urb->status);
|
||||
if (edc_inc(&i1480u->rx_errors, EDC_MAX_ERRORS,
|
||||
EDC_ERROR_TIMEFRAME)) {
|
||||
dev_err(dev, "RX: max acceptable errors exceeded,"
|
||||
" resetting device.\n");
|
||||
i1480u_rx_unlink_urbs(i1480u);
|
||||
wlp_reset_all(&i1480u->wlp);
|
||||
goto error;
|
||||
}
|
||||
do_parse_buffer = 0;
|
||||
break;
|
||||
}
|
||||
spin_lock_irqsave(&i1480u->lock, flags);
|
||||
/* chew the data fragments, extract network packets */
|
||||
if (do_parse_buffer) {
|
||||
i1480u_rx_buffer(rx_buf);
|
||||
if (rx_buf->data) {
|
||||
rx_buf->urb->transfer_buffer = rx_buf->data->data;
|
||||
result = usb_submit_urb(rx_buf->urb, GFP_ATOMIC);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "RX URB[%u]: cannot submit %d\n",
|
||||
rx_buf_idx, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&i1480u->lock, flags);
|
||||
error:
|
||||
return;
|
||||
}
|
||||
|
@ -1,407 +0,0 @@
|
||||
/*
|
||||
* WUSB Wire Adapter: WLP interface
|
||||
* Sysfs interfaces
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include "i1480u-wlp.h"
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @dev: Class device from the net_device; assumed refcnted.
|
||||
*
|
||||
* Yes, I don't lock--we assume it is refcounted and I am getting a
|
||||
* single byte value that is kind of atomic to read.
|
||||
*/
|
||||
ssize_t uwb_phy_rate_show(const struct wlp_options *options, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%u\n",
|
||||
wlp_tx_hdr_phy_rate(&options->def_tx_hdr));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(uwb_phy_rate_show);
|
||||
|
||||
|
||||
ssize_t uwb_phy_rate_store(struct wlp_options *options,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
ssize_t result;
|
||||
unsigned rate;
|
||||
|
||||
result = sscanf(buf, "%u\n", &rate);
|
||||
if (result != 1) {
|
||||
result = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
result = -EINVAL;
|
||||
if (rate >= UWB_PHY_RATE_INVALID)
|
||||
goto out;
|
||||
wlp_tx_hdr_set_phy_rate(&options->def_tx_hdr, rate);
|
||||
result = 0;
|
||||
out:
|
||||
return result < 0 ? result : size;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(uwb_phy_rate_store);
|
||||
|
||||
|
||||
ssize_t uwb_rts_cts_show(const struct wlp_options *options, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%u\n",
|
||||
wlp_tx_hdr_rts_cts(&options->def_tx_hdr));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(uwb_rts_cts_show);
|
||||
|
||||
|
||||
ssize_t uwb_rts_cts_store(struct wlp_options *options,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
ssize_t result;
|
||||
unsigned value;
|
||||
|
||||
result = sscanf(buf, "%u\n", &value);
|
||||
if (result != 1) {
|
||||
result = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
result = -EINVAL;
|
||||
wlp_tx_hdr_set_rts_cts(&options->def_tx_hdr, !!value);
|
||||
result = 0;
|
||||
out:
|
||||
return result < 0 ? result : size;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(uwb_rts_cts_store);
|
||||
|
||||
|
||||
ssize_t uwb_ack_policy_show(const struct wlp_options *options, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%u\n",
|
||||
wlp_tx_hdr_ack_policy(&options->def_tx_hdr));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(uwb_ack_policy_show);
|
||||
|
||||
|
||||
ssize_t uwb_ack_policy_store(struct wlp_options *options,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
ssize_t result;
|
||||
unsigned value;
|
||||
|
||||
result = sscanf(buf, "%u\n", &value);
|
||||
if (result != 1 || value > UWB_ACK_B_REQ) {
|
||||
result = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
wlp_tx_hdr_set_ack_policy(&options->def_tx_hdr, value);
|
||||
result = 0;
|
||||
out:
|
||||
return result < 0 ? result : size;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(uwb_ack_policy_store);
|
||||
|
||||
|
||||
/**
|
||||
* Show the PCA base priority.
|
||||
*
|
||||
* We can access without locking, as the value is (for now) orthogonal
|
||||
* to other values.
|
||||
*/
|
||||
ssize_t uwb_pca_base_priority_show(const struct wlp_options *options,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%u\n",
|
||||
options->pca_base_priority);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(uwb_pca_base_priority_show);
|
||||
|
||||
|
||||
/**
|
||||
* Set the PCA base priority.
|
||||
*
|
||||
* We can access without locking, as the value is (for now) orthogonal
|
||||
* to other values.
|
||||
*/
|
||||
ssize_t uwb_pca_base_priority_store(struct wlp_options *options,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
ssize_t result = -EINVAL;
|
||||
u8 pca_base_priority;
|
||||
|
||||
result = sscanf(buf, "%hhu\n", &pca_base_priority);
|
||||
if (result != 1) {
|
||||
result = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
result = -EINVAL;
|
||||
if (pca_base_priority >= 8)
|
||||
goto out;
|
||||
options->pca_base_priority = pca_base_priority;
|
||||
/* Update TX header if we are currently using PCA. */
|
||||
if (result >= 0 && (wlp_tx_hdr_delivery_id_type(&options->def_tx_hdr) & WLP_DRP) == 0)
|
||||
wlp_tx_hdr_set_delivery_id_type(&options->def_tx_hdr, options->pca_base_priority);
|
||||
result = 0;
|
||||
out:
|
||||
return result < 0 ? result : size;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(uwb_pca_base_priority_store);
|
||||
|
||||
/**
|
||||
* Show current inflight values
|
||||
*
|
||||
* Will print the current MAX and THRESHOLD values for the basic flow
|
||||
* control. In addition it will report how many times the TX queue needed
|
||||
* to be restarted since the last time this query was made.
|
||||
*/
|
||||
static ssize_t wlp_tx_inflight_show(struct i1480u_tx_inflight *inflight,
|
||||
char *buf)
|
||||
{
|
||||
ssize_t result;
|
||||
unsigned long sec_elapsed = (jiffies - inflight->restart_ts)/HZ;
|
||||
unsigned long restart_count = atomic_read(&inflight->restart_count);
|
||||
|
||||
result = scnprintf(buf, PAGE_SIZE, "%lu %lu %d %lu %lu %lu\n"
|
||||
"#read: threshold max inflight_count restarts "
|
||||
"seconds restarts/sec\n"
|
||||
"#write: threshold max\n",
|
||||
inflight->threshold, inflight->max,
|
||||
atomic_read(&inflight->count),
|
||||
restart_count, sec_elapsed,
|
||||
sec_elapsed == 0 ? 0 : restart_count/sec_elapsed);
|
||||
inflight->restart_ts = jiffies;
|
||||
atomic_set(&inflight->restart_count, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
static
|
||||
ssize_t wlp_tx_inflight_store(struct i1480u_tx_inflight *inflight,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
unsigned long in_threshold, in_max;
|
||||
ssize_t result;
|
||||
result = sscanf(buf, "%lu %lu", &in_threshold, &in_max);
|
||||
if (result != 2)
|
||||
return -EINVAL;
|
||||
if (in_max <= in_threshold)
|
||||
return -EINVAL;
|
||||
inflight->max = in_max;
|
||||
inflight->threshold = in_threshold;
|
||||
return size;
|
||||
}
|
||||
/*
|
||||
* Glue (or function adaptors) for accesing info on sysfs
|
||||
*
|
||||
* [we need this indirection because the PCI driver does almost the
|
||||
* same]
|
||||
*
|
||||
* Linux 2.6.21 changed how 'struct netdevice' does attributes (from
|
||||
* having a 'struct class_dev' to having a 'struct device'). That is
|
||||
* quite of a pain.
|
||||
*
|
||||
* So we try to abstract that here. i1480u_SHOW() and i1480u_STORE()
|
||||
* create adaptors for extracting the 'struct i1480u' from a 'struct
|
||||
* dev' and calling a function for doing a sysfs operation (as we have
|
||||
* them factorized already). i1480u_ATTR creates the attribute file
|
||||
* (CLASS_DEVICE_ATTR or DEVICE_ATTR) and i1480u_ATTR_NAME produces a
|
||||
* class_device_attr_NAME or device_attr_NAME (for group registration).
|
||||
*/
|
||||
|
||||
#define i1480u_SHOW(name, fn, param) \
|
||||
static ssize_t i1480u_show_##name(struct device *dev, \
|
||||
struct device_attribute *attr,\
|
||||
char *buf) \
|
||||
{ \
|
||||
struct i1480u *i1480u = netdev_priv(to_net_dev(dev)); \
|
||||
return fn(&i1480u->param, buf); \
|
||||
}
|
||||
|
||||
#define i1480u_STORE(name, fn, param) \
|
||||
static ssize_t i1480u_store_##name(struct device *dev, \
|
||||
struct device_attribute *attr,\
|
||||
const char *buf, size_t size)\
|
||||
{ \
|
||||
struct i1480u *i1480u = netdev_priv(to_net_dev(dev)); \
|
||||
return fn(&i1480u->param, buf, size); \
|
||||
}
|
||||
|
||||
#define i1480u_ATTR(name, perm) static DEVICE_ATTR(name, perm, \
|
||||
i1480u_show_##name,\
|
||||
i1480u_store_##name)
|
||||
|
||||
#define i1480u_ATTR_SHOW(name) static DEVICE_ATTR(name, \
|
||||
S_IRUGO, \
|
||||
i1480u_show_##name, NULL)
|
||||
|
||||
#define i1480u_ATTR_NAME(a) (dev_attr_##a)
|
||||
|
||||
|
||||
/*
|
||||
* Sysfs adaptors
|
||||
*/
|
||||
i1480u_SHOW(uwb_phy_rate, uwb_phy_rate_show, options);
|
||||
i1480u_STORE(uwb_phy_rate, uwb_phy_rate_store, options);
|
||||
i1480u_ATTR(uwb_phy_rate, S_IRUGO | S_IWUSR);
|
||||
|
||||
i1480u_SHOW(uwb_rts_cts, uwb_rts_cts_show, options);
|
||||
i1480u_STORE(uwb_rts_cts, uwb_rts_cts_store, options);
|
||||
i1480u_ATTR(uwb_rts_cts, S_IRUGO | S_IWUSR);
|
||||
|
||||
i1480u_SHOW(uwb_ack_policy, uwb_ack_policy_show, options);
|
||||
i1480u_STORE(uwb_ack_policy, uwb_ack_policy_store, options);
|
||||
i1480u_ATTR(uwb_ack_policy, S_IRUGO | S_IWUSR);
|
||||
|
||||
i1480u_SHOW(uwb_pca_base_priority, uwb_pca_base_priority_show, options);
|
||||
i1480u_STORE(uwb_pca_base_priority, uwb_pca_base_priority_store, options);
|
||||
i1480u_ATTR(uwb_pca_base_priority, S_IRUGO | S_IWUSR);
|
||||
|
||||
i1480u_SHOW(wlp_eda, wlp_eda_show, wlp);
|
||||
i1480u_STORE(wlp_eda, wlp_eda_store, wlp);
|
||||
i1480u_ATTR(wlp_eda, S_IRUGO | S_IWUSR);
|
||||
|
||||
i1480u_SHOW(wlp_uuid, wlp_uuid_show, wlp);
|
||||
i1480u_STORE(wlp_uuid, wlp_uuid_store, wlp);
|
||||
i1480u_ATTR(wlp_uuid, S_IRUGO | S_IWUSR);
|
||||
|
||||
i1480u_SHOW(wlp_dev_name, wlp_dev_name_show, wlp);
|
||||
i1480u_STORE(wlp_dev_name, wlp_dev_name_store, wlp);
|
||||
i1480u_ATTR(wlp_dev_name, S_IRUGO | S_IWUSR);
|
||||
|
||||
i1480u_SHOW(wlp_dev_manufacturer, wlp_dev_manufacturer_show, wlp);
|
||||
i1480u_STORE(wlp_dev_manufacturer, wlp_dev_manufacturer_store, wlp);
|
||||
i1480u_ATTR(wlp_dev_manufacturer, S_IRUGO | S_IWUSR);
|
||||
|
||||
i1480u_SHOW(wlp_dev_model_name, wlp_dev_model_name_show, wlp);
|
||||
i1480u_STORE(wlp_dev_model_name, wlp_dev_model_name_store, wlp);
|
||||
i1480u_ATTR(wlp_dev_model_name, S_IRUGO | S_IWUSR);
|
||||
|
||||
i1480u_SHOW(wlp_dev_model_nr, wlp_dev_model_nr_show, wlp);
|
||||
i1480u_STORE(wlp_dev_model_nr, wlp_dev_model_nr_store, wlp);
|
||||
i1480u_ATTR(wlp_dev_model_nr, S_IRUGO | S_IWUSR);
|
||||
|
||||
i1480u_SHOW(wlp_dev_serial, wlp_dev_serial_show, wlp);
|
||||
i1480u_STORE(wlp_dev_serial, wlp_dev_serial_store, wlp);
|
||||
i1480u_ATTR(wlp_dev_serial, S_IRUGO | S_IWUSR);
|
||||
|
||||
i1480u_SHOW(wlp_dev_prim_category, wlp_dev_prim_category_show, wlp);
|
||||
i1480u_STORE(wlp_dev_prim_category, wlp_dev_prim_category_store, wlp);
|
||||
i1480u_ATTR(wlp_dev_prim_category, S_IRUGO | S_IWUSR);
|
||||
|
||||
i1480u_SHOW(wlp_dev_prim_OUI, wlp_dev_prim_OUI_show, wlp);
|
||||
i1480u_STORE(wlp_dev_prim_OUI, wlp_dev_prim_OUI_store, wlp);
|
||||
i1480u_ATTR(wlp_dev_prim_OUI, S_IRUGO | S_IWUSR);
|
||||
|
||||
i1480u_SHOW(wlp_dev_prim_OUI_sub, wlp_dev_prim_OUI_sub_show, wlp);
|
||||
i1480u_STORE(wlp_dev_prim_OUI_sub, wlp_dev_prim_OUI_sub_store, wlp);
|
||||
i1480u_ATTR(wlp_dev_prim_OUI_sub, S_IRUGO | S_IWUSR);
|
||||
|
||||
i1480u_SHOW(wlp_dev_prim_subcat, wlp_dev_prim_subcat_show, wlp);
|
||||
i1480u_STORE(wlp_dev_prim_subcat, wlp_dev_prim_subcat_store, wlp);
|
||||
i1480u_ATTR(wlp_dev_prim_subcat, S_IRUGO | S_IWUSR);
|
||||
|
||||
i1480u_SHOW(wlp_neighborhood, wlp_neighborhood_show, wlp);
|
||||
i1480u_ATTR_SHOW(wlp_neighborhood);
|
||||
|
||||
i1480u_SHOW(wss_activate, wlp_wss_activate_show, wlp.wss);
|
||||
i1480u_STORE(wss_activate, wlp_wss_activate_store, wlp.wss);
|
||||
i1480u_ATTR(wss_activate, S_IRUGO | S_IWUSR);
|
||||
|
||||
/*
|
||||
* Show the (min, max, avg) Line Quality Estimate (LQE, in dB) as over
|
||||
* the last 256 received WLP frames (ECMA-368 13.3).
|
||||
*
|
||||
* [the -7dB that have to be substracted from the LQI to make the LQE
|
||||
* are already taken into account].
|
||||
*/
|
||||
i1480u_SHOW(wlp_lqe, stats_show, lqe_stats);
|
||||
i1480u_STORE(wlp_lqe, stats_store, lqe_stats);
|
||||
i1480u_ATTR(wlp_lqe, S_IRUGO | S_IWUSR);
|
||||
|
||||
/*
|
||||
* Show the Receive Signal Strength Indicator averaged over all the
|
||||
* received WLP frames (ECMA-368 13.3). Still is not clear what
|
||||
* this value is, but is kind of a percentage of the signal strength
|
||||
* at the antenna.
|
||||
*/
|
||||
i1480u_SHOW(wlp_rssi, stats_show, rssi_stats);
|
||||
i1480u_STORE(wlp_rssi, stats_store, rssi_stats);
|
||||
i1480u_ATTR(wlp_rssi, S_IRUGO | S_IWUSR);
|
||||
|
||||
/**
|
||||
* We maintain a basic flow control counter. "count" how many TX URBs are
|
||||
* outstanding. Only allow "max"
|
||||
* TX URBs to be outstanding. If this value is reached the queue will be
|
||||
* stopped. The queue will be restarted when there are
|
||||
* "threshold" URBs outstanding.
|
||||
*/
|
||||
i1480u_SHOW(wlp_tx_inflight, wlp_tx_inflight_show, tx_inflight);
|
||||
i1480u_STORE(wlp_tx_inflight, wlp_tx_inflight_store, tx_inflight);
|
||||
i1480u_ATTR(wlp_tx_inflight, S_IRUGO | S_IWUSR);
|
||||
|
||||
static struct attribute *i1480u_attrs[] = {
|
||||
&i1480u_ATTR_NAME(uwb_phy_rate).attr,
|
||||
&i1480u_ATTR_NAME(uwb_rts_cts).attr,
|
||||
&i1480u_ATTR_NAME(uwb_ack_policy).attr,
|
||||
&i1480u_ATTR_NAME(uwb_pca_base_priority).attr,
|
||||
&i1480u_ATTR_NAME(wlp_lqe).attr,
|
||||
&i1480u_ATTR_NAME(wlp_rssi).attr,
|
||||
&i1480u_ATTR_NAME(wlp_eda).attr,
|
||||
&i1480u_ATTR_NAME(wlp_uuid).attr,
|
||||
&i1480u_ATTR_NAME(wlp_dev_name).attr,
|
||||
&i1480u_ATTR_NAME(wlp_dev_manufacturer).attr,
|
||||
&i1480u_ATTR_NAME(wlp_dev_model_name).attr,
|
||||
&i1480u_ATTR_NAME(wlp_dev_model_nr).attr,
|
||||
&i1480u_ATTR_NAME(wlp_dev_serial).attr,
|
||||
&i1480u_ATTR_NAME(wlp_dev_prim_category).attr,
|
||||
&i1480u_ATTR_NAME(wlp_dev_prim_OUI).attr,
|
||||
&i1480u_ATTR_NAME(wlp_dev_prim_OUI_sub).attr,
|
||||
&i1480u_ATTR_NAME(wlp_dev_prim_subcat).attr,
|
||||
&i1480u_ATTR_NAME(wlp_neighborhood).attr,
|
||||
&i1480u_ATTR_NAME(wss_activate).attr,
|
||||
&i1480u_ATTR_NAME(wlp_tx_inflight).attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group i1480u_attr_group = {
|
||||
.name = NULL, /* we want them in the same directory */
|
||||
.attrs = i1480u_attrs,
|
||||
};
|
||||
|
||||
int i1480u_sysfs_setup(struct i1480u *i1480u)
|
||||
{
|
||||
int result;
|
||||
struct device *dev = &i1480u->usb_iface->dev;
|
||||
result = sysfs_create_group(&i1480u->net_dev->dev.kobj,
|
||||
&i1480u_attr_group);
|
||||
if (result < 0)
|
||||
dev_err(dev, "cannot initialize sysfs attributes: %d\n",
|
||||
result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void i1480u_sysfs_release(struct i1480u *i1480u)
|
||||
{
|
||||
sysfs_remove_group(&i1480u->net_dev->dev.kobj,
|
||||
&i1480u_attr_group);
|
||||
}
|
@ -1,584 +0,0 @@
|
||||
/*
|
||||
* WUSB Wire Adapter: WLP interface
|
||||
* Deal with TX (massaging data to transmit, handling it)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
* Transmission engine. Get an skb, create from that a WLP transmit
|
||||
* context, add a WLP TX header (which we keep prefilled in the
|
||||
* device's instance), fill out the target-specific fields and
|
||||
* fire it.
|
||||
*
|
||||
* ROADMAP:
|
||||
*
|
||||
* Entry points:
|
||||
*
|
||||
* i1480u_tx_release(): called by i1480u_disconnect() to release
|
||||
* pending tx contexts.
|
||||
*
|
||||
* i1480u_tx_cb(): callback for TX contexts (USB URBs)
|
||||
* i1480u_tx_destroy():
|
||||
*
|
||||
* i1480u_tx_timeout(): called for timeout handling from the
|
||||
* network stack.
|
||||
*
|
||||
* i1480u_hard_start_xmit(): called for transmitting an skb from
|
||||
* the network stack. Will interact with WLP
|
||||
* substack to verify and prepare frame.
|
||||
* i1480u_xmit_frame(): actual transmission on hardware
|
||||
*
|
||||
* i1480u_tx_create() Creates TX context
|
||||
* i1480u_tx_create_1() For packets in 1 fragment
|
||||
* i1480u_tx_create_n() For packets in >1 fragments
|
||||
*
|
||||
* TODO:
|
||||
*
|
||||
* - FIXME: rewrite using usb_sg_*(), add asynch support to
|
||||
* usb_sg_*(). It might not make too much sense as most of
|
||||
* the times the MTU will be smaller than one page...
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include "i1480u-wlp.h"
|
||||
|
||||
enum {
|
||||
/* This is only for Next and Last TX packets */
|
||||
i1480u_MAX_PL_SIZE = i1480u_MAX_FRG_SIZE
|
||||
- sizeof(struct untd_hdr_rst),
|
||||
};
|
||||
|
||||
/* Free resources allocated to a i1480u tx context. */
|
||||
static
|
||||
void i1480u_tx_free(struct i1480u_tx *wtx)
|
||||
{
|
||||
kfree(wtx->buf);
|
||||
if (wtx->skb)
|
||||
dev_kfree_skb_irq(wtx->skb);
|
||||
usb_free_urb(wtx->urb);
|
||||
kfree(wtx);
|
||||
}
|
||||
|
||||
static
|
||||
void i1480u_tx_destroy(struct i1480u *i1480u, struct i1480u_tx *wtx)
|
||||
{
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(&i1480u->tx_list_lock, flags); /* not active any more */
|
||||
list_del(&wtx->list_node);
|
||||
i1480u_tx_free(wtx);
|
||||
spin_unlock_irqrestore(&i1480u->tx_list_lock, flags);
|
||||
}
|
||||
|
||||
static
|
||||
void i1480u_tx_unlink_urbs(struct i1480u *i1480u)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct i1480u_tx *wtx, *next;
|
||||
|
||||
spin_lock_irqsave(&i1480u->tx_list_lock, flags);
|
||||
list_for_each_entry_safe(wtx, next, &i1480u->tx_list, list_node) {
|
||||
usb_unlink_urb(wtx->urb);
|
||||
}
|
||||
spin_unlock_irqrestore(&i1480u->tx_list_lock, flags);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Callback for a completed tx USB URB.
|
||||
*
|
||||
* TODO:
|
||||
*
|
||||
* - FIXME: recover errors more gracefully
|
||||
* - FIXME: handle NAKs (I dont think they come here) for flow ctl
|
||||
*/
|
||||
static
|
||||
void i1480u_tx_cb(struct urb *urb)
|
||||
{
|
||||
struct i1480u_tx *wtx = urb->context;
|
||||
struct i1480u *i1480u = wtx->i1480u;
|
||||
struct net_device *net_dev = i1480u->net_dev;
|
||||
struct device *dev = &i1480u->usb_iface->dev;
|
||||
unsigned long flags;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
spin_lock_irqsave(&i1480u->lock, flags);
|
||||
net_dev->stats.tx_packets++;
|
||||
net_dev->stats.tx_bytes += urb->actual_length;
|
||||
spin_unlock_irqrestore(&i1480u->lock, flags);
|
||||
break;
|
||||
case -ECONNRESET: /* Not an error, but a controlled situation; */
|
||||
case -ENOENT: /* (we killed the URB)...so, no broadcast */
|
||||
dev_dbg(dev, "notif endp: reset/noent %d\n", urb->status);
|
||||
netif_stop_queue(net_dev);
|
||||
break;
|
||||
case -ESHUTDOWN: /* going away! */
|
||||
dev_dbg(dev, "notif endp: down %d\n", urb->status);
|
||||
netif_stop_queue(net_dev);
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "TX: unknown URB status %d\n", urb->status);
|
||||
if (edc_inc(&i1480u->tx_errors, EDC_MAX_ERRORS,
|
||||
EDC_ERROR_TIMEFRAME)) {
|
||||
dev_err(dev, "TX: max acceptable errors exceeded."
|
||||
"Reset device.\n");
|
||||
netif_stop_queue(net_dev);
|
||||
i1480u_tx_unlink_urbs(i1480u);
|
||||
wlp_reset_all(&i1480u->wlp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
i1480u_tx_destroy(i1480u, wtx);
|
||||
if (atomic_dec_return(&i1480u->tx_inflight.count)
|
||||
<= i1480u->tx_inflight.threshold
|
||||
&& netif_queue_stopped(net_dev)
|
||||
&& i1480u->tx_inflight.threshold != 0) {
|
||||
netif_start_queue(net_dev);
|
||||
atomic_inc(&i1480u->tx_inflight.restart_count);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Given a buffer that doesn't fit in a single fragment, create an
|
||||
* scatter/gather structure for delivery to the USB pipe.
|
||||
*
|
||||
* Implements functionality of i1480u_tx_create().
|
||||
*
|
||||
* @wtx: tx descriptor
|
||||
* @skb: skb to send
|
||||
* @gfp_mask: gfp allocation mask
|
||||
* @returns: Pointer to @wtx if ok, NULL on error.
|
||||
*
|
||||
* Sorry, TOO LONG a function, but breaking it up is kind of hard
|
||||
*
|
||||
* This will break the buffer in chunks smaller than
|
||||
* i1480u_MAX_FRG_SIZE (including the header) and add proper headers
|
||||
* to each:
|
||||
*
|
||||
* 1st header \
|
||||
* i1480 tx header | fragment 1
|
||||
* fragment data /
|
||||
* nxt header \ fragment 2
|
||||
* fragment data /
|
||||
* ..
|
||||
* ..
|
||||
* last header \ fragment 3
|
||||
* last fragment data /
|
||||
*
|
||||
* This does not fill the i1480 TX header, it is left up to the
|
||||
* caller to do that; you can get it from @wtx->wlp_tx_hdr.
|
||||
*
|
||||
* This function consumes the skb unless there is an error.
|
||||
*/
|
||||
static
|
||||
int i1480u_tx_create_n(struct i1480u_tx *wtx, struct sk_buff *skb,
|
||||
gfp_t gfp_mask)
|
||||
{
|
||||
int result;
|
||||
void *pl;
|
||||
size_t pl_size;
|
||||
|
||||
void *pl_itr, *buf_itr;
|
||||
size_t pl_size_left, frgs, pl_size_1st, frg_pl_size = 0;
|
||||
struct untd_hdr_1st *untd_hdr_1st;
|
||||
struct wlp_tx_hdr *wlp_tx_hdr;
|
||||
struct untd_hdr_rst *untd_hdr_rst;
|
||||
|
||||
wtx->skb = NULL;
|
||||
pl = skb->data;
|
||||
pl_itr = pl;
|
||||
pl_size = skb->len;
|
||||
pl_size_left = pl_size; /* payload size */
|
||||
/* First fragment; fits as much as i1480u_MAX_FRG_SIZE minus
|
||||
* the headers */
|
||||
pl_size_1st = i1480u_MAX_FRG_SIZE
|
||||
- sizeof(struct untd_hdr_1st) - sizeof(struct wlp_tx_hdr);
|
||||
BUG_ON(pl_size_1st > pl_size);
|
||||
pl_size_left -= pl_size_1st;
|
||||
/* The rest have an smaller header (no i1480 TX header). We
|
||||
* need to break up the payload in blocks smaller than
|
||||
* i1480u_MAX_PL_SIZE (payload excluding header). */
|
||||
frgs = (pl_size_left + i1480u_MAX_PL_SIZE - 1) / i1480u_MAX_PL_SIZE;
|
||||
/* Allocate space for the new buffer. In this new buffer we'll
|
||||
* place the headers followed by the data fragment, headers,
|
||||
* data fragments, etc..
|
||||
*/
|
||||
result = -ENOMEM;
|
||||
wtx->buf_size = sizeof(*untd_hdr_1st)
|
||||
+ sizeof(*wlp_tx_hdr)
|
||||
+ frgs * sizeof(*untd_hdr_rst)
|
||||
+ pl_size;
|
||||
wtx->buf = kmalloc(wtx->buf_size, gfp_mask);
|
||||
if (wtx->buf == NULL)
|
||||
goto error_buf_alloc;
|
||||
|
||||
buf_itr = wtx->buf; /* We got the space, let's fill it up */
|
||||
/* Fill 1st fragment */
|
||||
untd_hdr_1st = buf_itr;
|
||||
buf_itr += sizeof(*untd_hdr_1st);
|
||||
untd_hdr_set_type(&untd_hdr_1st->hdr, i1480u_PKT_FRAG_1ST);
|
||||
untd_hdr_set_rx_tx(&untd_hdr_1st->hdr, 0);
|
||||
untd_hdr_1st->hdr.len = cpu_to_le16(pl_size + sizeof(*wlp_tx_hdr));
|
||||
untd_hdr_1st->fragment_len =
|
||||
cpu_to_le16(pl_size_1st + sizeof(*wlp_tx_hdr));
|
||||
memset(untd_hdr_1st->padding, 0, sizeof(untd_hdr_1st->padding));
|
||||
/* Set up i1480 header info */
|
||||
wlp_tx_hdr = wtx->wlp_tx_hdr = buf_itr;
|
||||
buf_itr += sizeof(*wlp_tx_hdr);
|
||||
/* Copy the first fragment */
|
||||
memcpy(buf_itr, pl_itr, pl_size_1st);
|
||||
pl_itr += pl_size_1st;
|
||||
buf_itr += pl_size_1st;
|
||||
|
||||
/* Now do each remaining fragment */
|
||||
result = -EINVAL;
|
||||
while (pl_size_left > 0) {
|
||||
if (buf_itr + sizeof(*untd_hdr_rst) - wtx->buf
|
||||
> wtx->buf_size) {
|
||||
printk(KERN_ERR "BUG: no space for header\n");
|
||||
goto error_bug;
|
||||
}
|
||||
untd_hdr_rst = buf_itr;
|
||||
buf_itr += sizeof(*untd_hdr_rst);
|
||||
if (pl_size_left > i1480u_MAX_PL_SIZE) {
|
||||
frg_pl_size = i1480u_MAX_PL_SIZE;
|
||||
untd_hdr_set_type(&untd_hdr_rst->hdr, i1480u_PKT_FRAG_NXT);
|
||||
} else {
|
||||
frg_pl_size = pl_size_left;
|
||||
untd_hdr_set_type(&untd_hdr_rst->hdr, i1480u_PKT_FRAG_LST);
|
||||
}
|
||||
untd_hdr_set_rx_tx(&untd_hdr_rst->hdr, 0);
|
||||
untd_hdr_rst->hdr.len = cpu_to_le16(frg_pl_size);
|
||||
untd_hdr_rst->padding = 0;
|
||||
if (buf_itr + frg_pl_size - wtx->buf
|
||||
> wtx->buf_size) {
|
||||
printk(KERN_ERR "BUG: no space for payload\n");
|
||||
goto error_bug;
|
||||
}
|
||||
memcpy(buf_itr, pl_itr, frg_pl_size);
|
||||
buf_itr += frg_pl_size;
|
||||
pl_itr += frg_pl_size;
|
||||
pl_size_left -= frg_pl_size;
|
||||
}
|
||||
dev_kfree_skb_irq(skb);
|
||||
return 0;
|
||||
|
||||
error_bug:
|
||||
printk(KERN_ERR
|
||||
"BUG: skb %u bytes\n"
|
||||
"BUG: frg_pl_size %zd i1480u_MAX_FRG_SIZE %u\n"
|
||||
"BUG: buf_itr %zu buf_size %zu pl_size_left %zu\n",
|
||||
skb->len,
|
||||
frg_pl_size, i1480u_MAX_FRG_SIZE,
|
||||
buf_itr - wtx->buf, wtx->buf_size, pl_size_left);
|
||||
|
||||
kfree(wtx->buf);
|
||||
error_buf_alloc:
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Given a buffer that fits in a single fragment, fill out a @wtx
|
||||
* struct for transmitting it down the USB pipe.
|
||||
*
|
||||
* Uses the fact that we have space reserved in front of the skbuff
|
||||
* for hardware headers :]
|
||||
*
|
||||
* This does not fill the i1480 TX header, it is left up to the
|
||||
* caller to do that; you can get it from @wtx->wlp_tx_hdr.
|
||||
*
|
||||
* @pl: pointer to payload data
|
||||
* @pl_size: size of the payuload
|
||||
*
|
||||
* This function does not consume the @skb.
|
||||
*/
|
||||
static
|
||||
int i1480u_tx_create_1(struct i1480u_tx *wtx, struct sk_buff *skb,
|
||||
gfp_t gfp_mask)
|
||||
{
|
||||
struct untd_hdr_cmp *untd_hdr_cmp;
|
||||
struct wlp_tx_hdr *wlp_tx_hdr;
|
||||
|
||||
wtx->buf = NULL;
|
||||
wtx->skb = skb;
|
||||
BUG_ON(skb_headroom(skb) < sizeof(*wlp_tx_hdr));
|
||||
wlp_tx_hdr = (void *) __skb_push(skb, sizeof(*wlp_tx_hdr));
|
||||
wtx->wlp_tx_hdr = wlp_tx_hdr;
|
||||
BUG_ON(skb_headroom(skb) < sizeof(*untd_hdr_cmp));
|
||||
untd_hdr_cmp = (void *) __skb_push(skb, sizeof(*untd_hdr_cmp));
|
||||
|
||||
untd_hdr_set_type(&untd_hdr_cmp->hdr, i1480u_PKT_FRAG_CMP);
|
||||
untd_hdr_set_rx_tx(&untd_hdr_cmp->hdr, 0);
|
||||
untd_hdr_cmp->hdr.len = cpu_to_le16(skb->len - sizeof(*untd_hdr_cmp));
|
||||
untd_hdr_cmp->padding = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Given a skb to transmit, massage it to become palatable for the TX pipe
|
||||
*
|
||||
* This will break the buffer in chunks smaller than
|
||||
* i1480u_MAX_FRG_SIZE and add proper headers to each.
|
||||
*
|
||||
* 1st header \
|
||||
* i1480 tx header | fragment 1
|
||||
* fragment data /
|
||||
* nxt header \ fragment 2
|
||||
* fragment data /
|
||||
* ..
|
||||
* ..
|
||||
* last header \ fragment 3
|
||||
* last fragment data /
|
||||
*
|
||||
* Each fragment will be always smaller or equal to i1480u_MAX_FRG_SIZE.
|
||||
*
|
||||
* If the first fragment is smaller than i1480u_MAX_FRG_SIZE, then the
|
||||
* following is composed:
|
||||
*
|
||||
* complete header \
|
||||
* i1480 tx header | single fragment
|
||||
* packet data /
|
||||
*
|
||||
* We were going to use s/g support, but because the interface is
|
||||
* synch and at the end there is plenty of overhead to do it, it
|
||||
* didn't seem that worth for data that is going to be smaller than
|
||||
* one page.
|
||||
*/
|
||||
static
|
||||
struct i1480u_tx *i1480u_tx_create(struct i1480u *i1480u,
|
||||
struct sk_buff *skb, gfp_t gfp_mask)
|
||||
{
|
||||
int result;
|
||||
struct usb_endpoint_descriptor *epd;
|
||||
int usb_pipe;
|
||||
unsigned long flags;
|
||||
|
||||
struct i1480u_tx *wtx;
|
||||
const size_t pl_max_size =
|
||||
i1480u_MAX_FRG_SIZE - sizeof(struct untd_hdr_cmp)
|
||||
- sizeof(struct wlp_tx_hdr);
|
||||
|
||||
wtx = kmalloc(sizeof(*wtx), gfp_mask);
|
||||
if (wtx == NULL)
|
||||
goto error_wtx_alloc;
|
||||
wtx->urb = usb_alloc_urb(0, gfp_mask);
|
||||
if (wtx->urb == NULL)
|
||||
goto error_urb_alloc;
|
||||
epd = &i1480u->usb_iface->cur_altsetting->endpoint[2].desc;
|
||||
usb_pipe = usb_sndbulkpipe(i1480u->usb_dev, epd->bEndpointAddress);
|
||||
/* Fits in a single complete packet or need to split? */
|
||||
if (skb->len > pl_max_size) {
|
||||
result = i1480u_tx_create_n(wtx, skb, gfp_mask);
|
||||
if (result < 0)
|
||||
goto error_create;
|
||||
usb_fill_bulk_urb(wtx->urb, i1480u->usb_dev, usb_pipe,
|
||||
wtx->buf, wtx->buf_size, i1480u_tx_cb, wtx);
|
||||
} else {
|
||||
result = i1480u_tx_create_1(wtx, skb, gfp_mask);
|
||||
if (result < 0)
|
||||
goto error_create;
|
||||
usb_fill_bulk_urb(wtx->urb, i1480u->usb_dev, usb_pipe,
|
||||
skb->data, skb->len, i1480u_tx_cb, wtx);
|
||||
}
|
||||
spin_lock_irqsave(&i1480u->tx_list_lock, flags);
|
||||
list_add(&wtx->list_node, &i1480u->tx_list);
|
||||
spin_unlock_irqrestore(&i1480u->tx_list_lock, flags);
|
||||
return wtx;
|
||||
|
||||
error_create:
|
||||
kfree(wtx->urb);
|
||||
error_urb_alloc:
|
||||
kfree(wtx);
|
||||
error_wtx_alloc:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Actual fragmentation and transmission of frame
|
||||
*
|
||||
* @wlp: WLP substack data structure
|
||||
* @skb: To be transmitted
|
||||
* @dst: Device address of destination
|
||||
* @returns: 0 on success, <0 on failure
|
||||
*
|
||||
* This function can also be called directly (not just from
|
||||
* hard_start_xmit), so we also check here if the interface is up before
|
||||
* taking sending anything.
|
||||
*/
|
||||
int i1480u_xmit_frame(struct wlp *wlp, struct sk_buff *skb,
|
||||
struct uwb_dev_addr *dst)
|
||||
{
|
||||
int result = -ENXIO;
|
||||
struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp);
|
||||
struct device *dev = &i1480u->usb_iface->dev;
|
||||
struct net_device *net_dev = i1480u->net_dev;
|
||||
struct i1480u_tx *wtx;
|
||||
struct wlp_tx_hdr *wlp_tx_hdr;
|
||||
static unsigned char dev_bcast[2] = { 0xff, 0xff };
|
||||
|
||||
BUG_ON(i1480u->wlp.rc == NULL);
|
||||
if ((net_dev->flags & IFF_UP) == 0)
|
||||
goto out;
|
||||
result = -EBUSY;
|
||||
if (atomic_read(&i1480u->tx_inflight.count) >= i1480u->tx_inflight.max) {
|
||||
netif_stop_queue(net_dev);
|
||||
goto error_max_inflight;
|
||||
}
|
||||
result = -ENOMEM;
|
||||
wtx = i1480u_tx_create(i1480u, skb, GFP_ATOMIC);
|
||||
if (unlikely(wtx == NULL)) {
|
||||
if (printk_ratelimit())
|
||||
dev_err(dev, "TX: no memory for WLP TX URB,"
|
||||
"dropping packet (in flight %d)\n",
|
||||
atomic_read(&i1480u->tx_inflight.count));
|
||||
netif_stop_queue(net_dev);
|
||||
goto error_wtx_alloc;
|
||||
}
|
||||
wtx->i1480u = i1480u;
|
||||
/* Fill out the i1480 header; @i1480u->def_tx_hdr read without
|
||||
* locking. We do so because they are kind of orthogonal to
|
||||
* each other (and thus not changed in an atomic batch).
|
||||
* The ETH header is right after the WLP TX header. */
|
||||
wlp_tx_hdr = wtx->wlp_tx_hdr;
|
||||
*wlp_tx_hdr = i1480u->options.def_tx_hdr;
|
||||
wlp_tx_hdr->dstaddr = *dst;
|
||||
if (!memcmp(&wlp_tx_hdr->dstaddr, dev_bcast, sizeof(dev_bcast))
|
||||
&& (wlp_tx_hdr_delivery_id_type(wlp_tx_hdr) & WLP_DRP)) {
|
||||
/*Broadcast message directed to DRP host. Send as best effort
|
||||
* on PCA. */
|
||||
wlp_tx_hdr_set_delivery_id_type(wlp_tx_hdr, i1480u->options.pca_base_priority);
|
||||
}
|
||||
|
||||
result = usb_submit_urb(wtx->urb, GFP_ATOMIC); /* Go baby */
|
||||
if (result < 0) {
|
||||
dev_err(dev, "TX: cannot submit URB: %d\n", result);
|
||||
/* We leave the freeing of skb to calling function */
|
||||
wtx->skb = NULL;
|
||||
goto error_tx_urb_submit;
|
||||
}
|
||||
atomic_inc(&i1480u->tx_inflight.count);
|
||||
net_dev->trans_start = jiffies;
|
||||
return result;
|
||||
|
||||
error_tx_urb_submit:
|
||||
i1480u_tx_destroy(i1480u, wtx);
|
||||
error_wtx_alloc:
|
||||
error_max_inflight:
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Transmit an skb Called when an skbuf has to be transmitted
|
||||
*
|
||||
* The skb is first passed to WLP substack to ensure this is a valid
|
||||
* frame. If valid the device address of destination will be filled and
|
||||
* the WLP header prepended to the skb. If this step fails we fake sending
|
||||
* the frame, if we return an error the network stack will just keep trying.
|
||||
*
|
||||
* Broadcast frames inside a WSS needs to be treated special as multicast is
|
||||
* not supported. A broadcast frame is sent as unicast to each member of the
|
||||
* WSS - this is done by the WLP substack when it finds a broadcast frame.
|
||||
* So, we test if the WLP substack took over the skb and only transmit it
|
||||
* if it has not (been taken over).
|
||||
*
|
||||
* @net_dev->xmit_lock is held
|
||||
*/
|
||||
netdev_tx_t i1480u_hard_start_xmit(struct sk_buff *skb,
|
||||
struct net_device *net_dev)
|
||||
{
|
||||
int result;
|
||||
struct i1480u *i1480u = netdev_priv(net_dev);
|
||||
struct device *dev = &i1480u->usb_iface->dev;
|
||||
struct uwb_dev_addr dst;
|
||||
|
||||
if ((net_dev->flags & IFF_UP) == 0)
|
||||
goto error;
|
||||
result = wlp_prepare_tx_frame(dev, &i1480u->wlp, skb, &dst);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "WLP verification of TX frame failed (%d). "
|
||||
"Dropping packet.\n", result);
|
||||
goto error;
|
||||
} else if (result == 1) {
|
||||
/* trans_start time will be set when WLP actually transmits
|
||||
* the frame */
|
||||
goto out;
|
||||
}
|
||||
result = i1480u_xmit_frame(&i1480u->wlp, skb, &dst);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "Frame TX failed (%d).\n", result);
|
||||
goto error;
|
||||
}
|
||||
return NETDEV_TX_OK;
|
||||
error:
|
||||
dev_kfree_skb_any(skb);
|
||||
net_dev->stats.tx_dropped++;
|
||||
out:
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Called when a pkt transmission doesn't complete in a reasonable period
|
||||
* Device reset may sleep - do it outside of interrupt context (delayed)
|
||||
*/
|
||||
void i1480u_tx_timeout(struct net_device *net_dev)
|
||||
{
|
||||
struct i1480u *i1480u = netdev_priv(net_dev);
|
||||
|
||||
wlp_reset_all(&i1480u->wlp);
|
||||
}
|
||||
|
||||
|
||||
void i1480u_tx_release(struct i1480u *i1480u)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct i1480u_tx *wtx, *next;
|
||||
int count = 0, empty;
|
||||
|
||||
spin_lock_irqsave(&i1480u->tx_list_lock, flags);
|
||||
list_for_each_entry_safe(wtx, next, &i1480u->tx_list, list_node) {
|
||||
count++;
|
||||
usb_unlink_urb(wtx->urb);
|
||||
}
|
||||
spin_unlock_irqrestore(&i1480u->tx_list_lock, flags);
|
||||
count = count*10; /* i1480ut 200ms per unlinked urb (intervals of 20ms) */
|
||||
/*
|
||||
* We don't like this sollution too much (dirty as it is), but
|
||||
* it is cheaper than putting a refcount on each i1480u_tx and
|
||||
* i1480uting for all of them to go away...
|
||||
*
|
||||
* Called when no more packets can be added to tx_list
|
||||
* so can i1480ut for it to be empty.
|
||||
*/
|
||||
while (1) {
|
||||
spin_lock_irqsave(&i1480u->tx_list_lock, flags);
|
||||
empty = list_empty(&i1480u->tx_list);
|
||||
spin_unlock_irqrestore(&i1480u->tx_list_lock, flags);
|
||||
if (empty)
|
||||
break;
|
||||
count--;
|
||||
BUG_ON(count == 0);
|
||||
msleep(20);
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
obj-$(CONFIG_UWB_WLP) := wlp.o
|
||||
|
||||
wlp-objs := \
|
||||
driver.o \
|
||||
eda.o \
|
||||
messages.o \
|
||||
sysfs.o \
|
||||
txrx.o \
|
||||
wlp-lc.o \
|
||||
wss-lc.o
|
@ -1,43 +0,0 @@
|
||||
/*
|
||||
* 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");
|
@ -1,415 +0,0 @@
|
||||
/*
|
||||
* 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/slab.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",
|
||||
ð_addr[0], ð_addr[1],
|
||||
ð_addr[2], ð_addr[3],
|
||||
ð_addr[4], ð_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
@ -1,708 +0,0 @@
|
||||
/*
|
||||
* 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 const 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,
|
||||
};
|
@ -1,354 +0,0 @@
|
||||
/*
|
||||
* 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/slab.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 successfully 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);
|
@ -1,224 +0,0 @@
|
||||
/*
|
||||
* 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__ */
|
@ -1,560 +0,0 @@
|
||||
/*
|
||||
* 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 <linux/slab.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);
|
@ -1,959 +0,0 @@
|
||||
/*
|
||||
* 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/slab.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 reinitialized 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: "
|
||||
"%pM, Dev: %02x:%02x) is not connected.\n",
|
||||
eth_addr, 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);
|
@ -1,736 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* - Does not (yet) include support for WLP control frames
|
||||
* WLP Draft 0.99 [6.5].
|
||||
*
|
||||
* A visual representation of the data structures.
|
||||
*
|
||||
* wssidB wssidB
|
||||
* ^ ^
|
||||
* | |
|
||||
* wssidA wssidA
|
||||
* wlp interface { ^ ^
|
||||
* ... | |
|
||||
* ... ... wssid wssid ...
|
||||
* wlp --- ... | |
|
||||
* }; neighbors --> neighbA --> neighbB
|
||||
* ...
|
||||
* wss
|
||||
* ...
|
||||
* eda cache --> neighborA --> neighborB --> neighborC ...
|
||||
*/
|
||||
|
||||
#ifndef __LINUX__WLP_H_
|
||||
#define __LINUX__WLP_H_
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/uwb.h>
|
||||
|
||||
/**
|
||||
* WLP Protocol ID
|
||||
* WLP Draft 0.99 [6.2]
|
||||
*
|
||||
* The MUX header for all WLP frames
|
||||
*/
|
||||
#define WLP_PROTOCOL_ID 0x0100
|
||||
|
||||
/**
|
||||
* WLP Version
|
||||
* WLP version placed in the association frames (WLP 0.99 [6.6])
|
||||
*/
|
||||
#define WLP_VERSION 0x10
|
||||
|
||||
/**
|
||||
* Bytes needed to print UUID as string
|
||||
*/
|
||||
#define WLP_WSS_UUID_STRSIZE 48
|
||||
|
||||
/**
|
||||
* Bytes needed to print nonce as string
|
||||
*/
|
||||
#define WLP_WSS_NONCE_STRSIZE 48
|
||||
|
||||
|
||||
/**
|
||||
* Size used for WLP name size
|
||||
*
|
||||
* The WSS name is set to 65 bytes, 1 byte larger than the maximum
|
||||
* allowed by the WLP spec. This is to have a null terminated string
|
||||
* for display to the user. A maximum of 64 bytes will still be used
|
||||
* when placing the WSS name field in association frames.
|
||||
*/
|
||||
#define WLP_WSS_NAME_SIZE 65
|
||||
|
||||
/**
|
||||
* Number of bytes added by WLP to data frame
|
||||
*
|
||||
* A data frame transmitted from a host will be placed in a Standard or
|
||||
* Abbreviated WLP frame. These have an extra 4 bytes of header (struct
|
||||
* wlp_frame_std_abbrv_hdr).
|
||||
* When the stack sends this data frame for transmission it needs to ensure
|
||||
* there is enough headroom for this header.
|
||||
*/
|
||||
#define WLP_DATA_HLEN 4
|
||||
|
||||
/**
|
||||
* State of device regarding WLP Service Set
|
||||
*
|
||||
* WLP_WSS_STATE_NONE: the host does not participate in any WSS
|
||||
* WLP_WSS_STATE_PART_ENROLLED: used as part of the enrollment sequence
|
||||
* ("Partial Enroll"). This state is used to
|
||||
* indicate the first part of enrollment that is
|
||||
* unsecure. If the WSS is unsecure then the
|
||||
* state will promptly go to WLP_WSS_STATE_ENROLLED,
|
||||
* if the WSS is not secure then the enrollment
|
||||
* procedure is a few more steps before we are
|
||||
* enrolled.
|
||||
* WLP_WSS_STATE_ENROLLED: the host is enrolled in a WSS
|
||||
* WLP_WSS_STATE_ACTIVE: WSS is activated
|
||||
* WLP_WSS_STATE_CONNECTED: host is connected to neighbor in WSS
|
||||
*
|
||||
*/
|
||||
enum wlp_wss_state {
|
||||
WLP_WSS_STATE_NONE = 0,
|
||||
WLP_WSS_STATE_PART_ENROLLED,
|
||||
WLP_WSS_STATE_ENROLLED,
|
||||
WLP_WSS_STATE_ACTIVE,
|
||||
WLP_WSS_STATE_CONNECTED,
|
||||
};
|
||||
|
||||
/**
|
||||
* WSS Secure status
|
||||
* WLP 0.99 Table 6
|
||||
*
|
||||
* Set to one if the WSS is secure, zero if it is not secure
|
||||
*/
|
||||
enum wlp_wss_sec_status {
|
||||
WLP_WSS_UNSECURE = 0,
|
||||
WLP_WSS_SECURE,
|
||||
};
|
||||
|
||||
/**
|
||||
* WLP frame type
|
||||
* WLP Draft 0.99 [6.2 Table 1]
|
||||
*/
|
||||
enum wlp_frame_type {
|
||||
WLP_FRAME_STANDARD = 0,
|
||||
WLP_FRAME_ABBREVIATED,
|
||||
WLP_FRAME_CONTROL,
|
||||
WLP_FRAME_ASSOCIATION,
|
||||
};
|
||||
|
||||
/**
|
||||
* WLP Association Message Type
|
||||
* WLP Draft 0.99 [6.6.1.2 Table 8]
|
||||
*/
|
||||
enum wlp_assoc_type {
|
||||
WLP_ASSOC_D1 = 2,
|
||||
WLP_ASSOC_D2 = 3,
|
||||
WLP_ASSOC_M1 = 4,
|
||||
WLP_ASSOC_M2 = 5,
|
||||
WLP_ASSOC_M3 = 7,
|
||||
WLP_ASSOC_M4 = 8,
|
||||
WLP_ASSOC_M5 = 9,
|
||||
WLP_ASSOC_M6 = 10,
|
||||
WLP_ASSOC_M7 = 11,
|
||||
WLP_ASSOC_M8 = 12,
|
||||
WLP_ASSOC_F0 = 14,
|
||||
WLP_ASSOC_E1 = 32,
|
||||
WLP_ASSOC_E2 = 33,
|
||||
WLP_ASSOC_C1 = 34,
|
||||
WLP_ASSOC_C2 = 35,
|
||||
WLP_ASSOC_C3 = 36,
|
||||
WLP_ASSOC_C4 = 37,
|
||||
};
|
||||
|
||||
/**
|
||||
* WLP Attribute Type
|
||||
* WLP Draft 0.99 [6.6.1 Table 6]
|
||||
*/
|
||||
enum wlp_attr_type {
|
||||
WLP_ATTR_AUTH = 0x1005, /* Authenticator */
|
||||
WLP_ATTR_DEV_NAME = 0x1011, /* Device Name */
|
||||
WLP_ATTR_DEV_PWD_ID = 0x1012, /* Device Password ID */
|
||||
WLP_ATTR_E_HASH1 = 0x1014, /* E-Hash1 */
|
||||
WLP_ATTR_E_HASH2 = 0x1015, /* E-Hash2 */
|
||||
WLP_ATTR_E_SNONCE1 = 0x1016, /* E-SNonce1 */
|
||||
WLP_ATTR_E_SNONCE2 = 0x1017, /* E-SNonce2 */
|
||||
WLP_ATTR_ENCR_SET = 0x1018, /* Encrypted Settings */
|
||||
WLP_ATTR_ENRL_NONCE = 0x101A, /* Enrollee Nonce */
|
||||
WLP_ATTR_KEYWRAP_AUTH = 0x101E, /* Key Wrap Authenticator */
|
||||
WLP_ATTR_MANUF = 0x1021, /* Manufacturer */
|
||||
WLP_ATTR_MSG_TYPE = 0x1022, /* Message Type */
|
||||
WLP_ATTR_MODEL_NAME = 0x1023, /* Model Name */
|
||||
WLP_ATTR_MODEL_NR = 0x1024, /* Model Number */
|
||||
WLP_ATTR_PUB_KEY = 0x1032, /* Public Key */
|
||||
WLP_ATTR_REG_NONCE = 0x1039, /* Registrar Nonce */
|
||||
WLP_ATTR_R_HASH1 = 0x103D, /* R-Hash1 */
|
||||
WLP_ATTR_R_HASH2 = 0x103E, /* R-Hash2 */
|
||||
WLP_ATTR_R_SNONCE1 = 0x103F, /* R-SNonce1 */
|
||||
WLP_ATTR_R_SNONCE2 = 0x1040, /* R-SNonce2 */
|
||||
WLP_ATTR_SERIAL = 0x1042, /* Serial number */
|
||||
WLP_ATTR_UUID_E = 0x1047, /* UUID-E */
|
||||
WLP_ATTR_UUID_R = 0x1048, /* UUID-R */
|
||||
WLP_ATTR_PRI_DEV_TYPE = 0x1054, /* Primary Device Type */
|
||||
WLP_ATTR_SEC_DEV_TYPE = 0x1055, /* Secondary Device Type */
|
||||
WLP_ATTR_PORT_DEV = 0x1056, /* Portable Device */
|
||||
WLP_ATTR_APP_EXT = 0x1058, /* Application Extension */
|
||||
WLP_ATTR_WLP_VER = 0x2000, /* WLP Version */
|
||||
WLP_ATTR_WSSID = 0x2001, /* WSSID */
|
||||
WLP_ATTR_WSS_NAME = 0x2002, /* WSS Name */
|
||||
WLP_ATTR_WSS_SEC_STAT = 0x2003, /* WSS Secure Status */
|
||||
WLP_ATTR_WSS_BCAST = 0x2004, /* WSS Broadcast Address */
|
||||
WLP_ATTR_WSS_M_KEY = 0x2005, /* WSS Master Key */
|
||||
WLP_ATTR_ACC_ENRL = 0x2006, /* Accepting Enrollment */
|
||||
WLP_ATTR_WSS_INFO = 0x2007, /* WSS Information */
|
||||
WLP_ATTR_WSS_SEL_MTHD = 0x2008, /* WSS Selection Method */
|
||||
WLP_ATTR_ASSC_MTHD_LIST = 0x2009, /* Association Methods List */
|
||||
WLP_ATTR_SEL_ASSC_MTHD = 0x200A, /* Selected Association Method */
|
||||
WLP_ATTR_ENRL_HASH_COMM = 0x200B, /* Enrollee Hash Commitment */
|
||||
WLP_ATTR_WSS_TAG = 0x200C, /* WSS Tag */
|
||||
WLP_ATTR_WSS_VIRT = 0x200D, /* WSS Virtual EUI-48 */
|
||||
WLP_ATTR_WLP_ASSC_ERR = 0x200E, /* WLP Association Error */
|
||||
WLP_ATTR_VNDR_EXT = 0x200F, /* Vendor Extension */
|
||||
};
|
||||
|
||||
/**
|
||||
* WLP Category ID of primary/secondary device
|
||||
* WLP Draft 0.99 [6.6.1.8 Table 12]
|
||||
*/
|
||||
enum wlp_dev_category_id {
|
||||
WLP_DEV_CAT_COMPUTER = 1,
|
||||
WLP_DEV_CAT_INPUT,
|
||||
WLP_DEV_CAT_PRINT_SCAN_FAX_COPIER,
|
||||
WLP_DEV_CAT_CAMERA,
|
||||
WLP_DEV_CAT_STORAGE,
|
||||
WLP_DEV_CAT_INFRASTRUCTURE,
|
||||
WLP_DEV_CAT_DISPLAY,
|
||||
WLP_DEV_CAT_MULTIM,
|
||||
WLP_DEV_CAT_GAMING,
|
||||
WLP_DEV_CAT_TELEPHONE,
|
||||
WLP_DEV_CAT_OTHER = 65535,
|
||||
};
|
||||
|
||||
/**
|
||||
* WLP WSS selection method
|
||||
* WLP Draft 0.99 [6.6.1.6 Table 10]
|
||||
*/
|
||||
enum wlp_wss_sel_mthd {
|
||||
WLP_WSS_ENRL_SELECT = 1, /* Enrollee selects */
|
||||
WLP_WSS_REG_SELECT, /* Registrar selects */
|
||||
};
|
||||
|
||||
/**
|
||||
* WLP association error values
|
||||
* WLP Draft 0.99 [6.6.1.5 Table 9]
|
||||
*/
|
||||
enum wlp_assc_error {
|
||||
WLP_ASSOC_ERROR_NONE,
|
||||
WLP_ASSOC_ERROR_AUTH, /* Authenticator Failure */
|
||||
WLP_ASSOC_ERROR_ROGUE, /* Rogue activity suspected */
|
||||
WLP_ASSOC_ERROR_BUSY, /* Device busy */
|
||||
WLP_ASSOC_ERROR_LOCK, /* Setup Locked */
|
||||
WLP_ASSOC_ERROR_NOT_READY, /* Registrar not ready */
|
||||
WLP_ASSOC_ERROR_INV, /* Invalid WSS selection */
|
||||
WLP_ASSOC_ERROR_MSG_TIME, /* Message timeout */
|
||||
WLP_ASSOC_ERROR_ENR_TIME, /* Enrollment session timeout */
|
||||
WLP_ASSOC_ERROR_PW, /* Device password invalid */
|
||||
WLP_ASSOC_ERROR_VER, /* Unsupported version */
|
||||
WLP_ASSOC_ERROR_INT, /* Internal error */
|
||||
WLP_ASSOC_ERROR_UNDEF, /* Undefined error */
|
||||
WLP_ASSOC_ERROR_NUM, /* Numeric comparison failure */
|
||||
WLP_ASSOC_ERROR_WAIT, /* Waiting for user input */
|
||||
};
|
||||
|
||||
/**
|
||||
* WLP Parameters
|
||||
* WLP 0.99 [7.7]
|
||||
*/
|
||||
enum wlp_parameters {
|
||||
WLP_PER_MSG_TIMEOUT = 15, /* Seconds to wait for response to
|
||||
association message. */
|
||||
};
|
||||
|
||||
/**
|
||||
* WLP IE
|
||||
*
|
||||
* The WLP IE should be included in beacons by all devices.
|
||||
*
|
||||
* The driver can set only a few of the fields in this information element,
|
||||
* most fields are managed by the device self. When the driver needs to set
|
||||
* a field it will only provide values for the fields of interest, the rest
|
||||
* will be filled with zeroes. The fields of interest are:
|
||||
*
|
||||
* Element ID
|
||||
* Length
|
||||
* Capabilities (only to include WSSID Hash list length)
|
||||
* WSSID Hash List fields
|
||||
*
|
||||
* WLP 0.99 [6.7]
|
||||
*
|
||||
* Only the fields that will be used are detailed in this structure, rest
|
||||
* are not detailed or marked as "notused".
|
||||
*/
|
||||
struct wlp_ie {
|
||||
struct uwb_ie_hdr hdr;
|
||||
__le16 capabilities;
|
||||
__le16 cycle_param;
|
||||
__le16 acw_anchor_addr;
|
||||
u8 wssid_hash_list[];
|
||||
} __packed;
|
||||
|
||||
static inline int wlp_ie_hash_length(struct wlp_ie *ie)
|
||||
{
|
||||
return (le16_to_cpu(ie->capabilities) >> 12) & 0xf;
|
||||
}
|
||||
|
||||
static inline void wlp_ie_set_hash_length(struct wlp_ie *ie, int hash_length)
|
||||
{
|
||||
u16 caps = le16_to_cpu(ie->capabilities);
|
||||
caps = (caps & ~(0xf << 12)) | (hash_length << 12);
|
||||
ie->capabilities = cpu_to_le16(caps);
|
||||
}
|
||||
|
||||
/**
|
||||
* WLP nonce
|
||||
* WLP Draft 0.99 [6.6.1 Table 6]
|
||||
*
|
||||
* A 128-bit random number often used (E-SNonce1, E-SNonce2, Enrollee
|
||||
* Nonce, Registrar Nonce, R-SNonce1, R-SNonce2). It is passed to HW so
|
||||
* it is packed.
|
||||
*/
|
||||
struct wlp_nonce {
|
||||
u8 data[16];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* WLP UUID
|
||||
* WLP Draft 0.99 [6.6.1 Table 6]
|
||||
*
|
||||
* Universally Unique Identifier (UUID) encoded as an octet string in the
|
||||
* order the octets are shown in string representation in RFC4122. A UUID
|
||||
* is often used (UUID-E, UUID-R, WSSID). It is passed to HW so it is packed.
|
||||
*/
|
||||
struct wlp_uuid {
|
||||
u8 data[16];
|
||||
} __packed;
|
||||
|
||||
|
||||
/**
|
||||
* Primary and secondary device type attributes
|
||||
* WLP Draft 0.99 [6.6.1.8]
|
||||
*/
|
||||
struct wlp_dev_type {
|
||||
enum wlp_dev_category_id category:16;
|
||||
u8 OUI[3];
|
||||
u8 OUIsubdiv;
|
||||
__le16 subID;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* WLP frame header
|
||||
* WLP Draft 0.99 [6.2]
|
||||
*/
|
||||
struct wlp_frame_hdr {
|
||||
__le16 mux_hdr; /* WLP_PROTOCOL_ID */
|
||||
enum wlp_frame_type type:8;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* WLP attribute field header
|
||||
* WLP Draft 0.99 [6.6.1]
|
||||
*
|
||||
* Header of each attribute found in an association frame
|
||||
*/
|
||||
struct wlp_attr_hdr {
|
||||
__le16 type;
|
||||
__le16 length;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* Device information commonly used together
|
||||
*
|
||||
* Each of these device information elements has a specified range in which it
|
||||
* should fit (WLP 0.99 [Table 6]). This range provided in the spec does not
|
||||
* include the termination null '\0' character (when used in the
|
||||
* association protocol the attribute fields are accompanied
|
||||
* with a "length" field so the full range from the spec can be used for
|
||||
* the value). We thus allocate an extra byte to be able to store a string
|
||||
* of max length with a terminating '\0'.
|
||||
*/
|
||||
struct wlp_device_info {
|
||||
char name[33];
|
||||
char model_name[33];
|
||||
char manufacturer[65];
|
||||
char model_nr[33];
|
||||
char serial[33];
|
||||
struct wlp_dev_type prim_dev_type;
|
||||
};
|
||||
|
||||
/**
|
||||
* Macros for the WLP attributes
|
||||
*
|
||||
* There are quite a few attributes (total is 43). The attribute layout can be
|
||||
* in one of three categories: one value, an array, an enum forced to 8 bits.
|
||||
* These macros help with their definitions.
|
||||
*/
|
||||
#define wlp_attr(type, name) \
|
||||
struct wlp_attr_##name { \
|
||||
struct wlp_attr_hdr hdr; \
|
||||
type name; \
|
||||
} __packed;
|
||||
|
||||
#define wlp_attr_array(type, name) \
|
||||
struct wlp_attr_##name { \
|
||||
struct wlp_attr_hdr hdr; \
|
||||
type name[]; \
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* WLP association attribute fields
|
||||
* WLP Draft 0.99 [6.6.1 Table 6]
|
||||
*
|
||||
* Attributes appear in same order as the Table in the spec
|
||||
* FIXME Does not define all attributes yet
|
||||
*/
|
||||
|
||||
/* Device name: Friendly name of sending device */
|
||||
wlp_attr_array(u8, dev_name)
|
||||
|
||||
/* Enrollee Nonce: Random number generated by enrollee for an enrollment
|
||||
* session */
|
||||
wlp_attr(struct wlp_nonce, enonce)
|
||||
|
||||
/* Manufacturer name: Name of manufacturer of the sending device */
|
||||
wlp_attr_array(u8, manufacturer)
|
||||
|
||||
/* WLP Message Type */
|
||||
wlp_attr(u8, msg_type)
|
||||
|
||||
/* WLP Model name: Model name of sending device */
|
||||
wlp_attr_array(u8, model_name)
|
||||
|
||||
/* WLP Model number: Model number of sending device */
|
||||
wlp_attr_array(u8, model_nr)
|
||||
|
||||
/* Registrar Nonce: Random number generated by registrar for an enrollment
|
||||
* session */
|
||||
wlp_attr(struct wlp_nonce, rnonce)
|
||||
|
||||
/* Serial number of device */
|
||||
wlp_attr_array(u8, serial)
|
||||
|
||||
/* UUID of enrollee */
|
||||
wlp_attr(struct wlp_uuid, uuid_e)
|
||||
|
||||
/* UUID of registrar */
|
||||
wlp_attr(struct wlp_uuid, uuid_r)
|
||||
|
||||
/* WLP Primary device type */
|
||||
wlp_attr(struct wlp_dev_type, prim_dev_type)
|
||||
|
||||
/* WLP Secondary device type */
|
||||
wlp_attr(struct wlp_dev_type, sec_dev_type)
|
||||
|
||||
/* WLP protocol version */
|
||||
wlp_attr(u8, version)
|
||||
|
||||
/* WLP service set identifier */
|
||||
wlp_attr(struct wlp_uuid, wssid)
|
||||
|
||||
/* WLP WSS name */
|
||||
wlp_attr_array(u8, wss_name)
|
||||
|
||||
/* WLP WSS Secure Status */
|
||||
wlp_attr(u8, wss_sec_status)
|
||||
|
||||
/* WSS Broadcast Address */
|
||||
wlp_attr(struct uwb_mac_addr, wss_bcast)
|
||||
|
||||
/* WLP Accepting Enrollment */
|
||||
wlp_attr(u8, accept_enrl)
|
||||
|
||||
/**
|
||||
* WSS information attributes
|
||||
* WLP Draft 0.99 [6.6.3 Table 15]
|
||||
*/
|
||||
struct wlp_wss_info {
|
||||
struct wlp_attr_wssid wssid;
|
||||
struct wlp_attr_wss_name name;
|
||||
struct wlp_attr_accept_enrl accept;
|
||||
struct wlp_attr_wss_sec_status sec_stat;
|
||||
struct wlp_attr_wss_bcast bcast;
|
||||
} __packed;
|
||||
|
||||
/* WLP WSS Information */
|
||||
wlp_attr_array(struct wlp_wss_info, wss_info)
|
||||
|
||||
/* WLP WSS Selection method */
|
||||
wlp_attr(u8, wss_sel_mthd)
|
||||
|
||||
/* WLP WSS tag */
|
||||
wlp_attr(u8, wss_tag)
|
||||
|
||||
/* WSS Virtual Address */
|
||||
wlp_attr(struct uwb_mac_addr, wss_virt)
|
||||
|
||||
/* WLP association error */
|
||||
wlp_attr(u8, wlp_assc_err)
|
||||
|
||||
/**
|
||||
* WLP standard and abbreviated frames
|
||||
*
|
||||
* WLP Draft 0.99 [6.3] and [6.4]
|
||||
*
|
||||
* The difference between the WLP standard frame and the WLP
|
||||
* abbreviated frame is that the standard frame includes the src
|
||||
* and dest addresses from the Ethernet header, the abbreviated frame does
|
||||
* not.
|
||||
* The src/dest (as well as the type/length and client data) are already
|
||||
* defined as part of the Ethernet header, we do not do this here.
|
||||
* From this perspective the standard and abbreviated frames appear the
|
||||
* same - they will be treated differently though.
|
||||
*
|
||||
* The size of this header is also captured in WLP_DATA_HLEN to enable
|
||||
* interfaces to prepare their headroom.
|
||||
*/
|
||||
struct wlp_frame_std_abbrv_hdr {
|
||||
struct wlp_frame_hdr hdr;
|
||||
u8 tag;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* WLP association frames
|
||||
*
|
||||
* WLP Draft 0.99 [6.6]
|
||||
*/
|
||||
struct wlp_frame_assoc {
|
||||
struct wlp_frame_hdr hdr;
|
||||
enum wlp_assoc_type type:8;
|
||||
struct wlp_attr_version version;
|
||||
struct wlp_attr_msg_type msg_type;
|
||||
u8 attr[];
|
||||
} __packed;
|
||||
|
||||
/* Ethernet to dev address mapping */
|
||||
struct wlp_eda {
|
||||
spinlock_t lock;
|
||||
struct list_head cache; /* Eth<->Dev Addr cache */
|
||||
};
|
||||
|
||||
/**
|
||||
* WSS information temporary storage
|
||||
*
|
||||
* This information is only stored temporarily during discovery. It should
|
||||
* not be stored unless the device is enrolled in the advertised WSS. This
|
||||
* is done mainly because we follow the letter of the spec in this regard.
|
||||
* See WLP 0.99 [7.2.3].
|
||||
* When the device does become enrolled in a WSS the WSS information will
|
||||
* be stored as part of the more comprehensive struct wlp_wss.
|
||||
*/
|
||||
struct wlp_wss_tmp_info {
|
||||
char name[WLP_WSS_NAME_SIZE];
|
||||
u8 accept_enroll;
|
||||
u8 sec_status;
|
||||
struct uwb_mac_addr bcast;
|
||||
};
|
||||
|
||||
struct wlp_wssid_e {
|
||||
struct list_head node;
|
||||
struct wlp_uuid wssid;
|
||||
struct wlp_wss_tmp_info *info;
|
||||
};
|
||||
|
||||
/**
|
||||
* A cache entry of WLP neighborhood
|
||||
*
|
||||
* @node: head of list is wlp->neighbors
|
||||
* @wssid: list of wssids of this neighbor, element is wlp_wssid_e
|
||||
* @info: temporary storage for information learned during discovery. This
|
||||
* storage is used together with the wssid_e temporary storage
|
||||
* during discovery.
|
||||
*/
|
||||
struct wlp_neighbor_e {
|
||||
struct list_head node;
|
||||
struct wlp_uuid uuid;
|
||||
struct uwb_dev *uwb_dev;
|
||||
struct list_head wssid; /* Elements are wlp_wssid_e */
|
||||
struct wlp_device_info *info;
|
||||
};
|
||||
|
||||
struct wlp;
|
||||
/**
|
||||
* Information for an association session in progress.
|
||||
*
|
||||
* @exp_message: The type of the expected message. Both this message and a
|
||||
* F0 message (which can be sent in response to any
|
||||
* association frame) will be accepted as a valid message for
|
||||
* this session.
|
||||
* @cb: The function that will be called upon receipt of this
|
||||
* message.
|
||||
* @cb_priv: Private data of callback
|
||||
* @data: Data used in association process (always a sk_buff?)
|
||||
* @neighbor: Address of neighbor with which association session is in
|
||||
* progress.
|
||||
*/
|
||||
struct wlp_session {
|
||||
enum wlp_assoc_type exp_message;
|
||||
void (*cb)(struct wlp *);
|
||||
void *cb_priv;
|
||||
void *data;
|
||||
struct uwb_dev_addr neighbor_addr;
|
||||
};
|
||||
|
||||
/**
|
||||
* WLP Service Set
|
||||
*
|
||||
* @mutex: used to protect entire WSS structure.
|
||||
*
|
||||
* @name: The WSS name is set to 65 bytes, 1 byte larger than the maximum
|
||||
* allowed by the WLP spec. This is to have a null terminated string
|
||||
* for display to the user. A maximum of 64 bytes will still be used
|
||||
* when placing the WSS name field in association frames.
|
||||
*
|
||||
* @accept_enroll: Accepting enrollment: Set to one if registrar is
|
||||
* accepting enrollment in WSS, or zero otherwise.
|
||||
*
|
||||
* Global and local information for each WSS in which we are enrolled.
|
||||
* WLP 0.99 Section 7.2.1 and Section 7.2.2
|
||||
*/
|
||||
struct wlp_wss {
|
||||
struct mutex mutex;
|
||||
struct kobject kobj;
|
||||
/* Global properties. */
|
||||
struct wlp_uuid wssid;
|
||||
u8 hash;
|
||||
char name[WLP_WSS_NAME_SIZE];
|
||||
struct uwb_mac_addr bcast;
|
||||
u8 secure_status:1;
|
||||
u8 master_key[16];
|
||||
/* Local properties. */
|
||||
u8 tag;
|
||||
struct uwb_mac_addr virtual_addr;
|
||||
/* Extra */
|
||||
u8 accept_enroll:1;
|
||||
enum wlp_wss_state state;
|
||||
};
|
||||
|
||||
/**
|
||||
* WLP main structure
|
||||
* @mutex: protect changes to WLP structure. We only allow changes to the
|
||||
* uuid, so currently this mutex only protects this field.
|
||||
*/
|
||||
struct wlp {
|
||||
struct mutex mutex;
|
||||
struct uwb_rc *rc; /* UWB radio controller */
|
||||
struct net_device *ndev;
|
||||
struct uwb_pal pal;
|
||||
struct wlp_eda eda;
|
||||
struct wlp_uuid uuid;
|
||||
struct wlp_session *session;
|
||||
struct wlp_wss wss;
|
||||
struct mutex nbmutex; /* Neighbor mutex protects neighbors list */
|
||||
struct list_head neighbors; /* Elements are wlp_neighbor_e */
|
||||
struct uwb_notifs_handler uwb_notifs_handler;
|
||||
struct wlp_device_info *dev_info;
|
||||
void (*fill_device_info)(struct wlp *wlp, struct wlp_device_info *info);
|
||||
int (*xmit_frame)(struct wlp *, struct sk_buff *,
|
||||
struct uwb_dev_addr *);
|
||||
void (*stop_queue)(struct wlp *);
|
||||
void (*start_queue)(struct wlp *);
|
||||
};
|
||||
|
||||
/* sysfs */
|
||||
|
||||
|
||||
struct wlp_wss_attribute {
|
||||
struct attribute attr;
|
||||
ssize_t (*show)(struct wlp_wss *wss, char *buf);
|
||||
ssize_t (*store)(struct wlp_wss *wss, const char *buf, size_t count);
|
||||
};
|
||||
|
||||
#define WSS_ATTR(_name, _mode, _show, _store) \
|
||||
static struct wlp_wss_attribute wss_attr_##_name = __ATTR(_name, _mode, \
|
||||
_show, _store)
|
||||
|
||||
extern int wlp_setup(struct wlp *, struct uwb_rc *, struct net_device *ndev);
|
||||
extern void wlp_remove(struct wlp *);
|
||||
extern ssize_t wlp_neighborhood_show(struct wlp *, char *);
|
||||
extern int wlp_wss_setup(struct net_device *, struct wlp_wss *);
|
||||
extern void wlp_wss_remove(struct wlp_wss *);
|
||||
extern ssize_t wlp_wss_activate_show(struct wlp_wss *, char *);
|
||||
extern ssize_t wlp_wss_activate_store(struct wlp_wss *, const char *, size_t);
|
||||
extern ssize_t wlp_eda_show(struct wlp *, char *);
|
||||
extern ssize_t wlp_eda_store(struct wlp *, const char *, size_t);
|
||||
extern ssize_t wlp_uuid_show(struct wlp *, char *);
|
||||
extern ssize_t wlp_uuid_store(struct wlp *, const char *, size_t);
|
||||
extern ssize_t wlp_dev_name_show(struct wlp *, char *);
|
||||
extern ssize_t wlp_dev_name_store(struct wlp *, const char *, size_t);
|
||||
extern ssize_t wlp_dev_manufacturer_show(struct wlp *, char *);
|
||||
extern ssize_t wlp_dev_manufacturer_store(struct wlp *, const char *, size_t);
|
||||
extern ssize_t wlp_dev_model_name_show(struct wlp *, char *);
|
||||
extern ssize_t wlp_dev_model_name_store(struct wlp *, const char *, size_t);
|
||||
extern ssize_t wlp_dev_model_nr_show(struct wlp *, char *);
|
||||
extern ssize_t wlp_dev_model_nr_store(struct wlp *, const char *, size_t);
|
||||
extern ssize_t wlp_dev_serial_show(struct wlp *, char *);
|
||||
extern ssize_t wlp_dev_serial_store(struct wlp *, const char *, size_t);
|
||||
extern ssize_t wlp_dev_prim_category_show(struct wlp *, char *);
|
||||
extern ssize_t wlp_dev_prim_category_store(struct wlp *, const char *,
|
||||
size_t);
|
||||
extern ssize_t wlp_dev_prim_OUI_show(struct wlp *, char *);
|
||||
extern ssize_t wlp_dev_prim_OUI_store(struct wlp *, const char *, size_t);
|
||||
extern ssize_t wlp_dev_prim_OUI_sub_show(struct wlp *, char *);
|
||||
extern ssize_t wlp_dev_prim_OUI_sub_store(struct wlp *, const char *,
|
||||
size_t);
|
||||
extern ssize_t wlp_dev_prim_subcat_show(struct wlp *, char *);
|
||||
extern ssize_t wlp_dev_prim_subcat_store(struct wlp *, const char *,
|
||||
size_t);
|
||||
extern int wlp_receive_frame(struct device *, struct wlp *, struct sk_buff *,
|
||||
struct uwb_dev_addr *);
|
||||
extern int wlp_prepare_tx_frame(struct device *, struct wlp *,
|
||||
struct sk_buff *, struct uwb_dev_addr *);
|
||||
void wlp_reset_all(struct wlp *wlp);
|
||||
|
||||
/**
|
||||
* Initialize WSS
|
||||
*/
|
||||
static inline
|
||||
void wlp_wss_init(struct wlp_wss *wss)
|
||||
{
|
||||
mutex_init(&wss->mutex);
|
||||
}
|
||||
|
||||
static inline
|
||||
void wlp_init(struct wlp *wlp)
|
||||
{
|
||||
INIT_LIST_HEAD(&wlp->neighbors);
|
||||
mutex_init(&wlp->mutex);
|
||||
mutex_init(&wlp->nbmutex);
|
||||
wlp_wss_init(&wlp->wss);
|
||||
}
|
||||
|
||||
|
||||
#endif /* #ifndef __LINUX__WLP_H_ */
|
Loading…
Reference in New Issue
Block a user