add idl4k kernel firmware version 1.13.0.105

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

View File

@@ -0,0 +1,2 @@
obj-$(CONFIG_UWB_I1480U) += dfu/ i1480-est.o
obj-$(CONFIG_UWB_I1480U_WLP) += i1480u-wlp/

View File

@@ -0,0 +1,9 @@
obj-$(CONFIG_UWB_I1480U) += i1480-dfu-usb.o
i1480-dfu-usb-objs := \
dfu.o \
mac.o \
phy.o \
usb.o

View File

@@ -0,0 +1,211 @@
/*
* Intel Wireless UWB Link 1480
* Main driver
*
* 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.
*
*
* Common code for firmware upload used by the USB and PCI version;
* i1480_fw_upload() takes a device descriptor and uses the function
* pointers it provides to upload firmware and prepare the PHY.
*
* As well, provides common functions used by the rest of the code.
*/
#include "i1480-dfu.h"
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/device.h>
#include <linux/uwb.h>
#include <linux/random.h>
/*
* i1480_rceb_check - Check RCEB for expected field values
* @i1480: pointer to device for which RCEB is being checked
* @rceb: RCEB being checked
* @cmd: which command the RCEB is related to
* @context: expected context
* @expected_type: expected event type
* @expected_event: expected event
*
* If @cmd is NULL, do not print error messages, but still return an error
* code.
*
* Return 0 if @rceb matches the expected values, -EINVAL otherwise.
*/
int i1480_rceb_check(const struct i1480 *i1480, const struct uwb_rceb *rceb,
const char *cmd, u8 context, u8 expected_type,
unsigned expected_event)
{
int result = 0;
struct device *dev = i1480->dev;
if (rceb->bEventContext != context) {
if (cmd)
dev_err(dev, "%s: unexpected context id 0x%02x "
"(expected 0x%02x)\n", cmd,
rceb->bEventContext, context);
result = -EINVAL;
}
if (rceb->bEventType != expected_type) {
if (cmd)
dev_err(dev, "%s: unexpected event type 0x%02x "
"(expected 0x%02x)\n", cmd,
rceb->bEventType, expected_type);
result = -EINVAL;
}
if (le16_to_cpu(rceb->wEvent) != expected_event) {
if (cmd)
dev_err(dev, "%s: unexpected event 0x%04x "
"(expected 0x%04x)\n", cmd,
le16_to_cpu(rceb->wEvent), expected_event);
result = -EINVAL;
}
return result;
}
EXPORT_SYMBOL_GPL(i1480_rceb_check);
/*
* Execute a Radio Control Command
*
* Command data has to be in i1480->cmd_buf.
*
* @returns size of the reply data filled in i1480->evt_buf or < 0 errno
* code on error.
*/
ssize_t i1480_cmd(struct i1480 *i1480, const char *cmd_name, size_t cmd_size,
size_t reply_size)
{
ssize_t result;
struct uwb_rceb *reply = i1480->evt_buf;
struct uwb_rccb *cmd = i1480->cmd_buf;
u16 expected_event = reply->wEvent;
u8 expected_type = reply->bEventType;
u8 context;
init_completion(&i1480->evt_complete);
i1480->evt_result = -EINPROGRESS;
do {
get_random_bytes(&context, 1);
} while (context == 0x00 || context == 0xff);
cmd->bCommandContext = context;
result = i1480->cmd(i1480, cmd_name, cmd_size);
if (result < 0)
goto error;
/* wait for the callback to report a event was received */
result = wait_for_completion_interruptible_timeout(
&i1480->evt_complete, HZ);
if (result == 0) {
result = -ETIMEDOUT;
goto error;
}
if (result < 0)
goto error;
result = i1480->evt_result;
if (result < 0) {
dev_err(i1480->dev, "%s: command reply reception failed: %zd\n",
cmd_name, result);
goto error;
}
/*
* Firmware versions >= 1.4.12224 for IOGear GUWA100U generate a
* spurious notification after firmware is downloaded. So check whether
* the receibed RCEB is such notification before assuming that the
* command has failed.
*/
if (i1480_rceb_check(i1480, i1480->evt_buf, NULL,
0, 0xfd, 0x0022) == 0) {
/* Now wait for the actual RCEB for this command. */
result = i1480->wait_init_done(i1480);
if (result < 0)
goto error;
result = i1480->evt_result;
}
if (result != reply_size) {
dev_err(i1480->dev, "%s returned only %zu bytes, %zu expected\n",
cmd_name, result, reply_size);
result = -EINVAL;
goto error;
}
/* Verify we got the right event in response */
result = i1480_rceb_check(i1480, i1480->evt_buf, cmd_name, context,
expected_type, expected_event);
error:
return result;
}
EXPORT_SYMBOL_GPL(i1480_cmd);
static
int i1480_print_state(struct i1480 *i1480)
{
int result;
u32 *buf = (u32 *) i1480->cmd_buf;
result = i1480->read(i1480, 0x80080000, 2 * sizeof(*buf));
if (result < 0) {
dev_err(i1480->dev, "cannot read U & L states: %d\n", result);
goto error;
}
dev_info(i1480->dev, "state U 0x%08x, L 0x%08x\n", buf[0], buf[1]);
error:
return result;
}
/*
* PCI probe, firmware uploader
*
* _mac_fw_upload() will call rc_setup(), which needs an rc_release().
*/
int i1480_fw_upload(struct i1480 *i1480)
{
int result;
result = i1480_pre_fw_upload(i1480); /* PHY pre fw */
if (result < 0 && result != -ENOENT) {
i1480_print_state(i1480);
goto error;
}
result = i1480_mac_fw_upload(i1480); /* MAC fw */
if (result < 0) {
if (result == -ENOENT)
dev_err(i1480->dev, "Cannot locate MAC FW file '%s'\n",
i1480->mac_fw_name);
else
i1480_print_state(i1480);
goto error;
}
result = i1480_phy_fw_upload(i1480); /* PHY fw */
if (result < 0 && result != -ENOENT) {
i1480_print_state(i1480);
goto error_rc_release;
}
/*
* FIXME: find some reliable way to check whether firmware is running
* properly. Maybe use some standard request that has no side effects?
*/
dev_info(i1480->dev, "firmware uploaded successfully\n");
error_rc_release:
if (i1480->rc_release)
i1480->rc_release(i1480);
result = 0;
error:
return result;
}
EXPORT_SYMBOL_GPL(i1480_fw_upload);

View File

