{ const struct il3945_eeprom_txpower_group *chnl_grp = NULL; struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; s32 idx0, idx1; s32 power = 2 * requested_power; s32 i; const struct il3945_eeprom_txpower_sample *samples; s32 gains0, gains1; s32 res; s32 denominator; chnl_grp = &eeprom->groups[setting_idx]; samples = chnl_grp->samples; for (i = 0; i < 5; i++) { if (power == samples[i].power) { *new_idx = samples[i].gain_idx; return 0; } } if (power > samples[1].power) { idx0 = 0; idx1 = 1; } else if (power > samples[2].power) { idx0 = 1; idx1 = 2; } else if (power > samples[3].power) { idx0 = 2; idx1 = 3; } else { idx0 = 3; idx1 = 4; } denominator = (s32) samples[idx1].power - (s32) samples[idx0].power; if (denominator == 0) return -EINVAL; gains0 = (s32) samples[idx0].gain_idx * (1 << 19); gains1 = (s32) samples[idx1].gain_idx * (1 << 19); res = gains0 + (gains1 - gains0) * ((s32) power - (s32) samples[idx0].power) / denominator + (1 << 18); *new_idx = res >> 19; return 0; } static void il3945_hw_reg_init_channel_groups(struct il_priv *il) { u32 i; s32 rate_idx; struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; const struct il3945_eeprom_txpower_group *group; D_POWER("Initializing factory calib info from EEPROM\n"); for (i = 0; i < IL_NUM_TX_CALIB_GROUPS; i++) { s8 *clip_pwrs; /* table of power levels for each rate */ s8 satur_pwr; /* saturation power for each chnl group */ group = &eeprom->groups[i]; /* sanity check on factory saturation power value */ if (group->saturation_power < 40) { IL_WARN("Error: saturation power is %d, " "less than minimum expected 40\n", group->saturation_power); return; } /* * Derive requested power levels for each rate, based on * hardware capabilities (saturation power for band). * Basic value is 3dB down from saturation, with further * power reductions for highest 3 data rates. These * backoffs provide headroom for high rate modulation * power peaks, without too much distortion (clipping). */ /* we'll fill in this array with h/w max power levels */ clip_pwrs = (s8 *) il->_3945.clip_groups[i].clip_powers; /* divide factory saturation power by 2 to find -3dB level */ satur_pwr = (s8) (group->saturation_power >> 1); /* fill in channel group's nominal powers for each rate */ for (rate_idx = 0; rate_idx < RATE_COUNT_3945; rate_idx++, clip_pwrs++) { switch (rate_idx) { case RATE_36M_IDX_TBL: if (i == 0) /* B/G */ *clip_pwrs = satur_pwr; else /* A */ *clip_pwrs = satur_pwr - 5; break; case RATE_48M_IDX_TBL: if (i == 0) *clip_pwrs = satur_pwr - 7; else *clip_pwrs = satur_pwr - 10; break; case RATE_54M_IDX_TBL: if (i == 0) *clip_pwrs = satur_pwr - 9; else *clip_pwrs = satur_pwr - 12; break; default: *clip_pwrs = satur_pwr; break; } } } } /* * il3945_txpower_set_from_eeprom - Set channel power info based on EEPROM * * Second pass (during init) to set up il->channel_info * * Set up Tx-power settings in our channel info database for each VALID * (for this geo/SKU) channel, at all Tx data rates, based on eeprom values * and current temperature. * * Since this is based on current temperature (at init time), these values may * not be valid for very long, but it gives us a starting/default point, * and allows us to active (i.e. using Tx) scan. * * This does *not* write values to NIC, just sets up our internal table. */ int il3945_txpower_set_from_eeprom(struct il_priv *il) { struct il_channel_info *ch_info = NULL; struct il3945_channel_power_info *pwr_info; struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; int delta_idx; u8 rate_idx; u8 scan_tbl_idx; const s8 *clip_pwrs; /* array of power levels for each rate */ u8 gain, dsp_atten; s8 power; u8 pwr_idx, base_pwr_idx, a_band; u8 i; int temperature; /* save temperature reference, * so we can determine next time to calibrate */ temperature = il3945_hw_reg_txpower_get_temperature(il); il->last_temperature = temperature; il3945_hw_reg_init_channel_groups(il); /* initialize Tx power info for each and every channel, 2.4 and 5.x */ for (i = 0, ch_info = il->channel_info; i < il->channel_count; i++, ch_info++) { a_band = il_is_channel_a_band(ch_info); if (!il_is_channel_valid(ch_info)) continue; /* find this channel's channel group (*not* "band") idx */ ch_info->group_idx = il3945_hw_reg_get_ch_grp_idx(il, ch_info); /* Get this chnlgrp's rate->max/clip-powers table */ clip_pwrs = il->_3945.clip_groups[ch_info->group_idx].clip_powers; /* calculate power idx *adjustment* value according to * diff between current temperature and factory temperature */ delta_idx = il3945_hw_reg_adjust_power_by_temp(temperature, eeprom->groups[ch_info-> group_idx]. temperature); D_POWER("Delta idx for channel %d: %d [%d]\n", ch_info->channel, delta_idx, temperature + IL_TEMP_CONVERT); /* set tx power value for all OFDM rates */ for (rate_idx = 0; rate_idx < IL_OFDM_RATES; rate_idx++) { s32 power_idx; int rc; /* use channel group's clip-power table, * but don't exceed channel's max power */ s8 pwr = min(ch_info->max_power_avg, clip_pwrs[rate_idx]); pwr_info = &ch_info->power_info[rate_idx]; /* get base (i.e. at factory-measured temperature) * power table idx for this rate's power */ rc = il3945_hw_reg_get_matched_power_idx(il, pwr, ch_info-> group_idx, &power_idx); if (rc) { IL_ERR("Invalid power idx\n"); return rc; } pwr_info->base_power_idx = (u8) power_idx; /* temperature compensate */ power_idx += delta_idx; /* stay within range of gain table */ power_idx = il3945_hw_reg_fix_power_idx(power_idx); /* fill 1 OFDM rate's il3945_channel_power_info struct */ pwr_info->requested_power = pwr; pwr_info->power_table_idx = (u8) power_idx; pwr_info->tpc.tx_gain = power_gain_table[a_band][power_idx].tx_gain; pwr_info->tpc.dsp_atten = power_gain_table[a_band][power_idx].dsp_atten; } /* set tx power for CCK rates, based on OFDM 12 Mbit settings */ pwr_info = &ch_info->power_info[RATE_12M_IDX_TBL]; power = pwr_info->requested_power + IL_CCK_FROM_OFDM_POWER_DIFF; pwr_idx = pwr_info->power_table_idx + IL_CCK_FROM_OFDM_IDX_DIFF; base_pwr_idx = pwr_info->base_power_idx + IL_CCK_FROM_OFDM_IDX_DIFF; /* stay within table range */ pwr_idx = il3945_hw_reg_fix_power_idx(pwr_idx); gain = power_gain_table[a_band][pwr_idx].tx_gain; dsp_atten = power_gain_table[a_band][pwr_idx].dsp_atten; /* fill each CCK rate's il3945_channel_power_info structure * NOTE: All CCK-rate Txpwrs are the same for a given chnl! * NOTE: CCK rates start at end of OFDM rates! */ for (rate_idx = 0; rate_idx < IL_CCK_RATES; rate_idx++) { pwr_info = &ch_info->power_info[rate_idx + IL_OFDM_RATES]; pwr_info->requested_power = power; pwr_info->power_table_idx = pwr_idx; pwr_info->base_power_idx = base_pwr_idx; pwr_info->tpc.tx_gain = gain; pwr_info->tpc.dsp_atten = dsp_atten; } /* set scan tx power, 1Mbit for CCK, 6Mbit for OFDM */ for (scan_tbl_idx = 0; scan_tbl_idx < IL_NUM_SCAN_RATES; scan_tbl_idx++) { s32 actual_idx = (scan_tbl_idx == 0) ? RATE_1M_IDX_TBL : RATE_6M_IDX_TBL; il3945_hw_reg_set_scan_power(il, scan_tbl_idx, actual_idx, clip_pwrs, ch_info, a_band); } } return 0; } int il3945_hw_rxq_stop(struct il_priv *il) { int ret; _il_wr(il, FH39_RCSR_CONFIG(0), 0); ret = _il_poll_bit(il, FH39_RSSR_STATUS, FH39_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, FH39_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 1000); if (ret < 0) IL_ERR("Can't stop Rx DMA.\n"); return 0; } int il3945_hw_tx_queue_init(struct il_priv *il, struct il_tx_queue *txq) { int txq_id = txq->q.id; struct il3945_shared *shared_data = il->_3945.shared_virt; shared_data->tx_base_ptr[txq_id] = cpu_to_le32((u32) txq->q.dma_addr); il_wr(il, FH39_CBCC_CTRL(txq_id), 0); il_wr(il, FH39_CBCC_BASE(txq_id), 0); il_wr(il, FH39_TCSR_CONFIG(txq_id), FH39_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_NOINT | FH39_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_TXF | FH39_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_IFTFD | FH39_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL | FH39_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE); /* fake read to flush all prev. writes */ _il_rd(il, FH39_TSSR_CBB_BASE); return 0; } /* * HCMD utils */ static u16 il3945_get_hcmd_size(u8 cmd_id, u16 len) { switch (cmd_id) { case C_RXON: return sizeof(struct il3945_rxon_cmd); case C_POWER_TBL: return sizeof(struct il3945_powertable_cmd); default: return len; } } static u16 il3945_build_addsta_hcmd(const struct il_addsta_cmd *cmd, u8 * data) { struct il3945_addsta_cmd *addsta = (struct il3945_addsta_cmd *)data; addsta->mode = cmd->mode; memcpy(&addsta->sta, &cmd->sta, sizeof(struct sta_id_modify)); memcpy(&addsta->key, &cmd->key, sizeof(struct il4965_keyinfo)); addsta->station_flags = cmd->station_flags; addsta->station_flags_msk = cmd->station_flags_msk; addsta->tid_disable_tx = cpu_to_le16(0); addsta->rate_n_flags = cmd->rate_n_flags; addsta->add_immediate_ba_tid = cmd->add_immediate_ba_tid; addsta->remove_immediate_ba_tid = cmd->remove_immediate_ba_tid; addsta->add_immediate_ba_ssn = cmd->add_immediate_ba_ssn; return (u16) sizeof(struct il3945_addsta_cmd); } static int il3945_add_bssid_station(struct il_priv *il, const u8 * addr, u8 * sta_id_r) { int ret; u8 sta_id; unsigned long flags; if (sta_id_r) *sta_id_r = IL_INVALID_STATION; ret = il_add_station_common(il, addr, 0, NULL, &sta_id); if (ret) { IL_ERR("Unable to add station %pM\n", addr); return ret; } if (sta_id_r) *sta_id_r = sta_id; spin_lock_irqsave(&il->sta_lock, flags); il->stations[sta_id].used |= IL_STA_LOCAL; spin_unlock_irqrestore(&il->sta_lock, flags); return 0; } static int il3945_manage_ibss_station(struct il_priv *il, struct ieee80211_vif *vif, bool add) { struct il_vif_priv *vif_priv = (void *)vif->drv_priv; int ret; if (add) { ret = il3945_add_bssid_station(il, vif->bss_conf.bssid, &vif_priv->ibss_bssid_sta_id); if (ret) return ret; il3945_sync_sta(il, vif_priv->ibss_bssid_sta_id, (il->band == NL80211_BAND_5GHZ) ? RATE_6M_PLCP : RATE_1M_PLCP); il3945_rate_scale_init(il->hw, vif_priv->ibss_bssid_sta_id); return 0; } return il_remove_station(il, vif_priv->ibss_bssid_sta_id, vif->bss_conf.bssid); } /* * il3945_init_hw_rate_table - Initialize the hardware rate fallback table */ int il3945_init_hw_rate_table(struct il_priv *il) { int rc, i, idx, prev_idx; struct il3945_rate_scaling_cmd rate_cmd = { .reserved = {0, 0, 0}, }; struct il3945_rate_scaling_info *table = rate_cmd.table; for (i = 0; i < ARRAY_SIZE(il3945_rates); i++) { idx = il3945_rates[i].table_rs_idx; table[idx].rate_n_flags = cpu_to_le16(il3945_rates[i].plcp); table[idx].try_cnt = il->retry_rate; prev_idx = il3945_get_prev_ieee_rate(i); table[idx].next_rate_idx = il3945_rates[prev_idx].table_rs_idx; } switch (il->band) { case NL80211_BAND_5GHZ: D_RATE("Select A mode rate scale\n"); /* If one of the following CCK rates is used, * have it fall back to the 6M OFDM rate */ for (i = RATE_1M_IDX_TBL; i <= RATE_11M_IDX_TBL; i++) table[i].next_rate_idx = il3945_rates[IL_FIRST_OFDM_RATE].table_rs_idx; /* Don't fall back to CCK rates */ table[RATE_12M_IDX_TBL].next_rate_idx = RATE_9M_IDX_TBL; /* Don't drop out of OFDM rates */ table[RATE_6M_IDX_TBL].next_rate_idx = il3945_rates[IL_FIRST_OFDM_RATE].table_rs_idx; break; case NL80211_BAND_2GHZ: D_RATE("Select B/G mode rate scale\n"); /* If an OFDM rate is used, have it fall back to the * 1M CCK rates */ if (!(il->_3945.sta_supp_rates & IL_OFDM_RATES_MASK) && il_is_associated(il)) { idx = IL_FIRST_CCK_RATE; for (i = RATE_6M_IDX_TBL; i <= RATE_54M_IDX_TBL; i++) table[i].next_rate_idx = il3945_rates[idx].table_rs_idx; idx = RATE_11M_IDX_TBL; /* CCK shouldn't fall back to OFDM... */ table[idx].next_rate_idx = RATE_5M_IDX_TBL; } break; default: WARN_ON(1); break; } /* Update the rate scaling for control frame Tx */ rate_cmd.table_id = 0; rc = il_send_cmd_pdu(il, C_RATE_SCALE, sizeof(rate_cmd), &rate_cmd); if (rc) return rc; /* Update the rate scaling for data frame Tx */ rate_cmd.table_id = 1; return il_send_cmd_pdu(il, C_RATE_SCALE, sizeof(rate_cmd), &rate_cmd); } /* Called when initializing driver */ int il3945_hw_set_hw_params(struct il_priv *il) { memset((void *)&il->hw_params, 0, sizeof(struct il_hw_params)); il->_3945.shared_virt = dma_alloc_coherent(&il->pci_dev->dev, sizeof(struct il3945_shared), &il->_3945.shared_phys, GFP_KERNEL); if (!il->_3945.shared_virt) return -ENOMEM; il->hw_params.bcast_id = IL3945_BROADCAST_ID; /* Assign number of Usable TX queues */ il->hw_params.max_txq_num = il->cfg->num_of_queues; il->hw_params.tfd_size = sizeof(struct il3945_tfd); il->hw_params.rx_page_order = get_order(IL_RX_BUF_SIZE_3K); il->hw_params.max_rxq_size = RX_QUEUE_SIZE; il->hw_params.max_rxq_log = RX_QUEUE_SIZE_LOG; il->hw_params.max_stations = IL3945_STATION_COUNT; il->sta_key_max_num = STA_KEY_MAX_NUM; il->hw_params.rx_wrt_ptr_reg = FH39_RSCSR_CHNL0_WPTR; il->hw_params.max_beacon_itrvl = IL39_MAX_UCODE_BEACON_INTERVAL; il->hw_params.beacon_time_tsf_bits = IL3945_EXT_BEACON_TIME_POS; return 0; } unsigned int il3945_hw_get_beacon_cmd(struct il_priv *il, struct il3945_frame *frame, u8 rate) { struct il3945_tx_beacon_cmd *tx_beacon_cmd; unsigned int frame_size; tx_beacon_cmd = (struct il3945_tx_beacon_cmd *)&frame->u; memset(tx_beacon_cmd, 0, sizeof(*tx_beacon_cmd)); tx_beacon_cmd->tx.sta_id = il->hw_params.bcast_id; tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; frame_size = il3945_fill_beacon_frame(il, tx_beacon_cmd->frame, sizeof(frame->u) - sizeof(*tx_beacon_cmd)); BUG_ON(frame_size > MAX_MPDU_SIZE); tx_beacon_cmd->tx.len = cpu_to_le16((u16) frame_size); tx_beacon_cmd->tx.rate = rate; tx_beacon_cmd->tx.tx_flags = (TX_CMD_FLG_SEQ_CTL_MSK | TX_CMD_FLG_TSF_MSK); /* supp_rates[0] == OFDM start at IL_FIRST_OFDM_RATE */ tx_beacon_cmd->tx.supp_rates[0] = (IL_OFDM_BASIC_RATES_MASK >> IL_FIRST_OFDM_RATE) & 0xFF; tx_beacon_cmd->tx.supp_rates[1] = (IL_CCK_BASIC_RATES_MASK & 0xF); return sizeof(struct il3945_tx_beacon_cmd) + frame_size; } void il3945_hw_handler_setup(struct il_priv *il) { il->handlers[C_TX] = il3945_hdl_tx; il->handlers[N_3945_RX] = il3945_hdl_rx; } void il3945_hw_setup_deferred_work(struct il_priv *il) { INIT_DELAYED_WORK(&il->_3945.thermal_periodic, il3945_bg_reg_txpower_periodic); } void il3945_hw_cancel_deferred_work(struct il_priv *il) { cancel_delayed_work(&il->_3945.thermal_periodic); } /* check contents of special bootstrap uCode SRAM */ static int il3945_verify_bsm(struct il_priv *il) { __le32 *image = il->ucode_boot.v_addr; u32 len = il->ucode_boot.len; u32 reg; u32 val; D_INFO("Begin verify bsm\n"); /* verify BSM SRAM contents */ val = il_rd_prph(il, BSM_WR_DWCOUNT_REG); for (reg = BSM_SRAM_LOWER_BOUND; reg < BSM_SRAM_LOWER_BOUND + len; reg += sizeof(u32), image++) { val = il_rd_prph(il, reg); if (val != le32_to_cpu(*image)) { IL_ERR("BSM uCode verification failed at " "addr 0x%08X+%u (of %u), is 0x%x, s/b 0x%x\n", BSM_SRAM_LOWER_BOUND, reg - BSM_SRAM_LOWER_BOUND, len, val, le32_to_cpu(*image)); return -EIO; } } D_INFO("BSM bootstrap uCode image OK\n"); return 0; } /****************************************************************************** * * EEPROM related functions * ******************************************************************************/ /* * Clear the OWNER_MSK, to establish driver (instead of uCode running on * embedded controller) as EEPROM reader; each read is a series of pulses * to/from the EEPROM chip, not a single event, so even reads could conflict * if they weren't arbitrated by some ownership mechanism. Here, the driver * simply claims ownership, which should be safe when this function is called * (i.e. before loading uCode!). */ static int il3945_eeprom_acquire_semaphore(struct il_priv *il) { _il_clear_bit(il, CSR_EEPROM_GP, CSR_EEPROM_GP_IF_OWNER_MSK); return 0; } static void il3945_eeprom_release_semaphore(struct il_priv *il) { return; } /* * il3945_load_bsm - Load bootstrap instructions * * BSM operation: * * The Bootstrap State Machine (BSM) stores a short bootstrap uCode program * in special SRAM that does not power down during RFKILL. When powering back * up after power-saving sleeps (or during initial uCode load), the BSM loads * the bootstrap program into the on-board processor, and starts it. * * The bootstrap program loads (via DMA) instructions and data for a new * program from host DRAM locations indicated by the host driver in the * BSM_DRAM_* registers. Once the new program is loaded, it starts * automatically. * * When initializing the NIC, the host driver points the BSM to the * "initialize" uCode image. This uCode sets up some internal data, then * notifies host via "initialize alive" that it is complete. * * The host then replaces the BSM_DRAM_* pointer values to point to the * normal runtime uCode instructions and a backup uCode data cache buffer * (filled initially with starting data values for the on-board processor), * then triggers the "initialize" uCode to load and launch the runtime uCode, * which begins normal operation. * * When doing a power-save shutdown, runtime uCode saves data SRAM into * the backup data cache in DRAM before SRAM is powered down. * * When powering back up, the BSM loads the bootstrap program. This reloads * the runtime uCode instructions and the backup data cache into SRAM, * and re-launches the runtime uCode from where it left off. */ static int il3945_load_bsm(struct il_priv *il) { __le32 *image = il->ucode_boot.v_addr; u32 len = il->ucode_boot.len; dma_addr_t pinst; dma_addr_t pdata; u32 inst_len; u32 data_len; int rc; int i; u32 done; u32 reg_offset; D_INFO("Begin load bsm\n"); /* make sure bootstrap program is no larger than BSM's SRAM size */ if (len > IL39_MAX_BSM_SIZE) return -EINVAL; /* Tell bootstrap uCode where to find the "Initialize" uCode * in host DRAM ... host DRAM physical address bits 31:0 for 3945. * NOTE: il3945_initialize_alive_start() will replace these values, * after the "initialize" uCode has run, to point to * runtime/protocol instructions and backup data cache. */ pinst = il->ucode_init.p_addr; pdata = il->ucode_init_data.p_addr; inst_len = il->ucode_init.len; data_len = il->ucode_init_data.len; il_wr_prph(il, BSM_DRAM_INST_PTR_REG, pinst); il_wr_prph(il, BSM_DRAM_DATA_PTR_REG, pdata); il_wr_prph(il, BSM_DRAM_INST_BYTECOUNT_REG, inst_len); il_wr_prph(il, BSM_DRAM_DATA_BYTECOUNT_REG, data_len); /* Fill BSM memory with bootstrap instructions */ for (reg_offset = BSM_SRAM_LOWER_BOUND; reg_offset < BSM_SRAM_LOWER_BOUND + len; reg_offset += sizeof(u32), image++) _il_wr_prph(il, reg_offset, le32_to_cpu(*image)); rc = il3945_verify_bsm(il); if (rc) return rc; /* Tell BSM to copy from BSM SRAM into instruction SRAM, when asked */ il_wr_prph(il, BSM_WR_MEM_SRC_REG, 0x0); il_wr_prph(il, BSM_WR_MEM_DST_REG, IL39_RTC_INST_LOWER_BOUND); il_wr_prph(il, BSM_WR_DWCOUNT_REG, len / sizeof(u32)); /* Load bootstrap code into instruction SRAM now, * to prepare to load "initialize" uCode */ il_wr_prph(il, BSM_WR_CTRL_REG, BSM_WR_CTRL_REG_BIT_START); /* Wait for load of bootstrap uCode to finish */ for (i = 0; i < 100; i++) { done = il_rd_prph(il, BSM_WR_CTRL_REG); if (!(done & BSM_WR_CTRL_REG_BIT_START)) break; udelay(10); } if (i < 100) D_INFO("BSM write complete, poll %d iterations\n", i); else { IL_ERR("BSM write did not complete!\n"); return -EIO; } /* Enable future boot loads whenever power management unit triggers it * (e.g. when powering back up after power-save shutdown) */ il_wr_prph(il, BSM_WR_CTRL_REG, BSM_WR_CTRL_REG_BIT_START_EN); return 0; } const struct il_ops il3945_ops = { .txq_attach_buf_to_tfd = il3945_hw_txq_attach_buf_to_tfd, .txq_free_tfd = il3945_hw_txq_free_tfd, .txq_init = il3945_hw_tx_queue_init, .load_ucode = il3945_load_bsm, .dump_nic_error_log = il3945_dump_nic_error_log, .apm_init = il3945_apm_init, .send_tx_power = il3945_send_tx_power, .is_valid_rtc_data_addr = il3945_hw_valid_rtc_data_addr, .eeprom_acquire_semaphore = il3945_eeprom_acquire_semaphore, .eeprom_release_semaphore = il3945_eeprom_release_semaphore, .rxon_assoc = il3945_send_rxon_assoc, .commit_rxon = il3945_commit_rxon, .get_hcmd_size = il3945_get_hcmd_size, .build_addsta_hcmd = il3945_build_addsta_hcmd, .request_scan = il3945_request_scan, .post_scan = il3945_post_scan, .post_associate = il3945_post_associate, .config_ap = il3945_config_ap, .manage_ibss_station = il3945_manage_ibss_station, .send_led_cmd = il3945_send_led_cmd, }; static const struct il_cfg il3945_bg_cfg = { .name = "3945BG", .fw_name_pre = IL3945_FW_PRE, .ucode_api_max = IL3945_UCODE_API_MAX, .ucode_api_min = IL3945_UCODE_API_MIN, .sku = IL_SKU_G, .eeprom_ver = EEPROM_3945_EEPROM_VERSION, .mod_params = &il3945_mod_params, .led_mode = IL_LED_BLINK, .eeprom_size = IL3945_EEPROM_IMG_SIZE, .num_of_queues = IL39_NUM_QUEUES, .pll_cfg_val = CSR39_ANA_PLL_CFG_VAL, .set_l0s = false, .use_bsm = true, .led_compensation = 64, .wd_timeout = IL_DEF_WD_TIMEOUT, .regulatory_bands = { EEPROM_REGULATORY_BAND_1_CHANNELS, EEPROM_REGULATORY_BAND_2_CHANNELS, EEPROM_REGULATORY_BAND_3_CHANNELS, EEPROM_REGULATORY_BAND_4_CHANNELS, EEPROM_REGULATORY_BAND_5_CHANNELS, EEPROM_REGULATORY_BAND_NO_HT40, EEPROM_REGULATORY_BAND_NO_HT40, }, }; static const struct il_cfg il3945_abg_cfg = { .name = "3945ABG", .fw_name_pre = IL3945_FW_PRE, .ucode_api_max = IL3945_UCODE_API_MAX, .ucode_api_min = IL3945_UCODE_API_MIN, .sku = IL_SKU_A | IL_SKU_G, .eeprom_ver = EEPROM_3945_EEPROM_VERSION, .mod_params = &il3945_mod_params, .led_mode = IL_LED_BLINK, .eeprom_size = IL3945_EEPROM_IMG_SIZE, .num_of_queues = IL39_NUM_QUEUES, .pll_cfg_val = CSR39_ANA_PLL_CFG_VAL, .set_l0s = false, .use_bsm = true, .led_compensation = 64, .wd_timeout = IL_DEF_WD_TIMEOUT, .regulatory_bands = { EEPROM_REGULATORY_BAND_1_CHANNELS, EEPROM_REGULATORY_BAND_2_CHANNELS, EEPROM_REGULATORY_BAND_3_CHANNELS, EEPROM_REGULATORY_BAND_4_CHANNELS, EEPROM_REGULATORY_BAND_5_CHANNELS, EEPROM_REGULATORY_BAND_NO_HT40, EEPROM_REGULATORY_BAND_NO_HT40, }, }; const struct pci_device_id il3945_hw_card_ids[] = { {IL_PCI_DEVICE(0x4222, 0x1005, il3945_bg_cfg)}, {IL_PCI_DEVICE(0x4222, 0x1034, il3945_bg_cfg)}, {IL_PCI_DEVICE(0x4222, 0x1044, il3945_bg_cfg)}, {IL_PCI_DEVICE(0x4227, 0x1014, il3945_bg_cfg)}, {IL_PCI_DEVICE(0x4222, PCI_ANY_ID, il3945_abg_cfg)}, {IL_PCI_DEVICE(0x4227, PCI_ANY_ID, il3945_abg_cfg)}, {0} }; MODULE_DEVICE_TABLE(pci, il3945_hw_card_ids);