/* Read the first dword (or portion) byte by byte */ if (unlikely(dif_len)) {
_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr); /* Start reading at aligned_addr + dif_len */ for (i = dif_len; ((i < 4) && (num > 0)); i++, num--)
*buf++ = _ipw_read8(priv, IPW_INDIRECT_DATA + i);
aligned_addr += 4;
}
/* Read all of the middle dwords as dwords, with auto-increment */
_ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr); for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
*(u32 *) buf = _ipw_read32(priv, IPW_AUTOINC_DATA);
/* Read the last dword (or portion) byte by byte */ if (unlikely(num)) {
_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr); for (i = 0; num > 0; i++, num--)
*buf++ = ipw_read8(priv, IPW_INDIRECT_DATA + i);
}
}
/* General purpose, no alignment requirement, iterative (multi-byte) write, */ /* for area above 1st 4K of SRAM/reg space */ staticvoid _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * buf, int num)
{
u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK; /* dword align */
u32 dif_len = addr - aligned_addr;
u32 i;
/* Write the first dword (or portion) byte by byte */ if (unlikely(dif_len)) {
_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr); /* Start writing at aligned_addr + dif_len */ for (i = dif_len; ((i < 4) && (num > 0)); i++, num--, buf++)
_ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
aligned_addr += 4;
}
/* Write all of the middle dwords as dwords, with auto-increment */
_ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr); for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
_ipw_write32(priv, IPW_AUTOINC_DATA, *(u32 *) buf);
/* Write the last dword (or portion) byte by byte */ if (unlikely(num)) {
_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr); for (i = 0; num > 0; i++, num--, buf++)
_ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
}
}
/* General purpose, no alignment requirement, iterative (multi-byte) write, */ /* for 1st 4K of SRAM/regs space */ staticvoid ipw_write_direct(struct ipw_priv *priv, u32 addr, void *buf, int num)
{
memcpy_toio((priv->hw_base + addr), buf, num);
}
/* Set bit(s) in low 4K of SRAM/regs */ staticinlinevoid ipw_set_bit(struct ipw_priv *priv, u32 reg, u32 mask)
{
ipw_write32(priv, reg, ipw_read32(priv, reg) | mask);
}
staticchar *ipw_error_desc(u32 val)
{ switch (val) { case IPW_FW_ERROR_OK: return"ERROR_OK"; case IPW_FW_ERROR_FAIL: return"ERROR_FAIL"; case IPW_FW_ERROR_MEMORY_UNDERFLOW: return"MEMORY_UNDERFLOW"; case IPW_FW_ERROR_MEMORY_OVERFLOW: return"MEMORY_OVERFLOW"; case IPW_FW_ERROR_BAD_PARAM: return"BAD_PARAM"; case IPW_FW_ERROR_BAD_CHECKSUM: return"BAD_CHECKSUM"; case IPW_FW_ERROR_NMI_INTERRUPT: return"NMI_INTERRUPT"; case IPW_FW_ERROR_BAD_DATABASE: return"BAD_DATABASE"; case IPW_FW_ERROR_ALLOC_FAIL: return"ALLOC_FAIL"; case IPW_FW_ERROR_DMA_UNDERRUN: return"DMA_UNDERRUN"; case IPW_FW_ERROR_DMA_STATUS: return"DMA_STATUS"; case IPW_FW_ERROR_DINO_ERROR: return"DINO_ERROR"; case IPW_FW_ERROR_EEPROM_ERROR: return"EEPROM_ERROR"; case IPW_FW_ERROR_SYSASSERT: return"SYSASSERT"; case IPW_FW_ERROR_FATAL_ERROR: return"FATAL_ERROR"; default: return"UNKNOWN_ERROR";
}
}
/* remove the table id from the ordinal */
ord &= IPW_ORD_TABLE_VALUE_MASK;
/* boundary check */ if (ord > priv->table0_len) {
IPW_DEBUG_ORD("ordinal value (%i) longer then " "max (%i)\n", ord, priv->table0_len); return -EINVAL;
}
/* verify we have enough room to store the value */ if (*len < sizeof(u32)) {
IPW_DEBUG_ORD("ordinal buffer length too small, " "need %zd\n", sizeof(u32)); return -EINVAL;
}
case IPW_ORD_TABLE_1_MASK: /* *TABLE1:Indirectaccesstoatableof32bitvalues * *Thisisafairlylargetableofu32valueseach *representingstartingaddrforthedata(whichis *alsoau32)
*/
/* remove the table id from the ordinal */
ord &= IPW_ORD_TABLE_VALUE_MASK;
/* boundary check */ if (ord > priv->table1_len) {
IPW_DEBUG_ORD("ordinal value too long\n"); return -EINVAL;
}
/* verify we have enough room to store the value */ if (*len < sizeof(u32)) {
IPW_DEBUG_ORD("ordinal buffer length too small, " "need %zd\n", sizeof(u32)); return -EINVAL;
}
case IPW_ORD_TABLE_2_MASK: /* *TABLE2:Indirectaccesstoatableofvariablesizedvalues * *Thistableconsistofsixvalues,eachcontaining *-dwordcontainingthestartingoffsetofthedata *-dwordcontainingthelenghinthefirst16bits *andthecountinthesecond16bits
*/
/* remove the table id from the ordinal */
ord &= IPW_ORD_TABLE_VALUE_MASK;
/* boundary check */ if (ord > priv->table2_len) {
IPW_DEBUG_ORD("ordinal value too long\n"); return -EINVAL;
}
/* get the address of statistic */
addr = ipw_read_reg32(priv, priv->table2_addr + (ord << 3));
/* get the second DW of statistics ;
* two 16-bit words - first is length, second is count */
field_info =
ipw_read_reg32(priv,
priv->table2_addr + (ord << 3) + sizeof(u32));
/* get each entry length */
field_len = *((u16 *) & field_info);
/* get number of entries */
field_count = *(((u16 *) & field_info) + 1);
/* abort if not enough memory */
total_len = field_len * field_count; if (total_len > *len) {
*len = total_len; return -EINVAL;
}
/* If configured to not use LEDs, or nic_type is 1,
* then we don't toggle a LINK led */ if (priv->config & CFG_NO_LED || priv->nic_type == EEPROM_NIC_TYPE_1) return;
spin_lock_irqsave(&priv->lock, flags);
if (!(priv->status & STATUS_RF_KILL_MASK) &&
!(priv->status & STATUS_LED_LINK_ON)) {
IPW_DEBUG_LED("Link LED On\n");
led = ipw_read_reg32(priv, IPW_EVENT_REG);
led |= priv->led_association_on;
/* If we aren't associated, schedule turning the LED off */ if (!(priv->status & STATUS_ASSOCIATED))
schedule_delayed_work(&priv->led_link_off,
LD_TIME_LINK_ON);
}
/* If configured not to use LEDs, or nic type is 1,
* then we don't goggle the LINK led. */ if (priv->config & CFG_NO_LED || priv->nic_type == EEPROM_NIC_TYPE_1) return;
spin_lock_irqsave(&priv->lock, flags);
if (priv->status & STATUS_LED_LINK_ON) {
led = ipw_read_reg32(priv, IPW_EVENT_REG);
led &= priv->led_association_off;
led = ipw_register_toggle(led);
/* If we aren't associated and the radio is on, schedule
* turning the LED on (blink while unassociated) */ if (!(priv->status & STATUS_RF_KILL_MASK) &&
!(priv->status & STATUS_ASSOCIATED))
schedule_delayed_work(&priv->led_link_on,
LD_TIME_LINK_OFF);
cancel_delayed_work(&priv->led_act_off);
schedule_delayed_work(&priv->led_act_off, LD_TIME_ACT_ON);
} else { /* Reschedule LED off for full time period */
cancel_delayed_work(&priv->led_act_off);
schedule_delayed_work(&priv->led_act_off, LD_TIME_ACT_ON);
}
}
/* Only nic type 1 supports mode LEDs */ if (priv->config & CFG_NO_LED ||
priv->nic_type != EEPROM_NIC_TYPE_1 || !priv->assoc_network) return;
spin_lock_irqsave(&priv->lock, flags);
led = ipw_read_reg32(priv, IPW_EVENT_REG); if (priv->assoc_network->mode == IEEE_A) {
led |= priv->led_ofdm_on;
led &= priv->led_association_off;
IPW_DEBUG_LED("Mode LED On: 802.11a\n");
} elseif (priv->assoc_network->mode == IEEE_G) {
led |= priv->led_ofdm_on;
led |= priv->led_association_on;
IPW_DEBUG_LED("Mode LED On: 802.11g\n");
} else {
led &= priv->led_ofdm_off;
led |= priv->led_association_on;
IPW_DEBUG_LED("Mode LED On: 802.11b\n");
}
/* Set the default PINs for the OFDM leds */
priv->led_ofdm_on = IPW_OFDM_LED;
priv->led_ofdm_off = ~(IPW_OFDM_LED);
switch (priv->nic_type) { case EEPROM_NIC_TYPE_1: /* In this NIC type, the LEDs are reversed.... */
priv->led_activity_on = IPW_ASSOCIATED_LED;
priv->led_activity_off = ~(IPW_ASSOCIATED_LED);
priv->led_association_on = IPW_ACTIVITY_LED;
priv->led_association_off = ~(IPW_ACTIVITY_LED);
if (!(priv->config & CFG_NO_LED))
ipw_led_band_on(priv);
/* And we don't blink link LEDs for this nic, so
* just return here */ return;
case EEPROM_NIC_TYPE_3: case EEPROM_NIC_TYPE_2: case EEPROM_NIC_TYPE_4: case EEPROM_NIC_TYPE_0: break;
default:
IPW_DEBUG_INFO("Unknown NIC type from EEPROM: %d\n",
priv->nic_type);
priv->nic_type = EEPROM_NIC_TYPE_0; break;
}
if (!(priv->config & CFG_NO_LED)) { if (priv->status & STATUS_ASSOCIATED)
ipw_led_link_on(priv); else
ipw_led_link_off(priv);
}
}
IPW_DEBUG_RF_KILL("Manual SW RF Kill set to: RADIO %s\n",
disable_radio ? "OFF" : "ON");
if (disable_radio) {
priv->status |= STATUS_RF_KILL_SW;
cancel_delayed_work(&priv->request_scan);
cancel_delayed_work(&priv->request_direct_scan);
cancel_delayed_work(&priv->request_passive_scan);
cancel_delayed_work(&priv->scan_event);
schedule_work(&priv->down);
} else {
priv->status &= ~STATUS_RF_KILL_SW; if (rf_kill_active(priv)) {
IPW_DEBUG_RF_KILL("Can not turn radio back on - " "disabled by HW switch\n"); /* Make sure the RF_KILL check timer is running */
cancel_delayed_work(&priv->rf_kill);
schedule_delayed_work(&priv->rf_kill,
round_jiffies_relative(2 * HZ));
} else
schedule_work(&priv->up);
}
/* list of space separated channels to scan, optionally ending with 0 */ while ((channel = simple_strtol(p, NULL, 0))) { if (pos == MAX_SPEED_SCAN - 1) {
priv->speed_scan[pos] = 0; break;
}
if (libipw_is_valid_channel(priv->ieee, channel))
priv->speed_scan[pos++] = channel; else
IPW_WARNING("Skipping invalid channel request: %d\n",
channel);
p = strchr(p, ' '); if (!p) break; while (*p == ' ' || *p == '\t')
p++;
}
/* XXX: If hardware encryption is for WPA/WPA2,
* we have to notify the supplicant. */ if (priv->ieee->sec.encrypt) {
priv->status &= ~STATUS_ASSOCIATED;
notify_wx_assoc_event(priv);
}
/* Keep the restart process from trying to send host
* commands by clearing the INIT status bit */
priv->status &= ~STATUS_INIT;
/* Cancel currently queued command. */
priv->status &= ~STATUS_HCMD_ACTIVE;
wake_up_interruptible(&priv->wait_command_queue);
now = jiffies;
end = now + HOST_COMPLETE_TIMEOUT;
again:
rc = wait_event_interruptible_timeout(priv->wait_command_queue,
!(priv->
status & STATUS_HCMD_ACTIVE),
end - now); if (rc < 0) {
now = jiffies; if (time_before(now, end)) goto again;
rc = 0;
}
if (!priv) {
IPW_ERROR("Invalid args\n"); return -1;
}
/* If on battery, set to 3, if AC set to CAM, else user
* level */ switch (mode) { case IPW_POWER_BATTERY:
param = cpu_to_le32(IPW_POWER_INDEX_3); break; case IPW_POWER_AC:
param = cpu_to_le32(IPW_POWER_MODE_CAM); break; default:
param = cpu_to_le32(mode); break;
}
/* write a 32 bit value into the indirect accessor register */ staticinlinevoid eeprom_write_reg(struct ipw_priv *p, u32 data)
{
ipw_write_reg32(p, FW_MEM_REG_EEPROM_ACCESS, data);
/* the eeprom requires some time to complete the operation */
udelay(p->eeprom_delay);
}
/* push a single bit down to the eeprom */ staticinlinevoid eeprom_write_bit(struct ipw_priv *p, u8 bit)
{ int d = (bit ? EEPROM_BIT_DI : 0);
eeprom_write_reg(p, EEPROM_BIT_CS | d);
eeprom_write_reg(p, EEPROM_BIT_CS | d | EEPROM_BIT_SK);
}
/* push an opcode followed by an address down to the eeprom */ staticvoid eeprom_op(struct ipw_priv *priv, u8 op, u8 addr)
{ int i;
eeprom_cs(priv);
eeprom_write_bit(priv, 1);
eeprom_write_bit(priv, op & 2);
eeprom_write_bit(priv, op & 1); for (i = 7; i >= 0; i--) {
eeprom_write_bit(priv, addr & (1 << i));
}
}
/* pull 16 bits off the eeprom, one bit at a time */ static u16 eeprom_read_u16(struct ipw_priv *priv, u8 addr)
{ int i;
u16 r = 0;
/* Send dummy bit */
eeprom_write_reg(priv, EEPROM_BIT_CS);
/* Read the byte off the eeprom one bit at a time */ for (i = 0; i < 16; i++) {
u32 data = 0;
eeprom_write_reg(priv, EEPROM_BIT_CS | EEPROM_BIT_SK);
eeprom_write_reg(priv, EEPROM_BIT_CS);
data = ipw_read_reg32(priv, FW_MEM_REG_EEPROM_ACCESS);
r = (r << 1) | ((data & EEPROM_BIT_DO) ? 1 : 0);
}
/* Send another dummy bit */
eeprom_write_reg(priv, 0);
eeprom_disable_cs(priv);
return r;
}
/* helper function for pulling the mac address out of the private */ /* data's copy of the eeprom data */ staticvoid eeprom_parse_mac(struct ipw_priv *priv, u8 * mac)
{
memcpy(mac, &priv->eeprom[EEPROM_MAC_ADDRESS], ETH_ALEN);
}
/* Ifthedatalookscorrect,thencopyittoourprivate copy.Otherwiseletthefirmwareknowtoperformtheoperation onitsown.
*/ if (priv->eeprom[EEPROM_VERSION] != 0) {
IPW_DEBUG_INFO("Writing EEPROM data into SRAM\n");
/* write the eeprom data to sram */ for (i = 0; i < IPW_EEPROM_IMAGE_SIZE; i++)
ipw_write8(priv, IPW_EEPROM_DATA + i, priv->eeprom[i]);
/* Do not load eeprom data on fatal error or suspend */
ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0);
} else {
IPW_DEBUG_INFO("Enabling FW initialization of SRAM\n");
/* Load eeprom data on fatal error or suspend */
ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 1);
}
staticint ipw_fw_dma_enable(struct ipw_priv *priv)
{ /* start dma engine but no transfers yet */
IPW_DEBUG_FW(">> :\n");
/* Start the dma */
ipw_fw_dma_reset_command_blocks(priv);
/* Write CB base address */
ipw_write_reg32(priv, IPW_DMA_I_CB_BASE, IPW_SHARED_SRAM_DMA_CONTROL);
IPW_DEBUG_FW("<< :\n"); return0;
}
staticvoid ipw_fw_dma_abort(struct ipw_priv *priv)
{
u32 control = 0;
IPW_DEBUG_FW(">> :\n");
/* set the Stop and Abort bit */
control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_STOP_AND_ABORT;
ipw_write_reg32(priv, IPW_DMA_I_DMA_CONTROL, control);
priv->sram_desc.last_cb_index = 0;
/* Read the DMA Controlor register */
register_value = ipw_read_reg32(priv, IPW_DMA_I_DMA_CONTROL);
IPW_DEBUG_FW_INFO("IPW_DMA_I_DMA_CONTROL is 0x%x\n", register_value);
/* Print the CB values */
cb_fields_address = address;
register_value = ipw_read_reg32(priv, cb_fields_address);
IPW_DEBUG_FW_INFO("Current CB Control Field is 0x%x\n", register_value);
cb_fields_address += sizeof(u32);
register_value = ipw_read_reg32(priv, cb_fields_address);
IPW_DEBUG_FW_INFO("Current CB Source Field is 0x%x\n", register_value);
cb_fields_address += sizeof(u32);
register_value = ipw_read_reg32(priv, cb_fields_address);
IPW_DEBUG_FW_INFO("Current CB Destination Field is 0x%x\n",
register_value);
cb_fields_address += sizeof(u32);
register_value = ipw_read_reg32(priv, cb_fields_address);
IPW_DEBUG_FW_INFO("Current CB Status Field is 0x%x\n", register_value);
for (i = 0; i < nr; i++) {
size = min_t(u32, len - i * CB_MAX_LENGTH, CB_MAX_LENGTH);
ret = ipw_fw_dma_add_command_block(priv, src_address[i],
dest_address +
i * CB_MAX_LENGTH, size, 0, 0); if (ret) {
IPW_DEBUG_FW_INFO(": Failed\n"); return -1;
} else
IPW_DEBUG_FW_INFO(": Added new cb\n");
}
/* timeout in msec, attempted in 10-msec quanta */ staticint ipw_poll_bit(struct ipw_priv *priv, u32 addr, u32 mask, int timeout)
{ int i = 0;
do { if ((ipw_read32(priv, addr) & mask) == mask) return i;
mdelay(10);
i += 10;
} while (i < timeout);
return -ETIME;
}
/* These functions load the firmware and micro code for the operation of *theipwhardware.Itassumesthebufferhasallthebitsforthe *imageandthecallerishandlingthememoryallocationandcleanup.
*/
staticint ipw_stop_master(struct ipw_priv *priv)
{ int rc;
/* this is where the igx / win driver deveates from the VAP driver. */
/* wait for alive response */ for (i = 0; i < 100; i++) { /* poll for incoming data */
cr = ipw_read_reg8(priv, IPW_BASEBAND_CONTROL_STATUS); if (cr & DINO_RXFIFO_DATA) break;
mdelay(1);
}
if (cr & DINO_RXFIFO_DATA) { /* alive_command_responce size is NOT multiple of 4 */
__le32 response_buffer[(sizeof(priv->dino_alive) + 3) / 4];
nr = (chunk_len + CB_MAX_LENGTH - 1) / CB_MAX_LENGTH; for (i = 0; i < nr; i++) {
virts[total_nr] = dma_pool_alloc(pool, GFP_KERNEL,
&phys[total_nr]); if (!virts[total_nr]) {
ret = -ENOMEM; goto out;
}
size = min_t(u32, chunk_len - i * CB_MAX_LENGTH,
CB_MAX_LENGTH);
memcpy(virts[total_nr], start, size);
start += size;
total_nr++; /* We don't support fw chunk larger than 64*8K */
BUG_ON(total_nr > CB_NUMBER_OF_ELEMENTS_SMALL);
}
/* build DMA packet and queue up for sending */ /* dma to chunk->address, the chunk->length bytes from data +
* offeset*/ /* Dma loading */
ret = ipw_fw_dma_add_buffer(priv, &phys[total_nr - nr],
nr, le32_to_cpu(chunk->address),
chunk_len); if (ret) {
IPW_DEBUG_INFO("dmaAddBuffer Failed\n"); goto out;
}
offset += chunk_len;
} while (offset < len);
/* Run the DMA and wait for the answer */
ret = ipw_fw_dma_kick(priv); if (ret) {
IPW_ERROR("dmaKick Failed\n"); goto out;
}
ret = ipw_fw_dma_wait(priv); if (ret) {
IPW_ERROR("dmaWaitSync Failed\n"); goto out;
}
out: for (i = 0; i < total_nr; i++)
dma_pool_free(pool, virts[i], phys[i]);
/* enable power management */
ipw_set_bit(priv, IPW_GP_CNTRL_RW,
IPW_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY);
IPW_DEBUG_TRACE("<<\n");
}
staticint ipw_init_nic(struct ipw_priv *priv)
{ int rc;
IPW_DEBUG_TRACE(">>\n"); /* reset */ /*prvHwInitNic */ /* set "initialization complete" bit to move adapter to D0 state */
ipw_set_bit(priv, IPW_GP_CNTRL_RW, IPW_GP_CNTRL_BIT_INIT_DONE);
/* set "initialization complete" bit to move adapter to D0 state */
ipw_set_bit(priv, IPW_GP_CNTRL_RW, IPW_GP_CNTRL_BIT_INIT_DONE);
IPW_DEBUG_TRACE(">>\n"); return0;
}
/* Call this function from process context, it will sleep in request_firmware. *Probeisanokplacetocallthisfrom.
*/ staticint ipw_reset_nic(struct ipw_priv *priv)
{ int rc = 0; unsignedlong flags;
/* ask firmware_class module to get the boot firmware off disk */
rc = request_firmware(raw, name, &priv->pci_dev->dev); if (rc < 0) {
IPW_ERROR("%s request_firmware failed: Reason %d\n", name, rc); return rc;
}
if ((*raw)->size < sizeof(*fw)) {
IPW_ERROR("%s is too small (%zd)\n", name, (*raw)->size); return -EINVAL;
}
fw = (void *)(*raw)->data;
if ((*raw)->size < sizeof(*fw) + le32_to_cpu(fw->boot_size) +
le32_to_cpu(fw->ucode_size) + le32_to_cpu(fw->fw_size)) {
IPW_ERROR("%s is too small or corrupt (%zd)\n",
name, (*raw)->size); return -EINVAL;
}
/* Fill the rx_used queue with _all_ of the Rx buffers */ for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) { /* In the reset function, these buffers may have been allocated
* to an SKB, so we need to unmap and free potential storage */ if (rxq->pool[i].skb != NULL) {
dma_unmap_single(&priv->pci_dev->dev,
rxq->pool[i].dma_addr,
IPW_RX_BUF_SIZE, DMA_FROM_DEVICE);
dev_kfree_skb_irq(rxq->pool[i].skb);
rxq->pool[i].skb = NULL;
}
list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
}
/* Set us so that we have processed and used all buffers, but have
* not restocked the Rx queue with fresh buffers */
rxq->read = rxq->write = 0;
rxq->free_count = 0;
spin_unlock_irqrestore(&rxq->lock, flags);
}
switch (priv->ieee->iw_mode) { case IW_MODE_ADHOC:
name = "ipw2200-ibss.fw"; break; #ifdef CONFIG_IPW2200_MONITOR case IW_MODE_MONITOR:
name = "ipw2200-sniffer.fw"; break; #endif case IW_MODE_INFRA:
name = "ipw2200-bss.fw"; break;
}
/* *ipw_rx_queue_space-Returnnumberoffreeslotsavailableinqueue.
*/ staticint ipw_rx_queue_space(conststruct ipw_rx_queue *q)
{ int s = q->read - q->write; if (s <= 0)
s += RX_QUEUE_SIZE; /* keep some buffer to not confuse full and empty queue */
s -= 2; if (s < 0)
s = 0; return s;
}
staticinlineint ipw_tx_queue_space(conststruct clx2_queue *q)
{ int s = q->last_used - q->first_empty; if (s <= 0)
s += q->n_bd;
s -= 2; /* keep some reserve to not confuse empty and full situations */ if (s < 0)
s = 0; return s;
}
/* classify bd */ if (bd->control_flags.message_type == TX_HOST_COMMAND_TYPE) /* nothing to cleanup after for host commands */ return;
/* sanity check */ if (le32_to_cpu(bd->u.data.num_chunks) > NUM_TFD_CHUNKS) {
IPW_ERROR("Too many chunks: %i\n",
le32_to_cpu(bd->u.data.num_chunks)); /* @todo issue fatal error, it is quite serious situation */ return;
}
/* unmap chunks if any */ for (i = 0; i < le32_to_cpu(bd->u.data.num_chunks); i++) {
dma_unmap_single(&dev->dev,
le32_to_cpu(bd->u.data.chunk_ptr[i]),
le16_to_cpu(bd->u.data.chunk_len[i]),
DMA_TO_DEVICE); if (txq->txb[txq->q.last_used]) {
libipw_txb_free(txq->txb[txq->q.last_used]);
txq->txb[txq->q.last_used] = NULL;
}
}
}
for (i = 0; i < priv->num_stations; i++) { if (ether_addr_equal(priv->stations[i], bssid)) { /* Another node is active in network */
priv->missed_adhoc_beacons = 0; if (!(priv->config & CFG_STATIC_CHANNEL)) /* when other nodes drop out, we drop out */
priv->config &= ~CFG_ADHOC_PERSIST;
return i;
}
}
if (i == MAX_STATIONS) return IPW_INVALID_STATION;
staticconststruct ipw_status_code ipw_status_codes[] = {
{0x00, "Successful"},
{0x01, "Unspecified failure"},
{0x0A, "Cannot support all requested capabilities in the " "Capability information field"},
{0x0B, "Reassociation denied due to inability to confirm that " "association exists"},
{0x0C, "Association denied due to reason outside the scope of this " "standard"},
{0x0D, "Responding station does not support the specified authentication " "algorithm"},
{0x0E, "Received an Authentication frame with authentication sequence " "transaction sequence number out of expected sequence"},
{0x0F, "Authentication rejected because of challenge failure"},
{0x10, "Authentication rejected due to timeout waiting for next " "frame in sequence"},
{0x11, "Association denied because AP is unable to handle additional " "associated stations"},
{0x12, "Association denied due to requesting station not supporting all " "of the datarates in the BSSBasicServiceSet Parameter"},
{0x13, "Association denied due to requesting station not supporting " "short preamble operation"},
{0x14, "Association denied due to requesting station not supporting " "PBCC encoding"},
{0x15, "Association denied due to requesting station not supporting " "channel agility"},
{0x19, "Association denied due to requesting station not supporting " "short slot operation"},
{0x1A, "Association denied due to requesting station not supporting " "DSSS-OFDM operation"},
{0x28, "Invalid Information Element"},
{0x29, "Group Cipher is not valid"},
{0x2A, "Pairwise Cipher is not valid"},
{0x2B, "AKMP is not valid"},
{0x2C, "Unsupported RSN IE version"},
{0x2D, "Invalid RSN IE Capabilities"},
{0x2E, "Cipher suite is rejected per security policy"},
};
staticconstchar *ipw_get_status_code(u16 status)
{ int i; for (i = 0; i < ARRAY_SIZE(ipw_status_codes); i++) if (ipw_status_codes[i].status == (status & 0xff)) return ipw_status_codes[i].reason; return"Unknown status value.";
}
staticinlinevoid average_init(struct average *avg)
{
memset(avg, 0, sizeof(*avg));
}
/* Firmware managed, reset only when NIC is restarted, so we have to
* normalize on the current value */
ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC,
&priv->last_rx_err, &len);
ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE,
&priv->last_tx_failures, &len);
/* Driver managed, reset with each association */
priv->missed_adhoc_beacons = 0;
priv->missed_beacons = 0;
priv->tx_packets = 0;
priv->rx_packets = 0;
}
static u32 ipw_get_max_rate(struct ipw_priv *priv)
{
u32 i = 0x80000000;
u32 mask = priv->rates_mask; /* If currently associated in B mode, restrict the maximum
* rate match to B rates */ if (priv->assoc_request.ieee_mode == IPW_B_MODE)
mask &= LIBIPW_CCK_RATES_MASK;
/* TODO: Verify that the rate is supported by the current rates
* list. */
while (i && !(mask & i))
i >>= 1; switch (i) { case LIBIPW_CCK_RATE_1MB_MASK: return1000000; case LIBIPW_CCK_RATE_2MB_MASK: return2000000; case LIBIPW_CCK_RATE_5MB_MASK: return5500000; case LIBIPW_OFDM_RATE_6MB_MASK: return6000000; case LIBIPW_OFDM_RATE_9MB_MASK: return9000000; case LIBIPW_CCK_RATE_11MB_MASK: return11000000; case LIBIPW_OFDM_RATE_12MB_MASK: return12000000; case LIBIPW_OFDM_RATE_18MB_MASK: return18000000; case LIBIPW_OFDM_RATE_24MB_MASK: return24000000; case LIBIPW_OFDM_RATE_36MB_MASK: return36000000; case LIBIPW_OFDM_RATE_48MB_MASK: return48000000; case LIBIPW_OFDM_RATE_54MB_MASK: return54000000;
}
if (priv->ieee->mode == IEEE_B) return11000000; else return54000000;
}
static u32 ipw_get_current_rate(struct ipw_priv *priv)
{
u32 rate, len = sizeof(rate); int err;
switch (rate) { case IPW_TX_RATE_1MB: return1000000; case IPW_TX_RATE_2MB: return2000000; case IPW_TX_RATE_5MB: return5500000; case IPW_TX_RATE_6MB: return6000000; case IPW_TX_RATE_9MB: return9000000; case IPW_TX_RATE_11MB: return11000000; case IPW_TX_RATE_12MB: return12000000; case IPW_TX_RATE_18MB: return18000000; case IPW_TX_RATE_24MB: return24000000; case IPW_TX_RATE_36MB: return36000000; case IPW_TX_RATE_48MB: return48000000; case IPW_TX_RATE_54MB: return54000000;
}
/* Missed beacon behavior: *1stmissed->roaming_threshold,justwait,don'tdoanyscan/roam. *roaming_threshold->disassociate_threshold,scanandroamforbettersignal. *Abovedisassociatethreshold,giveupandstopscanning.
* Roaming is disabled if disassociate_threshold <= roaming_threshold */ staticvoid ipw_handle_missed_beacon(struct ipw_priv *priv, int missed_count)
{
priv->notif_missed_beacons = missed_count;
if (missed_count > priv->disassociate_threshold &&
priv->status & STATUS_ASSOCIATED) { /* If associated and we've hit the missed *beaconthreshold,disassociate,turn
* off roaming, and abort any active scans */
IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
IPW_DL_STATE | IPW_DL_ASSOC, "Missed beacon: %d - disassociate\n", missed_count);
priv->status &= ~STATUS_ROAMING; if (priv->status & STATUS_SCANNING) {
IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
IPW_DL_STATE, "Aborting scan with missed beacon.\n");
schedule_work(&priv->abort_scan);
}
schedule_work(&priv->disassociate); return;
}
if (priv->status & STATUS_ROAMING) { /* If we are currently roaming, then just
* print a debug statement... */
IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE, "Missed beacon: %d - roam in progress\n",
missed_count); return;
}
if (roaming &&
(missed_count > priv->roaming_threshold &&
missed_count <= priv->disassociate_threshold)) { /* If we are not already roaming, set the ROAM *bitinthestatusandkickoffascan. *Thiscanhappenseveraltimesbeforewereach
* disassociate_threshold. */
IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE, "Missed beacon: %d - initiate " "roaming\n", missed_count); if (!(priv->status & STATUS_ROAMING)) {
priv->status |= STATUS_ROAMING; if (!(priv->status & STATUS_SCANNING))
schedule_delayed_work(&priv->request_scan, 0);
} return;
}
if (priv->status & STATUS_SCANNING &&
missed_count > IPW_MB_SCAN_CANCEL_THRESHOLD) { /* Stop scan to keep fw from getting *stuck(onlyifwearen'troaming-- *otherwisewe'llneverscanmorethan2or3
* channels..) */
IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF | IPW_DL_STATE, "Aborting scan with missed beacon.\n");
schedule_work(&priv->abort_scan);
}
case HOST_NOTIFICATION_STATUS_SCAN_CHANNEL_RESULT:{ struct notif_channel_result *x =
¬if->u.channel_result;
if (size == sizeof(*x)) {
IPW_DEBUG_SCAN("Scan result for channel %d\n",
x->channel_num);
} else {
IPW_DEBUG_SCAN("Scan result of wrong size %d " "(should be %zd)\n",
size, sizeof(*x));
} break;
}
case HOST_NOTIFICATION_STATUS_SCAN_COMPLETED:{ struct notif_scan_complete *x = ¬if->u.scan_complete; if (size == sizeof(*x)) {
IPW_DEBUG_SCAN
("Scan completed: type %d, %d channels, " "%d status\n", x->scan_type,
x->num_channels, x->status);
} else {
IPW_ERROR("Scan completed of wrong size %d " "(should be %zd)\n",
size, sizeof(*x));
}
/* Do queued direct scans first */ if (priv->status & STATUS_DIRECT_SCAN_PENDING)
schedule_delayed_work(&priv->request_direct_scan, 0);
if (!(priv->status & (STATUS_ASSOCIATED |
STATUS_ASSOCIATING |
STATUS_ROAMING |
STATUS_DISASSOCIATING)))
schedule_work(&priv->associate); elseif (priv->status & STATUS_ROAMING) { if (x->status == SCAN_COMPLETED_STATUS_COMPLETE) /* If a scan completed and we are in roam mode, then *thescanthatcompletedwastheonerequestedasa *resultofenteringroam...so,schedulethe
* roam work */
schedule_work(&priv->roam); else /* Don't schedule if we aborted the scan */
priv->status &= ~STATUS_ROAMING;
} elseif (priv->status & STATUS_SCAN_PENDING)
schedule_delayed_work(&priv->request_scan, 0); elseif (priv->config & CFG_BACKGROUND_SCAN
&& priv->status & STATUS_ASSOCIATED)
schedule_delayed_work(&priv->request_scan,
round_jiffies_relative(HZ));
/* Send an empty event to user space. *Wedon'tsendthereceiveddataontheeventbecause *itwouldrequireustodocomplextranscoding,and *wewanttominimisetheworkdoneintheirqhandler *Usearequesttoextractthedata. *Also,wegeneratethisevenforanyscan,regardless *onhowthescanwasinitiated.Userspacecanjust *synconperiodicscantogetfreshdata...
* Jean II */ if (x->status == SCAN_COMPLETED_STATUS_COMPLETE)
handle_scan_event(priv); break;
}
case HOST_NOTIFICATION_STATUS_FRAG_LENGTH:{ struct notif_frag_length *x = ¬if->u.frag_len;
if (size == sizeof(*x))
IPW_ERROR("Frag length: %d\n",
le16_to_cpu(x->frag_length)); else
IPW_ERROR("Frag length of wrong size %d " "(should be %zd)\n",
size, sizeof(*x)); break;
}
case HOST_NOTIFICATION_STATUS_LINK_DETERIORATION:{ struct notif_link_deterioration *x =
¬if->u.link_deterioration;
if (size == sizeof(*x)) {
IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE, "link deterioration: type %d, cnt %d\n",
x->silence_notification_type,
x->silence_count);
memcpy(&priv->last_link_deterioration, x, sizeof(*x));
} else {
IPW_ERROR("Link Deterioration of wrong size %d " "(should be %zd)\n",
size, sizeof(*x));
} break;
}
case HOST_NOTIFICATION_DINO_CONFIG_RESPONSE:{
IPW_ERROR("Dino config\n"); if (priv->hcmd
&& priv->hcmd->cmd != HOST_CMD_DINO_CONFIG)
IPW_ERROR("Unexpected DINO_CONFIG_RESPONSE\n");
break;
}
case HOST_NOTIFICATION_STATUS_BEACON_STATE:{ struct notif_beacon_state *x = ¬if->u.beacon_state; if (size != sizeof(*x)) {
IPW_ERROR
("Beacon state of wrong size %d (should " "be %zd)\n", size, sizeof(*x)); break;
}
if (le32_to_cpu(x->state) ==
HOST_NOTIFICATION_STATUS_BEACON_MISSING)
ipw_handle_missed_beacon(priv,
le32_to_cpu(x->
number));
break;
}
case HOST_NOTIFICATION_STATUS_TGI_TX_KEY:{ struct notif_tgi_tx_key *x = ¬if->u.tgi_tx_key; if (size == sizeof(*x)) {
IPW_ERROR("TGi Tx Key: state 0x%02x sec type " "0x%02x station %d\n",
x->key_state, x->security_type,
x->station_index); break;
}
IPW_ERROR
("TGi Tx Key of wrong size %d (should be %zd)\n",
size, sizeof(*x)); break;
}
case HOST_NOTIFICATION_CALIB_KEEP_RESULTS:{ struct notif_calibration *x = ¬if->u.calibration;
/* If the pre-allocated buffer pool is dropping low, schedule to
* refill it */ if (rxq->free_count <= RX_LOW_WATERMARK)
schedule_work(&priv->rx_replenish);
/* If we've added more space for the firmware to place data, tell it */ if (write != rxq->write)
ipw_write32(priv, IPW_RX_WRITE_INDEX, rxq->write);
}
spin_lock_irqsave(&rxq->lock, flags); while (!list_empty(&rxq->rx_used)) {
element = rxq->rx_used.next;
rxb = list_entry(element, struct ipw_rx_mem_buffer, list);
rxb->skb = alloc_skb(IPW_RX_BUF_SIZE, GFP_ATOMIC); if (!rxb->skb) {
printk(KERN_CRIT "%s: Can not allocate SKB buffers.\n",
priv->net_dev->name); /* We don't reschedule replenish work here -- we will *calltherestockmethodandifitstillneeds
* more buffers it will schedule replenish */ break;
}
list_del(element);
/* Assumes that the skb field of the buffers in 'pool' is kept accurate. *IfanSKBhasbeendetached,thePOOLneedstohaveitsSKBsettoNULL *ThisfreeroutinewalksthelistofPOOLentriesandifSKBissetto *nonNULLitisunmappedandfreed
*/ staticvoid ipw_rx_queue_free(struct ipw_priv *priv, struct ipw_rx_queue *rxq)
{ int i;
if (!rxq) return;
for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) { if (rxq->pool[i].skb != NULL) {
dma_unmap_single(&priv->pci_dev->dev,
rxq->pool[i].dma_addr,
IPW_RX_BUF_SIZE, DMA_FROM_DEVICE);
dev_kfree_skb(rxq->pool[i].skb);
}
}
/* Fill the rx_used queue with _all_ of the Rx buffers */ for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++)
list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
/* Set us so that we have processed and used all buffers, but have
* not restocked the Rx queue with fresh buffers */
rxq->read = rxq->write = 0;
rxq->free_count = 0;
staticvoid ipw_copy_rates(struct ipw_supported_rates *dest, conststruct ipw_supported_rates *src)
{
u8 i; for (i = 0; i < src->num_rates; i++)
dest->supported_rates[i] = src->supported_rates[i];
dest->num_rates = src->num_rates;
}
/* TODO: Look at sniffed packets in the air to determine if the basic rate *maskshouldeverbeused--rightnowallcallerstoaddthescanratesare
* set with the modulation = CCK, so BASIC_RATE_MASK is never set... */ staticvoid ipw_add_cck_scan_rates(struct ipw_supported_rates *rates,
u8 modulation, u32 rate_mask)
{
u8 basic_mask = (LIBIPW_OFDM_MODULATION == modulation) ?
LIBIPW_BASIC_RATE_MASK : 0;
if (rate_mask & LIBIPW_CCK_RATE_1MB_MASK)
rates->supported_rates[rates->num_rates++] =
LIBIPW_BASIC_RATE_MASK | LIBIPW_CCK_RATE_1MB;
if (rate_mask & LIBIPW_CCK_RATE_2MB_MASK)
rates->supported_rates[rates->num_rates++] =
LIBIPW_BASIC_RATE_MASK | LIBIPW_CCK_RATE_2MB;
if (rate_mask & LIBIPW_CCK_RATE_5MB_MASK)
rates->supported_rates[rates->num_rates++] = basic_mask |
LIBIPW_CCK_RATE_5MB;
if (rate_mask & LIBIPW_CCK_RATE_11MB_MASK)
rates->supported_rates[rates->num_rates++] = basic_mask |
LIBIPW_CCK_RATE_11MB;
}
/* Verify that this network's capability is compatible with the
* current mode (AdHoc or Infrastructure) */ if ((priv->ieee->iw_mode == IW_MODE_ADHOC &&
!(network->capability & WLAN_CAPABILITY_IBSS))) {
IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded due to capability mismatch.\n",
network->ssid_len, network->ssid,
network->bssid); return0;
}
if (unlikely(roaming)) { /* If we are roaming, then ensure check if this is a valid
* network to try and roam to */ if ((network->ssid_len != match->network->ssid_len) ||
memcmp(network->ssid, match->network->ssid,
network->ssid_len)) {
IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of non-network ESSID.\n",
network->ssid_len, network->ssid,
network->bssid); return0;
}
} else { /* If an ESSID has been configured then compare the broadcast
* ESSID to ours */ if ((priv->config & CFG_STATIC_ESSID) &&
((network->ssid_len != priv->essid_len) ||
memcmp(network->ssid, priv->essid,
min(network->ssid_len, priv->essid_len)))) {
IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of ESSID mismatch: '%*pE'.\n",
network->ssid_len, network->ssid,
network->bssid, priv->essid_len,
priv->essid); return0;
}
}
/* If the old network rate is better than this one, don't bother
* testing everything else. */
if (network->time_stamp[0] < match->network->time_stamp[0]) {
IPW_DEBUG_MERGE("Network '%*pE excluded because newer than current network.\n",
match->network->ssid_len, match->network->ssid); return0;
} elseif (network->time_stamp[1] < match->network->time_stamp[1]) {
IPW_DEBUG_MERGE("Network '%*pE excluded because newer than current network.\n",
match->network->ssid_len, match->network->ssid); return0;
}
/* Now go through and see if the requested network is valid... */ if (priv->ieee->scan_age != 0 &&
time_after(jiffies, network->last_scanned + priv->ieee->scan_age)) {
IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of age: %ums.\n",
network->ssid_len, network->ssid,
network->bssid,
jiffies_to_msecs(jiffies -
network->last_scanned)); return0;
}
if ((priv->config & CFG_STATIC_CHANNEL) &&
(network->channel != priv->channel)) {
IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of channel mismatch: %d != %d.\n",
network->ssid_len, network->ssid,
network->bssid,
network->channel, priv->channel); return0;
}
if (ether_addr_equal(network->bssid, priv->bssid)) {
IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of the same BSSID match: %pM.\n",
network->ssid_len, network->ssid,
network->bssid, priv->bssid); return0;
}
/* Filter out any incompatible freq / mode combinations */ if (!libipw_is_valid_mode(priv->ieee, network->mode)) {
IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of invalid frequency/mode combination.\n",
network->ssid_len, network->ssid,
network->bssid); return0;
}
/* 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)) {
IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because configured rate mask excludes AP mandatory rate.\n",
network->ssid_len, network->ssid,
network->bssid); return0;
}
if (rates.num_rates == 0) {
IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of no compatible rates.\n",
network->ssid_len, network->ssid,
network->bssid); return0;
}
/* TODO: Perform any further minimal comparititive tests. We do not *wanttoputtoomuchpolicylogichere;intelligentscanselection
* should occur within a generic IEEE 802.11 user space tool. */
/* Set up 'new' AP to this network */
ipw_copy_rates(&match->rates, &rates);
match->network = network;
IPW_DEBUG_MERGE("Network '%*pE (%pM)' is a viable match.\n",
network->ssid_len, network->ssid, network->bssid);
if ((priv->status & STATUS_ASSOCIATED) &&
(priv->ieee->iw_mode == IW_MODE_ADHOC)) { /* First pass through ROAM process -- look for a better
* network */ unsignedlong flags;
/* Verify that this network's capability is compatible with the
* current mode (AdHoc or Infrastructure) */ if ((priv->ieee->iw_mode == IW_MODE_INFRA &&
!(network->capability & WLAN_CAPABILITY_ESS)) ||
(priv->ieee->iw_mode == IW_MODE_ADHOC &&
!(network->capability & WLAN_CAPABILITY_IBSS))) {
IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded due to capability mismatch.\n",
network->ssid_len, network->ssid,
network->bssid); return0;
}
if (unlikely(roaming)) { /* If we are roaming, then ensure check if this is a valid
* network to try and roam to */ if ((network->ssid_len != match->network->ssid_len) ||
memcmp(network->ssid, match->network->ssid,
network->ssid_len)) {
IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of non-network ESSID.\n",
network->ssid_len, network->ssid,
network->bssid); return0;
}
} else { /* If an ESSID has been configured then compare the broadcast
* ESSID to ours */ if ((priv->config & CFG_STATIC_ESSID) &&
((network->ssid_len != priv->essid_len) ||
memcmp(network->ssid, priv->essid,
min(network->ssid_len, priv->essid_len)))) {
IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of ESSID mismatch: '%*pE'.\n",
network->ssid_len, network->ssid,
network->bssid, priv->essid_len,
priv->essid); return0;
}
}
/* If the old network rate is better than this one, don't bother
* testing everything else. */ if (match->network && match->network->stats.rssi > network->stats.rssi) {
IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because '%*pE (%pM)' has a stronger signal.\n",
network->ssid_len, network->ssid,
network->bssid, match->network->ssid_len,
match->network->ssid, match->network->bssid); return0;
}
/* If this network has already had an association attempt within the
* last 3 seconds, do not try and associate again... */ if (network->last_associate &&
time_after(network->last_associate + (HZ * 3UL), jiffies)) {
IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of storming (%ums since last assoc attempt).\n",
network->ssid_len, network->ssid,
network->bssid,
jiffies_to_msecs(jiffies -
network->last_associate)); return0;
}
/* Now go through and see if the requested network is valid... */ if (priv->ieee->scan_age != 0 &&
time_after(jiffies, network->last_scanned + priv->ieee->scan_age)) {
IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of age: %ums.\n",
network->ssid_len, network->ssid,
network->bssid,
jiffies_to_msecs(jiffies -
network->last_scanned)); return0;
}
if ((priv->config & CFG_STATIC_CHANNEL) &&
(network->channel != priv->channel)) {
IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of channel mismatch: %d != %d.\n",
network->ssid_len, network->ssid,
network->bssid,
network->channel, priv->channel); return0;
}
if ((priv->config & CFG_STATIC_BSSID) &&
!ether_addr_equal(network->bssid, priv->bssid)) {
IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of BSSID mismatch: %pM.\n",
network->ssid_len, network->ssid,
network->bssid, priv->bssid); return0;
}
/* Filter out any incompatible freq / mode combinations */ if (!libipw_is_valid_mode(priv->ieee, network->mode)) {
IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of invalid frequency/mode combination.\n",
network->ssid_len, network->ssid,
network->bssid); return0;
}
/* Filter out invalid channel in current GEO */ if (!libipw_is_valid_channel(priv->ieee, network->channel)) {
IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of invalid channel in current GEO\n",
network->ssid_len, network->ssid,
network->bssid); return0;
}
/* 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)) {
IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because configured rate mask excludes AP mandatory rate.\n",
network->ssid_len, network->ssid,
network->bssid); return0;
}
if (rates.num_rates == 0) {
IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of no compatible rates.\n",
network->ssid_len, network->ssid,
network->bssid); return0;
}
/* TODO: Perform any further minimal comparititive tests. We do not *wanttoputtoomuchpolicylogichere;intelligentscanselection
* should occur within a generic IEEE 802.11 user space tool. */
/* Set up 'new' AP to this network */
ipw_copy_rates(&match->rates, &rates);
match->network = network;
IPW_DEBUG_ASSOC("Network '%*pE (%pM)' is a viable match.\n",
network->ssid_len, network->ssid, network->bssid);
staticvoid ipw_send_wep_keys(struct ipw_priv *priv, int type)
{ struct ipw_wep_key key; int i;
key.cmd_id = DINO_CMD_WEP_KEY;
key.seq_num = 0;
/* Note: AES keys cannot be set for multiple times.
* Only set it at the first time. */ for (i = 0; i < 4; i++) {
key.key_index = i | type; if (!(priv->ieee->sec.flags & (1 << i))) {
key.key_size = 0; continue;
}
if (priv->ieee->freq_band & LIBIPW_24GHZ_BAND) { int start = channel_index; if (priv->config & CFG_SPEED_SCAN) { int index;
u8 channels[LIBIPW_24GHZ_CHANNELS] = { /* nop out the list */
[0] = 0
};
/* Use active scan by default. */ if (priv->config & CFG_SPEED_SCAN)
scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
cpu_to_le16(30); else
scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
cpu_to_le16(20);
/* NOTE: The card will sit on this channel for this time *period.Scanabortsaretimingsensitiveandfrequently *resultinfirmwarerestarts.Assuch,itisbestto *setasmalldwell_timehereandjustkeepre-issuing *scans.Otherwisefastchannelhoppingwillnotactually *hopchannels. *
* TODO: Move SPEED SCAN support to all modes and bands */
scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] =
cpu_to_le16(2000);
} else { #endif/* CONFIG_IPW2200_MONITOR */ /* Honor direct scans first, otherwise if we are roaming make *thisadirectscanforthecurrentnetwork.Finally,
* ensure that every other scan is a fast channel hop scan */ if (direct) {
err = ipw_send_ssid(priv, priv->direct_scan_ssid,
priv->direct_scan_ssid_len); if (err) {
IPW_DEBUG_HC("Attempt to send SSID command " "failed\n"); goto done;
}
staticint 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; return0;
}
staticint ipw_wpa_set_auth_algs(struct ipw_priv *priv, int value)
{ struct libipw_device *ieee = priv->ieee; struct libipw_security sec = {
.flags = SEC_AUTH_MODE,
}; int ret = 0;
switch (param->flags & IW_AUTH_INDEX) { case IW_AUTH_WPA_VERSION: case IW_AUTH_CIPHER_PAIRWISE: case IW_AUTH_CIPHER_GROUP: case IW_AUTH_KEY_MGMT: /* *wpa_supplicantwillcontroltheseinternally
*/ return -EOPNOTSUPP;
case IW_AUTH_TKIP_COUNTERMEASURES:
crypt = priv->ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx]; if (!crypt || !crypt->ops->get_flags) break;
if (priv->qos_data.qos_enable && qos_data->supported) {
IPW_DEBUG_QOS("QoS will be enabled for this association\n");
priv->assoc_request.policy_support |= HC_QOS_SUPPORT_ASSOC; return ipw_qos_set_info_element(priv);
}
/* Clear the short preamble if we won't be supporting it */
priv->assoc_request.capability &=
~cpu_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE);
}
/* Clear capability bits that aren't used in Ad Hoc */ if (priv->ieee->iw_mode == IW_MODE_ADHOC)
priv->assoc_request.capability &=
~cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT_TIME);
/* The roaming process is as follows: * *1.Missedbeaconthresholdtriggerstheroamingprocessby *settingthestatusROAMbitandrequestingascan. *2.Whenthescancompletes,itschedulestheROAMwork *3.TheROAMworklooksatalloftheknownnetworksforonethat *isabetternetworkthanthecurrentlyassociated.Ifnone *found,theROAMprocessisover(ROAMbitcleared) *4.Ifabetternetworkisfound,adisassociationrequestis *sent. *5.Whenthedisassociationcompletes,theroamworkisagain *scheduled.Thesecondtimethrough,thedriverisnolonger *associated,andthenewlyselectednetworkissentan *associationrequest. *6.Atthispoint,theroamingprocessiscompleteandtheROAM *statusbitiscleared.
*/
/* If we are no longer associated, and the roaming bit is no longer
* set, then we are not actively roaming, so just return */ if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ROAMING))) return;
if (priv->status & STATUS_ASSOCIATED) { /* First pass through ROAM process -- look for a better
* network */ unsignedlong flags;
u8 rssi = priv->assoc_network->stats.rssi;
priv->assoc_network->stats.rssi = -128;
spin_lock_irqsave(&priv->ieee->lock, flags);
list_for_each_entry(network, &priv->ieee->network_list, list) { if (network != priv->assoc_network)
ipw_best_network(priv, &match, network, 1);
}
spin_unlock_irqrestore(&priv->ieee->lock, flags);
priv->assoc_network->stats.rssi = rssi;
if (match.network == priv->assoc_network) {
IPW_DEBUG_ASSOC("No better APs in this network to " "roam to.\n");
priv->status &= ~STATUS_ROAMING;
ipw_debug_config(priv); return;
}
if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
IPW_DEBUG_ASSOC("Not attempting association (monitor mode)\n"); return0;
}
if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
IPW_DEBUG_ASSOC("Not attempting association (already in " "progress)\n"); return0;
}
if (priv->status & STATUS_DISASSOCIATING) {
IPW_DEBUG_ASSOC("Not attempting association (in disassociating)\n");
schedule_work(&priv->associate); return0;
}
if (!ipw_is_init(priv) || (priv->status & STATUS_SCANNING)) {
IPW_DEBUG_ASSOC("Not attempting association (scanning or not " "initialized)\n"); return0;
}
if (!(priv->config & CFG_ASSOCIATE) &&
!(priv->config & (CFG_STATIC_ESSID | CFG_STATIC_BSSID))) {
IPW_DEBUG_ASSOC("Not attempting association (associate=0)\n"); return0;
}
/* Protect our use of the network_list */
spin_lock_irqsave(&priv->ieee->lock, flags);
list_for_each_entry(network, &priv->ieee->network_list, list)
ipw_best_network(priv, &match, network, 0);
network = match.network;
rates = &match.rates;
if (network == NULL &&
priv->ieee->iw_mode == IW_MODE_ADHOC &&
priv->config & CFG_ADHOC_CREATE &&
priv->config & CFG_STATIC_ESSID &&
priv->config & CFG_STATIC_CHANNEL) { /* Use oldest network if the free list is empty */ if (list_empty(&priv->ieee->network_free_list)) { struct libipw_network *oldest = NULL; struct libipw_network *target;
/* If there are no more slots, expire the oldest */
list_del(&oldest->list);
target = oldest;
IPW_DEBUG_ASSOC("Expired '%*pE' (%pM) from network list.\n",
target->ssid_len, target->ssid,
target->bssid);
list_add_tail(&target->list,
&priv->ieee->network_free_list);
}
/* We received data from the HW, so stop the watchdog */
netif_trans_update(dev);
/* 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))) {
dev->stats.rx_errors++;
priv->wstats.discard.misc++;
IPW_DEBUG_DROP("Corruption detected! Oh no!\n"); return;
} elseif (unlikely(!netif_running(priv->net_dev))) {
dev->stats.rx_dropped++;
priv->wstats.discard.misc++;
IPW_DEBUG_DROP("Dropping packet while interface is not up.\n"); return;
}
/* Advance skb->data to the start of the actual payload */
skb_reserve(rxb->skb, offsetof(struct ipw_rx_packet, u.frame.data));
/* Set the size of the skb to the size of the frame */
skb_put(rxb->skb, le16_to_cpu(pkt->u.frame.length));
IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len);
/* HW decrypt will not clear the WEP bit, MIC, PN, etc. */
hdr = (struct libipw_hdr_4addr *)rxb->skb->data; if (priv->ieee->iw_mode != IW_MODE_MONITOR &&
(is_multicast_ether_addr(hdr->addr1) ?
!priv->ieee->host_mc_decrypt : !priv->ieee->host_decrypt))
ipw_rebuild_decrypted_skb(priv, rxb->skb);
if (!libipw_rx(priv->ieee, rxb->skb, stats))
dev->stats.rx_errors++; else { /* libipw_rx succeeded, so it now owns the SKB */
rxb->skb = NULL;
__ipw_led_activity_on(priv);
}
}
/* 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 *tobuildthismanuallyelementbyelement,wecanwriteitmuch
* more efficiently than we can parse it. ORDER MATTERS HERE */ struct ipw_rt_hdr *ipw_rt;
unsignedshort len = le16_to_cpu(pkt->u.frame.length);
/* We received data from the HW, so stop the watchdog */
netif_trans_update(dev);
/* 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))) {
dev->stats.rx_errors++;
priv->wstats.discard.misc++;
IPW_DEBUG_DROP("Corruption detected! Oh no!\n"); return;
} elseif (unlikely(!netif_running(priv->net_dev))) {
dev->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 */
dev->stats.rx_dropped++;
priv->wstats.discard.misc++;
IPW_DEBUG_DROP("Dropping too large packet in monitor\n"); return;
}
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 = cpu_to_le16(sizeof(struct ipw_rt_hdr)); /* total header+data */
/* Big bitfield of all the fields we provide in radiotap */
ipw_rt->rt_hdr.it_present = cpu_to_le32(
(1 << IEEE80211_RADIOTAP_TSFT) |
(1 << IEEE80211_RADIOTAP_FLAGS) |
(1 << IEEE80211_RADIOTAP_RATE) |
(1 << IEEE80211_RADIOTAP_CHANNEL) |
(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |
(1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) |
(1 << IEEE80211_RADIOTAP_ANTENNA));
/* Zero the flags, we'll add to them as we go */
ipw_rt->rt_flags = 0;
ipw_rt->rt_tsf = (u64)(frame->parent_tsf[3] << 24 |
frame->parent_tsf[2] << 16 |
frame->parent_tsf[1] << 8 |
frame->parent_tsf[0]);
/* Convert signal to DBM */
ipw_rt->rt_dbmsignal = antsignal;
ipw_rt->rt_dbmnoise = (s8) le16_to_cpu(frame->noise);
/* 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));
} elseif (antennaAndPhy & 32) { /* 802.11b */
ipw_rt->rt_chbitmask =
cpu_to_le16((IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ));
} else { /* 802.11g */
ipw_rt->rt_chbitmask =
cpu_to_le16(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 (!libipw_rx(priv->ieee, rxb->skb, stats))
dev->stats.rx_errors++; else { /* libipw_rx succeeded, so it now owns the SKB */
rxb->skb = NULL; /* no LED during capture */
}
} #endif
/* First cache any information we need before we overwrite
* the information provided in the skb from the hardware */ struct ieee80211_hdr *hdr;
u16 channel = frame->received_channel;
u8 phy_flags = frame->antennaAndPhy;
s8 signal = frame->rssi_dbm - IPW_RSSI_TO_DBM;
s8 noise = (s8) le16_to_cpu(frame->noise);
u8 rate = frame->rate; unsignedshort len = le16_to_cpu(pkt->u.frame.length); struct sk_buff *skb; int hdr_only = 0;
u16 filter = priv->prom_priv->filter;
/* If the filter is set to not include Rx frames then return */ if (filter & IPW_PROM_NO_RX) return;
/* We received data from the HW, so stop the watchdog */
netif_trans_update(dev);
/* We only process data packets if the interface is open */ if (unlikely(!netif_running(dev))) {
dev->stats.rx_dropped++;
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 */
dev->stats.rx_dropped++;
IPW_DEBUG_DROP("Dropping too large packet in monitor\n"); return;
}
hdr = (void *)rxb->skb->data + IPW_RX_FRAME_SIZE; if (libipw_is_management(le16_to_cpu(hdr->frame_control))) { if (filter & IPW_PROM_NO_MGMT) return; if (filter & IPW_PROM_MGMT_HEADER_ONLY)
hdr_only = 1;
} elseif (libipw_is_control(le16_to_cpu(hdr->frame_control))) { if (filter & IPW_PROM_NO_CTL) return; if (filter & IPW_PROM_CTL_HEADER_ONLY)
hdr_only = 1;
} elseif (libipw_is_data(le16_to_cpu(hdr->frame_control))) { if (filter & IPW_PROM_NO_DATA) return; if (filter & IPW_PROM_DATA_HEADER_ONLY)
hdr_only = 1;
}
/* Copy the SKB since this is for the promiscuous side */
skb = skb_copy(rxb->skb, GFP_ATOMIC); if (skb == NULL) {
IPW_ERROR("skb_clone failed for promiscuous copy.\n"); return;
}
/* copy the frame data to write after where the radiotap header goes */
ipw_rt = (void *)skb->data;
if (hdr_only)
len = libipw_get_hdrlen(le16_to_cpu(hdr->frame_control));
memcpy(ipw_rt->payload, hdr, len);
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 = cpu_to_le16(sizeof(*ipw_rt)); /* total header+data */
/* Set the size of the skb to the size of the frame */
skb_put(skb, sizeof(*ipw_rt) + len);
/* Big bitfield of all the fields we provide in radiotap */
ipw_rt->rt_hdr.it_present = cpu_to_le32(
(1 << IEEE80211_RADIOTAP_TSFT) |
(1 << IEEE80211_RADIOTAP_FLAGS) |
(1 << IEEE80211_RADIOTAP_RATE) |
(1 << IEEE80211_RADIOTAP_CHANNEL) |
(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |
(1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) |
(1 << IEEE80211_RADIOTAP_ANTENNA));
/* Zero the flags, we'll add to them as we go */
ipw_rt->rt_flags = 0;
ipw_rt->rt_tsf = (u64)(frame->parent_tsf[3] << 24 |
frame->parent_tsf[2] << 16 |
frame->parent_tsf[1] << 8 |
frame->parent_tsf[0]);
/* Convert the channel data and set the flags */
ipw_rt->rt_channel = cpu_to_le16(ieee80211chan2mhz(channel)); if (channel > 14) { /* 802.11a */
ipw_rt->rt_chbitmask =
cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ));
} elseif (phy_flags & (1 << 5)) { /* 802.11b */
ipw_rt->rt_chbitmask =
cpu_to_le16((IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ));
} else { /* 802.11g */
ipw_rt->rt_chbitmask =
cpu_to_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ);
}
/* set the rate in multiples of 500k/s */ switch (rate) { 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 = (phy_flags & 3);
/* set the preamble flag if we have it */ if (phy_flags & (1 << 6))
ipw_rt->rt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
IPW_DEBUG_RX("Rx packet of %d bytes.\n", skb->len);
if (!libipw_rx(priv->prom_priv->ieee, skb, stats)) {
dev->stats.rx_errors++;
dev_kfree_skb_any(skb);
}
} #endif
staticint is_network_packet(struct ipw_priv *priv, struct libipw_hdr_4addr *header)
{ /* Filter incoming packets to determine if they are targeted toward
* this network, discarding packets coming from ourselves */ switch (priv->ieee->iw_mode) { case IW_MODE_ADHOC: /* Header: Dest. | Source | BSSID */ /* packets from our adapter are dropped (echo) */ if (ether_addr_equal(header->addr2, priv->net_dev->dev_addr)) return0;
/* {broad,multi}cast packets to our BSSID go through */ if (is_multicast_ether_addr(header->addr1)) return ether_addr_equal(header->addr3, priv->bssid);
/* packets to our adapter go through */ return ether_addr_equal(header->addr1,
priv->net_dev->dev_addr);
case IW_MODE_INFRA: /* Header: Dest. | BSSID | Source */ /* packets from our adapter are dropped (echo) */ if (ether_addr_equal(header->addr3, priv->net_dev->dev_addr)) return0;
/* {broad,multi}cast packets to our BSS go through */ if (is_multicast_ether_addr(header->addr1)) return ether_addr_equal(header->addr2, priv->bssid);
/* packets to our adapter go through */ return ether_addr_equal(header->addr1,
priv->net_dev->dev_addr);
}
drop: /* Comment this line now since we observed the card receives *duplicatepacketsbuttheFCTL_RETRYbitisnotsetinthe *IBSSmodewithfragmentationenabled.
BUG_ON(!(le16_to_cpu(header->frame_control) & IEEE80211_FCTL_RETRY)); */ return1;
}
if (priv->ieee->iw_mode == IW_MODE_ADHOC &&
((WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) ==
IEEE80211_STYPE_PROBE_RESP) ||
(WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) ==
IEEE80211_STYPE_BEACON))) { if (ether_addr_equal(header->addr3, priv->bssid))
ipw_add_station(priv, header->addr2);
}
if (priv->config & CFG_NET_STATS) {
IPW_DEBUG_HC("sending stat packet\n");
/* Set the size of the skb to the size of the full
* ipw header and 802.11 frame */
skb_put(skb, le16_to_cpu(pkt->u.frame.length) +
IPW_RX_FRAME_SIZE);
/* Advance past the ipw packet header to the 802.11 frame */
skb_pull(skb, IPW_RX_FRAME_SIZE);
/* Push the libipw_rx_stats before the 802.11 frame */
memcpy(skb_push(skb, sizeof(*stats)), stats, sizeof(*stats));
skb->dev = priv->ieee->dev;
/* Point raw at the libipw_stats */
skb_reset_mac_header(skb);
header =
(struct libipw_hdr_4addr *)(rxb->skb->
data +
IPW_RX_FRAME_SIZE); /* TODO: Check Ad-Hoc dest/source and make sure *thatweareactuallyparsingthesepackets *correctly--weshouldprobablyusethe *framecontrolofthepacketanddisregard
* the current iw_mode */
default:
IPW_DEBUG_RX("Bad Rx packet of type %d\n",
pkt->header.message_type); break;
}
/* For now we just don't re-use anything. We can tweak this *latertotryandre-usenotificationpacketsandSKBsthat
* fail to Rx correctly */ if (rxb->skb != NULL) {
dev_kfree_skb_any(rxb->skb);
rxb->skb = NULL;
}
/* If there are a lot of unsued frames, restock the Rx queue
* so the ucode won't assert */ if (fill_rx) {
priv->rxq->read = i;
ipw_rx_queue_replenish(priv);
}
}
/* Backtrack one entry */
priv->rxq->read = i;
ipw_rx_queue_restock(priv);
}
staticint ipw_set_channel(struct ipw_priv *priv, u8 channel)
{ if (channel == 0) {
IPW_DEBUG_INFO("Setting channel to ANY (0)\n");
priv->config &= ~CFG_STATIC_CHANNEL;
IPW_DEBUG_ASSOC("Attempting to associate with new " "parameters.\n");
ipw_associate(priv); return0;
}
priv->config |= CFG_STATIC_CHANNEL;
if (priv->channel == channel) {
IPW_DEBUG_INFO("Request to set channel to current value (%d)\n",
channel); return0;
}
IPW_DEBUG_INFO("Setting channel to %i\n", (int)channel);
priv->channel = channel;
#ifdef CONFIG_IPW2200_MONITOR if (priv->ieee->iw_mode == IW_MODE_MONITOR) { int i; if (priv->status & STATUS_SCANNING) {
IPW_DEBUG_SCAN("Scan abort triggered due to " "channel change.\n");
ipw_abort_scan(priv);
}
for (i = 1000; i && (priv->status & STATUS_SCANNING); i--)
udelay(10);
if (priv->status & STATUS_SCANNING)
IPW_DEBUG_SCAN("Still scanning...\n"); else
IPW_DEBUG_SCAN("Took %dms to abort current scan\n", 1000 - i);
return0;
} #endif/* CONFIG_IPW2200_MONITOR */
/* Network configuration changed -- force [re]association */
IPW_DEBUG_ASSOC("[re]association triggered due to channel change.\n"); if (!ipw_disassociate(priv))
ipw_associate(priv);
return0;
}
staticint ipw_wx_set_freq(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{ struct ipw_priv *priv = libipw_priv(dev); conststruct libipw_geo *geo = libipw_get_geo(priv->ieee); struct iw_freq *fwrq = &wrqu->freq; int ret = 0, i;
u8 channel, flags; int band;
if (fwrq->m == 0) {
IPW_DEBUG_WX("SET Freq/Channel -> any\n");
mutex_lock(&priv->mutex);
ret = ipw_set_channel(priv, 0);
mutex_unlock(&priv->mutex); return ret;
} /* if setting by freq convert to channel */ if (fwrq->e == 1) {
channel = libipw_freq_to_channel(priv->ieee, fwrq->m); if (channel == 0) return -EINVAL;
} else
channel = fwrq->m;
if (!(band = libipw_is_valid_channel(priv->ieee, channel))) return -EINVAL;
if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
i = libipw_channel_to_index(priv->ieee, channel); if (i == -1) return -EINVAL;
/* If we are associated, trying to associate, or have a statically
* configured CHANNEL then return that; otherwise return ANY */
mutex_lock(&priv->mutex); if (priv->config & CFG_STATIC_CHANNEL ||
priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED)) { int i;
i = libipw_channel_to_index(priv->ieee, priv->channel);
BUG_ON(i == -1);
wrqu->freq.e = 1;
range->max_qual.qual = 100; /* TODO: Find real max RSSI and stick here */
range->max_qual.level = 0;
range->max_qual.noise = 0;
range->max_qual.updated = 7; /* Updated all three */
range->avg_qual.qual = 70; /* TODO: Find real 'good' to 'bad' threshold value for RSSI */
range->avg_qual.level = 0; /* FIXME to real average level */
range->avg_qual.noise = 0;
range->avg_qual.updated = 7; /* Updated all three */
mutex_lock(&priv->mutex);
range->num_bitrates = min(priv->rates.num_rates, (u8) IW_MAX_BITRATES);
for (i = 0; i < range->num_bitrates; i++)
range->bitrate[i] = (priv->rates.supported_rates[i] & 0x7F) * 500000;
if (wrqu->ap_addr.sa_family != ARPHRD_ETHER) return -EINVAL;
mutex_lock(&priv->mutex); if (is_broadcast_ether_addr(wrqu->ap_addr.sa_data) ||
is_zero_ether_addr(wrqu->ap_addr.sa_data)) { /* we disable mandatory BSSID association */
IPW_DEBUG_WX("Setting AP BSSID to ANY\n");
priv->config &= ~CFG_STATIC_BSSID;
IPW_DEBUG_ASSOC("Attempting to associate with new " "parameters.\n");
ipw_associate(priv);
mutex_unlock(&priv->mutex); return0;
}
priv->config |= CFG_STATIC_BSSID; if (ether_addr_equal(priv->bssid, wrqu->ap_addr.sa_data)) {
IPW_DEBUG_WX("BSSID set to current BSSID.\n");
mutex_unlock(&priv->mutex); return0;
}
IPW_DEBUG_WX("Setting mandatory BSSID to %pM\n",
wrqu->ap_addr.sa_data);
/* Network configuration changed -- force [re]association */
IPW_DEBUG_ASSOC("[re]association triggered due to BSSID change.\n"); if (!ipw_disassociate(priv))
ipw_associate(priv);
/* If we are associated, trying to associate, or have a statically
* configured BSSID then return that; otherwise return ANY */
mutex_lock(&priv->mutex); if (priv->config & CFG_STATIC_BSSID ||
priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
wrqu->ap_addr.sa_family = ARPHRD_ETHER;
memcpy(wrqu->ap_addr.sa_data, priv->bssid, ETH_ALEN);
} else
eth_zero_addr(wrqu->ap_addr.sa_data);
/* Network configuration changed -- force [re]association */
IPW_DEBUG_ASSOC("[re]association triggered due to ESSID change.\n"); if (!ipw_disassociate(priv))
ipw_associate(priv);
staticint ipw_wx_set_rate(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{ /* TODO: We should use semaphores or locks for access to priv */ struct ipw_priv *priv = libipw_priv(dev);
u32 target_rate = wrqu->bitrate.value;
u32 fixed, mask;
/* value = -1, fixed = 0 means auto only, so we should use all rates offered by AP */ /* value = X, fixed = 1 means only rate X */ /* value = X, fixed = 0 means all rates lower equal X */
if (target_rate == -1) {
fixed = 0;
mask = LIBIPW_DEFAULT_RATES_MASK; /* Now we should reassociate */ goto apply;
}
mask = 0;
fixed = wrqu->bitrate.fixed;
if (target_rate == 1000000 || !fixed)
mask |= LIBIPW_CCK_RATE_1MB_MASK; if (target_rate == 1000000) goto apply;
if (target_rate == 2000000 || !fixed)
mask |= LIBIPW_CCK_RATE_2MB_MASK; if (target_rate == 2000000) goto apply;
if (target_rate == 5500000 || !fixed)
mask |= LIBIPW_CCK_RATE_5MB_MASK; if (target_rate == 5500000) goto apply;
if (target_rate == 6000000 || !fixed)
mask |= LIBIPW_OFDM_RATE_6MB_MASK; if (target_rate == 6000000) goto apply;
if (target_rate == 9000000 || !fixed)
mask |= LIBIPW_OFDM_RATE_9MB_MASK; if (target_rate == 9000000) goto apply;
if (target_rate == 11000000 || !fixed)
mask |= LIBIPW_CCK_RATE_11MB_MASK; if (target_rate == 11000000) goto apply;
if (target_rate == 12000000 || !fixed)
mask |= LIBIPW_OFDM_RATE_12MB_MASK; if (target_rate == 12000000) goto apply;
if (target_rate == 18000000 || !fixed)
mask |= LIBIPW_OFDM_RATE_18MB_MASK; if (target_rate == 18000000) goto apply;
if (target_rate == 24000000 || !fixed)
mask |= LIBIPW_OFDM_RATE_24MB_MASK; if (target_rate == 24000000) goto apply;
if (target_rate == 36000000 || !fixed)
mask |= LIBIPW_OFDM_RATE_36MB_MASK; if (target_rate == 36000000) goto apply;
if (target_rate == 48000000 || !fixed)
mask |= LIBIPW_OFDM_RATE_48MB_MASK; if (target_rate == 48000000) goto apply;
if (target_rate == 54000000 || !fixed)
mask |= LIBIPW_OFDM_RATE_54MB_MASK; if (target_rate == 54000000) goto apply;
if (priv->rates_mask == mask) {
IPW_DEBUG_WX("Mask set to current mask.\n");
mutex_unlock(&priv->mutex); return0;
}
priv->rates_mask = mask;
/* Network configuration changed -- force [re]association */
IPW_DEBUG_ASSOC("[re]association triggered due to rates change.\n"); if (!ipw_disassociate(priv))
ipw_associate(priv);
staticint ipw_wx_set_encode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *key)
{ struct ipw_priv *priv = libipw_priv(dev); int ret;
u32 cap = priv->capability;
mutex_lock(&priv->mutex);
ret = libipw_wx_set_encode(priv->ieee, info, wrqu, key);
/* 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);
staticint ipw_wx_set_power(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{ struct ipw_priv *priv = libipw_priv(dev); int err;
mutex_lock(&priv->mutex); if (wrqu->power.disabled) {
priv->power_mode = IPW_POWER_LEVEL(priv->power_mode);
err = ipw_send_power_mode(priv, IPW_POWER_MODE_CAM); if (err) {
IPW_DEBUG_WX("failed setting power mode.\n");
mutex_unlock(&priv->mutex); return err;
}
IPW_DEBUG_WX("SET Power Management Mode -> off\n");
mutex_unlock(&priv->mutex); return0;
}
switch (wrqu->power.flags & IW_POWER_MODE) { case IW_POWER_ON: /* If not specified */ case IW_POWER_MODE: /* If set all mask */ case IW_POWER_ALL_R: /* If explicitly state all */ break; default: /* Otherwise we don't support it */
IPW_DEBUG_WX("SET PM Mode: %X not supported.\n",
wrqu->power.flags);
mutex_unlock(&priv->mutex); return -EOPNOTSUPP;
}
/* If the user hasn't specified a power management mode yet, default
* to BATTERY */ if (IPW_POWER_LEVEL(priv->power_mode) == IPW_POWER_AC)
priv->power_mode = IPW_POWER_ENABLED | IPW_POWER_BATTERY; else
priv->power_mode = IPW_POWER_ENABLED | priv->power_mode;
err = ipw_send_power_mode(priv, IPW_POWER_LEVEL(priv->power_mode)); if (err) {
IPW_DEBUG_WX("failed setting power mode.\n");
mutex_unlock(&priv->mutex); return err;
}
IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n", priv->power_mode);
mutex_unlock(&priv->mutex); return0;
}
ret = ipw_sw_reset(priv, 2); if (!ret) {
free_firmware();
ipw_adapter_restart(priv);
}
/* The SW reset bit might have been toggled on by the 'disable'
* module parameter, so take appropriate action */
ipw_radio_kill_sw(priv, priv->status & STATUS_RF_KILL_SW);
if (!(priv->status & STATUS_RF_KILL_MASK)) { /* Configuration likely changed -- force [re]association */
IPW_DEBUG_ASSOC("[re]association triggered due to sw " "reset.\n"); if (!ipw_disassociate(priv))
ipw_associate(priv);
}
if (!(priv->status & STATUS_ASSOCIATED)) goto drop;
hdr_len = libipw_get_hdrlen(le16_to_cpu(hdr->frame_ctl)); switch (priv->ieee->iw_mode) { case IW_MODE_ADHOC:
unicast = !is_multicast_ether_addr(hdr->addr1);
id = ipw_find_station(priv, hdr->addr1); if (id == IPW_INVALID_STATION) {
id = ipw_add_station(priv, hdr->addr1); if (id == IPW_INVALID_STATION) {
IPW_WARNING("Attempt to send data to " "invalid cell: %pM\n",
hdr->addr1); goto drop;
}
} break;
case IW_MODE_INFRA: default:
unicast = !is_multicast_ether_addr(hdr->addr3);
id = 0; break;
}
if (likely(unicast))
tfd->u.data.tx_flags |= DCT_FLAG_ACK_REQD;
if (txb->encrypted && !priv->ieee->host_encrypt) { switch (priv->ieee->sec.level) { case SEC_LEVEL_3:
tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |=
cpu_to_le16(IEEE80211_FCTL_PROTECTED); /* XXX: ACK flag must be set for CCMP even if it *isamulticast/broadcastpacket,becauseCCMP *groupcommunicationencryptedbyGTKis
* actually done by the AP. */ if (!unicast)
tfd->u.data.tx_flags |= DCT_FLAG_ACK_REQD;
/* Filtering of fragment chains is done against the first fragment */
hdr = (void *)txb->fragments[0]->data; if (libipw_is_management(le16_to_cpu(hdr->frame_control))) { if (filter & IPW_PROM_NO_MGMT) return; if (filter & IPW_PROM_MGMT_HEADER_ONLY)
hdr_only = 1;
} elseif (libipw_is_control(le16_to_cpu(hdr->frame_control))) { if (filter & IPW_PROM_NO_CTL) return; if (filter & IPW_PROM_CTL_HEADER_ONLY)
hdr_only = 1;
} elseif (libipw_is_data(le16_to_cpu(hdr->frame_control))) { if (filter & IPW_PROM_NO_DATA) return; if (filter & IPW_PROM_DATA_HEADER_ONLY)
hdr_only = 1;
}
rt_hdr->it_version = PKTHDR_RADIOTAP_VERSION;
rt_hdr->it_pad = 0;
rt_hdr->it_present = 0; /* after all, it's just an idea */
rt_hdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_CHANNEL);
/* RF Kill is now disabled, so bring the device back up */
if (!(priv->status & STATUS_RF_KILL_MASK)) {
IPW_DEBUG_RF_KILL("HW RF Kill no longer active, restarting " "device\n");
/* we can not do an adapter restart while inside an irq lock */
schedule_work(&priv->adapter_restart);
} else
IPW_DEBUG_RF_KILL("HW RF Kill deactivated. SW RF Kill still " "enabled\n");
staticint ipw_config(struct ipw_priv *priv)
{ /* This is only called from ipw_up, which resets/reloads the firmware so,wedon'tneedtofirstdisablethecardbeforeweconfigure
it */ if (ipw_set_tx_power(priv)) goto error;
/* initialize adapter address */ if (ipw_send_adapter_address(priv, priv->net_dev->dev_addr)) goto error;
/* set basic system config settings */
init_sys_config(&priv->sys_config);
/* Support Bluetooth if we have BT h/w on board, and user wants to.
* Does not support BT priority yet (don't abort or defer our Tx) */ if (bt_coexist) { unsignedchar bt_caps = priv->eeprom[EEPROM_SKU_CAPABILITY];
if (bt_caps & EEPROM_SKU_CAP_BT_CHANNEL_SIG)
priv->sys_config.bt_coexistence
|= CFG_BT_COEXISTENCE_SIGNAL_CHNL; if (bt_caps & EEPROM_SKU_CAP_BT_OOB)
priv->sys_config.bt_coexistence
|= CFG_BT_COEXISTENCE_OOB;
}
/* 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);
/* Age scan list entries found before suspend */ if (priv->suspend_time) {
libipw_networks_age(priv->ieee, priv->suspend_time);
priv->suspend_time = 0;
}
if (priv->status & STATUS_EXIT_PENDING) return -EIO;
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: %d\n", rc); return rc;
}
ipw_init_ordinals(priv); if (!(priv->config & CFG_CUSTOM_MAC))
eeprom_parse_mac(priv, priv->mac_addr);
eth_hw_addr_set(priv->net_dev, priv->mac_addr);
ipw_set_geo(priv);
if (priv->status & STATUS_RF_KILL_SW) {
IPW_WARNING("Radio disabled by module parameter.\n"); return0;
} elseif (rf_kill_active(priv)) {
IPW_WARNING("Radio Frequency Kill Switch is On:\n" "Kill switch must be turned off for " "wireless networking to work.\n");
schedule_delayed_work(&priv->rf_kill, 2 * HZ); return0;
}
rc = ipw_config(priv); if (!rc) {
IPW_DEBUG_INFO("Configured device on count %i\n", i);
/* If configure to try and auto-associate, kick
* off a scan. */
schedule_delayed_work(&priv->request_scan, 0);
return0;
}
IPW_DEBUG_INFO("Device configuration failed: 0x%08X\n", rc);
IPW_DEBUG_INFO("Failed to config device on retry %d of %d\n",
i, MAX_HW_RESTARTS);
/* We had an error bringing up the hardware, so take it
* all the way back down so we can try again */
ipw_down(priv);
}
/* tried to restart and config the device for as long as our
* patience could withstand */
IPW_ERROR("Unable to initialize device after %d attempts.\n", i);
staticvoid ipw_deinit(struct ipw_priv *priv)
{ int i;
if (priv->status & STATUS_SCANNING) {
IPW_DEBUG_INFO("Aborting scan during shutdown.\n");
ipw_abort_scan(priv);
}
if (priv->status & STATUS_ASSOCIATED) {
IPW_DEBUG_INFO("Disassociating during shutdown.\n");
ipw_disassociate(priv);
}
ipw_led_shutdown(priv);
/* Wait up to 1s for status to change to not scanning and not *associated(disassociationcantakeawhileforaful802.11
* exchange */ for (i = 1000; i && (priv->status &
(STATUS_DISASSOCIATING |
STATUS_ASSOCIATED | STATUS_SCANNING)); i--)
udelay(10);
if (priv->status & (STATUS_DISASSOCIATING |
STATUS_ASSOCIATED | STATUS_SCANNING))
IPW_DEBUG_INFO("Still associated or scanning...\n"); else
IPW_DEBUG_INFO("Took %dms to de-init\n", 1000 - i);
/* Attempt to disable the card */
ipw_send_card_disable(priv, 0);
priv->net_dev = net_dev;
priv->pci_dev = pdev;
ipw_debug_level = debug;
spin_lock_init(&priv->irq_lock);
spin_lock_init(&priv->lock); for (i = 0; i < IPW_IBSS_MAC_HASH_SIZE; i++)
INIT_LIST_HEAD(&priv->ibss_mac_hash[i]);
mutex_init(&priv->mutex); if (pci_enable_device(pdev)) {
err = -ENODEV; goto out_free_libipw;
}
pci_set_master(pdev);
err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); if (!err)
err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); if (err) {
printk(KERN_WARNING DRV_NAME ": No suitable DMA available.\n"); goto out_pci_disable_device;
}
pci_set_drvdata(pdev, priv);
err = pci_request_regions(pdev, DRV_NAME); if (err) goto out_pci_disable_device;
/* We disable the RETRY_TIMEOUT register (0x41) to keep
* PCI Tx retries from interfering with C3 CPU state */
pci_read_config_dword(pdev, 0x40, &val); if ((val & 0x0000ff00) != 0)
pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
if (priv->rxq) {
ipw_rx_queue_free(priv, priv->rxq);
priv->rxq = NULL;
}
ipw_tx_queue_free(priv);
if (priv->cmdlog) {
kfree(priv->cmdlog);
priv->cmdlog = NULL;
}
/* make sure all works are inactive */
cancel_delayed_work_sync(&priv->adhoc_check);
cancel_work_sync(&priv->associate);
cancel_work_sync(&priv->disassociate);
cancel_work_sync(&priv->system_config);
cancel_work_sync(&priv->rx_replenish);
cancel_work_sync(&priv->adapter_restart);
cancel_delayed_work_sync(&priv->rf_kill);
cancel_work_sync(&priv->up);
cancel_work_sync(&priv->down);
cancel_delayed_work_sync(&priv->request_scan);
cancel_delayed_work_sync(&priv->request_direct_scan);
cancel_delayed_work_sync(&priv->request_passive_scan);
cancel_delayed_work_sync(&priv->scan_event);
cancel_delayed_work_sync(&priv->gather_stats);
cancel_work_sync(&priv->abort_scan);
cancel_work_sync(&priv->roam);
cancel_delayed_work_sync(&priv->scan_check);
cancel_work_sync(&priv->link_up);
cancel_work_sync(&priv->link_down);
cancel_delayed_work_sync(&priv->led_link_on);
cancel_delayed_work_sync(&priv->led_link_off);
cancel_delayed_work_sync(&priv->led_act_off);
cancel_work_sync(&priv->merge_networks);
/* Free MAC hash list for ADHOC */ for (i = 0; i < IPW_IBSS_MAC_HASH_SIZE; i++) {
list_for_each_safe(p, q, &priv->ibss_mac_hash[i]) {
list_del(p);
kfree(list_entry(p, struct ipw_ibss_seq, list));
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.