@@ -0,0 +1,260 @@
/*
* i1480 Device Firmware Upload
*
* 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 driver is the firmware uploader for the Intel Wireless UWB
* Link 1480 device (both in the USB and PCI incarnations).
*
* The process is quite simple: we stop the device, write the firmware
* to its memory and then restart it. Wait for the device to let us
* know it is done booting firmware. Ready.
*
* We might have to upload before or after a phy firmware (which might
* be done in two methods, using a normal firmware image or through
* the MPI port).
*
* Because USB and PCI use common methods, we just make ops out of the
* common operations (read, write, wait_init_done and cmd) and
* implement them in usb.c and pci.c.
*
* The flow is (some parts omitted):
*
* i1480_{usb,pci}_probe() On enumerate/discovery
* i1480_fw_upload()
* i1480_pre_fw_upload()
* __mac_fw_upload()
* fw_hdrs_load()
* mac_fw_hdrs_push()
* i1480->write() [i1480_{usb,pci}_write()]
* i1480_fw_cmp()
* i1480->read() [i1480_{usb,pci}_read()]
* i1480_mac_fw_upload()
* __mac_fw_upload()
* i1480->setup(()
* i1480->wait_init_done()
* i1480_cmd_reset()
* i1480->cmd() [i1480_{usb,pci}_cmd()]
* ...
* i1480_phy_fw_upload()
* request_firmware()
* i1480_mpi_write()
* i1480->cmd() [i1480_{usb,pci}_cmd()]
*
* Once the probe function enumerates the device and uploads the
* firmware, we just exit with -ENODEV, as we don't really want to
* attach to the device.
*/
#ifndef __i1480_DFU_H__
#define __i1480_DFU_H__
#include <linux/uwb/spec.h>
#include <linux/types.h>
#include <linux/completion.h>
#define i1480_FW_UPLOAD_MODE_MASK (cpu_to_le32(0x00000018))
#if i1480_FW > 0x00000302
#define i1480_RCEB_EXTENDED
#endif
struct uwb_rccb;
struct uwb_rceb;
/*
* Common firmware upload handlers
*
* Normally you embed this struct in another one specific to your hw.
*
* @write Write to device's memory from buffer.
* @read Read from device's memory to i1480->evt_buf.
* @setup Setup device after basic firmware is uploaded
* @wait_init_done
* Wait for the device to send a notification saying init
* is done.
* @cmd FOP for issuing the command to the hardware. The
* command data is contained in i1480->cmd_buf and the size
* is supplied as an argument. The command replied is put
* in i1480->evt_buf and the size in i1480->evt_result (or if
* an error, a < 0 errno code).
*
* @cmd_buf Memory buffer used to send commands to the device.
* Allocated by the upper layers i1480_fw_upload().
* Size has to be @buf_size.
* @evt_buf Memory buffer used to place the async notifications
* received by the hw. Allocated by the upper layers
* i1480_fw_upload().
* Size has to be @buf_size.
* @cmd_complete
* Low level driver uses this to notify code waiting afor
* an event that the event has arrived and data is in
* i1480->evt_buf (and size/result in i1480->evt_result).
* @hw_rev
* Use this value to activate dfu code to support new revisions
* of hardware. i1480_init() sets this to a default value.
* It should be updated by the USB and PCI code.
*/
struct i1480 {
struct device *dev;
int (*write)(struct i1480 *, u32 addr, const void *, size_t);
int (*read)(struct i1480 *, u32 addr, size_t);
int (*rc_setup)(struct i1480 *);
void (*rc_release)(struct i1480 *);
int (*wait_init_done)(struct i1480 *);
int (*cmd)(struct i1480 *, const char *cmd_name, size_t cmd_size);
const char *pre_fw_name;
const char *mac_fw_name;
const char *mac_fw_name_deprecate; /* FIXME: Will go away */
const char *phy_fw_name;
u8 hw_rev;
size_t buf_size; /* size of both evt_buf and cmd_buf */
void *evt_buf, *cmd_buf;
ssize_t evt_result;
struct completion evt_complete;
};
static inline
void i1480_init(struct i1480 *i1480)
{
i1480->hw_rev = 1;
init_completion(&i1480->evt_complete);
}
extern int i1480_fw_upload(struct i1480 *);
extern int i1480_pre_fw_upload(struct i1480 *);
extern int i1480_mac_fw_upload(struct i1480 *);
extern int i1480_phy_fw_upload(struct i1480 *);
extern ssize_t i1480_cmd(struct i1480 *, const char *, size_t, size_t);
extern int i1480_rceb_check(const struct i1480 *,
const struct uwb_rceb *, const char *, u8,
u8, unsigned);
enum {
/* Vendor specific command type */
i1480_CET_VS1 = 0xfd,
/* i1480 commands */
i1480_CMD_SET_IP_MAS = 0x000e,
i1480_CMD_GET_MAC_PHY_INFO = 0x0003,
i1480_CMD_MPI_WRITE = 0x000f,
i1480_CMD_MPI_READ = 0x0010,
/* i1480 events */
#if i1480_FW > 0x00000302
i1480_EVT_CONFIRM = 0x0002,
i1480_EVT_RM_INIT_DONE = 0x0101,
i1480_EVT_DEV_ADD = 0x0103,
i1480_EVT_DEV_RM = 0x0104,
i1480_EVT_DEV_ID_CHANGE = 0x0105,
i1480_EVT_GET_MAC_PHY_INFO = i1480_CMD_GET_MAC_PHY_INFO,
#else
i1480_EVT_CONFIRM = 0x0002,
i1480_EVT_RM_INIT_DONE = 0x0101,
i1480_EVT_DEV_ADD = 0x0103,
i1480_EVT_DEV_RM = 0x0104,
i1480_EVT_DEV_ID_CHANGE = 0x0105,
i1480_EVT_GET_MAC_PHY_INFO = i1480_EVT_CONFIRM,
#endif
};
struct i1480_evt_confirm {
struct uwb_rceb rceb;
#ifdef i1480_RCEB_EXTENDED
__le16 wParamLength;
#endif
u8 bResultCode;
} __attribute__((packed));
struct i1480_rceb {
struct uwb_rceb rceb;
#ifdef i1480_RCEB_EXTENDED
__le16 wParamLength;
#endif
} __attribute__((packed));
/**
* Get MAC & PHY Information confirm event structure
*
* Confirm event returned by the command.
*/
struct i1480_evt_confirm_GMPI {
#if i1480_FW > 0x00000302
struct uwb_rceb rceb;
__le16 wParamLength;
__le16 status;
u8 mac_addr[6]; /* EUI-64 bit IEEE address [still 8 bytes?] */
u8 dev_addr[2];
__le16 mac_fw_rev; /* major = v >> 8; minor = v & 0xff */
u8 hw_rev;
u8 phy_vendor;
u8 phy_rev; /* major v = >> 8; minor = v & 0xff */
__le16 mac_caps;
u8 phy_caps[3];
u8 key_stores;
__le16 mcast_addr_stores;
u8 sec_mode_supported;
#else
struct uwb_rceb rceb;
u8 status;
u8 mac_addr[8]; /* EUI-64 bit IEEE address [still 8 bytes?] */
u8 dev_addr[2];
__le16 mac_fw_rev; /* major = v >> 8; minor = v & 0xff */
__le16 phy_fw_rev; /* major v = >> 8; minor = v & 0xff */
__le16 mac_caps;
u8 phy_caps;
u8 key_stores;
__le16 mcast_addr_stores;
u8 sec_mode_supported;
#endif
} __attribute__((packed));
struct i1480_cmd_mpi_write {
struct uwb_rccb rccb;
__le16 size;
u8 data[];
};
struct i1480_cmd_mpi_read {
struct uwb_rccb rccb;
__le16 size;
struct {
u8 page, offset;
} __attribute__((packed)) data[];
} __attribute__((packed));
struct i1480_evt_mpi_read {
struct uwb_rceb rceb;
#ifdef i1480_RCEB_EXTENDED
__le16 wParamLength;
#endif
u8 bResultCode;
__le16 size;
struct {
u8 page, offset, value;
} __attribute__((packed)) data[];
} __attribute__((packed));
#endif /* #ifndef __i1480_DFU_H__ */

