******************************************************************************/
#include "ipw2200.h"
+#include <linux/version.h>
-#define IPW2200_VERSION "1.0.5"
+#define IPW2200_VERSION "git-1.0.8"
#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2200/2915 Network Driver"
-#define DRV_COPYRIGHT "Copyright(c) 2003-2004 Intel Corporation"
+#define DRV_COPYRIGHT "Copyright(c) 2003-2005 Intel Corporation"
#define DRV_VERSION IPW2200_VERSION
#define ETH_P_80211_STATS (ETH_P_80211_RAW + 1)
MODULE_AUTHOR(DRV_COPYRIGHT);
MODULE_LICENSE("GPL");
+static int cmdlog = 0;
static int debug = 0;
static int channel = 0;
static int mode = 0;
*qos_param);
#endif /* CONFIG_IPW_QOS */
+static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev);
static void ipw_remove_current_network(struct ipw_priv *priv);
static void ipw_rx(struct ipw_priv *priv);
static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
static void ipw_set_hwcrypto_keys(struct ipw_priv *);
static void ipw_send_wep_keys(struct ipw_priv *, int);
-static char *snprint_line(char *buf, size_t count,
- const u8 * data, u32 len, u32 ofs)
+static int ipw_is_valid_channel(struct ieee80211_device *, u8);
+static int ipw_channel_to_index(struct ieee80211_device *, u8);
+static u8 ipw_freq_to_channel(struct ieee80211_device *, u32);
+static int ipw_set_geo(struct ieee80211_device *, const struct ieee80211_geo *);
+static const struct ieee80211_geo *ipw_get_geo(struct ieee80211_device *);
+
+static int snprint_line(char *buf, size_t count,
+ const u8 * data, u32 len, u32 ofs)
{
int out, i, j, l;
char c;
out += snprintf(buf + out, count - out, " ");
}
- return buf;
+ return out;
}
static void printk_buf(int level, const u8 * data, u32 len)
return;
while (len) {
- printk(KERN_DEBUG "%s\n",
- snprint_line(line, sizeof(line), &data[ofs],
- min(len, 16U), ofs));
+ snprint_line(line, sizeof(line), &data[ofs],
+ min(len, 16U), ofs);
+ printk(KERN_DEBUG "%s\n", line);
ofs += 16;
len -= min(len, 16U);
}
}
+static int snprintk_buf(u8 * output, size_t size, const u8 * data, size_t len)
+{
+ size_t out = size;
+ u32 ofs = 0;
+ int total = 0;
+
+ while (size && len) {
+ out = snprint_line(output, size, &data[ofs],
+ min_t(size_t, len, 16U), ofs);
+
+ ofs += 16;
+ output += out;
+ size -= out;
+ len -= min_t(size_t, len, 16U);
+ total += out;
+ }
+ return total;
+}
+
static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg);
#define ipw_read_reg32(a, b) _ipw_read_reg32(a, b)
#define ipw_read32(ipw, ofs) __ipw_read32(__FILE__, __LINE__, ipw, ofs)
static void _ipw_read_indirect(struct ipw_priv *, u32, u8 *, int);
-#define ipw_read_indirect(a, b, c, d) \
- IPW_DEBUG_IO("%s %d: read_indirect(0x%08X) %d bytes\n", __FILE__, __LINE__, (u32)(b), d); \
- _ipw_read_indirect(a, b, c, d)
+static inline void __ipw_read_indirect(const char *f, int l,
+ struct ipw_priv *a, u32 b, u8 * c, int d)
+{
+ IPW_DEBUG_IO("%s %d: read_indirect(0x%08X) %d bytes\n", f, l, (u32) (b),
+ d);
+ _ipw_read_indirect(a, b, c, d);
+}
+
+#define ipw_read_indirect(a, b, c, d) __ipw_read_indirect(__FILE__, __LINE__, a, b, c, d)
static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * data,
int num);
ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
}
+#ifdef CONFIG_IPW_DEBUG
static char *ipw_error_desc(u32 val)
{
switch (val) {
}
}
-static void ipw_dump_nic_error_log(struct ipw_priv *priv)
+static void ipw_dump_error_log(struct ipw_priv *priv,
+ struct ipw_fw_error *error)
{
- u32 desc, time, blink1, blink2, ilink1, ilink2, idata, i, count, base;
-
- base = ipw_read32(priv, IPWSTATUS_ERROR_LOG);
- count = ipw_read_reg32(priv, base);
+ u32 i;
- if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) {
- IPW_ERROR("Start IPW Error Log Dump:\n");
- IPW_ERROR("Status: 0x%08X, Config: %08X\n",
- priv->status, priv->config);
+ if (!error) {
+ IPW_ERROR("Error allocating and capturing error log. "
+ "Nothing to dump.\n");
+ return;
}
- for (i = ERROR_START_OFFSET;
- i <= count * ERROR_ELEM_SIZE; i += ERROR_ELEM_SIZE) {
- desc = ipw_read_reg32(priv, base + i);
- time = ipw_read_reg32(priv, base + i + 1 * sizeof(u32));
- blink1 = ipw_read_reg32(priv, base + i + 2 * sizeof(u32));
- blink2 = ipw_read_reg32(priv, base + i + 3 * sizeof(u32));
- ilink1 = ipw_read_reg32(priv, base + i + 4 * sizeof(u32));
- ilink2 = ipw_read_reg32(priv, base + i + 5 * sizeof(u32));
- idata = ipw_read_reg32(priv, base + i + 6 * sizeof(u32));
+ IPW_ERROR("Start IPW Error Log Dump:\n");
+ IPW_ERROR("Status: 0x%08X, Config: %08X\n",
+ error->status, error->config);
+ for (i = 0; i < error->elem_len; i++)
IPW_ERROR("%s %i 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
- ipw_error_desc(desc), time, blink1, blink2,
- ilink1, ilink2, idata);
- }
+ ipw_error_desc(error->elem[i].desc),
+ error->elem[i].time,
+ error->elem[i].blink1,
+ error->elem[i].blink2,
+ error->elem[i].link1,
+ error->elem[i].link2, error->elem[i].data);
+ for (i = 0; i < error->log_len; i++)
+ IPW_ERROR("%i\t0x%08x\t%i\n",
+ error->log[i].time,
+ error->log[i].data, error->log[i].event);
}
-
-static void ipw_dump_nic_event_log(struct ipw_priv *priv)
-{
- u32 ev, time, data, i, count, base;
-
- base = ipw_read32(priv, IPW_EVENT_LOG);
- count = ipw_read_reg32(priv, base);
-
- if (EVENT_START_OFFSET <= count * EVENT_ELEM_SIZE)
- IPW_ERROR("Start IPW Event Log Dump:\n");
-
- for (i = EVENT_START_OFFSET;
- i <= count * EVENT_ELEM_SIZE; i += EVENT_ELEM_SIZE) {
- ev = ipw_read_reg32(priv, base + i);
- time = ipw_read_reg32(priv, base + i + 1 * sizeof(u32));
- data = ipw_read_reg32(priv, base + i + 2 * sizeof(u32));
-
-#ifdef CONFIG_IPW_DEBUG
- IPW_ERROR("%i\t0x%08x\t%i\n", time, data, ev);
#endif
- }
-}
static inline int ipw_is_init(struct ipw_priv *priv)
{
static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO,
show_debug_level, store_debug_level);
+static inline u32 ipw_get_event_log_len(struct ipw_priv *priv)
+{
+ return ipw_read_reg32(priv, ipw_read32(priv, IPW_EVENT_LOG));
+}
+
+static void ipw_capture_event_log(struct ipw_priv *priv,
+ u32 log_len, struct ipw_event *log)
+{
+ u32 base;
+
+ if (log_len) {
+ base = ipw_read32(priv, IPW_EVENT_LOG);
+ ipw_read_indirect(priv, base + sizeof(base) + sizeof(u32),
+ (u8 *) log, sizeof(*log) * log_len);
+ }
+}
+
+static struct ipw_fw_error *ipw_alloc_error_log(struct ipw_priv *priv)
+{
+ struct ipw_fw_error *error;
+ u32 log_len = ipw_get_event_log_len(priv);
+ u32 base = ipw_read32(priv, IPW_ERROR_LOG);
+ u32 elem_len = ipw_read_reg32(priv, base);
+
+ error = kmalloc(sizeof(*error) +
+ sizeof(*error->elem) * elem_len +
+ sizeof(*error->log) * log_len, GFP_ATOMIC);
+ if (!error) {
+ IPW_ERROR("Memory allocation for firmware error log "
+ "failed.\n");
+ return NULL;
+ }
+ error->jiffies = jiffies;
+ error->status = priv->status;
+ error->config = priv->config;
+ error->elem_len = elem_len;
+ error->log_len = log_len;
+ error->elem = (struct ipw_error_elem *)error->payload;
+ error->log = (struct ipw_event *)(error->elem +
+ (sizeof(*error->elem) * elem_len));
+
+ ipw_capture_event_log(priv, log_len, error->log);
+
+ if (elem_len)
+ ipw_read_indirect(priv, base + sizeof(base), (u8 *) error->elem,
+ sizeof(*error->elem) * elem_len);
+
+ return error;
+}
+
+static void ipw_free_error_log(struct ipw_fw_error *error)
+{
+ if (error)
+ kfree(error);
+}
+
+static ssize_t show_event_log(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct ipw_priv *priv = dev_get_drvdata(d);
+ u32 log_len = ipw_get_event_log_len(priv);
+ struct ipw_event log[log_len];
+ u32 len = 0, i;
+
+ ipw_capture_event_log(priv, log_len, log);
+
+ len += snprintf(buf + len, PAGE_SIZE - len, "%08X", log_len);
+ for (i = 0; i < log_len; i++)
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "\n%08X%08X%08X",
+ log[i].time, log[i].event, log[i].data);
+ len += snprintf(buf + len, PAGE_SIZE - len, "\n");
+ return len;
+}
+
+static DEVICE_ATTR(event_log, S_IRUGO, show_event_log, NULL);
+
+static ssize_t show_error(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct ipw_priv *priv = dev_get_drvdata(d);
+ u32 len = 0, i;
+ if (!priv->error)
+ return 0;
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "%08lX%08X%08X%08X",
+ priv->error->jiffies,
+ priv->error->status,
+ priv->error->config, priv->error->elem_len);
+ for (i = 0; i < priv->error->elem_len; i++)
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "\n%08X%08X%08X%08X%08X%08X%08X",
+ priv->error->elem[i].time,
+ priv->error->elem[i].desc,
+ priv->error->elem[i].blink1,
+ priv->error->elem[i].blink2,
+ priv->error->elem[i].link1,
+ priv->error->elem[i].link2,
+ priv->error->elem[i].data);
+
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "\n%08X", priv->error->log_len);
+ for (i = 0; i < priv->error->log_len; i++)
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "\n%08X%08X%08X",
+ priv->error->log[i].time,
+ priv->error->log[i].event,
+ priv->error->log[i].data);
+ len += snprintf(buf + len, PAGE_SIZE - len, "\n");
+ return len;
+}
+
+static ssize_t clear_error(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ipw_priv *priv = dev_get_drvdata(d);
+ if (priv->error) {
+ ipw_free_error_log(priv->error);
+ priv->error = NULL;
+ }
+ return count;
+}
+
+static DEVICE_ATTR(error, S_IRUGO | S_IWUSR, show_error, clear_error);
+
+static ssize_t show_cmd_log(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct ipw_priv *priv = dev_get_drvdata(d);
+ u32 len = 0, i;
+ if (!priv->cmdlog)
+ return 0;
+ for (i = (priv->cmdlog_pos + 1) % priv->cmdlog_len;
+ (i != priv->cmdlog_pos) && (PAGE_SIZE - len);
+ i = (i + 1) % priv->cmdlog_len) {
+ len +=
+ snprintf(buf + len, PAGE_SIZE - len,
+ "\n%08lX%08X%08X%08X\n", priv->cmdlog[i].jiffies,
+ priv->cmdlog[i].retcode, priv->cmdlog[i].cmd.cmd,
+ priv->cmdlog[i].cmd.len);
+ len +=
+ snprintk_buf(buf + len, PAGE_SIZE - len,
+ (u8 *) priv->cmdlog[i].cmd.param,
+ priv->cmdlog[i].cmd.len);
+ len += snprintf(buf + len, PAGE_SIZE - len, "\n");
+ }
+ len += snprintf(buf + len, PAGE_SIZE - len, "\n");
+ return len;
+}
+
+static DEVICE_ATTR(cmd_log, S_IRUGO, show_cmd_log, NULL);
+
static ssize_t show_scan_age(struct device *d, struct device_attribute *attr,
char *buf)
{
static DEVICE_ATTR(nic_type, S_IRUGO, show_nic_type, NULL);
-static ssize_t dump_error_log(struct device *d,
- struct device_attribute *attr, const char *buf,
- size_t count)
-{
- char *p = (char *)buf;
-
- if (p[0] == '1')
- ipw_dump_nic_error_log((struct ipw_priv *)d->driver_data);
-
- return strnlen(buf, count);
-}
-
-static DEVICE_ATTR(dump_errors, S_IWUSR, NULL, dump_error_log);
-
-static ssize_t dump_event_log(struct device *d,
- struct device_attribute *attr, const char *buf,
- size_t count)
-{
- char *p = (char *)buf;
-
- if (p[0] == '1')
- ipw_dump_nic_event_log((struct ipw_priv *)d->driver_data);
-
- return strnlen(buf, count);
-}
-
-static DEVICE_ATTR(dump_events, S_IWUSR, NULL, dump_event_log);
-
static ssize_t show_ucode_version(struct device *d,
struct device_attribute *attr, char *buf)
{
break;
}
- if (ieee80211_is_valid_channel(priv->ieee, channel))
+ if (ipw_is_valid_channel(priv->ieee, channel))
priv->speed_scan[pos++] = channel;
else
IPW_WARNING("Skipping invalid channel request: %d\n",
if (inta & IPW_INTA_BIT_FATAL_ERROR) {
IPW_ERROR("Firmware error detected. Restarting.\n");
+ if (priv->error) {
+ IPW_ERROR("Sysfs 'error' log already exists.\n");
#ifdef CONFIG_IPW_DEBUG
- if (ipw_debug_level & IPW_DL_FW_ERRORS) {
- ipw_dump_nic_error_log(priv);
- ipw_dump_nic_event_log(priv);
- }
+ if (ipw_debug_level & IPW_DL_FW_ERRORS) {
+ struct ipw_fw_error *error =
+ ipw_alloc_error_log(priv);
+ ipw_dump_error_log(priv, error);
+ if (error)
+ ipw_free_error_log(error);
+ }
+#endif
+ } else {
+ priv->error = ipw_alloc_error_log(priv);
+ if (priv->error)
+ IPW_ERROR("Sysfs 'error' log captured.\n");
+ else
+ IPW_ERROR("Error allocating sysfs 'error' "
+ "log.\n");
+#ifdef CONFIG_IPW_DEBUG
+ if (ipw_debug_level & IPW_DL_FW_ERRORS)
+ ipw_dump_error_log(priv, priv->error);
#endif
+ }
+
/* XXX: If hardware encryption is for WPA/WPA2,
* we have to notify the supplicant. */
if (priv->ieee->sec.encrypt) {
spin_unlock_irqrestore(&priv->lock, flags);
}
-#ifdef CONFIG_IPW_DEBUG
#define IPW_CMD(x) case IPW_CMD_ ## x : return #x
static char *get_cmd_string(u8 cmd)
{
return "UNKNOWN";
}
}
-#endif
#define HOST_COMPLETE_TIMEOUT HZ
static int ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd)
spin_lock_irqsave(&priv->lock, flags);
if (priv->status & STATUS_HCMD_ACTIVE) {
- IPW_ERROR("Already sending a command\n");
+ IPW_ERROR("Failed to send %s: Already sending a command.\n",
+ get_cmd_string(cmd->cmd));
spin_unlock_irqrestore(&priv->lock, flags);
- return -1;
+ return -EAGAIN;
}
priv->status |= STATUS_HCMD_ACTIVE;
+ if (priv->cmdlog) {
+ priv->cmdlog[priv->cmdlog_pos].jiffies = jiffies;
+ priv->cmdlog[priv->cmdlog_pos].cmd.cmd = cmd->cmd;
+ priv->cmdlog[priv->cmdlog_pos].cmd.len = cmd->len;
+ memcpy(priv->cmdlog[priv->cmdlog_pos].cmd.param, cmd->param,
+ cmd->len);
+ priv->cmdlog[priv->cmdlog_pos].retcode = -1;
+ }
+
IPW_DEBUG_HC("%s command (#%d) %d bytes: 0x%08X\n",
get_cmd_string(cmd->cmd), cmd->cmd, cmd->len,
priv->status);
rc = ipw_queue_tx_hcmd(priv, cmd->cmd, &cmd->param, cmd->len, 0);
if (rc) {
priv->status &= ~STATUS_HCMD_ACTIVE;
+ IPW_ERROR("Failed to send %s: Reason %d\n",
+ get_cmd_string(cmd->cmd), rc);
spin_unlock_irqrestore(&priv->lock, flags);
- return rc;
+ goto exit;
}
spin_unlock_irqrestore(&priv->lock, flags);
if (rc == 0) {
spin_lock_irqsave(&priv->lock, flags);
if (priv->status & STATUS_HCMD_ACTIVE) {
- IPW_DEBUG_INFO("Command completion failed out after "
- "%dms.\n",
- 1000 * (HOST_COMPLETE_TIMEOUT / HZ));
+ IPW_ERROR("Failed to send %s: Command timed out.\n",
+ get_cmd_string(cmd->cmd));
priv->status &= ~STATUS_HCMD_ACTIVE;
spin_unlock_irqrestore(&priv->lock, flags);
- return -EIO;
+ rc = -EIO;
+ goto exit;
}
spin_unlock_irqrestore(&priv->lock, flags);
- }
+ } else
+ rc = 0;
if (priv->status & STATUS_RF_KILL_HW) {
- IPW_DEBUG_INFO("Command aborted due to RF Kill Switch\n");
- return -EIO;
+ IPW_ERROR("Failed to send %s: Aborted due to RF kill switch.\n",
+ get_cmd_string(cmd->cmd));
+ rc = -EIO;
+ goto exit;
}
- return 0;
+ exit:
+ if (priv->cmdlog) {
+ priv->cmdlog[priv->cmdlog_pos++].retcode = rc;
+ priv->cmdlog_pos %= priv->cmdlog_len;
+ }
+ return rc;
}
static int ipw_send_host_complete(struct ipw_priv *priv)
return -1;
}
- if (ipw_send_cmd(priv, &cmd)) {
- IPW_ERROR("failed to send HOST_COMPLETE command\n");
- return -1;
- }
-
- return 0;
+ return ipw_send_cmd(priv, &cmd);
}
static int ipw_send_system_config(struct ipw_priv *priv,
}
memcpy(cmd.param, config, sizeof(*config));
- if (ipw_send_cmd(priv, &cmd)) {
- IPW_ERROR("failed to send SYSTEM_CONFIG command\n");
- return -1;
- }
-
- return 0;
+ return ipw_send_cmd(priv, &cmd);
}
static int ipw_send_ssid(struct ipw_priv *priv, u8 * ssid, int len)
}
memcpy(cmd.param, ssid, cmd.len);
- if (ipw_send_cmd(priv, &cmd)) {
- IPW_ERROR("failed to send SSID command\n");
- return -1;
- }
-
- return 0;
+ return ipw_send_cmd(priv, &cmd);
}
static int ipw_send_adapter_address(struct ipw_priv *priv, u8 * mac)
priv->net_dev->name, MAC_ARG(mac));
memcpy(cmd.param, mac, ETH_ALEN);
- if (ipw_send_cmd(priv, &cmd)) {
- IPW_ERROR("failed to send ADAPTER_ADDRESS command\n");
- return -1;
- }
-
- return 0;
+ return ipw_send_cmd(priv, &cmd);
}
/*
};
memcpy(cmd.param, request, sizeof(*request));
- if (ipw_send_cmd(priv, &cmd)) {
- IPW_ERROR("failed to send SCAN_REQUEST_EXT command\n");
- return -1;
- }
-
- return 0;
+ return ipw_send_cmd(priv, &cmd);
}
static int ipw_send_scan_abort(struct ipw_priv *priv)
return -1;
}
- if (ipw_send_cmd(priv, &cmd)) {
- IPW_ERROR("failed to send SCAN_ABORT command\n");
- return -1;
- }
-
- return 0;
+ return ipw_send_cmd(priv, &cmd);
}
static int ipw_set_sensitivity(struct ipw_priv *priv, u16 sens)
struct ipw_sensitivity_calib *calib = (struct ipw_sensitivity_calib *)
&cmd.param;
calib->beacon_rssi_raw = sens;
- if (ipw_send_cmd(priv, &cmd)) {
- IPW_ERROR("failed to send SENSITIVITY CALIB command\n");
- return -1;
- }
-
- return 0;
+ return ipw_send_cmd(priv, &cmd);
}
static int ipw_send_associate(struct ipw_priv *priv,
}
memcpy(cmd.param, &tmp_associate, sizeof(*associate));
- if (ipw_send_cmd(priv, &cmd)) {
- IPW_ERROR("failed to send ASSOCIATE command\n");
- return -1;
- }
-
- return 0;
+ return ipw_send_cmd(priv, &cmd);
}
static int ipw_send_supported_rates(struct ipw_priv *priv,
}
memcpy(cmd.param, rates, sizeof(*rates));
- if (ipw_send_cmd(priv, &cmd)) {
- IPW_ERROR("failed to send SUPPORTED_RATES command\n");
- return -1;
- }
-
- return 0;
+ return ipw_send_cmd(priv, &cmd);
}
static int ipw_set_random_seed(struct ipw_priv *priv)
get_random_bytes(&cmd.param, sizeof(u32));
- if (ipw_send_cmd(priv, &cmd)) {
- IPW_ERROR("failed to send SEED_NUMBER command\n");
- return -1;
- }
-
- return 0;
+ return ipw_send_cmd(priv, &cmd);
}
static int ipw_send_card_disable(struct ipw_priv *priv, u32 phy_off)
*((u32 *) & cmd.param) = phy_off;
- if (ipw_send_cmd(priv, &cmd)) {
- IPW_ERROR("failed to send CARD_DISABLE command\n");
- return -1;
- }
-
- return 0;
+ return ipw_send_cmd(priv, &cmd);
}
static int ipw_send_tx_power(struct ipw_priv *priv, struct ipw_tx_power *power)
}
memcpy(cmd.param, power, sizeof(*power));
- if (ipw_send_cmd(priv, &cmd)) {
- IPW_ERROR("failed to send TX_POWER command\n");
- return -1;
+ return ipw_send_cmd(priv, &cmd);
+}
+
+static int ipw_set_tx_power(struct ipw_priv *priv)
+{
+ const struct ieee80211_geo *geo = ipw_get_geo(priv->ieee);
+ struct ipw_tx_power tx_power;
+ s8 max_power;
+ int i;
+
+ memset(&tx_power, 0, sizeof(tx_power));
+
+ /* configure device for 'G' band */
+ tx_power.ieee_mode = IPW_G_MODE;
+ tx_power.num_channels = geo->bg_channels;
+ for (i = 0; i < geo->bg_channels; i++) {
+ max_power = geo->bg[i].max_power;
+ tx_power.channels_tx_power[i].channel_number =
+ geo->bg[i].channel;
+ tx_power.channels_tx_power[i].tx_power = max_power ?
+ min(max_power, priv->tx_power) : priv->tx_power;
}
+ if (ipw_send_tx_power(priv, &tx_power))
+ return -EIO;
+
+ /* configure device to also handle 'B' band */
+ tx_power.ieee_mode = IPW_B_MODE;
+ if (ipw_send_tx_power(priv, &tx_power))
+ return -EIO;
+ /* configure device to also handle 'A' band */
+ if (priv->ieee->abg_true) {
+ tx_power.ieee_mode = IPW_A_MODE;
+ tx_power.num_channels = geo->a_channels;
+ for (i = 0; i < tx_power.num_channels; i++) {
+ max_power = geo->a[i].max_power;
+ tx_power.channels_tx_power[i].channel_number =
+ geo->a[i].channel;
+ tx_power.channels_tx_power[i].tx_power = max_power ?
+ min(max_power, priv->tx_power) : priv->tx_power;
+ }
+ if (ipw_send_tx_power(priv, &tx_power))
+ return -EIO;
+ }
return 0;
}
}
memcpy(cmd.param, &rts_threshold, sizeof(rts_threshold));
- if (ipw_send_cmd(priv, &cmd)) {
- IPW_ERROR("failed to send RTS_THRESHOLD command\n");
- return -1;
- }
-
- return 0;
+ return ipw_send_cmd(priv, &cmd);
}
static int ipw_send_frag_threshold(struct ipw_priv *priv, u16 frag)
}
memcpy(cmd.param, &frag_threshold, sizeof(frag_threshold));
- if (ipw_send_cmd(priv, &cmd)) {
- IPW_ERROR("failed to send FRAG_THRESHOLD command\n");
- return -1;
- }
-
- return 0;
+ return ipw_send_cmd(priv, &cmd);
}
static int ipw_send_power_mode(struct ipw_priv *priv, u32 mode)
break;
}
- if (ipw_send_cmd(priv, &cmd)) {
- IPW_ERROR("failed to send POWER_MODE command\n");
- return -1;
- }
-
- return 0;
+ return ipw_send_cmd(priv, &cmd);
}
static int ipw_send_retry_limit(struct ipw_priv *priv, u8 slimit, u8 llimit)
}
memcpy(cmd.param, &retry_limit, sizeof(retry_limit));
- if (ipw_send_cmd(priv, &cmd)) {
- IPW_ERROR("failed to send RETRY_LIMIT command\n");
- return -1;
- }
-
- return 0;
+ return ipw_send_cmd(priv, &cmd);
}
/*
};
#define IPW_FW_MAJOR_VERSION 2
-#define IPW_FW_MINOR_VERSION 3
+#define IPW_FW_MINOR_VERSION 4
#define IPW_FW_MINOR(x) ((x & 0xff) >> 8)
#define IPW_FW_MAJOR(x) (x & 0xff)
rc = ipw_load_firmware(priv, bootfw->data + sizeof(struct fw_header),
bootfw->size - sizeof(struct fw_header));
if (rc < 0) {
- IPW_ERROR("Unable to load boot firmware\n");
+ IPW_ERROR("Unable to load boot firmware: %d\n", rc);
goto error;
}
rc = ipw_load_ucode(priv, ucode->data + sizeof(struct fw_header),
ucode->size - sizeof(struct fw_header));
if (rc < 0) {
- IPW_ERROR("Unable to load ucode\n");
+ IPW_ERROR("Unable to load ucode: %d\n", rc);
goto error;
}
sizeof(struct fw_header),
firmware->size - sizeof(struct fw_header));
if (rc < 0) {
- IPW_ERROR("Unable to load firmware\n");
+ IPW_ERROR("Unable to load firmware: %d\n", rc);
goto error;
}
ipw_queue_tx_free(priv, &priv->txq[3]);
}
-static void inline __maybe_wake_tx(struct ipw_priv *priv)
-{
- if (netif_running(priv->net_dev)) {
- switch (priv->port_type) {
- case DCR_TYPE_MU_BSS:
- case DCR_TYPE_MU_IBSS:
- if (!(priv->status & STATUS_ASSOCIATED))
- return;
- }
- netif_wake_queue(priv->net_dev);
- }
-
-}
-
static inline void ipw_create_bssid(struct ipw_priv *priv, u8 * bssid)
{
/* First 3 bytes are manufacturer */
{
int err;
- if (!(priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED))) {
+ if (priv->status & STATUS_ASSOCIATING) {
+ IPW_DEBUG_ASSOC("Disassociating while associating.\n");
+ queue_work(priv->workqueue, &priv->disassociate);
+ return;
+ }
+
+ if (!(priv->status & STATUS_ASSOCIATED)) {
IPW_DEBUG_ASSOC("Disassociating while not associated.\n");
return;
}
priv->assoc_request.assoc_type = HC_DISASSOC_QUIET;
else
priv->assoc_request.assoc_type = HC_DISASSOCIATE;
+
err = ipw_send_associate(priv, &priv->assoc_request);
if (err) {
IPW_DEBUG_HC("Attempt to send [dis]associate command "
up(&priv->sem);
}
+static void ipw_system_config(void *data)
+{
+ struct ipw_priv *priv = data;
+ ipw_send_system_config(priv, &priv->sys_config);
+}
+
struct ipw_status_code {
u16 status;
const char *reason;
up(&priv->sem);
}
+/* Missed beacon behavior:
+ * 1st missed -> roaming_threshold, just wait, don't do any scan/roam.
+ * roaming_threshold -> disassociate_threshold, scan and roam for better signal.
+ * Above disassociate threshold, give up and stop scanning.
+ * Roaming is disabled if disassociate_threshold <= roaming_threshold */
static inline void ipw_handle_missed_beacon(struct ipw_priv *priv,
int missed_count)
{
return;
}
- if (missed_count > priv->roaming_threshold) {
+ if (missed_count > priv->roaming_threshold &&
+ missed_count <= priv->disassociate_threshold) {
/* If we are not already roaming, set the ROAM
- * bit in the status and kick off a scan */
+ * bit in the status and kick off a scan.
+ * This can happen several times before we reach
+ * disassociate_threshold. */
IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
"Missed beacon: %d - initiate "
"roaming\n", missed_count);
priv->status &= ~STATUS_ASSOCIATING;
priv->status |= STATUS_ASSOCIATED;
+ queue_work(priv->workqueue,
+ &priv->system_config);
#ifdef CONFIG_IPW_QOS
#define IPW_GET_PACKET_STYPE(x) WLAN_FC_GET_STYPE( \
== IEEE80211_STYPE_ASSOC_RESP)) {
if ((sizeof
(struct
- ieee80211_assoc_response_frame)
+ ieee80211_assoc_response)
<= notif->size)
&& (notif->size <= 2314)) {
struct
ieee80211_rx_mgt(priv->
ieee,
(struct
- ieee80211_hdr
+ ieee80211_hdr_4addr
*)
¬if->u.raw, &stats);
}
priv->status &=
~(STATUS_SCANNING | STATUS_SCAN_ABORTING);
+ wake_up_interruptible(&priv->wait_state);
cancel_delayed_work(&priv->scan_check);
if (priv->status & STATUS_EXIT_PENDING)
STATUS_DISASSOCIATING)))
queue_work(priv->workqueue, &priv->associate);
else if (priv->status & STATUS_ROAMING) {
- /* If a scan completed and we are in roam mode, then
- * the scan that completed was the one requested as a
- * result of entering roam... so, schedule the
- * roam work */
- queue_work(priv->workqueue, &priv->roam);
+ if (x->status == SCAN_COMPLETED_STATUS_COMPLETE)
+ /* If a scan completed and we are in roam mode, then
+ * the scan that completed was the one requested as a
+ * result of entering roam... so, schedule the
+ * roam work */
+ queue_work(priv->workqueue,
+ &priv->roam);
+ else
+ /* Don't schedule if we aborted the scan */
+ priv->status &= ~STATUS_ROAMING;
} else if (priv->status & STATUS_SCAN_PENDING)
queue_work(priv->workqueue,
&priv->request_scan);
priv->tx_packets++;
}
done:
- if (ipw_queue_space(q) > q->low_mark && qindex >= 0)
- __maybe_wake_tx(priv);
+ if ((ipw_queue_space(q) > q->low_mark) &&
+ (qindex >= 0) &&
+ (priv->status & STATUS_ASSOCIATED) && netif_running(priv->net_dev))
+ netif_wake_queue(priv->net_dev);
used = q->first_empty - q->last_used;
if (used < 0)
used += q->n_bd;
return 0;
}
+ if (!priv->ieee->wpa_enabled && (network->wpa_ie_len > 0 ||
+ network->rsn_ie_len > 0)) {
+ IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
+ "because of WPA capability mismatch.\n",
+ escape_essid(network->ssid, network->ssid_len),
+ MAC_ARG(network->bssid));
+ return 0;
+ }
+
if ((priv->config & CFG_STATIC_BSSID) &&
memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
return 0;
}
+ /* Filter out invalid channel in current GEO */
+ if (!ipw_is_valid_channel(priv->ieee, network->channel)) {
+ IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
+ "because of invalid channel in current GEO\n",
+ escape_essid(network->ssid, network->ssid_len),
+ MAC_ARG(network->bssid));
+ return 0;
+ }
+
/* Ensure that the rates supported by the driver are compatible with
* this AP, including verification of basic rates (mandatory) */
if (!ipw_compatible_rates(priv, network, &rates)) {
static void ipw_adhoc_create(struct ipw_priv *priv,
struct ieee80211_network *network)
{
- const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee);
+ const struct ieee80211_geo *geo = ipw_get_geo(priv->ieee);
int i;
/*
* FW fatal error.
*
*/
- switch (ieee80211_is_valid_channel(priv->ieee, priv->channel)) {
+ switch (ipw_is_valid_channel(priv->ieee, priv->channel)) {
case IEEE80211_52GHZ_BAND:
network->mode = IEEE_A;
- i = ieee80211_channel_to_index(priv->ieee, priv->channel);
+ i = ipw_channel_to_index(priv->ieee, priv->channel);
if (i == -1)
BUG();
if (geo->a[i].flags & IEEE80211_CH_PASSIVE_ONLY) {
network->mode = IEEE_G;
else
network->mode = IEEE_B;
+ i = ipw_channel_to_index(priv->ieee, priv->channel);
+ if (i == -1)
+ BUG();
+ if (geo->bg[i].flags & IEEE80211_CH_PASSIVE_ONLY) {
+ IPW_WARNING("Overriding invalid channel\n");
+ priv->channel = geo->bg[0].channel;
+ }
break;
default:
key->tx_counter[0] = 0;
key->tx_counter[1] = 0;
- if (ipw_send_cmd(priv, &cmd)) {
- IPW_ERROR("failed to send TGI_TX_KEY command\n");
- return;
- }
+ ipw_send_cmd(priv, &cmd);
}
static void ipw_send_wep_keys(struct ipw_priv *priv, int type)
key->key_size = priv->ieee->sec.key_sizes[i];
memcpy(key->key, priv->ieee->sec.keys[i], key->key_size);
- if (ipw_send_cmd(priv, &cmd)) {
- IPW_ERROR("failed to send WEP_KEY command\n");
- return;
- }
+ ipw_send_cmd(priv, &cmd);
}
}
-static void ipw_set_hwcrypto_keys(struct ipw_priv *priv)
+static void ipw_set_hw_decrypt_unicast(struct ipw_priv *priv, int level)
{
- switch (priv->ieee->sec.level) {
- case SEC_LEVEL_3:
- if (!(priv->ieee->sec.flags & SEC_ACTIVE_KEY))
- break;
-
- ipw_send_tgi_tx_key(priv, DCT_FLAG_EXT_SECURITY_CCM,
- priv->ieee->sec.active_key);
- ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_CCM);
+ if (priv->ieee->host_encrypt)
+ return;
+ switch (level) {
+ case SEC_LEVEL_3:
priv->sys_config.disable_unicast_decryption = 0;
- priv->sys_config.disable_multicast_decryption = 0;
priv->ieee->host_decrypt = 0;
- if (ipw_send_system_config(priv, &priv->sys_config))
- IPW_ERROR("ipw_send_system_config failed\n");
-
break;
case SEC_LEVEL_2:
- if (!(priv->ieee->sec.flags & SEC_ACTIVE_KEY))
- break;
-
- ipw_send_tgi_tx_key(priv, DCT_FLAG_EXT_SECURITY_TKIP,
- priv->ieee->sec.active_key);
-
priv->sys_config.disable_unicast_decryption = 1;
- priv->sys_config.disable_multicast_decryption = 1;
priv->ieee->host_decrypt = 1;
- if (ipw_send_system_config(priv, &priv->sys_config))
- IPW_ERROR("ipw_send_system_config failed\n");
-
break;
case SEC_LEVEL_1:
- ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_WEP);
-
priv->sys_config.disable_unicast_decryption = 0;
- priv->sys_config.disable_multicast_decryption = 0;
priv->ieee->host_decrypt = 0;
- if (ipw_send_system_config(priv, &priv->sys_config))
- IPW_ERROR("ipw_send_system_config failed\n");
-
break;
case SEC_LEVEL_0:
+ priv->sys_config.disable_unicast_decryption = 1;
+ break;
default:
break;
}
}
-static void ipw_adhoc_check(void *data)
+static void ipw_set_hw_decrypt_multicast(struct ipw_priv *priv, int level)
{
- struct ipw_priv *priv = data;
+ if (priv->ieee->host_encrypt)
+ return;
+
+ switch (level) {
+ case SEC_LEVEL_3:
+ priv->sys_config.disable_multicast_decryption = 0;
+ break;
+ case SEC_LEVEL_2:
+ priv->sys_config.disable_multicast_decryption = 1;
+ break;
+ case SEC_LEVEL_1:
+ priv->sys_config.disable_multicast_decryption = 0;
+ break;
+ case SEC_LEVEL_0:
+ priv->sys_config.disable_multicast_decryption = 1;
+ break;
+ default:
+ break;
+ }
+}
+
+static void ipw_set_hwcrypto_keys(struct ipw_priv *priv)
+{
+ switch (priv->ieee->sec.level) {
+ case SEC_LEVEL_3:
+ if (priv->ieee->sec.flags & SEC_ACTIVE_KEY)
+ ipw_send_tgi_tx_key(priv,
+ DCT_FLAG_EXT_SECURITY_CCM,
+ priv->ieee->sec.active_key);
+
+ if (!priv->ieee->host_mc_decrypt)
+ ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_CCM);
+ break;
+ case SEC_LEVEL_2:
+ if (priv->ieee->sec.flags & SEC_ACTIVE_KEY)
+ ipw_send_tgi_tx_key(priv,
+ DCT_FLAG_EXT_SECURITY_TKIP,
+ priv->ieee->sec.active_key);
+ break;
+ case SEC_LEVEL_1:
+ ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_WEP);
+ ipw_set_hw_decrypt_unicast(priv, priv->ieee->sec.level);
+ ipw_set_hw_decrypt_multicast(priv, priv->ieee->sec.level);
+ break;
+ case SEC_LEVEL_0:
+ default:
+ break;
+ }
+}
+
+static void ipw_adhoc_check(void *data)
+{
+ struct ipw_priv *priv = data;
if (priv->missed_adhoc_beacons++ > priv->disassociate_threshold &&
!(priv->config & CFG_ADHOC_PERSIST)) {
const struct ieee80211_geo *geo;
int i;
- geo = ieee80211_get_geo(priv->ieee);
+ geo = ipw_get_geo(priv->ieee);
if (priv->ieee->freq_band & IEEE80211_52GHZ_BAND) {
int start = channel_index;
continue;
channel_index++;
scan->channels_list[channel_index] = geo->a[i].channel;
- ipw_set_scan_type(scan, channel_index, scan_type);
+ ipw_set_scan_type(scan, channel_index,
+ geo->a[i].
+ flags & IEEE80211_CH_PASSIVE_ONLY ?
+ IPW_SCAN_PASSIVE_FULL_DWELL_SCAN :
+ scan_type);
}
if (start != channel_index) {
if (priv->ieee->freq_band & IEEE80211_24GHZ_BAND) {
int start = channel_index;
if (priv->config & CFG_SPEED_SCAN) {
+ int index;
u8 channels[IEEE80211_24GHZ_CHANNELS] = {
/* nop out the list */
[0] = 0
priv->speed_scan_pos++;
channel_index++;
scan->channels_list[channel_index] = channel;
+ index =
+ ipw_channel_to_index(priv->ieee, channel);
ipw_set_scan_type(scan, channel_index,
- scan_type);
+ geo->bg[index].
+ flags &
+ IEEE80211_CH_PASSIVE_ONLY ?
+ IPW_SCAN_PASSIVE_FULL_DWELL_SCAN
+ : scan_type);
}
} else {
for (i = 0; i < geo->bg_channels; i++) {
scan->channels_list[channel_index] =
geo->bg[i].channel;
ipw_set_scan_type(scan, channel_index,
- scan_type);
+ geo->bg[i].
+ flags &
+ IEEE80211_CH_PASSIVE_ONLY ?
+ IPW_SCAN_PASSIVE_FULL_DWELL_SCAN
+ : scan_type);
}
}
scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] =
cpu_to_le16(20);
- scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(20);
+ scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(120);
scan.full_scan_index = cpu_to_le32(ieee80211_get_scans(priv->ieee));
u8 channel;
u8 band = 0;
- switch (ieee80211_is_valid_channel(priv->ieee, priv->channel)) {
+ switch (ipw_is_valid_channel(priv->ieee, priv->channel)) {
case IEEE80211_52GHZ_BAND:
band = (u8) (IPW_A_MODE << 6) | 1;
channel = priv->channel;
up(&priv->sem);
}
-#if WIRELESS_EXT < 18
-/* Support for wpa_supplicant before WE-18, deprecated. */
-
-/* following definitions must match definitions in driver_ipw.c */
-
-#define IPW_IOCTL_WPA_SUPPLICANT SIOCIWFIRSTPRIV+30
-
-#define IPW_CMD_SET_WPA_PARAM 1
-#define IPW_CMD_SET_WPA_IE 2
-#define IPW_CMD_SET_ENCRYPTION 3
-#define IPW_CMD_MLME 4
-
-#define IPW_PARAM_WPA_ENABLED 1
-#define IPW_PARAM_TKIP_COUNTERMEASURES 2
-#define IPW_PARAM_DROP_UNENCRYPTED 3
-#define IPW_PARAM_PRIVACY_INVOKED 4
-#define IPW_PARAM_AUTH_ALGS 5
-#define IPW_PARAM_IEEE_802_1X 6
-
-#define IPW_MLME_STA_DEAUTH 1
-#define IPW_MLME_STA_DISASSOC 2
-
-#define IPW_CRYPT_ERR_UNKNOWN_ALG 2
-#define IPW_CRYPT_ERR_UNKNOWN_ADDR 3
-#define IPW_CRYPT_ERR_CRYPT_INIT_FAILED 4
-#define IPW_CRYPT_ERR_KEY_SET_FAILED 5
-#define IPW_CRYPT_ERR_TX_KEY_SET_FAILED 6
-#define IPW_CRYPT_ERR_CARD_CONF_FAILED 7
-
-#define IPW_CRYPT_ALG_NAME_LEN 16
-
-struct ipw_param {
- u32 cmd;
- u8 sta_addr[ETH_ALEN];
- union {
- struct {
- u8 name;
- u32 value;
- } wpa_param;
- struct {
- u32 len;
- u8 reserved[32];
- u8 data[0];
- } wpa_ie;
- struct {
- u32 command;
- u32 reason_code;
- } mlme;
- struct {
- u8 alg[IPW_CRYPT_ALG_NAME_LEN];
- u8 set_tx;
- u32 err;
- u8 idx;
- u8 seq[8]; /* sequence counter (set: RX, get: TX) */
- u16 key_len;
- u8 key[0];
- } crypt;
-
- } u;
-};
-
-/* end of driver_ipw.c code */
-#endif
-
static int ipw_wpa_enable(struct ipw_priv *priv, int value)
{
/* This is called when wpa_supplicant loads and closes the driver
* interface. */
+ priv->ieee->wpa_enabled = value;
return 0;
}
-#if WIRELESS_EXT < 18
-#define IW_AUTH_ALG_OPEN_SYSTEM 0x1
-#define IW_AUTH_ALG_SHARED_KEY 0x2
-#endif
-
static int ipw_wpa_set_auth_algs(struct ipw_priv *priv, int value)
{
struct ieee80211_device *ieee = priv->ieee;
IPW_DEBUG_HC("HOST_CMD_RSN_CAPABILITIES\n");
memcpy(cmd.param, capabilities, length);
- if (ipw_send_cmd(priv, &cmd)) {
- IPW_ERROR("failed to send HOST_CMD_RSN_CAPABILITIES command\n");
- return -1;
- }
- return 0;
-}
-
-#if WIRELESS_EXT < 18
-static int ipw_wpa_set_param(struct net_device *dev, u8 name, u32 value)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- struct ieee80211_crypt_data *crypt;
- unsigned long flags;
- int ret = 0;
-
- switch (name) {
- case IPW_PARAM_WPA_ENABLED:
- ret = ipw_wpa_enable(priv, value);
- break;
-
- case IPW_PARAM_TKIP_COUNTERMEASURES:
- crypt = priv->ieee->crypt[priv->ieee->tx_keyidx];
- if (!crypt || !crypt->ops->set_flags || !crypt->ops->get_flags) {
- IPW_WARNING("Can't set TKIP countermeasures: "
- "crypt not set!\n");
- break;
- }
-
- flags = crypt->ops->get_flags(crypt->priv);
-
- if (value)
- flags |= IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
- else
- flags &= ~IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
-
- crypt->ops->set_flags(flags, crypt->priv);
-
- break;
-
- case IPW_PARAM_DROP_UNENCRYPTED:{
- /* HACK:
- *
- * wpa_supplicant calls set_wpa_enabled when the driver
- * is loaded and unloaded, regardless of if WPA is being
- * used. No other calls are made which can be used to
- * determine if encryption will be used or not prior to
- * association being expected. If encryption is not being
- * used, drop_unencrypted is set to false, else true -- we
- * can use this to determine if the CAP_PRIVACY_ON bit should
- * be set.
- */
- struct ieee80211_security sec = {
- .flags = SEC_ENABLED,
- .enabled = value,
- };
- priv->ieee->drop_unencrypted = value;
- /* We only change SEC_LEVEL for open mode. Others
- * are set by ipw_wpa_set_encryption.
- */
- if (!value) {
- sec.flags |= SEC_LEVEL;
- sec.level = SEC_LEVEL_0;
- } else {
- sec.flags |= SEC_LEVEL;
- sec.level = SEC_LEVEL_1;
- }
- if (priv->ieee->set_security)
- priv->ieee->set_security(priv->ieee->dev, &sec);
- break;
- }
-
- case IPW_PARAM_PRIVACY_INVOKED:
- priv->ieee->privacy_invoked = value;
- break;
-
- case IPW_PARAM_AUTH_ALGS:
- ret = ipw_wpa_set_auth_algs(priv, value);
- break;
-
- case IPW_PARAM_IEEE_802_1X:
- priv->ieee->ieee802_1x = value;
- break;
-
- default:
- IPW_ERROR("%s: Unknown WPA param: %d\n", dev->name, name);
- ret = -EOPNOTSUPP;
- }
-
- return ret;
+ return ipw_send_cmd(priv, &cmd);
}
-static int ipw_wpa_mlme(struct net_device *dev, int command, int reason)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- int ret = 0;
-
- switch (command) {
- case IPW_MLME_STA_DEAUTH:
- // silently ignore
- break;
-
- case IPW_MLME_STA_DISASSOC:
- ipw_disassociate(priv);
- break;
-
- default:
- IPW_ERROR("%s: Unknown MLME request: %d\n", dev->name, command);
- ret = -EOPNOTSUPP;
- }
-
- return ret;
-}
-
-static int ipw_wpa_set_wpa_ie(struct net_device *dev,
- struct ipw_param *param, int plen)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- struct ieee80211_device *ieee = priv->ieee;
- u8 *buf;
-
- if (param->u.wpa_ie.len > MAX_WPA_IE_LEN ||
- (param->u.wpa_ie.len && param->u.wpa_ie.data == NULL))
- return -EINVAL;
-
- if (param->u.wpa_ie.len) {
- buf = kmalloc(param->u.wpa_ie.len, GFP_KERNEL);
- if (buf == NULL)
- return -ENOMEM;
-
- memcpy(buf, param->u.wpa_ie.data, param->u.wpa_ie.len);
- kfree(ieee->wpa_ie);
- ieee->wpa_ie = buf;
- ieee->wpa_ie_len = param->u.wpa_ie.len;
- } else {
- kfree(ieee->wpa_ie);
- ieee->wpa_ie = NULL;
- ieee->wpa_ie_len = 0;
- }
-
- ipw_wpa_assoc_frame(priv, ieee->wpa_ie, ieee->wpa_ie_len);
- return 0;
-}
-
-/* implementation borrowed from hostap driver */
-
-static int ipw_wpa_set_encryption(struct net_device *dev,
- struct ipw_param *param, int param_len)
-{
- int ret = 0;
- struct ipw_priv *priv = ieee80211_priv(dev);
- struct ieee80211_device *ieee = priv->ieee;
- struct ieee80211_crypto_ops *ops;
- struct ieee80211_crypt_data **crypt;
-
- struct ieee80211_security sec = {
- .flags = 0,
- };
-
- param->u.crypt.err = 0;
- param->u.crypt.alg[IPW_CRYPT_ALG_NAME_LEN - 1] = '\0';
-
- if (param_len !=
- (int)((char *)param->u.crypt.key - (char *)param) +
- param->u.crypt.key_len) {
- IPW_DEBUG_INFO("Len mismatch %d, %d\n", param_len,
- param->u.crypt.key_len);
- return -EINVAL;
- }
- if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
- param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
- param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) {
- if (param->u.crypt.idx >= WEP_KEYS)
- return -EINVAL;
- crypt = &ieee->crypt[param->u.crypt.idx];
- } else {
- return -EINVAL;
- }
-
- sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
- if (strcmp(param->u.crypt.alg, "none") == 0) {
- if (crypt) {
- sec.enabled = 0;
- sec.encrypt = 0;
- sec.level = SEC_LEVEL_0;
- sec.flags |= SEC_LEVEL;
- ieee80211_crypt_delayed_deinit(ieee, crypt);
- }
- goto done;
- }
- sec.enabled = 1;
- sec.encrypt = 1;
-
- /* IPW HW cannot build TKIP MIC, host decryption still needed. */
- if (strcmp(param->u.crypt.alg, "TKIP") == 0)
- ieee->host_encrypt_msdu = 1;
-
- if (!(ieee->host_encrypt || ieee->host_encrypt_msdu ||
- ieee->host_decrypt))
- goto skip_host_crypt;
-
- ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
- if (ops == NULL && strcmp(param->u.crypt.alg, "WEP") == 0) {
- request_module("ieee80211_crypt_wep");
- ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
- } else if (ops == NULL && strcmp(param->u.crypt.alg, "TKIP") == 0) {
- request_module("ieee80211_crypt_tkip");
- ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
- } else if (ops == NULL && strcmp(param->u.crypt.alg, "CCMP") == 0) {
- request_module("ieee80211_crypt_ccmp");
- ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
- }
- if (ops == NULL) {
- IPW_DEBUG_INFO("%s: unknown crypto alg '%s'\n",
- dev->name, param->u.crypt.alg);
- param->u.crypt.err = IPW_CRYPT_ERR_UNKNOWN_ALG;
- ret = -EINVAL;
- goto done;
- }
-
- if (*crypt == NULL || (*crypt)->ops != ops) {
- struct ieee80211_crypt_data *new_crypt;
-
- ieee80211_crypt_delayed_deinit(ieee, crypt);
-
- new_crypt = (struct ieee80211_crypt_data *)
- kmalloc(sizeof(*new_crypt), GFP_KERNEL);
- if (new_crypt == NULL) {
- ret = -ENOMEM;
- goto done;
- }
- memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
- new_crypt->ops = ops;
- if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
- new_crypt->priv =
- new_crypt->ops->init(param->u.crypt.idx);
-
- if (new_crypt->priv == NULL) {
- kfree(new_crypt);
- param->u.crypt.err = IPW_CRYPT_ERR_CRYPT_INIT_FAILED;
- ret = -EINVAL;
- goto done;
- }
-
- *crypt = new_crypt;
- }
-
- if (param->u.crypt.key_len > 0 && (*crypt)->ops->set_key &&
- (*crypt)->ops->set_key(param->u.crypt.key,
- param->u.crypt.key_len, param->u.crypt.seq,
- (*crypt)->priv) < 0) {
- IPW_DEBUG_INFO("%s: key setting failed\n", dev->name);
- param->u.crypt.err = IPW_CRYPT_ERR_KEY_SET_FAILED;
- ret = -EINVAL;
- goto done;
- }
-
- skip_host_crypt:
- if (param->u.crypt.set_tx) {
- ieee->tx_keyidx = param->u.crypt.idx;
- sec.active_key = param->u.crypt.idx;
- sec.flags |= SEC_ACTIVE_KEY;
- } else
- sec.flags &= ~SEC_ACTIVE_KEY;
-
- if (param->u.crypt.alg != NULL) {
- memcpy(sec.keys[param->u.crypt.idx],
- param->u.crypt.key, param->u.crypt.key_len);
- sec.key_sizes[param->u.crypt.idx] = param->u.crypt.key_len;
- sec.flags |= (1 << param->u.crypt.idx);
-
- if (strcmp(param->u.crypt.alg, "WEP") == 0) {
- sec.flags |= SEC_LEVEL;
- sec.level = SEC_LEVEL_1;
- } else if (strcmp(param->u.crypt.alg, "TKIP") == 0) {
- sec.flags |= SEC_LEVEL;
- sec.level = SEC_LEVEL_2;
- } else if (strcmp(param->u.crypt.alg, "CCMP") == 0) {
- sec.flags |= SEC_LEVEL;
- sec.level = SEC_LEVEL_3;
- }
- }
- done:
- if (ieee->set_security)
- ieee->set_security(ieee->dev, &sec);
-
- /* Do not reset port if card is in Managed mode since resetting will
- * generate new IEEE 802.11 authentication which may end up in looping
- * with IEEE 802.1X. If your hardware requires a reset after WEP
- * configuration (for example... Prism2), implement the reset_port in
- * the callbacks structures used to initialize the 802.11 stack. */
- if (ieee->reset_on_keychange &&
- ieee->iw_mode != IW_MODE_INFRA &&
- ieee->reset_port && ieee->reset_port(dev)) {
- IPW_DEBUG_INFO("%s: reset_port failed\n", dev->name);
- param->u.crypt.err = IPW_CRYPT_ERR_CARD_CONF_FAILED;
- return -EINVAL;
- }
-
- return ret;
-}
-
-static int ipw_wpa_supplicant(struct net_device *dev, struct iw_point *p)
-{
- struct ipw_param *param;
- struct ipw_priv *priv = ieee80211_priv(dev);
- int ret = 0;
-
- IPW_DEBUG_INFO("wpa_supplicant: len=%d\n", p->length);
-
- if (p->length < sizeof(struct ipw_param) || !p->pointer)
- return -EINVAL;
-
- param = (struct ipw_param *)kmalloc(p->length, GFP_KERNEL);
- if (param == NULL)
- return -ENOMEM;
-
- if (copy_from_user(param, p->pointer, p->length)) {
- kfree(param);
- return -EFAULT;
- }
-
- down(&priv->sem);
- switch (param->cmd) {
-
- case IPW_CMD_SET_WPA_PARAM:
- ret = ipw_wpa_set_param(dev, param->u.wpa_param.name,
- param->u.wpa_param.value);
- break;
-
- case IPW_CMD_SET_WPA_IE:
- ret = ipw_wpa_set_wpa_ie(dev, param, p->length);
- break;
-
- case IPW_CMD_SET_ENCRYPTION:
- ret = ipw_wpa_set_encryption(dev, param, p->length);
- break;
-
- case IPW_CMD_MLME:
- ret = ipw_wpa_mlme(dev, param->u.mlme.command,
- param->u.mlme.reason_code);
- break;
-
- default:
- IPW_ERROR("%s: Unknown WPA supplicant request: %d\n",
- dev->name, param->cmd);
- ret = -EOPNOTSUPP;
- }
-
- up(&priv->sem);
- if (ret == 0 && copy_to_user(p->pointer, param, p->length))
- ret = -EFAULT;
-
- kfree(param);
- return ret;
-}
-#else
/*
* WE-18 support
*/
return err;
}
+static int wext_cipher2level(int cipher)
+{
+ switch (cipher) {
+ case IW_AUTH_CIPHER_NONE:
+ return SEC_LEVEL_0;
+ case IW_AUTH_CIPHER_WEP40:
+ case IW_AUTH_CIPHER_WEP104:
+ return SEC_LEVEL_1;
+ case IW_AUTH_CIPHER_TKIP:
+ return SEC_LEVEL_2;
+ case IW_AUTH_CIPHER_CCMP:
+ return SEC_LEVEL_3;
+ default:
+ return -1;
+ }
+}
+
/* SIOCSIWAUTH */
static int ipw_wx_set_auth(struct net_device *dev,
struct iw_request_info *info,
switch (param->flags & IW_AUTH_INDEX) {
case IW_AUTH_WPA_VERSION:
+ break;
case IW_AUTH_CIPHER_PAIRWISE:
+ ipw_set_hw_decrypt_unicast(priv,
+ wext_cipher2level(param->value));
+ break;
case IW_AUTH_CIPHER_GROUP:
+ ipw_set_hw_decrypt_multicast(priv,
+ wext_cipher2level(param->value));
+ break;
case IW_AUTH_KEY_MGMT:
/*
* ipw2200 does not use these parameters
case IW_AUTH_TKIP_COUNTERMEASURES:
crypt = priv->ieee->crypt[priv->ieee->tx_keyidx];
- if (!crypt || !crypt->ops->set_flags || !crypt->ops->get_flags) {
- IPW_WARNING("Can't set TKIP countermeasures: "
- "crypt not set!\n");
+ if (!crypt || !crypt->ops->set_flags || !crypt->ops->get_flags)
break;
- }
flags = crypt->ops->get_flags(crypt->priv);
case IW_AUTH_TKIP_COUNTERMEASURES:
crypt = priv->ieee->crypt[priv->ieee->tx_keyidx];
- if (!crypt || !crypt->ops->get_flags) {
- IPW_WARNING("Can't get TKIP countermeasures: "
- "crypt not set!\n");
+ if (!crypt || !crypt->ops->get_flags)
break;
- }
param->value = (crypt->ops->get_flags(crypt->priv) &
IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) ? 1 : 0;
struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
if (hwcrypto) {
- /* IPW HW can't build TKIP MIC, host decryption still needed */
if (ext->alg == IW_ENCODE_ALG_TKIP) {
- priv->ieee->host_encrypt = 0;
- priv->ieee->host_encrypt_msdu = 1;
- priv->ieee->host_decrypt = 1;
+ /* IPW HW can't build TKIP MIC,
+ host decryption still needed */
+ if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY)
+ priv->ieee->host_mc_decrypt = 1;
+ else {
+ priv->ieee->host_encrypt = 0;
+ priv->ieee->host_encrypt_msdu = 1;
+ priv->ieee->host_decrypt = 1;
+ }
} else {
priv->ieee->host_encrypt = 0;
priv->ieee->host_encrypt_msdu = 0;
priv->ieee->host_decrypt = 0;
+ priv->ieee->host_mc_decrypt = 0;
}
}
}
return 0;
}
-#endif
#ifdef CONFIG_IPW_QOS
/*
* Handle management frame beacon and probe response
*/
-static int ipw_qos_handle_probe_reponse(struct ipw_priv *priv,
- int active_network,
- struct ieee80211_network *network)
+static int ipw_qos_handle_probe_response(struct ipw_priv *priv,
+ int active_network,
+ struct ieee80211_network *network)
{
u32 size = sizeof(struct ieee80211_qos_parameters);
up(&priv->sem);
}
-/*
-* Handler for probe responce and beacon frame
-*/
-static int ipw_handle_management_frame(struct net_device *dev,
- struct ieee80211_network *network,
- u16 type)
+static int ipw_handle_probe_response(struct net_device *dev,
+ struct ieee80211_probe_response *resp,
+ struct ieee80211_network *network)
{
struct ipw_priv *priv = ieee80211_priv(dev);
- int active_network;
+ int active_network = ((priv->status & STATUS_ASSOCIATED) &&
+ (network == priv->assoc_network));
- if (priv->status & STATUS_ASSOCIATED && network == priv->assoc_network)
- active_network = 1;
- else
- active_network = 0;
+ ipw_qos_handle_probe_response(priv, active_network, network);
- switch (type) {
- case IEEE80211_STYPE_PROBE_RESP:
- case IEEE80211_STYPE_BEACON:
- ipw_qos_handle_probe_reponse(priv, active_network, network);
- break;
- case IEEE80211_STYPE_ASSOC_RESP:
- ipw_qos_association_resp(priv, network);
- break;
- default:
- break;
- }
+ return 0;
+}
+static int ipw_handle_beacon(struct net_device *dev,
+ struct ieee80211_beacon *resp,
+ struct ieee80211_network *network)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ int active_network = ((priv->status & STATUS_ASSOCIATED) &&
+ (network == priv->assoc_network));
+
+ ipw_qos_handle_probe_response(priv, active_network, network);
+
+ return 0;
+}
+
+static int ipw_handle_assoc_response(struct net_device *dev,
+ struct ieee80211_assoc_response *resp,
+ struct ieee80211_network *network)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ ipw_qos_association_resp(priv, network);
return 0;
}
{
struct host_cmd cmd = {
.cmd = IPW_CMD_QOS_PARAMETERS,
- .len = (sizeof(struct ieee80211_qos_parameters) * 3)
- };
-
- if (!priv || !qos_param) {
- IPW_ERROR("Invalid args\n");
- return -1;
- }
+ .len = (sizeof(struct ieee80211_qos_parameters) * 3)
+ };
memcpy(cmd.param, qos_param, sizeof(*qos_param) * 3);
- if (ipw_send_cmd(priv, &cmd)) {
- IPW_ERROR("failed to send IPW_CMD_QOS_PARAMETERS command\n");
- return -1;
- }
-
- return 0;
+ return ipw_send_cmd(priv, &cmd);
}
static int ipw_send_qos_info_command(struct ipw_priv *priv, struct ieee80211_qos_information_element
.len = sizeof(*qos_param)
};
- if (!priv || !qos_param) {
- IPW_ERROR("Invalid args\n");
- return -1;
- }
-
memcpy(cmd.param, qos_param, sizeof(*qos_param));
- if (ipw_send_cmd(priv, &cmd)) {
- IPW_ERROR("failed to send CMD_QOS_INFO command\n");
- return -1;
- }
-
- return 0;
+ return ipw_send_cmd(priv, &cmd);
}
#endif /* CONFIG_IPW_QOS */
return 0;
}
+ if (priv->status & STATUS_DISASSOCIATING) {
+ IPW_DEBUG_ASSOC("Not attempting association (in "
+ "disassociating)\n ");
+ queue_work(priv->workqueue, &priv->associate);
+ return 0;
+ }
+
if (!ipw_is_init(priv) || (priv->status & STATUS_SCANNING)) {
IPW_DEBUG_ASSOC("Not attempting association (scanning or not "
"initialized)\n");
memmove(skb->data + IEEE80211_3ADDR_LEN,
skb->data + IEEE80211_3ADDR_LEN + 8,
skb->len - IEEE80211_3ADDR_LEN - 8);
- skb_trim(skb, skb->len - 8); /* MIC */
+ skb_trim(skb, skb->len - 16); /* CCMP_HDR_LEN + CCMP_MIC_LEN */
break;
case SEC_LEVEL_2:
break;
memmove(skb->data + IEEE80211_3ADDR_LEN,
skb->data + IEEE80211_3ADDR_LEN + 4,
skb->len - IEEE80211_3ADDR_LEN - 4);
- skb_trim(skb, skb->len - 4); /* ICV */
+ skb_trim(skb, skb->len - 8); /* IV + ICV */
break;
case SEC_LEVEL_0:
break;
struct ipw_rx_mem_buffer *rxb,
struct ieee80211_rx_stats *stats)
{
+ struct ieee80211_hdr_4addr *hdr;
struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data;
/* We received data from the HW, so stop the watchdog */
IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len);
/* HW decrypt will not clear the WEP bit, MIC, PN, etc. */
- if (!priv->ieee->host_decrypt)
+ hdr = (struct ieee80211_hdr_4addr *)rxb->skb->data;
+ if (priv->ieee->iw_mode != IW_MODE_MONITOR &&
+ ((is_multicast_ether_addr(hdr->addr1) ||
+ is_broadcast_ether_addr(hdr->addr1)) ?
+ !priv->ieee->host_mc_decrypt : !priv->ieee->host_decrypt))
ipw_rebuild_decrypted_skb(priv, rxb->skb);
if (!ieee80211_rx(priv->ieee, rxb->skb, stats))
}
}
+#ifdef CONFIG_IEEE80211_RADIOTAP
+static void ipw_handle_data_packet_monitor(struct ipw_priv *priv,
+ struct ipw_rx_mem_buffer *rxb,
+ struct ieee80211_rx_stats *stats)
+{
+ struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data;
+ struct ipw_rx_frame *frame = &pkt->u.frame;
+
+ /* initial pull of some data */
+ u16 received_channel = frame->received_channel;
+ u8 antennaAndPhy = frame->antennaAndPhy;
+ s8 antsignal = frame->rssi_dbm - IPW_RSSI_TO_DBM; /* call it signed anyhow */
+ u16 pktrate = frame->rate;
+
+ /* Magic struct that slots into the radiotap header -- no reason
+ * to build this manually element by element, we can write it much
+ * more efficiently than we can parse it. ORDER MATTERS HERE */
+ struct ipw_rt_hdr {
+ struct ieee80211_radiotap_header rt_hdr;
+ u8 rt_flags; /* radiotap packet flags */
+ u8 rt_rate; /* rate in 500kb/s */
+ u16 rt_channel; /* channel in mhz */
+ u16 rt_chbitmask; /* channel bitfield */
+ s8 rt_dbmsignal; /* signal in dbM, kluged to signed */
+ u8 rt_antenna; /* antenna number */
+ } *ipw_rt;
+
+ short len = le16_to_cpu(pkt->u.frame.length);
+
+ /* We received data from the HW, so stop the watchdog */
+ priv->net_dev->trans_start = jiffies;
+
+ /* We only process data packets if the
+ * interface is open */
+ if (unlikely((le16_to_cpu(pkt->u.frame.length) + IPW_RX_FRAME_SIZE) >
+ skb_tailroom(rxb->skb))) {
+ priv->ieee->stats.rx_errors++;
+ priv->wstats.discard.misc++;
+ IPW_DEBUG_DROP("Corruption detected! Oh no!\n");
+ return;
+ } else if (unlikely(!netif_running(priv->net_dev))) {
+ priv->ieee->stats.rx_dropped++;
+ priv->wstats.discard.misc++;
+ IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
+ return;
+ }
+
+ /* Libpcap 0.9.3+ can handle variable length radiotap, so we'll use
+ * that now */
+ if (len > IPW_RX_BUF_SIZE - sizeof(struct ipw_rt_hdr)) {
+ /* FIXME: Should alloc bigger skb instead */
+ priv->ieee->stats.rx_dropped++;
+ priv->wstats.discard.misc++;
+ IPW_DEBUG_DROP("Dropping too large packet in monitor\n");
+ return;
+ }
+
+ /* copy the frame itself */
+ memmove(rxb->skb->data + sizeof(struct ipw_rt_hdr),
+ rxb->skb->data + IPW_RX_FRAME_SIZE, len);
+
+ /* Zero the radiotap static buffer ... We only need to zero the bytes NOT
+ * part of our real header, saves a little time.
+ *
+ * No longer necessary since we fill in all our data. Purge before merging
+ * patch officially.
+ * memset(rxb->skb->data + sizeof(struct ipw_rt_hdr), 0,
+ * IEEE80211_RADIOTAP_HDRLEN - sizeof(struct ipw_rt_hdr));
+ */
+
+ ipw_rt = (struct ipw_rt_hdr *)rxb->skb->data;
+
+ ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION;
+ ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */
+ ipw_rt->rt_hdr.it_len = sizeof(struct ipw_rt_hdr); /* total header+data */
+
+ /* Big bitfield of all the fields we provide in radiotap */
+ ipw_rt->rt_hdr.it_present =
+ ((1 << IEEE80211_RADIOTAP_FLAGS) |
+ (1 << IEEE80211_RADIOTAP_RATE) |
+ (1 << IEEE80211_RADIOTAP_CHANNEL) |
+ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |
+ (1 << IEEE80211_RADIOTAP_ANTENNA));
+
+ /* Zero the flags, we'll add to them as we go */
+ ipw_rt->rt_flags = 0;
+
+ /* Convert signal to DBM */
+ ipw_rt->rt_dbmsignal = antsignal;
+
+ /* Convert the channel data and set the flags */
+ ipw_rt->rt_channel = cpu_to_le16(ieee80211chan2mhz(received_channel));
+ if (received_channel > 14) { /* 802.11a */
+ ipw_rt->rt_chbitmask =
+ cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ));
+ } else if (antennaAndPhy & 32) { /* 802.11b */
+ ipw_rt->rt_chbitmask =
+ cpu_to_le16((IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ));
+ } else { /* 802.11g */
+ ipw_rt->rt_chbitmask =
+ (IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ);
+ }
+
+ /* set the rate in multiples of 500k/s */
+ switch (pktrate) {
+ case IPW_TX_RATE_1MB:
+ ipw_rt->rt_rate = 2;
+ break;
+ case IPW_TX_RATE_2MB:
+ ipw_rt->rt_rate = 4;
+ break;
+ case IPW_TX_RATE_5MB:
+ ipw_rt->rt_rate = 10;
+ break;
+ case IPW_TX_RATE_6MB:
+ ipw_rt->rt_rate = 12;
+ break;
+ case IPW_TX_RATE_9MB:
+ ipw_rt->rt_rate = 18;
+ break;
+ case IPW_TX_RATE_11MB:
+ ipw_rt->rt_rate = 22;
+ break;
+ case IPW_TX_RATE_12MB:
+ ipw_rt->rt_rate = 24;
+ break;
+ case IPW_TX_RATE_18MB:
+ ipw_rt->rt_rate = 36;
+ break;
+ case IPW_TX_RATE_24MB:
+ ipw_rt->rt_rate = 48;
+ break;
+ case IPW_TX_RATE_36MB:
+ ipw_rt->rt_rate = 72;
+ break;
+ case IPW_TX_RATE_48MB:
+ ipw_rt->rt_rate = 96;
+ break;
+ case IPW_TX_RATE_54MB:
+ ipw_rt->rt_rate = 108;
+ break;
+ default:
+ ipw_rt->rt_rate = 0;
+ break;
+ }
+
+ /* antenna number */
+ ipw_rt->rt_antenna = (antennaAndPhy & 3); /* Is this right? */
+
+ /* set the preamble flag if we have it */
+ if ((antennaAndPhy & 64))
+ ipw_rt->rt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
+
+ /* Set the size of the skb to the size of the frame */
+ skb_put(rxb->skb, len + sizeof(struct ipw_rt_hdr));
+
+ IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len);
+
+ if (!ieee80211_rx(priv->ieee, rxb->skb, stats))
+ priv->ieee->stats.rx_errors++;
+ else { /* ieee80211_rx succeeded, so it now owns the SKB */
+ rxb->skb = NULL;
+ /* no LED during capture */
+ }
+}
+#endif
+
static inline int is_network_packet(struct ipw_priv *priv,
struct ieee80211_hdr_4addr *header)
{
if (!memcmp(header->addr2, priv->net_dev->dev_addr, ETH_ALEN))
return 0;
- /* multicast packets to our IBSS go through */
- if (is_multicast_ether_addr(header->addr1))
+ /* {broad,multi}cast packets to our BSSID go through */
+ if (is_multicast_ether_addr(header->addr1) ||
+ is_broadcast_ether_addr(header->addr1))
return !memcmp(header->addr3, priv->bssid, ETH_ALEN);
/* packets to our adapter go through */
return !memcmp(header->addr1, priv->net_dev->dev_addr,
ETH_ALEN);
- case IW_MODE_INFRA: /* Header: Dest. | AP{BSSID} | Source */
+ case IW_MODE_INFRA: /* Header: Dest. | BSSID | Source */
/* packets from our adapter are dropped (echo) */
if (!memcmp(header->addr3, priv->net_dev->dev_addr, ETH_ALEN))
return 0;
- /* {broad,multi}cast packets to our IBSS go through */
- if (is_multicast_ether_addr(header->addr1))
+ /* {broad,multi}cast packets to our BSS go through */
+ if (is_multicast_ether_addr(header->addr1) ||
+ is_broadcast_ether_addr(header->addr1))
return !memcmp(header->addr2, priv->bssid, ETH_ALEN);
/* packets to our adapter go through */
static inline int is_duplicate_packet(struct ipw_priv *priv,
struct ieee80211_hdr_4addr *header)
{
- u16 fc = le16_to_cpu(header->frame_ctl);
u16 sc = le16_to_cpu(header->seq_ctl);
u16 seq = WLAN_GET_SEQ_SEQ(sc);
u16 frag = WLAN_GET_SEQ_FRAG(sc);
if (*last_frag + 1 != frag)
/* out-of-order fragment */
goto drop;
- *last_frag = frag;
} else
*last_seq = seq;
+ *last_frag = frag;
*last_time = jiffies;
return 0;
drop:
- BUG_ON(!(fc & IEEE80211_FCTL_RETRY));
+ /* Comment this line now since we observed the card receives
+ * duplicate packets but the FCTL_RETRY bit is not set in the
+ * IBSS mode with fragmentation enabled.
+ BUG_ON(!(le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_RETRY)); */
return 1;
}
#ifdef CONFIG_IPW2200_MONITOR
if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
+#ifdef CONFIG_IEEE80211_RADIOTAP
+ ipw_handle_data_packet_monitor(priv,
+ rxb,
+ &stats);
+#else
ipw_handle_data_packet(priv, rxb,
&stats);
+#endif
break;
}
#endif
#ifdef CONFIG_IPW2200_MONITOR
case 2:
priv->ieee->iw_mode = IW_MODE_MONITOR;
+#ifdef CONFIG_IEEE80211_RADIOTAP
+ priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
+#else
priv->net_dev->type = ARPHRD_IEEE80211;
+#endif
break;
#endif
default:
priv->ieee->host_encrypt = 0;
priv->ieee->host_encrypt_msdu = 0;
priv->ieee->host_decrypt = 0;
+ priv->ieee->host_mc_decrypt = 0;
}
IPW_DEBUG_INFO("Hardware crypto [%s]\n", hwcrypto ? "on" : "off");
+ /* IPW2200/2915 is abled to do hardware fragmentation. */
+ priv->ieee->host_open_frag = 0;
+
if ((priv->pci_dev->device == 0x4223) ||
(priv->pci_dev->device == 0x4224)) {
if (init)
priv->power_mode = IPW_POWER_AC;
priv->tx_power = IPW_TX_POWER_DEFAULT;
- return old_mode == priv->ieee->mode;
+ return old_mode == priv->ieee->iw_mode;
}
/*
union iwreq_data *wrqu, char *extra)
{
struct ipw_priv *priv = ieee80211_priv(dev);
- const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee);
+ const struct ieee80211_geo *geo = ipw_get_geo(priv->ieee);
struct iw_freq *fwrq = &wrqu->freq;
int ret = 0, i;
- u8 channel;
+ u8 channel, flags;
+ int band;
if (fwrq->m == 0) {
IPW_DEBUG_WX("SET Freq/Channel -> any\n");
}
/* if setting by freq convert to channel */
if (fwrq->e == 1) {
- channel = ieee80211_freq_to_channel(priv->ieee, fwrq->m);
+ channel = ipw_freq_to_channel(priv->ieee, fwrq->m);
if (channel == 0)
return -EINVAL;
} else
channel = fwrq->m;
- if (!ieee80211_is_valid_channel(priv->ieee, channel))
+ if (!(band = ipw_is_valid_channel(priv->ieee, channel)))
return -EINVAL;
- if (priv->ieee->iw_mode == IW_MODE_ADHOC && priv->ieee->mode & IEEE_A) {
- i = ieee80211_channel_to_index(priv->ieee, channel);
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+ i = ipw_channel_to_index(priv->ieee, channel);
if (i == -1)
return -EINVAL;
- if (geo->a[i].flags & IEEE80211_CH_PASSIVE_ONLY) {
+
+ flags = (band == IEEE80211_24GHZ_BAND) ?
+ geo->bg[i].flags : geo->a[i].flags;
+ if (flags & IEEE80211_CH_PASSIVE_ONLY) {
IPW_DEBUG_WX("Invalid Ad-Hoc channel for 802.11a\n");
return -EINVAL;
}
priv->net_dev->type = ARPHRD_ETHER;
if (wrqu->mode == IW_MODE_MONITOR)
+#ifdef CONFIG_IEEE80211_RADIOTAP
+ priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
+#else
priv->net_dev->type = ARPHRD_IEEE80211;
+#endif
#endif /* CONFIG_IPW2200_MONITOR */
/* Free the existing firmware and reset the fw_loaded
{
struct ipw_priv *priv = ieee80211_priv(dev);
struct iw_range *range = (struct iw_range *)extra;
- const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee);
+ const struct ieee80211_geo *geo = ipw_get_geo(priv->ieee);
int i = 0, j;
wrqu->data.length = sizeof(*range);
range->num_frequency = i;
up(&priv->sem);
+
+ /* Event capability (kernel + driver) */
+ range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
+ IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) |
+ IW_EVENT_CAPA_MASK(SIOCGIWAP));
+ range->event_capa[1] = IW_EVENT_CAPA_K_1;
+
IPW_DEBUG_WX("GET Range\n");
return 0;
}
union iwreq_data *wrqu, char *extra)
{
struct ipw_priv *priv = ieee80211_priv(dev);
- struct ipw_tx_power tx_power;
- int i;
+ int err = 0;
down(&priv->sem);
if (ipw_radio_kill_sw(priv, wrqu->power.disabled)) {
- up(&priv->sem);
- return -EINPROGRESS;
+ err = -EINPROGRESS;
+ goto out;
}
if (!wrqu->power.fixed)
wrqu->power.value = IPW_TX_POWER_DEFAULT;
if (wrqu->power.flags != IW_TXPOW_DBM) {
- up(&priv->sem);
- return -EINVAL;
+ err = -EINVAL;
+ goto out;
}
if ((wrqu->power.value > IPW_TX_POWER_MAX) ||
(wrqu->power.value < IPW_TX_POWER_MIN)) {
- up(&priv->sem);
- return -EINVAL;
+ err = -EINVAL;
+ goto out;
}
priv->tx_power = wrqu->power.value;
-
- memset(&tx_power, 0, sizeof(tx_power));
-
- /* configure device for 'G' band */
- tx_power.ieee_mode = IPW_G_MODE;
- tx_power.num_channels = 11;
- for (i = 0; i < 11; i++) {
- tx_power.channels_tx_power[i].channel_number = i + 1;
- tx_power.channels_tx_power[i].tx_power = priv->tx_power;
- }
- if (ipw_send_tx_power(priv, &tx_power))
- goto error;
-
- /* configure device to also handle 'B' band */
- tx_power.ieee_mode = IPW_B_MODE;
- if (ipw_send_tx_power(priv, &tx_power))
- goto error;
-
- up(&priv->sem);
- return 0;
-
- error:
+ err = ipw_set_tx_power(priv);
+ out:
up(&priv->sem);
- return -EIO;
+ return err;
}
static int ipw_wx_get_txpow(struct net_device *dev,
up(&priv->sem);
IPW_DEBUG_WX("GET TX Power -> %s %d \n",
- wrqu->power.disabled ? "ON" : "OFF", wrqu->power.value);
+ wrqu->power.disabled ? "OFF" : "ON", wrqu->power.value);
return 0;
}
return 0;
}
-#if WIRELESS_EXT > 17
static int ipw_request_direct_scan(struct ipw_priv *priv, char *essid,
int essid_len)
{
struct ipw_scan_request_ext scan;
int err = 0, scan_type;
+ if (!(priv->status & STATUS_INIT) ||
+ (priv->status & STATUS_EXIT_PENDING))
+ return 0;
+
down(&priv->sem);
if (priv->status & STATUS_RF_KILL_MASK) {
scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] =
cpu_to_le16(20);
- scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(20);
+ scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(120);
scan.dwell_time[IPW_SCAN_ACTIVE_DIRECT_SCAN] = cpu_to_le16(20);
scan.full_scan_index = cpu_to_le32(ieee80211_get_scans(priv->ieee));
up(&priv->sem);
return err;
}
-#endif /* WIRELESS_EXT > 17 */
static int ipw_wx_set_scan(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct ipw_priv *priv = ieee80211_priv(dev);
-#if WIRELESS_EXT > 17
struct iw_scan_req *req = NULL;
if (wrqu->data.length
&& wrqu->data.length == sizeof(struct iw_scan_req)) {
return 0;
}
}
-#endif
+
IPW_DEBUG_WX("Start scan\n");
queue_work(priv->workqueue, &priv->request_scan);
{
struct ipw_priv *priv = ieee80211_priv(dev);
int ret;
+ u32 cap = priv->capability;
down(&priv->sem);
ret = ieee80211_wx_set_encode(priv->ieee, info, wrqu, key);
- up(&priv->sem);
+ /* In IBSS mode, we need to notify the firmware to update
+ * the beacon info after we changed the capability. */
+ if (cap != priv->capability &&
+ priv->ieee->iw_mode == IW_MODE_ADHOC &&
+ priv->status & STATUS_ASSOCIATED)
+ ipw_disassociate(priv);
+
+ up(&priv->sem);
return ret;
}
IPW_DEBUG_WX("SET MONITOR: %d %d\n", enable, parms[1]);
if (enable) {
if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
+#ifdef CONFIG_IEEE80211_RADIOTAP
+ priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
+#else
priv->net_dev->type = ARPHRD_IEEE80211;
+#endif
queue_work(priv->workqueue, &priv->adapter_restart);
}
IW_IOCTL(SIOCGIWSPY) = iw_handler_get_spy,
IW_IOCTL(SIOCSIWTHRSPY) = iw_handler_set_thrspy,
IW_IOCTL(SIOCGIWTHRSPY) = iw_handler_get_thrspy,
-#if WIRELESS_EXT > 17
IW_IOCTL(SIOCSIWGENIE) = ipw_wx_set_genie,
IW_IOCTL(SIOCGIWGENIE) = ipw_wx_get_genie,
IW_IOCTL(SIOCSIWMLME) = ipw_wx_set_mlme,
IW_IOCTL(SIOCGIWAUTH) = ipw_wx_get_auth,
IW_IOCTL(SIOCSIWENCODEEXT) = ipw_wx_set_encodeext,
IW_IOCTL(SIOCGIWENCODEEXT) = ipw_wx_get_encodeext,
-#endif
};
enum {
.num_private_args = ARRAY_SIZE(ipw_priv_args),
.private = ipw_priv_handler,
.private_args = ipw_priv_args,
+ .get_wireless_stats = ipw_get_wireless_stats,
};
-static struct iw_public_data ipw_wx_data;
-
/*
* Get wireless statistics.
* Called by /proc/net/wireless
we need to heavily modify the ieee80211_skb_to_txb.
*/
-static inline void ipw_tx_skb(struct ipw_priv *priv, struct ieee80211_txb *txb,
- int pri)
+static inline int ipw_tx_skb(struct ipw_priv *priv, struct ieee80211_txb *txb,
+ int pri)
{
struct ieee80211_hdr_3addr *hdr = (struct ieee80211_hdr_3addr *)
txb->fragments[0]->data;
u16 remaining_bytes;
int fc;
+ /* If there isn't room in the queue, we return busy and let the
+ * network stack requeue the packet for us */
+ if (ipw_queue_space(q) < q->high_mark)
+ return NETDEV_TX_BUSY;
+
switch (priv->ieee->iw_mode) {
case IW_MODE_ADHOC:
hdr_len = IEEE80211_3ADDR_LEN;
- unicast = !is_multicast_ether_addr(hdr->addr1);
+ unicast = !(is_multicast_ether_addr(hdr->addr1) ||
+ is_broadcast_ether_addr(hdr->addr1));
id = ipw_find_station(priv, hdr->addr1);
if (id == IPW_INVALID_STATION) {
id = ipw_add_station(priv, hdr->addr1);
case IW_MODE_INFRA:
default:
- unicast = !is_multicast_ether_addr(hdr->addr3);
+ unicast = !(is_multicast_ether_addr(hdr->addr3) ||
+ is_broadcast_ether_addr(hdr->addr3));
hdr_len = IEEE80211_3ADDR_LEN;
id = 0;
break;
q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd);
ipw_write32(priv, q->reg_w, q->first_empty);
- if (ipw_queue_space(q) < q->high_mark)
- netif_stop_queue(priv->net_dev);
-
- return;
+ return NETDEV_TX_OK;
drop:
IPW_DEBUG_DROP("Silently dropping Tx packet.\n");
ieee80211_txb_free(txb);
+ return NETDEV_TX_OK;
+}
+
+static int ipw_net_is_queue_full(struct net_device *dev, int pri)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+#ifdef CONFIG_IPW_QOS
+ int tx_id = ipw_get_tx_queue_number(priv, pri);
+ struct clx2_tx_queue *txq = &priv->txq[tx_id];
+#else
+ struct clx2_tx_queue *txq = &priv->txq[0];
+#endif /* CONFIG_IPW_QOS */
+
+ if (ipw_queue_space(&txq->q) < txq->q.high_mark)
+ return 1;
+
+ return 0;
}
static int ipw_net_hard_start_xmit(struct ieee80211_txb *txb,
{
struct ipw_priv *priv = ieee80211_priv(dev);
unsigned long flags;
+ int ret;
IPW_DEBUG_TX("dev->xmit(%d bytes)\n", txb->payload_size);
spin_lock_irqsave(&priv->lock, flags);
goto fail_unlock;
}
- ipw_tx_skb(priv, txb, pri);
- __ipw_led_activity_on(priv);
+ ret = ipw_tx_skb(priv, txb, pri);
+ if (ret == NETDEV_TX_OK)
+ __ipw_led_activity_on(priv);
spin_unlock_irqrestore(&priv->lock, flags);
- return 0;
+ return ret;
fail_unlock:
spin_unlock_irqrestore(&priv->lock, flags);
INIT_WORK(&priv->adhoc_check, ipw_bg_adhoc_check, priv);
INIT_WORK(&priv->associate, ipw_bg_associate, priv);
INIT_WORK(&priv->disassociate, ipw_bg_disassociate, priv);
+ INIT_WORK(&priv->system_config, ipw_system_config, priv);
INIT_WORK(&priv->rx_replenish, ipw_bg_rx_queue_replenish, priv);
INIT_WORK(&priv->adapter_restart, ipw_bg_adapter_restart, priv);
INIT_WORK(&priv->rf_kill, ipw_bg_rf_kill, priv);
priv->status |= STATUS_SECURITY_UPDATED;
}
- if (!priv->ieee->host_encrypt)
+ if (!priv->ieee->host_encrypt && (sec->flags & SEC_ENCRYPT))
ipw_set_hwcrypto_keys(priv);
/* To match current functionality of ipw2100 (which works well w/
static int ipw_config(struct ipw_priv *priv)
{
- int i;
- struct ipw_tx_power tx_power;
-
- memset(&priv->sys_config, 0, sizeof(priv->sys_config));
- memset(&tx_power, 0, sizeof(tx_power));
-
/* This is only called from ipw_up, which resets/reloads the firmware
so, we don't need to first disable the card before we configure
it */
-
- /* configure device for 'G' band */
- tx_power.ieee_mode = IPW_G_MODE;
- tx_power.num_channels = 11;
- for (i = 0; i < 11; i++) {
- tx_power.channels_tx_power[i].channel_number = i + 1;
- tx_power.channels_tx_power[i].tx_power = priv->tx_power;
- }
- if (ipw_send_tx_power(priv, &tx_power))
- goto error;
-
- /* configure device to also handle 'B' band */
- tx_power.ieee_mode = IPW_B_MODE;
- if (ipw_send_tx_power(priv, &tx_power))
+ if (ipw_set_tx_power(priv))
goto error;
/* initialize adapter address */
if (ipw_send_host_complete(priv))
goto error;
- /* If configured to try and auto-associate, kick off a scan */
- if (priv->config & CFG_ASSOCIATE)
- queue_work(priv->workqueue, &priv->request_scan);
+ priv->status |= STATUS_INIT;
+
+ ipw_led_init(priv);
+ ipw_led_radio_on(priv);
+ priv->notif_missed_beacons = 0;
+
+ /* Set hardware WEP key if it is configured. */
+ if ((priv->capability & CAP_PRIVACY_ON) &&
+ (priv->ieee->sec.level == SEC_LEVEL_1) &&
+ !(priv->ieee->host_encrypt || priv->ieee->host_decrypt))
+ ipw_set_hwcrypto_keys(priv);
return 0;
return -EIO;
}
-static const struct ieee80211_geo ipw_geo = {
- "---",
- .bg_channels = 11,
- .bg = {{2412, 1}, {2417, 2}, {2422, 3},
- {2427, 4}, {2432, 5}, {2437, 6},
- {2442, 7}, {2447, 8}, {2452, 9},
- {2457, 10}, {2462, 11}},
- .a_channels = 8,
- .a = {{5180, 36},
- {5200, 40},
- {5220, 44},
- {5240, 48},
- {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
- {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
- {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
- {5320, 64, IEEE80211_CH_PASSIVE_ONLY}},
+/*
+ * NOTE:
+ *
+ * These tables have been tested in conjunction with the
+ * Intel PRO/Wireless 2200BG and 2915ABG Network Connection Adapters.
+ *
+ * Altering this values, using it on other hardware, or in geographies
+ * not intended for resale of the above mentioned Intel adapters has
+ * not been tested.
+ *
+ */
+static const struct ieee80211_geo ipw_geos[] = {
+ { /* Restricted */
+ "---",
+ .bg_channels = 11,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11}},
+ },
+
+ { /* Custom US/Canada */
+ "ZZF",
+ .bg_channels = 11,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11}},
+ .a_channels = 8,
+ .a = {{5180, 36},
+ {5200, 40},
+ {5220, 44},
+ {5240, 48},
+ {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
+ {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
+ {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
+ {5320, 64, IEEE80211_CH_PASSIVE_ONLY}},
+ },
+
+ { /* Rest of World */
+ "ZZD",
+ .bg_channels = 13,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11}, {2467, 12},
+ {2472, 13}},
+ },
+
+ { /* Custom USA & Europe & High */
+ "ZZA",
+ .bg_channels = 11,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11}},
+ .a_channels = 13,
+ .a = {{5180, 36},
+ {5200, 40},
+ {5220, 44},
+ {5240, 48},
+ {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
+ {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
+ {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
+ {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
+ {5745, 149},
+ {5765, 153},
+ {5785, 157},
+ {5805, 161},
+ {5825, 165}},
+ },
+
+ { /* Custom NA & Europe */
+ "ZZB",
+ .bg_channels = 11,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11}},
+ .a_channels = 13,
+ .a = {{5180, 36},
+ {5200, 40},
+ {5220, 44},
+ {5240, 48},
+ {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
+ {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
+ {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
+ {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
+ {5745, 149, IEEE80211_CH_PASSIVE_ONLY},
+ {5765, 153, IEEE80211_CH_PASSIVE_ONLY},
+ {5785, 157, IEEE80211_CH_PASSIVE_ONLY},
+ {5805, 161, IEEE80211_CH_PASSIVE_ONLY},
+ {5825, 165, IEEE80211_CH_PASSIVE_ONLY}},
+ },
+
+ { /* Custom Japan */
+ "ZZC",
+ .bg_channels = 11,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11}},
+ .a_channels = 4,
+ .a = {{5170, 34}, {5190, 38},
+ {5210, 42}, {5230, 46}},
+ },
+
+ { /* Custom */
+ "ZZM",
+ .bg_channels = 11,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11}},
+ },
+
+ { /* Europe */
+ "ZZE",
+ .bg_channels = 13,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11}, {2467, 12},
+ {2472, 13}},
+ .a_channels = 19,
+ .a = {{5180, 36},
+ {5200, 40},
+ {5220, 44},
+ {5240, 48},
+ {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
+ {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
+ {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
+ {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
+ {5500, 100, IEEE80211_CH_PASSIVE_ONLY},
+ {5520, 104, IEEE80211_CH_PASSIVE_ONLY},
+ {5540, 108, IEEE80211_CH_PASSIVE_ONLY},
+ {5560, 112, IEEE80211_CH_PASSIVE_ONLY},
+ {5580, 116, IEEE80211_CH_PASSIVE_ONLY},
+ {5600, 120, IEEE80211_CH_PASSIVE_ONLY},
+ {5620, 124, IEEE80211_CH_PASSIVE_ONLY},
+ {5640, 128, IEEE80211_CH_PASSIVE_ONLY},
+ {5660, 132, IEEE80211_CH_PASSIVE_ONLY},
+ {5680, 136, IEEE80211_CH_PASSIVE_ONLY},
+ {5700, 140, IEEE80211_CH_PASSIVE_ONLY}},
+ },
+
+ { /* Custom Japan */
+ "ZZJ",
+ .bg_channels = 14,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11}, {2467, 12},
+ {2472, 13}, {2484, 14, IEEE80211_CH_B_ONLY}},
+ .a_channels = 4,
+ .a = {{5170, 34}, {5190, 38},
+ {5210, 42}, {5230, 46}},
+ },
+
+ { /* Rest of World */
+ "ZZR",
+ .bg_channels = 14,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11}, {2467, 12},
+ {2472, 13}, {2484, 14, IEEE80211_CH_B_ONLY |
+ IEEE80211_CH_PASSIVE_ONLY}},
+ },
+
+ { /* High Band */
+ "ZZH",
+ .bg_channels = 13,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11},
+ {2467, 12, IEEE80211_CH_PASSIVE_ONLY},
+ {2472, 13, IEEE80211_CH_PASSIVE_ONLY}},
+ .a_channels = 4,
+ .a = {{5745, 149}, {5765, 153},
+ {5785, 157}, {5805, 161}},
+ },
+
+ { /* Custom Europe */
+ "ZZG",
+ .bg_channels = 13,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11},
+ {2467, 12}, {2472, 13}},
+ .a_channels = 4,
+ .a = {{5180, 36}, {5200, 40},
+ {5220, 44}, {5240, 48}},
+ },
+
+ { /* Europe */
+ "ZZK",
+ .bg_channels = 13,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11},
+ {2467, 12, IEEE80211_CH_PASSIVE_ONLY},
+ {2472, 13, IEEE80211_CH_PASSIVE_ONLY}},
+ .a_channels = 24,
+ .a = {{5180, 36, IEEE80211_CH_PASSIVE_ONLY},
+ {5200, 40, IEEE80211_CH_PASSIVE_ONLY},
+ {5220, 44, IEEE80211_CH_PASSIVE_ONLY},
+ {5240, 48, IEEE80211_CH_PASSIVE_ONLY},
+ {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
+ {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
+ {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
+ {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
+ {5500, 100, IEEE80211_CH_PASSIVE_ONLY},
+ {5520, 104, IEEE80211_CH_PASSIVE_ONLY},
+ {5540, 108, IEEE80211_CH_PASSIVE_ONLY},
+ {5560, 112, IEEE80211_CH_PASSIVE_ONLY},
+ {5580, 116, IEEE80211_CH_PASSIVE_ONLY},
+ {5600, 120, IEEE80211_CH_PASSIVE_ONLY},
+ {5620, 124, IEEE80211_CH_PASSIVE_ONLY},
+ {5640, 128, IEEE80211_CH_PASSIVE_ONLY},
+ {5660, 132, IEEE80211_CH_PASSIVE_ONLY},
+ {5680, 136, IEEE80211_CH_PASSIVE_ONLY},
+ {5700, 140, IEEE80211_CH_PASSIVE_ONLY},
+ {5745, 149, IEEE80211_CH_PASSIVE_ONLY},
+ {5765, 153, IEEE80211_CH_PASSIVE_ONLY},
+ {5785, 157, IEEE80211_CH_PASSIVE_ONLY},
+ {5805, 161, IEEE80211_CH_PASSIVE_ONLY},
+ {5825, 165, IEEE80211_CH_PASSIVE_ONLY}},
+ },
+
+ { /* Europe */
+ "ZZL",
+ .bg_channels = 11,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11}},
+ .a_channels = 13,
+ .a = {{5180, 36, IEEE80211_CH_PASSIVE_ONLY},
+ {5200, 40, IEEE80211_CH_PASSIVE_ONLY},
+ {5220, 44, IEEE80211_CH_PASSIVE_ONLY},
+ {5240, 48, IEEE80211_CH_PASSIVE_ONLY},
+ {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
+ {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
+ {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
+ {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
+ {5745, 149, IEEE80211_CH_PASSIVE_ONLY},
+ {5765, 153, IEEE80211_CH_PASSIVE_ONLY},
+ {5785, 157, IEEE80211_CH_PASSIVE_ONLY},
+ {5805, 161, IEEE80211_CH_PASSIVE_ONLY},
+ {5825, 165, IEEE80211_CH_PASSIVE_ONLY}},
+ }
};
+/* GEO code borrowed from ieee80211_geo.c */
+static int ipw_is_valid_channel(struct ieee80211_device *ieee, u8 channel)
+{
+ int i;
+
+ /* Driver needs to initialize the geography map before using
+ * these helper functions */
+ BUG_ON(ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0);
+
+ if (ieee->freq_band & IEEE80211_24GHZ_BAND)
+ for (i = 0; i < ieee->geo.bg_channels; i++)
+ /* NOTE: If G mode is currently supported but
+ * this is a B only channel, we don't see it
+ * as valid. */
+ if ((ieee->geo.bg[i].channel == channel) &&
+ (!(ieee->mode & IEEE_G) ||
+ !(ieee->geo.bg[i].flags & IEEE80211_CH_B_ONLY)))
+ return IEEE80211_24GHZ_BAND;
+
+ if (ieee->freq_band & IEEE80211_52GHZ_BAND)
+ for (i = 0; i < ieee->geo.a_channels; i++)
+ if (ieee->geo.a[i].channel == channel)
+ return IEEE80211_52GHZ_BAND;
+
+ return 0;
+}
+
+static int ipw_channel_to_index(struct ieee80211_device *ieee, u8 channel)
+{
+ int i;
+
+ /* Driver needs to initialize the geography map before using
+ * these helper functions */
+ BUG_ON(ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0);
+
+ if (ieee->freq_band & IEEE80211_24GHZ_BAND)
+ for (i = 0; i < ieee->geo.bg_channels; i++)
+ if (ieee->geo.bg[i].channel == channel)
+ return i;
+
+ if (ieee->freq_band & IEEE80211_52GHZ_BAND)
+ for (i = 0; i < ieee->geo.a_channels; i++)
+ if (ieee->geo.a[i].channel == channel)
+ return i;
+
+ return -1;
+}
+
+static u8 ipw_freq_to_channel(struct ieee80211_device *ieee, u32 freq)
+{
+ int i;
+
+ /* Driver needs to initialize the geography map before using
+ * these helper functions */
+ BUG_ON(ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0);
+
+ freq /= 100000;
+
+ if (ieee->freq_band & IEEE80211_24GHZ_BAND)
+ for (i = 0; i < ieee->geo.bg_channels; i++)
+ if (ieee->geo.bg[i].freq == freq)
+ return ieee->geo.bg[i].channel;
+
+ if (ieee->freq_band & IEEE80211_52GHZ_BAND)
+ for (i = 0; i < ieee->geo.a_channels; i++)
+ if (ieee->geo.a[i].freq == freq)
+ return ieee->geo.a[i].channel;
+
+ return 0;
+}
+
+static int ipw_set_geo(struct ieee80211_device *ieee,
+ const struct ieee80211_geo *geo)
+{
+ memcpy(ieee->geo.name, geo->name, 3);
+ ieee->geo.name[3] = '\0';
+ ieee->geo.bg_channels = geo->bg_channels;
+ ieee->geo.a_channels = geo->a_channels;
+ memcpy(ieee->geo.bg, geo->bg, geo->bg_channels *
+ sizeof(struct ieee80211_channel));
+ memcpy(ieee->geo.a, geo->a, ieee->geo.a_channels *
+ sizeof(struct ieee80211_channel));
+ return 0;
+}
+
+static const struct ieee80211_geo *ipw_get_geo(struct ieee80211_device *ieee)
+{
+ return &ieee->geo;
+}
+
#define MAX_HW_RESTARTS 5
static int ipw_up(struct ipw_priv *priv)
{
- int rc, i;
+ int rc, i, j;
if (priv->status & STATUS_EXIT_PENDING)
return -EIO;
+ if (cmdlog && !priv->cmdlog) {
+ priv->cmdlog = kmalloc(sizeof(*priv->cmdlog) * cmdlog,
+ GFP_KERNEL);
+ if (priv->cmdlog == NULL) {
+ IPW_ERROR("Error allocating %d command log entries.\n",
+ cmdlog);
+ } else {
+ memset(priv->cmdlog, 0, sizeof(*priv->cmdlog) * cmdlog);
+ priv->cmdlog_len = cmdlog;
+ }
+ }
+
for (i = 0; i < MAX_HW_RESTARTS; i++) {
/* Load the microcode, firmware, and eeprom.
* Also start the clocks. */
rc = ipw_load(priv);
if (rc) {
- IPW_ERROR("Unable to load firmware: 0x%08X\n", rc);
+ IPW_ERROR("Unable to load firmware: %d\n", rc);
return rc;
}
eeprom_parse_mac(priv, priv->mac_addr);
memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN);
- memcpy(priv->country, &priv->eeprom[EEPROM_COUNTRY_CODE], 3);
- priv->country[3] = '\0';
- ieee80211_set_geo(priv->ieee, &ipw_geo);
+ for (j = 0; j < ARRAY_SIZE(ipw_geos); j++) {
+ if (!memcmp(&priv->eeprom[EEPROM_COUNTRY_CODE],
+ ipw_geos[j].name, 3))
+ break;
+ }
+ if (j == ARRAY_SIZE(ipw_geos)) {
+ IPW_WARNING("SKU [%c%c%c] not recognized.\n",
+ priv->eeprom[EEPROM_COUNTRY_CODE + 0],
+ priv->eeprom[EEPROM_COUNTRY_CODE + 1],
+ priv->eeprom[EEPROM_COUNTRY_CODE + 2]);
+ j = 0;
+ }
+ if (ipw_set_geo(priv->ieee, &ipw_geos[j])) {
+ IPW_WARNING("Could not set geography.");
+ return 0;
+ }
+
+ IPW_DEBUG_INFO("Geography %03d [%s] detected.\n",
+ j, priv->ieee->geo.name);
if (priv->status & STATUS_RF_KILL_SW) {
IPW_WARNING("Radio disabled by module parameter.\n");
rc = ipw_config(priv);
if (!rc) {
IPW_DEBUG_INFO("Configured device on count %i\n", i);
- ipw_led_init(priv);
- ipw_led_radio_on(priv);
- priv->notif_missed_beacons = 0;
- priv->status |= STATUS_INIT;
-
- /* Set hardware WEP key if it is configured. */
- if ((priv->capability & CAP_PRIVACY_ON) &&
- (priv->ieee->sec.level == SEC_LEVEL_1) &&
- !(priv->ieee->host_encrypt ||
- priv->ieee->host_decrypt))
- ipw_set_hwcrypto_keys(priv);
+
+ /* If configure to try and auto-associate, kick
+ * off a scan. */
+ queue_work(priv->workqueue, &priv->request_scan);
return 0;
}
up(&priv->sem);
}
-#if WIRELESS_EXT < 18
-static int ipw_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
- struct iwreq *wrq = (struct iwreq *)rq;
- int ret = -1;
- switch (cmd) {
- case IPW_IOCTL_WPA_SUPPLICANT:
- ret = ipw_wpa_supplicant(dev, &wrq->u.data);
- return ret;
-
- default:
- return -EOPNOTSUPP;
- }
-
- return -EOPNOTSUPP;
-}
-#endif
-
/* Called by register_netdev() */
static int ipw_net_init(struct net_device *dev)
{
&dev_attr_nic_type.attr,
&dev_attr_status.attr,
&dev_attr_cfg.attr,
- &dev_attr_dump_errors.attr,
- &dev_attr_dump_events.attr,
+ &dev_attr_error.attr,
+ &dev_attr_event_log.attr,
+ &dev_attr_cmd_log.attr,
&dev_attr_eeprom_delay.attr,
&dev_attr_ucode_version.attr,
&dev_attr_rtc.attr,
SET_MODULE_OWNER(net_dev);
SET_NETDEV_DEV(net_dev, &pdev->dev);
- ipw_wx_data.spy_data = &priv->ieee->spy_data;
- ipw_wx_data.ieee80211 = priv->ieee;
-
down(&priv->sem);
priv->ieee->hard_start_xmit = ipw_net_hard_start_xmit;
priv->ieee->set_security = shim__set_security;
+ priv->ieee->is_queue_full = ipw_net_is_queue_full;
#ifdef CONFIG_IPW_QOS
- priv->ieee->handle_management_frame = ipw_handle_management_frame;
+ priv->ieee->handle_probe_response = ipw_handle_beacon;
+ priv->ieee->handle_beacon = ipw_handle_probe_response;
+ priv->ieee->handle_assoc_response = ipw_handle_assoc_response;
#endif /* CONFIG_IPW_QOS */
priv->ieee->perfect_rssi = -20;
net_dev->open = ipw_net_open;
net_dev->stop = ipw_net_stop;
net_dev->init = ipw_net_init;
-#if WIRELESS_EXT < 18
- net_dev->do_ioctl = ipw_ioctl;
-#endif
net_dev->get_stats = ipw_net_get_stats;
net_dev->set_multicast_list = ipw_net_set_multicast_list;
net_dev->set_mac_address = ipw_net_set_mac_address;
- net_dev->get_wireless_stats = ipw_get_wireless_stats;
- net_dev->wireless_data = &ipw_wx_data;
+ priv->wireless_data.spy_data = &priv->ieee->spy_data;
+ priv->wireless_data.ieee80211 = priv->ieee;
+ net_dev->wireless_data = &priv->wireless_data;
net_dev->wireless_handlers = &ipw_wx_handler_def;
net_dev->ethtool_ops = &ipw_ethtool_ops;
net_dev->irq = pdev->irq;
}
ipw_tx_queue_free(priv);
+ if (priv->cmdlog) {
+ kfree(priv->cmdlog);
+ priv->cmdlog = NULL;
+ }
/* ipw_down will ensure that there is no more pending work
* in the workqueue's, so we can safely remove them now. */
cancel_delayed_work(&priv->adhoc_check);
}
}
+ if (priv->error) {
+ ipw_free_error_log(priv->error);
+ priv->error = NULL;
+ }
+
free_irq(pdev->irq, priv);
iounmap(priv->hw_base);
pci_release_regions(pdev);
module_param(hwcrypto, int, 0444);
MODULE_PARM_DESC(hwcrypto, "enable hardware crypto (default on)");
+module_param(cmdlog, int, 0444);
+MODULE_PARM_DESC(cmdlog,
+ "allocate a ring buffer for logging firmware commands");
+
module_exit(ipw_exit);
module_init(ipw_init);