View File

@@ -0,0 +1,509 @@
/*
* Intel Wireless UWB Link 1480
* MAC Firmware upload implementation
*
* 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.
*
*
* Implementation of the code for parsing the firmware file (extract
* the headers and binary code chunks) in the fw_*() functions. The
* code to upload pre and mac firmwares is the same, so it uses a
* common entry point in __mac_fw_upload(), which uses the i1480
* function pointers to push the firmware to the device.
*/
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/uwb.h>
#include "i1480-dfu.h"
/*
* Descriptor for a continuous segment of MAC fw data
*/
struct fw_hdr {
unsigned long address;
size_t length;
const u32 *bin;
struct fw_hdr *next;
};
/* Free a chain of firmware headers */
static
void fw_hdrs_free(struct fw_hdr *hdr)
{
struct fw_hdr *next;
while (hdr) {
next = hdr->next;
kfree(hdr);
hdr = next;
}
}
/* Fill a firmware header descriptor from a memory buffer */
static
int fw_hdr_load(struct i1480 *i1480, struct fw_hdr *hdr, unsigned hdr_cnt,
const char *_data, const u32 *data_itr, const u32 *data_top)
{
size_t hdr_offset = (const char *) data_itr - _data;
size_t remaining_size = (void *) data_top - (void *) data_itr;
if (data_itr + 2 > data_top) {
dev_err(i1480->dev, "fw hdr #%u/%zu: EOF reached in header at "
"offset %zu, limit %zu\n",
hdr_cnt, hdr_offset,
(const char *) data_itr + 2 - _data,
(const char *) data_top - _data);
return -EINVAL;
}
hdr->next = NULL;
hdr->address = le32_to_cpu(*data_itr++);
hdr->length = le32_to_cpu(*data_itr++);
hdr->bin = data_itr;
if (hdr->length > remaining_size) {
dev_err(i1480->dev, "fw hdr #%u/%zu: EOF reached in data; "
"chunk too long (%zu bytes), only %zu left\n",
hdr_cnt, hdr_offset, hdr->length, remaining_size);
return -EINVAL;
}
return 0;
}
/**
* Get a buffer where the firmware is supposed to be and create a
* chain of headers linking them together.
*
* @phdr: where to place the pointer to the first header (headers link
* to the next via the @hdr->next ptr); need to free the whole
* chain when done.
*
* @_data: Pointer to the data buffer.
*
* @_data_size: Size of the data buffer (bytes); data size has to be a
* multiple of 4. Function will fail if not.
*
* Goes over the whole binary blob; reads the first chunk and creates
* a fw hdr from it (which points to where the data is in @_data and
* the length of the chunk); then goes on to the next chunk until
* done. Each header is linked to the next.
*/
static
int fw_hdrs_load(struct i1480 *i1480, struct fw_hdr **phdr,
const char *_data, size_t data_size)
{
int result;
unsigned hdr_cnt = 0;
u32 *data = (u32 *) _data, *data_itr, *data_top;
struct fw_hdr *hdr, **prev_hdr = phdr;
result = -EINVAL;
/* Check size is ok and pointer is aligned */
if (data_size % sizeof(u32) != 0)
goto error;
if ((unsigned long) _data % sizeof(u16) != 0)
goto error;
*phdr = NULL;
data_itr = data;
data_top = (u32 *) (_data + data_size);
while (data_itr < data_top) {
result = -ENOMEM;
hdr = kmalloc(sizeof(*hdr), GFP_KERNEL);
if (hdr == NULL) {
dev_err(i1480->dev, "Cannot allocate fw header "
"for chunk #%u\n", hdr_cnt);
goto error_alloc;
}
result = fw_hdr_load(i1480, hdr, hdr_cnt,
_data, data_itr, data_top);
if (result < 0)
goto error_load;
data_itr += 2 + hdr->length;
*prev_hdr = hdr;
prev_hdr = &hdr->next;
hdr_cnt++;
};
*prev_hdr = NULL;
return 0;
error_load:
kfree(hdr);
error_alloc:
fw_hdrs_free(*phdr);
error:
return result;
}
/**
* Compares a chunk of fw with one in the devices's memory
*
* @i1480: Device instance
* @hdr: Pointer to the firmware chunk
* @returns: 0 if equal, < 0 errno on error. If > 0, it is the offset
* where the difference was found (plus one).
*
* Kind of dirty and simplistic, but does the trick in both the PCI
* and USB version. We do a quick[er] memcmp(), and if it fails, we do
* a byte-by-byte to find the offset.
*/
static
ssize_t i1480_fw_cmp(struct i1480 *i1480, struct fw_hdr *hdr)
{
ssize_t result = 0;
u32 src_itr = 0, cnt;
size_t size = hdr->length*sizeof(hdr->bin[0]);
size_t chunk_size;
u8 *bin = (u8 *) hdr->bin;
while (size > 0) {
chunk_size = size < i1480->buf_size ? size : i1480->buf_size;
result = i1480->read(i1480, hdr->address + src_itr, chunk_size);
if (result < 0) {
dev_err(i1480->dev, "error reading for verification: "
"%zd\n", result);
goto error;
}
if (memcmp(i1480->cmd_buf, bin + src_itr, result)) {
u8 *buf = i1480->cmd_buf;
for (cnt = 0; cnt < result; cnt++)
if (bin[src_itr + cnt] != buf[cnt]) {
dev_err(i1480->dev, "byte failed at "
"src_itr %u cnt %u [0x%02x "
"vs 0x%02x]\n", src_itr, cnt,
bin[src_itr + cnt], buf[cnt]);
result = src_itr + cnt + 1;
goto cmp_failed;
}
}
src_itr += result;
size -= result;
}
result = 0;
error:
cmp_failed:
return result;
}
/**
* Writes firmware headers to the device.
*
* @prd: PRD instance
* @hdr: Processed firmware
* @returns: 0 if ok, < 0 errno on error.
*/
static
int mac_fw_hdrs_push(struct i1480 *i1480, struct fw_hdr *hdr,
const char *fw_name, const char *fw_tag)
{
struct device *dev = i1480->dev;
ssize_t result = 0;
struct fw_hdr *hdr_itr;
int verif_retry_count;
/* Now, header by header, push them to the hw */
for (hdr_itr = hdr; hdr_itr != NULL; hdr_itr = hdr_itr->next) {
verif_retry_count = 0;
retry:
dev_dbg(dev, "fw chunk (%zu @ 0x%08lx)\n",
hdr_itr->length * sizeof(hdr_itr->bin[0]),
hdr_itr->address);
result = i1480->write(i1480, hdr_itr->address, hdr_itr->bin,
hdr_itr->length*sizeof(hdr_itr->bin[0]));
if (result < 0) {
dev_err(dev, "%s fw '%s': write failed (%zuB @ 0x%lx):"
" %zd\n", fw_tag, fw_name,
hdr_itr->length * sizeof(hdr_itr->bin[0]),
hdr_itr->address, result);
break;
}
result = i1480_fw_cmp(i1480, hdr_itr);
if (result < 0) {
dev_err(dev, "%s fw '%s': verification read "
"failed (%zuB @ 0x%lx): %zd\n",
fw_tag, fw_name,
hdr_itr->length * sizeof(hdr_itr->bin[0]),
hdr_itr->address, result);
break;
}
if (result > 0) { /* Offset where it failed + 1 */
result--;
dev_err(dev, "%s fw '%s': WARNING: verification "
"failed at 0x%lx: retrying\n",
fw_tag, fw_name, hdr_itr->address + result);
if (++verif_retry_count < 3)
goto retry; /* write this block again! */
dev_err(dev, "%s fw '%s': verification failed at 0x%lx: "
"tried %d times\n", fw_tag, fw_name,
hdr_itr->address + result, verif_retry_count);
result = -EINVAL;
break;
}
}
return result;
}
/** Puts the device in firmware upload mode.*/
static
int mac_fw_upload_enable(struct i1480 *i1480)
{
int result;
u32 reg = 0x800000c0;
u32 *buffer = (u32 *)i1480->cmd_buf;
if (i1480->hw_rev > 1)
reg = 0x8000d0d4;
result = i1480->read(i1480, reg, sizeof(u32));
if (result < 0)
goto error_cmd;
*buffer &= ~i1480_FW_UPLOAD_MODE_MASK;
result = i1480->write(i1480, reg, buffer, sizeof(u32));
if (result < 0)
goto error_cmd;
return 0;
error_cmd:
dev_err(i1480->dev, "can't enable fw upload mode: %d\n", result);
return result;
}
/** Gets the device out of firmware upload mode. */
static
int mac_fw_upload_disable(struct i1480 *i1480)
{
int result;
u32 reg = 0x800000c0;
u32 *buffer = (u32 *)i1480->cmd_buf;
if (i1480->hw_rev > 1)
reg = 0x8000d0d4;
result = i1480->read(i1480, reg, sizeof(u32));
if (result < 0)
goto error_cmd;
*buffer |= i1480_FW_UPLOAD_MODE_MASK;
result = i1480->write(i1480, reg, buffer, sizeof(u32));
if (result < 0)
goto error_cmd;
return 0;
error_cmd:
dev_err(i1480->dev, "can't disable fw upload mode: %d\n", result);
return result;
}
/**
* Generic function for uploading a MAC firmware.
*
* @i1480: Device instance
* @fw_name: Name of firmware file to upload.
* @fw_tag: Name of the firmware type (for messages)
* [eg: MAC, PRE]
* @do_wait: Wait for device to emit initialization done message (0
* for PRE fws, 1 for MAC fws).
* @returns: 0 if ok, < 0 errno on error.
*/
static
int __mac_fw_upload(struct i1480 *i1480, const char *fw_name,
const char *fw_tag)
{
int result;
const struct firmware *fw;
struct fw_hdr *fw_hdrs;
result = request_firmware(&fw, fw_name, i1480->dev);
if (result < 0) /* Up to caller to complain on -ENOENT */
goto out;
result = fw_hdrs_load(i1480, &fw_hdrs, fw->data, fw->size);
if (result < 0) {
dev_err(i1480->dev, "%s fw '%s': failed to parse firmware "
"file: %d\n", fw_tag, fw_name, result);
goto out_release;
}
result = mac_fw_upload_enable(i1480);
if (result < 0)
goto out_hdrs_release;
result = mac_fw_hdrs_push(i1480, fw_hdrs, fw_name, fw_tag);
mac_fw_upload_disable(i1480);
out_hdrs_release:
if (result >= 0)
dev_info(i1480->dev, "%s fw '%s': uploaded\n", fw_tag, fw_name);
else
dev_err(i1480->dev, "%s fw '%s': failed to upload (%d), "
"power cycle device\n", fw_tag, fw_name, result);
fw_hdrs_free(fw_hdrs);
out_release:
release_firmware(fw);
out:
return result;
}
/**
* Upload a pre-PHY firmware
*
*/
int i1480_pre_fw_upload(struct i1480 *i1480)
{
int result;
result = __mac_fw_upload(i1480, i1480->pre_fw_name, "PRE");
if (result == 0)
msleep(400);
return result;
}
/**
* Reset a the MAC and PHY
*
* @i1480: Device's instance
* @returns: 0 if ok, < 0 errno code on error
*
* We put the command on kmalloc'ed memory as some arches cannot do
* USB from the stack. The reply event is copied from an stage buffer,
* so it can be in the stack. See WUSB1.0[8.6.2.4] for more details.
*
* We issue the reset to make sure the UWB controller reinits the PHY;
* this way we can now if the PHY init went ok.
*/
static
int i1480_cmd_reset(struct i1480 *i1480)
{
int result;
struct uwb_rccb *cmd = (void *) i1480->cmd_buf;
struct i1480_evt_reset {
struct uwb_rceb rceb;
u8 bResultCode;
} __attribute__((packed)) *reply = (void *) i1480->evt_buf;
result = -ENOMEM;
cmd->bCommandType = UWB_RC_CET_GENERAL;
cmd->wCommand = cpu_to_le16(UWB_RC_CMD_RESET);
reply->rceb.bEventType = UWB_RC_CET_GENERAL;
reply->rceb.wEvent = UWB_RC_CMD_RESET;
result = i1480_cmd(i1480, "RESET", sizeof(*cmd), sizeof(*reply));
if (result < 0)
goto out;
if (reply->bResultCode != UWB_RC_RES_SUCCESS) {
dev_err(i1480->dev, "RESET: command execution failed: %u\n",
reply->bResultCode);
result = -EIO;
}
out:
return result;
}
/* Wait for the MAC FW to start running */
static
int i1480_fw_is_running_q(struct i1480 *i1480)
{
int cnt = 0;
int result;
u32 *val = (u32 *) i1480->cmd_buf;
for (cnt = 0; cnt < 10; cnt++) {
msleep(100);
result = i1480->read(i1480, 0x80080000, 4);
if (result < 0) {
dev_err(i1480->dev, "Can't read 0x8008000: %d\n", result);
goto out;
}
if (*val == 0x55555555UL) /* fw running? cool */
goto out;
}
dev_err(i1480->dev, "Timed out waiting for fw to start\n");
result = -ETIMEDOUT;
out:
return result;
}
/**
* Upload MAC firmware, wait for it to start
*
* @i1480: Device instance
* @fw_name: Name of the file that contains the firmware
*
* This has to be called after the pre fw has been uploaded (if
* there is any).
*/
int i1480_mac_fw_upload(struct i1480 *i1480)
{
int result = 0, deprecated_name = 0;
struct i1480_rceb *rcebe = (void *) i1480->evt_buf;
result = __mac_fw_upload(i1480, i1480->mac_fw_name, "MAC");
if (result == -ENOENT) {
result = __mac_fw_upload(i1480, i1480->mac_fw_name_deprecate,
"MAC");
deprecated_name = 1;
}
if (result < 0)
return result;
if (deprecated_name == 1)
dev_warn(i1480->dev,
"WARNING: firmware file name %s is deprecated, "
"please rename to %s\n",
i1480->mac_fw_name_deprecate, i1480->mac_fw_name);
result = i1480_fw_is_running_q(i1480);
if (result < 0)
goto error_fw_not_running;
result = i1480->rc_setup ? i1480->rc_setup(i1480) : 0;
if (result < 0) {
dev_err(i1480->dev, "Cannot setup after MAC fw upload: %d\n",
result);
goto error_setup;
}
result = i1480->wait_init_done(i1480); /* wait init'on */
if (result < 0) {
dev_err(i1480->dev, "MAC fw '%s': Initialization timed out "
"(%d)\n", i1480->mac_fw_name, result);
goto error_init_timeout;
}
/* verify we got the right initialization done event */
if (i1480->evt_result != sizeof(*rcebe)) {
dev_err(i1480->dev, "MAC fw '%s': initialization event returns "
"wrong size (%zu bytes vs %zu needed)\n",
i1480->mac_fw_name, i1480->evt_result, sizeof(*rcebe));
goto error_size;
}
result = -EIO;
if (i1480_rceb_check(i1480, &rcebe->rceb, NULL, 0, i1480_CET_VS1,
i1480_EVT_RM_INIT_DONE) < 0) {
dev_err(i1480->dev, "wrong initialization event 0x%02x/%04x/%02x "
"received; expected 0x%02x/%04x/00\n",
rcebe->rceb.bEventType, le16_to_cpu(rcebe->rceb.wEvent),
rcebe->rceb.bEventContext, i1480_CET_VS1,
i1480_EVT_RM_INIT_DONE);
goto error_init_timeout;
}
result = i1480_cmd_reset(i1480);
if (result < 0)
dev_err(i1480->dev, "MAC fw '%s': MBOA reset failed (%d)\n",
i1480->mac_fw_name, result);
error_fw_not_running:
error_init_timeout:
error_size:
error_setup:
return result;
}

View File

@@ -0,0 +1,203 @@
/*
* Intel Wireless UWB Link 1480
* PHY parameters upload
*
* 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.
*
*
* Code for uploading the PHY parameters to the PHY through the UWB
* Radio Control interface.
*
* We just send the data through the MPI interface using HWA-like
* commands and then reset the PHY to make sure it is ok.
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/firmware.h>
#include <linux/usb/wusb.h>
#include "i1480-dfu.h"
/**
* Write a value array to an address of the MPI interface
*
* @i1480: Device descriptor
* @data: Data array to write
* @size: Size of the data array
* @returns: 0 if ok, < 0 errno code on error.
*
* The data array is organized into pairs:
*
* ADDRESS VALUE
*
* ADDRESS is BE 16 bit unsigned, VALUE 8 bit unsigned. Size thus has
* to be a multiple of three.
*/
static
int i1480_mpi_write(struct i1480 *i1480, const void *data, size_t size)
{
int result;
struct i1480_cmd_mpi_write *cmd = i1480->cmd_buf;
struct i1480_evt_confirm *reply = i1480->evt_buf;
BUG_ON(size > 480);
result = -ENOMEM;
cmd->rccb.bCommandType = i1480_CET_VS1;
cmd->rccb.wCommand = cpu_to_le16(i1480_CMD_MPI_WRITE);
cmd->size = cpu_to_le16(size);
memcpy(cmd->data, data, size);
reply->rceb.bEventType = i1480_CET_VS1;
reply->rceb.wEvent = i1480_CMD_MPI_WRITE;
result = i1480_cmd(i1480, "MPI-WRITE", sizeof(*cmd) + size, sizeof(*reply));
if (result < 0)
goto out;
if (reply->bResultCode != UWB_RC_RES_SUCCESS) {
dev_err(i1480->dev, "MPI-WRITE: command execution failed: %d\n",
reply->bResultCode);
result = -EIO;
}
out:
return result;
}
/**
* Read a value array to from an address of the MPI interface
*
* @i1480: Device descriptor
* @data: where to place the read array
* @srcaddr: Where to read from
* @size: Size of the data read array
* @returns: 0 if ok, < 0 errno code on error.
*
* The command data array is organized into pairs ADDR0 ADDR1..., and
* the returned data in ADDR0 VALUE0 ADDR1 VALUE1...
*
* We generate the command array to be a sequential read and then
* rearrange the result.
*
* We use the i1480->cmd_buf for the command, i1480->evt_buf for the reply.
*
* As the reply has to fit in 512 bytes (i1480->evt_buffer), the max amount
* of values we can read is (512 - sizeof(*reply)) / 3
*/
static
int i1480_mpi_read(struct i1480 *i1480, u8 *data, u16 srcaddr, size_t size)
{
int result;
struct i1480_cmd_mpi_read *cmd = i1480->cmd_buf;
struct i1480_evt_mpi_read *reply = i1480->evt_buf;
unsigned cnt;
memset(i1480->cmd_buf, 0x69, 512);
memset(i1480->evt_buf, 0x69, 512);
BUG_ON(size > (i1480->buf_size - sizeof(*reply)) / 3);
result = -ENOMEM;
cmd->rccb.bCommandType = i1480_CET_VS1;
cmd->rccb.wCommand = cpu_to_le16(i1480_CMD_MPI_READ);
cmd->size = cpu_to_le16(3*size);
for (cnt = 0; cnt < size; cnt++) {
cmd->data[cnt].page = (srcaddr + cnt) >> 8;
cmd->data[cnt].offset = (srcaddr + cnt) & 0xff;
}
reply->rceb.bEventType = i1480_CET_VS1;
reply->rceb.wEvent = i1480_CMD_MPI_READ;
result = i1480_cmd(i1480, "MPI-READ", sizeof(*cmd) + 2*size,
sizeof(*reply) + 3*size);
if (result < 0)
goto out;
if (reply->bResultCode != UWB_RC_RES_SUCCESS) {
dev_err(i1480->dev, "MPI-READ: command execution failed: %d\n",
reply->bResultCode);
result = -EIO;
}
for (cnt = 0; cnt < size; cnt++) {
if (reply->data[cnt].page != (srcaddr + cnt) >> 8)
dev_err(i1480->dev, "MPI-READ: page inconsistency at "
"index %u: expected 0x%02x, got 0x%02x\n", cnt,
(srcaddr + cnt) >> 8, reply->data[cnt].page);
if (reply->data[cnt].offset != ((srcaddr + cnt) & 0x00ff))
dev_err(i1480->dev, "MPI-READ: offset inconsistency at "
"index %u: expected 0x%02x, got 0x%02x\n", cnt,
(srcaddr + cnt) & 0x00ff,
reply->data[cnt].offset);
data[cnt] = reply->data[cnt].value;
}
result = 0;
out:
return result;
}
/**
* Upload a PHY firmware, wait for it to start
*
* @i1480: Device instance
* @fw_name: Name of the file that contains the firmware
*
* We assume the MAC fw is up and running. This means we can use the
* MPI interface to write the PHY firmware. Once done, we issue an
* MBOA Reset, which will force the MAC to reset and reinitialize the
* PHY. If that works, we are ready to go.
*
* Max packet size for the MPI write is 512, so the max buffer is 480
* (which gives us 160 byte triads of MSB, LSB and VAL for the data).
*/
int i1480_phy_fw_upload(struct i1480 *i1480)
{
int result;
const struct firmware *fw;
const char *data_itr, *data_top;
const size_t MAX_BLK_SIZE = 480; /* 160 triads */
size_t data_size;
u8 phy_stat;
result = request_firmware(&fw, i1480->phy_fw_name, i1480->dev);
if (result < 0)
goto out;
/* Loop writing data in chunks as big as possible until done. */
for (data_itr = fw->data, data_top = data_itr + fw->size;
data_itr < data_top; data_itr += MAX_BLK_SIZE) {
data_size = min(MAX_BLK_SIZE, (size_t) (data_top - data_itr));
result = i1480_mpi_write(i1480, data_itr, data_size);
if (result < 0)
goto error_mpi_write;
}
/* Read MPI page 0, offset 6; if 0, PHY was initialized correctly. */
result = i1480_mpi_read(i1480, &phy_stat, 0x0006, 1);
if (result < 0) {
dev_err(i1480->dev, "PHY: can't get status: %d\n", result);
goto error_mpi_status;
}
if (phy_stat != 0) {
result = -ENODEV;
dev_info(i1480->dev, "error, PHY not ready: %u\n", phy_stat);
goto error_phy_status;
}
dev_info(i1480->dev, "PHY fw '%s': uploaded\n", i1480->phy_fw_name);
error_phy_status:
error_mpi_status:
error_mpi_write:
release_firmware(fw);
if (result < 0)
dev_err(i1480->dev, "PHY fw '%s': failed to upload (%d), "
"power cycle device\n", i1480->phy_fw_name, result);
out:
return result;
}

View File

@@ -0,0 +1,473 @@
/*
* Intel Wireless UWB Link 1480
* USB SKU firmware upload implementation
*
* 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 driver will prepare the i1480 device to behave as a real
* Wireless USB HWA adaptor by uploading the firmware.
*
* When the device is connected or driver is loaded, i1480_usb_probe()
* is called--this will allocate and initialize the device structure,
* fill in the pointers to the common functions (read, write,
* wait_init_done and cmd for HWA command execution) and once that is
* done, call the common firmware uploading routine. Then clean up and
* return -ENODEV, as we don't attach to the device.
*
* The rest are the basic ops we implement that the fw upload code
* uses to do its job. All the ops in the common code are i1480->NAME,
* the functions are i1480_usb_NAME().
*/
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/uwb.h>
#include <linux/usb/wusb.h>
#include <linux/usb/wusb-wa.h>
#include "i1480-dfu.h"
struct i1480_usb {
struct i1480 i1480;
struct usb_device *usb_dev;
struct usb_interface *usb_iface;
struct urb *neep_urb; /* URB for reading from EP1 */
};
static
void i1480_usb_init(struct i1480_usb *i1480_usb)
{
i1480_init(&i1480_usb->i1480);
}
static
int i1480_usb_create(struct i1480_usb *i1480_usb, struct usb_interface *iface)
{
struct usb_device *usb_dev = interface_to_usbdev(iface);
int result = -ENOMEM;
i1480_usb->usb_dev = usb_get_dev(usb_dev); /* bind the USB device */
i1480_usb->usb_iface = usb_get_intf(iface);
usb_set_intfdata(iface, i1480_usb); /* Bind the driver to iface0 */
i1480_usb->neep_urb = usb_alloc_urb(0, GFP_KERNEL);
if (i1480_usb->neep_urb == NULL)
goto error;
return 0;
error:
usb_set_intfdata(iface, NULL);
usb_put_intf(iface);
usb_put_dev(usb_dev);
return result;
}
static
void i1480_usb_destroy(struct i1480_usb *i1480_usb)
{
usb_kill_urb(i1480_usb->neep_urb);
usb_free_urb(i1480_usb->neep_urb);
usb_set_intfdata(i1480_usb->usb_iface, NULL);
usb_put_intf(i1480_usb->usb_iface);
usb_put_dev(i1480_usb->usb_dev);
}
/**
* Write a buffer to a memory address in the i1480 device
*
* @i1480: i1480 instance
* @memory_address:
* Address where to write the data buffer to.
* @buffer: Buffer to the data
* @size: Size of the buffer [has to be < 512].
* @returns: 0 if ok, < 0 errno code on error.
*
* Data buffers to USB cannot be on the stack or in vmalloc'ed areas,
* so we copy it to the local i1480 buffer before proceeding. In any
* case, we have a max size we can send, soooo.
*/
static
int i1480_usb_write(struct i1480 *i1480, u32 memory_address,
const void *buffer, size_t size)
{
int result = 0;
struct i1480_usb *i1480_usb = container_of(i1480, struct i1480_usb, i1480);
size_t buffer_size, itr = 0;
BUG_ON(size & 0x3); /* Needs to be a multiple of 4 */
while (size > 0) {
buffer_size = size < i1480->buf_size ? size : i1480->buf_size;
memcpy(i1480->cmd_buf, buffer + itr, buffer_size);
result = usb_control_msg(
i1480_usb->usb_dev, usb_sndctrlpipe(i1480_usb->usb_dev, 0),
0xf0, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
cpu_to_le16(memory_address & 0xffff),
cpu_to_le16((memory_address >> 16) & 0xffff),
i1480->cmd_buf, buffer_size, 100 /* FIXME: arbitrary */);
if (result < 0)
break;
itr += result;
memory_address += result;
size -= result;
}
return result;
}
/**
* Read a block [max size 512] of the device's memory to @i1480's buffer.
*
* @i1480: i1480 instance
* @memory_address:
* Address where to read from.
* @size: Size to read. Smaller than or equal to 512.
* @returns: >= 0 number of bytes written if ok, < 0 errno code on error.
*
* NOTE: if the memory address or block is incorrect, you might get a
* stall or a different memory read. Caller has to verify the
* memory address and size passed back in the @neh structure.
*/
static
int i1480_usb_read(struct i1480 *i1480, u32 addr, size_t size)
{
ssize_t result = 0, bytes = 0;
size_t itr, read_size = i1480->buf_size;
struct i1480_usb *i1480_usb = container_of(i1480, struct i1480_usb, i1480);
BUG_ON(size > i1480->buf_size);
BUG_ON(size & 0x3); /* Needs to be a multiple of 4 */
BUG_ON(read_size > 512);
if (addr >= 0x8000d200 && addr < 0x8000d400) /* Yeah, HW quirk */
read_size = 4;
for (itr = 0; itr < size; itr += read_size) {
size_t itr_addr = addr + itr;
size_t itr_size = min(read_size, size - itr);
result = usb_control_msg(
i1480_usb->usb_dev, usb_rcvctrlpipe(i1480_usb->usb_dev, 0),
0xf0, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
cpu_to_le16(itr_addr & 0xffff),
cpu_to_le16((itr_addr >> 16) & 0xffff),
i1480->cmd_buf + itr, itr_size,
100 /* FIXME: arbitrary */);
if (result < 0) {
dev_err(i1480->dev, "%s: USB read error: %zd\n",
__func__, result);
goto out;
}
if (result != itr_size) {
result = -EIO;
dev_err(i1480->dev,
"%s: partial read got only %zu bytes vs %zu expected\n",
__func__, result, itr_size);
goto out;
}
bytes += result;
}
result = bytes;
out:
return result;
}
/**
* Callback for reads on the notification/event endpoint
*
* Just enables the completion read handler.
*/
static
void i1480_usb_neep_cb(struct urb *urb)
{
struct i1480 *i1480 = urb->context;
struct device *dev = i1480->dev;
switch (urb->status) {
case 0:
break;
case -ECONNRESET: /* Not an error, but a controlled situation; */
case -ENOENT: /* (we killed the URB)...so, no broadcast */
dev_dbg(dev, "NEEP: reset/noent %d\n", urb->status);
break;
case -ESHUTDOWN: /* going away! */
dev_dbg(dev, "NEEP: down %d\n", urb->status);
break;
default:
dev_err(dev, "NEEP: unknown status %d\n", urb->status);
break;
}
i1480->evt_result = urb->actual_length;
complete(&i1480->evt_complete);
return;
}
/**
* Wait for the MAC FW to initialize
*
* MAC FW sends a 0xfd/0101/00 notification to EP1 when done
* initializing. Get that notification into i1480->evt_buf; upper layer
* will verify it.
*
* Set i1480->evt_result with the result of getting the event or its
* size (if succesful).
*
* Delivers the data directly to i1480->evt_buf
*/
static
int i1480_usb_wait_init_done(struct i1480 *i1480)
{
int result;
struct device *dev = i1480->dev;
struct i1480_usb *i1480_usb = container_of(i1480, struct i1480_usb, i1480);
struct usb_endpoint_descriptor *epd;
init_completion(&i1480->evt_complete);
i1480->evt_result = -EINPROGRESS;
epd = &i1480_usb->usb_iface->cur_altsetting->endpoint[0].desc;
usb_fill_int_urb(i1480_usb->neep_urb, i1480_usb->usb_dev,
usb_rcvintpipe(i1480_usb->usb_dev, epd->bEndpointAddress),
i1480->evt_buf, i1480->buf_size,
i1480_usb_neep_cb, i1480, epd->bInterval);
result = usb_submit_urb(i1480_usb->neep_urb, GFP_KERNEL);
if (result < 0) {
dev_err(dev, "init done: cannot submit NEEP read: %d\n",
result);
goto error_submit;
}
/* Wait for the USB callback to get the data */
result = wait_for_completion_interruptible_timeout(
&i1480->evt_complete, HZ);
if (result <= 0) {
result = result == 0 ? -ETIMEDOUT : result;
goto error_wait;
}
usb_kill_urb(i1480_usb->neep_urb);
return 0;
error_wait:
usb_kill_urb(i1480_usb->neep_urb);
error_submit:
i1480->evt_result = result;
return result;
}
/**
* Generic function for issuing commands to the i1480
*
* @i1480: i1480 instance
* @cmd_name: Name of the command (for error messages)
* @cmd: Pointer to command buffer
* @cmd_size: Size of the command buffer
* @reply: Buffer for the reply event
* @reply_size: Expected size back (including RCEB); the reply buffer
* is assumed to be as big as this.
* @returns: >= 0 size of the returned event data if ok,
* < 0 errno code on error.
*
* Arms the NE handle, issues the command to the device and checks the
* basics of the reply event.
*/
static
int i1480_usb_cmd(struct i1480 *i1480, const char *cmd_name, size_t cmd_size)
{
int result;
struct device *dev = i1480->dev;
struct i1480_usb *i1480_usb = container_of(i1480, struct i1480_usb, i1480);
struct usb_endpoint_descriptor *epd;
struct uwb_rccb *cmd = i1480->cmd_buf;
u8 iface_no;
/* Post a read on the notification & event endpoint */
iface_no = i1480_usb->usb_iface->cur_altsetting->desc.bInterfaceNumber;
epd = &i1480_usb->usb_iface->cur_altsetting->endpoint[0].desc;
usb_fill_int_urb(
i1480_usb->neep_urb, i1480_usb->usb_dev,
usb_rcvintpipe(i1480_usb->usb_dev, epd->bEndpointAddress),
i1480->evt_buf, i1480->buf_size,
i1480_usb_neep_cb, i1480, epd->bInterval);
result = usb_submit_urb(i1480_usb->neep_urb, GFP_KERNEL);
if (result < 0) {
dev_err(dev, "%s: cannot submit NEEP read: %d\n",
cmd_name, result);
goto error_submit_ep1;
}
/* Now post the command on EP0 */
result = usb_control_msg(
i1480_usb->usb_dev, usb_sndctrlpipe(i1480_usb->usb_dev, 0),
WA_EXEC_RC_CMD,
USB_DIR_OUT | USB_RECIP_INTERFACE | USB_TYPE_CLASS,
0, iface_no,
cmd, cmd_size,
100 /* FIXME: this is totally arbitrary */);
if (result < 0) {
dev_err(dev, "%s: control request failed: %d\n",
cmd_name, result);
goto error_submit_ep0;
}
return result;
error_submit_ep0:
usb_kill_urb(i1480_usb->neep_urb);
error_submit_ep1:
return result;
}
/*
* Probe a i1480 device for uploading firmware.
*
* We attach only to interface #0, which is the radio control interface.
*/
static
int i1480_usb_probe(struct usb_interface *iface, const struct usb_device_id *id)
{
struct i1480_usb *i1480_usb;
struct i1480 *i1480;
struct device *dev = &iface->dev;
int result;
result = -ENODEV;
if (iface->cur_altsetting->desc.bInterfaceNumber != 0) {
dev_dbg(dev, "not attaching to iface %d\n",
iface->cur_altsetting->desc.bInterfaceNumber);
goto error;
}
if (iface->num_altsetting > 1
&& interface_to_usbdev(iface)->descriptor.idProduct == 0xbabe) {
/* Need altsetting #1 [HW QUIRK] or EP1 won't work */
result = usb_set_interface(interface_to_usbdev(iface), 0, 1);
if (result < 0)
dev_warn(dev,
"can't set altsetting 1 on iface 0: %d\n",
result);
}
result = -ENOMEM;
i1480_usb = kzalloc(sizeof(*i1480_usb), GFP_KERNEL);
if (i1480_usb == NULL) {
dev_err(dev, "Unable to allocate instance\n");
goto error;
}
i1480_usb_init(i1480_usb);
i1480 = &i1480_usb->i1480;
i1480->buf_size = 512;
i1480->cmd_buf = kmalloc(2 * i1480->buf_size, GFP_KERNEL);
if (i1480->cmd_buf == NULL) {
dev_err(dev, "Cannot allocate transfer buffers\n");
result = -ENOMEM;
goto error_buf_alloc;
}
i1480->evt_buf = i1480->cmd_buf + i1480->buf_size;
result = i1480_usb_create(i1480_usb, iface);
if (result < 0) {
dev_err(dev, "Cannot create instance: %d\n", result);
goto error_create;
}
/* setup the fops and upload the firmware */
i1480->pre_fw_name = "i1480-pre-phy-0.0.bin";
i1480->mac_fw_name = "i1480-usb-0.0.bin";
i1480->mac_fw_name_deprecate = "ptc-0.0.bin";
i1480->phy_fw_name = "i1480-phy-0.0.bin";
i1480->dev = &iface->dev;
i1480->write = i1480_usb_write;
i1480->read = i1480_usb_read;
i1480->rc_setup = NULL;
i1480->wait_init_done = i1480_usb_wait_init_done;
i1480->cmd = i1480_usb_cmd;
result = i1480_fw_upload(&i1480_usb->i1480); /* the real thing */
if (result >= 0) {
usb_reset_device(i1480_usb->usb_dev);
result = -ENODEV; /* we don't want to bind to the iface */
}
i1480_usb_destroy(i1480_usb);
error_create:
kfree(i1480->cmd_buf);
error_buf_alloc:
kfree(i1480_usb);
error:
return result;
}
#define i1480_USB_DEV(v, p) \
{ \
.match_flags = USB_DEVICE_ID_MATCH_DEVICE \
| USB_DEVICE_ID_MATCH_DEV_INFO \
| USB_DEVICE_ID_MATCH_INT_INFO, \
.idVendor = (v), \
.idProduct = (p), \
.bDeviceClass = 0xff, \
.bDeviceSubClass = 0xff, \
.bDeviceProtocol = 0xff, \
.bInterfaceClass = 0xff, \
.bInterfaceSubClass = 0xff, \
.bInterfaceProtocol = 0xff, \
}
/** USB device ID's that we handle */
static struct usb_device_id i1480_usb_id_table[] = {
i1480_USB_DEV(0x8086, 0xdf3b),
i1480_USB_DEV(0x15a9, 0x0005),
i1480_USB_DEV(0x07d1, 0x3802),
i1480_USB_DEV(0x050d, 0x305a),
i1480_USB_DEV(0x3495, 0x3007),
{},
};
MODULE_DEVICE_TABLE(usb, i1480_usb_id_table);
static struct usb_driver i1480_dfu_driver = {
.name = "i1480-dfu-usb",
.id_table = i1480_usb_id_table,
.probe = i1480_usb_probe,
.disconnect = NULL,
};
/*
* Initialize the i1480 DFU driver.
*
* We also need to register our function for guessing event sizes.
*/
static int __init i1480_dfu_driver_init(void)
{
return usb_register(&i1480_dfu_driver);
}
module_init(i1480_dfu_driver_init);
static void __exit i1480_dfu_driver_exit(void)
{
usb_deregister(&i1480_dfu_driver);
}
module_exit(i1480_dfu_driver_exit);
MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
MODULE_DESCRIPTION("Intel Wireless UWB Link 1480 firmware uploader for USB");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,99 @@
/*
* Intel Wireless UWB Link 1480
* Event Size tables for Wired Adaptors
*
* 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/init.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/uwb.h>
#include "dfu/i1480-dfu.h"
/** Event size table for wEvents 0x00XX */
static struct uwb_est_entry i1480_est_fd00[] = {
/* Anybody expecting this response has to use
* neh->extra_size to specify the real size that will
* come back. */
[i1480_EVT_CONFIRM] = { .size = sizeof(struct i1480_evt_confirm) },
[i1480_CMD_SET_IP_MAS] = { .size = sizeof(struct i1480_evt_confirm) },
#ifdef i1480_RCEB_EXTENDED
[0x09] = {
.size = sizeof(struct i1480_rceb),
.offset = 1 + offsetof(struct i1480_rceb, wParamLength),
},
#endif
};
/** Event size table for wEvents 0x01XX */
static struct uwb_est_entry i1480_est_fd01[] = {
[0xff & i1480_EVT_RM_INIT_DONE] = { .size = sizeof(struct i1480_rceb) },
[0xff & i1480_EVT_DEV_ADD] = { .size = sizeof(struct i1480_rceb) + 9 },
[0xff & i1480_EVT_DEV_RM] = { .size = sizeof(struct i1480_rceb) + 9 },
[0xff & i1480_EVT_DEV_ID_CHANGE] = {
.size = sizeof(struct i1480_rceb) + 2 },
};
static int i1480_est_init(void)
{
int result = uwb_est_register(i1480_CET_VS1, 0x00, 0x8086, 0x0c3b,
i1480_est_fd00,
ARRAY_SIZE(i1480_est_fd00));
if (result < 0) {
printk(KERN_ERR "Can't register EST table fd00: %d\n", result);
return result;
}
result = uwb_est_register(i1480_CET_VS1, 0x01, 0x8086, 0x0c3b,
i1480_est_fd01, ARRAY_SIZE(i1480_est_fd01));
if (result < 0) {
printk(KERN_ERR "Can't register EST table fd01: %d\n", result);
return result;
}
return 0;
}
module_init(i1480_est_init);
static void i1480_est_exit(void)
{
uwb_est_unregister(i1480_CET_VS1, 0x00, 0x8086, 0x0c3b,
i1480_est_fd00, ARRAY_SIZE(i1480_est_fd00));
uwb_est_unregister(i1480_CET_VS1, 0x01, 0x8086, 0x0c3b,
i1480_est_fd01, ARRAY_SIZE(i1480_est_fd01));
}
module_exit(i1480_est_exit);
MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
MODULE_DESCRIPTION("i1480's Vendor Specific Event Size Tables");
MODULE_LICENSE("GPL");
/**
* USB device ID's that we handle
*
* [so we are loaded when this kind device is connected]
*/
static struct usb_device_id i1480_est_id_table[] = {
{ USB_DEVICE(0x8086, 0xdf3b), },
{ USB_DEVICE(0x8086, 0x0c3b), },
{ },
};
MODULE_DEVICE_TABLE(usb, i1480_est_id_table);

View File

@@ -0,0 +1,200 @@
/*
* 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__ */

View File

@@ -0,0 +1,8 @@
obj-$(CONFIG_UWB_I1480U_WLP) += i1480u-wlp.o
i1480u-wlp-objs := \
lc.o \
netdev.o \
rx.o \
sysfs.o \
tx.o

View File

@@ -0,0 +1,283 @@
/*
* 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__ */

View File

@@ -0,0 +1,423 @@
/*
* 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/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");

View File

@@ -0,0 +1,330 @@
/*
* 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/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);
}

View File

@@ -0,0 +1,473 @@
/*
* 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/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;
}

View File

@@ -0,0 +1,407 @@
/*
* 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);
}

View File

@@ -0,0 +1,583 @@
/*
* 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 "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);
}
}