12be45b66SKalle Valo /* Encapsulate basic setting changes and retrieval on Hermes hardware
22be45b66SKalle Valo *
32be45b66SKalle Valo * See copyright notice in main.c
42be45b66SKalle Valo */
52be45b66SKalle Valo #include <linux/kernel.h>
62be45b66SKalle Valo #include <linux/device.h>
72be45b66SKalle Valo #include <linux/if_arp.h>
82be45b66SKalle Valo #include <linux/ieee80211.h>
92be45b66SKalle Valo #include <linux/wireless.h>
102be45b66SKalle Valo #include <net/cfg80211.h>
112be45b66SKalle Valo #include "hermes.h"
122be45b66SKalle Valo #include "hermes_rid.h"
132be45b66SKalle Valo #include "orinoco.h"
142be45b66SKalle Valo
152be45b66SKalle Valo #include "hw.h"
162be45b66SKalle Valo
172be45b66SKalle Valo #define SYMBOL_MAX_VER_LEN (14)
182be45b66SKalle Valo
192be45b66SKalle Valo /* Symbol firmware has a bug allocating buffers larger than this */
202be45b66SKalle Valo #define TX_NICBUF_SIZE_BUG 1585
212be45b66SKalle Valo
222be45b66SKalle Valo /********************************************************************/
232be45b66SKalle Valo /* Data tables */
242be45b66SKalle Valo /********************************************************************/
252be45b66SKalle Valo
262be45b66SKalle Valo /* This tables gives the actual meanings of the bitrate IDs returned
272be45b66SKalle Valo * by the firmware. */
282be45b66SKalle Valo static const struct {
292be45b66SKalle Valo int bitrate; /* in 100s of kilobits */
302be45b66SKalle Valo int automatic;
312be45b66SKalle Valo u16 agere_txratectrl;
322be45b66SKalle Valo u16 intersil_txratectrl;
332be45b66SKalle Valo } bitrate_table[] = {
342be45b66SKalle Valo {110, 1, 3, 15}, /* Entry 0 is the default */
352be45b66SKalle Valo {10, 0, 1, 1},
362be45b66SKalle Valo {10, 1, 1, 1},
372be45b66SKalle Valo {20, 0, 2, 2},
382be45b66SKalle Valo {20, 1, 6, 3},
392be45b66SKalle Valo {55, 0, 4, 4},
402be45b66SKalle Valo {55, 1, 7, 7},
412be45b66SKalle Valo {110, 0, 5, 8},
422be45b66SKalle Valo };
432be45b66SKalle Valo #define BITRATE_TABLE_SIZE ARRAY_SIZE(bitrate_table)
442be45b66SKalle Valo
452be45b66SKalle Valo /* Firmware version encoding */
462be45b66SKalle Valo struct comp_id {
472be45b66SKalle Valo u16 id, variant, major, minor;
482be45b66SKalle Valo } __packed;
492be45b66SKalle Valo
determine_firmware_type(struct comp_id * nic_id)502be45b66SKalle Valo static inline enum fwtype determine_firmware_type(struct comp_id *nic_id)
512be45b66SKalle Valo {
522be45b66SKalle Valo if (nic_id->id < 0x8000)
532be45b66SKalle Valo return FIRMWARE_TYPE_AGERE;
542be45b66SKalle Valo else if (nic_id->id == 0x8000 && nic_id->major == 0)
552be45b66SKalle Valo return FIRMWARE_TYPE_SYMBOL;
562be45b66SKalle Valo else
572be45b66SKalle Valo return FIRMWARE_TYPE_INTERSIL;
582be45b66SKalle Valo }
592be45b66SKalle Valo
602be45b66SKalle Valo /* Set priv->firmware type, determine firmware properties
612be45b66SKalle Valo * This function can be called before we have registerred with netdev,
622be45b66SKalle Valo * so all errors go out with dev_* rather than printk
632be45b66SKalle Valo *
642be45b66SKalle Valo * If non-NULL stores a firmware description in fw_name.
652be45b66SKalle Valo * If non-NULL stores a HW version in hw_ver
662be45b66SKalle Valo *
672be45b66SKalle Valo * These are output via generic cfg80211 ethtool support.
682be45b66SKalle Valo */
determine_fw_capabilities(struct orinoco_private * priv,char * fw_name,size_t fw_name_len,u32 * hw_ver)692be45b66SKalle Valo int determine_fw_capabilities(struct orinoco_private *priv,
702be45b66SKalle Valo char *fw_name, size_t fw_name_len,
712be45b66SKalle Valo u32 *hw_ver)
722be45b66SKalle Valo {
732be45b66SKalle Valo struct device *dev = priv->dev;
742be45b66SKalle Valo struct hermes *hw = &priv->hw;
752be45b66SKalle Valo int err;
762be45b66SKalle Valo struct comp_id nic_id, sta_id;
772be45b66SKalle Valo unsigned int firmver;
782be45b66SKalle Valo char tmp[SYMBOL_MAX_VER_LEN + 1] __attribute__((aligned(2)));
792be45b66SKalle Valo
802be45b66SKalle Valo /* Get the hardware version */
81a3d8a259SSebastian Andrzej Siewior err = HERMES_READ_RECORD_PR(hw, USER_BAP, HERMES_RID_NICID, &nic_id);
822be45b66SKalle Valo if (err) {
832be45b66SKalle Valo dev_err(dev, "Cannot read hardware identity: error %d\n",
842be45b66SKalle Valo err);
852be45b66SKalle Valo return err;
862be45b66SKalle Valo }
872be45b66SKalle Valo
882be45b66SKalle Valo le16_to_cpus(&nic_id.id);
892be45b66SKalle Valo le16_to_cpus(&nic_id.variant);
902be45b66SKalle Valo le16_to_cpus(&nic_id.major);
912be45b66SKalle Valo le16_to_cpus(&nic_id.minor);
922be45b66SKalle Valo dev_info(dev, "Hardware identity %04x:%04x:%04x:%04x\n",
932be45b66SKalle Valo nic_id.id, nic_id.variant, nic_id.major, nic_id.minor);
942be45b66SKalle Valo
952be45b66SKalle Valo if (hw_ver)
962be45b66SKalle Valo *hw_ver = (((nic_id.id & 0xff) << 24) |
972be45b66SKalle Valo ((nic_id.variant & 0xff) << 16) |
982be45b66SKalle Valo ((nic_id.major & 0xff) << 8) |
992be45b66SKalle Valo (nic_id.minor & 0xff));
1002be45b66SKalle Valo
1012be45b66SKalle Valo priv->firmware_type = determine_firmware_type(&nic_id);
1022be45b66SKalle Valo
1032be45b66SKalle Valo /* Get the firmware version */
104a3d8a259SSebastian Andrzej Siewior err = HERMES_READ_RECORD_PR(hw, USER_BAP, HERMES_RID_STAID, &sta_id);
1052be45b66SKalle Valo if (err) {
1062be45b66SKalle Valo dev_err(dev, "Cannot read station identity: error %d\n",
1072be45b66SKalle Valo err);
1082be45b66SKalle Valo return err;
1092be45b66SKalle Valo }
1102be45b66SKalle Valo
1112be45b66SKalle Valo le16_to_cpus(&sta_id.id);
1122be45b66SKalle Valo le16_to_cpus(&sta_id.variant);
1132be45b66SKalle Valo le16_to_cpus(&sta_id.major);
1142be45b66SKalle Valo le16_to_cpus(&sta_id.minor);
1152be45b66SKalle Valo dev_info(dev, "Station identity %04x:%04x:%04x:%04x\n",
1162be45b66SKalle Valo sta_id.id, sta_id.variant, sta_id.major, sta_id.minor);
1172be45b66SKalle Valo
1182be45b66SKalle Valo switch (sta_id.id) {
1192be45b66SKalle Valo case 0x15:
1202be45b66SKalle Valo dev_err(dev, "Primary firmware is active\n");
1212be45b66SKalle Valo return -ENODEV;
1222be45b66SKalle Valo case 0x14b:
1232be45b66SKalle Valo dev_err(dev, "Tertiary firmware is active\n");
1242be45b66SKalle Valo return -ENODEV;
1252be45b66SKalle Valo case 0x1f: /* Intersil, Agere, Symbol Spectrum24 */
1262be45b66SKalle Valo case 0x21: /* Symbol Spectrum24 Trilogy */
1272be45b66SKalle Valo break;
1282be45b66SKalle Valo default:
1292be45b66SKalle Valo dev_notice(dev, "Unknown station ID, please report\n");
1302be45b66SKalle Valo break;
1312be45b66SKalle Valo }
1322be45b66SKalle Valo
1332be45b66SKalle Valo /* Default capabilities */
1342be45b66SKalle Valo priv->has_sensitivity = 1;
1352be45b66SKalle Valo priv->has_mwo = 0;
1362be45b66SKalle Valo priv->has_preamble = 0;
1372be45b66SKalle Valo priv->has_port3 = 1;
1382be45b66SKalle Valo priv->has_ibss = 1;
1392be45b66SKalle Valo priv->has_wep = 0;
1402be45b66SKalle Valo priv->has_big_wep = 0;
1412be45b66SKalle Valo priv->has_alt_txcntl = 0;
1422be45b66SKalle Valo priv->has_ext_scan = 0;
1432be45b66SKalle Valo priv->has_wpa = 0;
1442be45b66SKalle Valo priv->do_fw_download = 0;
1452be45b66SKalle Valo
1462be45b66SKalle Valo /* Determine capabilities from the firmware version */
1472be45b66SKalle Valo switch (priv->firmware_type) {
1482be45b66SKalle Valo case FIRMWARE_TYPE_AGERE:
1492be45b66SKalle Valo /* Lucent Wavelan IEEE, Lucent Orinoco, Cabletron RoamAbout,
1502be45b66SKalle Valo ELSA, Melco, HP, IBM, Dell 1150, Compaq 110/210 */
1512be45b66SKalle Valo if (fw_name)
1522be45b66SKalle Valo snprintf(fw_name, fw_name_len, "Lucent/Agere %d.%02d",
1532be45b66SKalle Valo sta_id.major, sta_id.minor);
1542be45b66SKalle Valo
1552be45b66SKalle Valo firmver = ((unsigned long)sta_id.major << 16) | sta_id.minor;
1562be45b66SKalle Valo
1572be45b66SKalle Valo priv->has_ibss = (firmver >= 0x60006);
1582be45b66SKalle Valo priv->has_wep = (firmver >= 0x40020);
1592be45b66SKalle Valo priv->has_big_wep = 1; /* FIXME: this is wrong - how do we tell
1602be45b66SKalle Valo Gold cards from the others? */
1612be45b66SKalle Valo priv->has_mwo = (firmver >= 0x60000);
1622be45b66SKalle Valo priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */
1632be45b66SKalle Valo priv->ibss_port = 1;
1642be45b66SKalle Valo priv->has_hostscan = (firmver >= 0x8000a);
1652be45b66SKalle Valo priv->do_fw_download = 1;
1662be45b66SKalle Valo priv->broken_monitor = (firmver >= 0x80000);
1672be45b66SKalle Valo priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */
1682be45b66SKalle Valo priv->has_ext_scan = (firmver >= 0x90000); /* All 9.x ? */
1692be45b66SKalle Valo priv->has_wpa = (firmver >= 0x9002a);
1702be45b66SKalle Valo /* Tested with Agere firmware :
1712be45b66SKalle Valo * 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II
1722be45b66SKalle Valo * Tested CableTron firmware : 4.32 => Anton */
1732be45b66SKalle Valo break;
1742be45b66SKalle Valo case FIRMWARE_TYPE_SYMBOL:
1752be45b66SKalle Valo /* Symbol , 3Com AirConnect, Intel, Ericsson WLAN */
1762be45b66SKalle Valo /* Intel MAC : 00:02:B3:* */
1772be45b66SKalle Valo /* 3Com MAC : 00:50:DA:* */
1782be45b66SKalle Valo memset(tmp, 0, sizeof(tmp));
1792be45b66SKalle Valo /* Get the Symbol firmware version */
180a3d8a259SSebastian Andrzej Siewior err = hw->ops->read_ltv_pr(hw, USER_BAP,
1812be45b66SKalle Valo HERMES_RID_SECONDARYVERSION_SYMBOL,
1822be45b66SKalle Valo SYMBOL_MAX_VER_LEN, NULL, &tmp);
1832be45b66SKalle Valo if (err) {
1842be45b66SKalle Valo dev_warn(dev, "Error %d reading Symbol firmware info. "
1852be45b66SKalle Valo "Wildly guessing capabilities...\n", err);
1862be45b66SKalle Valo firmver = 0;
1872be45b66SKalle Valo tmp[0] = '\0';
1882be45b66SKalle Valo } else {
1892be45b66SKalle Valo /* The firmware revision is a string, the format is
1902be45b66SKalle Valo * something like : "V2.20-01".
1912be45b66SKalle Valo * Quick and dirty parsing... - Jean II
1922be45b66SKalle Valo */
1932be45b66SKalle Valo firmver = ((tmp[1] - '0') << 16)
1942be45b66SKalle Valo | ((tmp[3] - '0') << 12)
1952be45b66SKalle Valo | ((tmp[4] - '0') << 8)
1962be45b66SKalle Valo | ((tmp[6] - '0') << 4)
1972be45b66SKalle Valo | (tmp[7] - '0');
1982be45b66SKalle Valo
1992be45b66SKalle Valo tmp[SYMBOL_MAX_VER_LEN] = '\0';
2002be45b66SKalle Valo }
2012be45b66SKalle Valo
2022be45b66SKalle Valo if (fw_name)
2032be45b66SKalle Valo snprintf(fw_name, fw_name_len, "Symbol %s", tmp);
2042be45b66SKalle Valo
2052be45b66SKalle Valo priv->has_ibss = (firmver >= 0x20000);
2062be45b66SKalle Valo priv->has_wep = (firmver >= 0x15012);
2072be45b66SKalle Valo priv->has_big_wep = (firmver >= 0x20000);
2082be45b66SKalle Valo priv->has_pm = (firmver >= 0x20000 && firmver < 0x22000) ||
2092be45b66SKalle Valo (firmver >= 0x29000 && firmver < 0x30000) ||
2102be45b66SKalle Valo firmver >= 0x31000;
2112be45b66SKalle Valo priv->has_preamble = (firmver >= 0x20000);
2122be45b66SKalle Valo priv->ibss_port = 4;
2132be45b66SKalle Valo
2142be45b66SKalle Valo /* Symbol firmware is found on various cards, but
2152be45b66SKalle Valo * there has been no attempt to check firmware
2162be45b66SKalle Valo * download on non-spectrum_cs based cards.
2172be45b66SKalle Valo *
2182be45b66SKalle Valo * Given that the Agere firmware download works
2192be45b66SKalle Valo * differently, we should avoid doing a firmware
2202be45b66SKalle Valo * download with the Symbol algorithm on non-spectrum
2212be45b66SKalle Valo * cards.
2222be45b66SKalle Valo *
2232be45b66SKalle Valo * For now we can identify a spectrum_cs based card
2242be45b66SKalle Valo * because it has a firmware reset function.
2252be45b66SKalle Valo */
2262be45b66SKalle Valo priv->do_fw_download = (priv->stop_fw != NULL);
2272be45b66SKalle Valo
2282be45b66SKalle Valo priv->broken_disableport = (firmver == 0x25013) ||
2292be45b66SKalle Valo (firmver >= 0x30000 && firmver <= 0x31000);
2302be45b66SKalle Valo priv->has_hostscan = (firmver >= 0x31001) ||
2312be45b66SKalle Valo (firmver >= 0x29057 && firmver < 0x30000);
2322be45b66SKalle Valo /* Tested with Intel firmware : 0x20015 => Jean II */
2332be45b66SKalle Valo /* Tested with 3Com firmware : 0x15012 & 0x22001 => Jean II */
2342be45b66SKalle Valo break;
2352be45b66SKalle Valo case FIRMWARE_TYPE_INTERSIL:
2362be45b66SKalle Valo /* D-Link, Linksys, Adtron, ZoomAir, and many others...
2372be45b66SKalle Valo * Samsung, Compaq 100/200 and Proxim are slightly
2382be45b66SKalle Valo * different and less well tested */
2392be45b66SKalle Valo /* D-Link MAC : 00:40:05:* */
2402be45b66SKalle Valo /* Addtron MAC : 00:90:D1:* */
2412be45b66SKalle Valo if (fw_name)
2422be45b66SKalle Valo snprintf(fw_name, fw_name_len, "Intersil %d.%d.%d",
2432be45b66SKalle Valo sta_id.major, sta_id.minor, sta_id.variant);
2442be45b66SKalle Valo
2452be45b66SKalle Valo firmver = ((unsigned long)sta_id.major << 16) |
2462be45b66SKalle Valo ((unsigned long)sta_id.minor << 8) | sta_id.variant;
2472be45b66SKalle Valo
2482be45b66SKalle Valo priv->has_ibss = (firmver >= 0x000700); /* FIXME */
2492be45b66SKalle Valo priv->has_big_wep = priv->has_wep = (firmver >= 0x000800);
2502be45b66SKalle Valo priv->has_pm = (firmver >= 0x000700);
2512be45b66SKalle Valo priv->has_hostscan = (firmver >= 0x010301);
2522be45b66SKalle Valo
2532be45b66SKalle Valo if (firmver >= 0x000800)
2542be45b66SKalle Valo priv->ibss_port = 0;
2552be45b66SKalle Valo else {
2562be45b66SKalle Valo dev_notice(dev, "Intersil firmware earlier than v0.8.x"
2572be45b66SKalle Valo " - several features not supported\n");
2582be45b66SKalle Valo priv->ibss_port = 1;
2592be45b66SKalle Valo }
2602be45b66SKalle Valo break;
2612be45b66SKalle Valo }
2622be45b66SKalle Valo if (fw_name)
2632be45b66SKalle Valo dev_info(dev, "Firmware determined as %s\n", fw_name);
2642be45b66SKalle Valo
2652be45b66SKalle Valo #ifndef CONFIG_HERMES_PRISM
2662be45b66SKalle Valo if (priv->firmware_type == FIRMWARE_TYPE_INTERSIL) {
2672be45b66SKalle Valo dev_err(dev, "Support for Prism chipset is not enabled\n");
2682be45b66SKalle Valo return -ENODEV;
2692be45b66SKalle Valo }
2702be45b66SKalle Valo #endif
2712be45b66SKalle Valo
2722be45b66SKalle Valo return 0;
2732be45b66SKalle Valo }
2742be45b66SKalle Valo
2752be45b66SKalle Valo /* Read settings from EEPROM into our private structure.
2762be45b66SKalle Valo * MAC address gets dropped into callers buffer
2772be45b66SKalle Valo * Can be called before netdev registration.
2782be45b66SKalle Valo */
orinoco_hw_read_card_settings(struct orinoco_private * priv,u8 * dev_addr)2792be45b66SKalle Valo int orinoco_hw_read_card_settings(struct orinoco_private *priv, u8 *dev_addr)
2802be45b66SKalle Valo {
2812be45b66SKalle Valo struct device *dev = priv->dev;
2822be45b66SKalle Valo struct hermes_idstring nickbuf;
2832be45b66SKalle Valo struct hermes *hw = &priv->hw;
2842be45b66SKalle Valo int len;
2852be45b66SKalle Valo int err;
2862be45b66SKalle Valo u16 reclen;
2872be45b66SKalle Valo
2882be45b66SKalle Valo /* Get the MAC address */
289a3d8a259SSebastian Andrzej Siewior err = hw->ops->read_ltv_pr(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
2902be45b66SKalle Valo ETH_ALEN, NULL, dev_addr);
2912be45b66SKalle Valo if (err) {
2922be45b66SKalle Valo dev_warn(dev, "Failed to read MAC address!\n");
2932be45b66SKalle Valo goto out;
2942be45b66SKalle Valo }
2952be45b66SKalle Valo
2962be45b66SKalle Valo dev_dbg(dev, "MAC address %pM\n", dev_addr);
2972be45b66SKalle Valo
2982be45b66SKalle Valo /* Get the station name */
299a3d8a259SSebastian Andrzej Siewior err = hw->ops->read_ltv_pr(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
3002be45b66SKalle Valo sizeof(nickbuf), &reclen, &nickbuf);
3012be45b66SKalle Valo if (err) {
3022be45b66SKalle Valo dev_err(dev, "failed to read station name\n");
3032be45b66SKalle Valo goto out;
3042be45b66SKalle Valo }
3052be45b66SKalle Valo if (nickbuf.len)
3062be45b66SKalle Valo len = min(IW_ESSID_MAX_SIZE, (int)le16_to_cpu(nickbuf.len));
3072be45b66SKalle Valo else
3082be45b66SKalle Valo len = min(IW_ESSID_MAX_SIZE, 2 * reclen);
3092be45b66SKalle Valo memcpy(priv->nick, &nickbuf.val, len);
3102be45b66SKalle Valo priv->nick[len] = '\0';
3112be45b66SKalle Valo
3122be45b66SKalle Valo dev_dbg(dev, "Station name \"%s\"\n", priv->nick);
3132be45b66SKalle Valo
3142be45b66SKalle Valo /* Get allowed channels */
315a3d8a259SSebastian Andrzej Siewior err = hermes_read_wordrec_pr(hw, USER_BAP, HERMES_RID_CHANNELLIST,
3162be45b66SKalle Valo &priv->channel_mask);
3172be45b66SKalle Valo if (err) {
3182be45b66SKalle Valo dev_err(dev, "Failed to read channel list!\n");
3192be45b66SKalle Valo goto out;
3202be45b66SKalle Valo }
3212be45b66SKalle Valo
3222be45b66SKalle Valo /* Get initial AP density */
323a3d8a259SSebastian Andrzej Siewior err = hermes_read_wordrec_pr(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE,
3242be45b66SKalle Valo &priv->ap_density);
3252be45b66SKalle Valo if (err || priv->ap_density < 1 || priv->ap_density > 3)
3262be45b66SKalle Valo priv->has_sensitivity = 0;
3272be45b66SKalle Valo
3282be45b66SKalle Valo /* Get initial RTS threshold */
329a3d8a259SSebastian Andrzej Siewior err = hermes_read_wordrec_pr(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD,
3302be45b66SKalle Valo &priv->rts_thresh);
3312be45b66SKalle Valo if (err) {
3322be45b66SKalle Valo dev_err(dev, "Failed to read RTS threshold!\n");
3332be45b66SKalle Valo goto out;
3342be45b66SKalle Valo }
3352be45b66SKalle Valo
3362be45b66SKalle Valo /* Get initial fragmentation settings */
3372be45b66SKalle Valo if (priv->has_mwo)
338a3d8a259SSebastian Andrzej Siewior err = hermes_read_wordrec_pr(hw, USER_BAP,
3392be45b66SKalle Valo HERMES_RID_CNFMWOROBUST_AGERE,
3402be45b66SKalle Valo &priv->mwo_robust);
3412be45b66SKalle Valo else
342a3d8a259SSebastian Andrzej Siewior err = hermes_read_wordrec_pr(hw, USER_BAP,
3432be45b66SKalle Valo HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
3442be45b66SKalle Valo &priv->frag_thresh);
3452be45b66SKalle Valo if (err) {
3462be45b66SKalle Valo dev_err(dev, "Failed to read fragmentation settings!\n");
3472be45b66SKalle Valo goto out;
3482be45b66SKalle Valo }
3492be45b66SKalle Valo
3502be45b66SKalle Valo /* Power management setup */
3512be45b66SKalle Valo if (priv->has_pm) {
3522be45b66SKalle Valo priv->pm_on = 0;
3532be45b66SKalle Valo priv->pm_mcast = 1;
354a3d8a259SSebastian Andrzej Siewior err = hermes_read_wordrec_pr(hw, USER_BAP,
3552be45b66SKalle Valo HERMES_RID_CNFMAXSLEEPDURATION,
3562be45b66SKalle Valo &priv->pm_period);
3572be45b66SKalle Valo if (err) {
3582be45b66SKalle Valo dev_err(dev, "Failed to read power management "
3592be45b66SKalle Valo "period!\n");
3602be45b66SKalle Valo goto out;
3612be45b66SKalle Valo }
362a3d8a259SSebastian Andrzej Siewior err = hermes_read_wordrec_pr(hw, USER_BAP,
3632be45b66SKalle Valo HERMES_RID_CNFPMHOLDOVERDURATION,
3642be45b66SKalle Valo &priv->pm_timeout);
3652be45b66SKalle Valo if (err) {
3662be45b66SKalle Valo dev_err(dev, "Failed to read power management "
3672be45b66SKalle Valo "timeout!\n");
3682be45b66SKalle Valo goto out;
3692be45b66SKalle Valo }
3702be45b66SKalle Valo }
3712be45b66SKalle Valo
3722be45b66SKalle Valo /* Preamble setup */
3732be45b66SKalle Valo if (priv->has_preamble) {
374a3d8a259SSebastian Andrzej Siewior err = hermes_read_wordrec_pr(hw, USER_BAP,
3752be45b66SKalle Valo HERMES_RID_CNFPREAMBLE_SYMBOL,
3762be45b66SKalle Valo &priv->preamble);
3772be45b66SKalle Valo if (err) {
3782be45b66SKalle Valo dev_err(dev, "Failed to read preamble setup\n");
3792be45b66SKalle Valo goto out;
3802be45b66SKalle Valo }
3812be45b66SKalle Valo }
3822be45b66SKalle Valo
3832be45b66SKalle Valo /* Retry settings */
384a3d8a259SSebastian Andrzej Siewior err = hermes_read_wordrec_pr(hw, USER_BAP, HERMES_RID_SHORTRETRYLIMIT,
3852be45b66SKalle Valo &priv->short_retry_limit);
3862be45b66SKalle Valo if (err) {
3872be45b66SKalle Valo dev_err(dev, "Failed to read short retry limit\n");
3882be45b66SKalle Valo goto out;
3892be45b66SKalle Valo }
3902be45b66SKalle Valo
391a3d8a259SSebastian Andrzej Siewior err = hermes_read_wordrec_pr(hw, USER_BAP, HERMES_RID_LONGRETRYLIMIT,
3922be45b66SKalle Valo &priv->long_retry_limit);
3932be45b66SKalle Valo if (err) {
3942be45b66SKalle Valo dev_err(dev, "Failed to read long retry limit\n");
3952be45b66SKalle Valo goto out;
3962be45b66SKalle Valo }
3972be45b66SKalle Valo
398a3d8a259SSebastian Andrzej Siewior err = hermes_read_wordrec_pr(hw, USER_BAP, HERMES_RID_MAXTRANSMITLIFETIME,
3992be45b66SKalle Valo &priv->retry_lifetime);
4002be45b66SKalle Valo if (err) {
4012be45b66SKalle Valo dev_err(dev, "Failed to read max retry lifetime\n");
4022be45b66SKalle Valo goto out;
4032be45b66SKalle Valo }
4042be45b66SKalle Valo
4052be45b66SKalle Valo out:
4062be45b66SKalle Valo return err;
4072be45b66SKalle Valo }
4082be45b66SKalle Valo
4092be45b66SKalle Valo /* Can be called before netdev registration */
orinoco_hw_allocate_fid(struct orinoco_private * priv)4102be45b66SKalle Valo int orinoco_hw_allocate_fid(struct orinoco_private *priv)
4112be45b66SKalle Valo {
4122be45b66SKalle Valo struct device *dev = priv->dev;
4132be45b66SKalle Valo struct hermes *hw = &priv->hw;
4142be45b66SKalle Valo int err;
4152be45b66SKalle Valo
4162be45b66SKalle Valo err = hw->ops->allocate(hw, priv->nicbuf_size, &priv->txfid);
4172be45b66SKalle Valo if (err == -EIO && priv->nicbuf_size > TX_NICBUF_SIZE_BUG) {
4182be45b66SKalle Valo /* Try workaround for old Symbol firmware bug */
4192be45b66SKalle Valo priv->nicbuf_size = TX_NICBUF_SIZE_BUG;
4202be45b66SKalle Valo err = hw->ops->allocate(hw, priv->nicbuf_size, &priv->txfid);
4212be45b66SKalle Valo
4222be45b66SKalle Valo dev_warn(dev, "Firmware ALLOC bug detected "
4232be45b66SKalle Valo "(old Symbol firmware?). Work around %s\n",
4242be45b66SKalle Valo err ? "failed!" : "ok.");
4252be45b66SKalle Valo }
4262be45b66SKalle Valo
4272be45b66SKalle Valo return err;
4282be45b66SKalle Valo }
4292be45b66SKalle Valo
orinoco_get_bitratemode(int bitrate,int automatic)4302be45b66SKalle Valo int orinoco_get_bitratemode(int bitrate, int automatic)
4312be45b66SKalle Valo {
4322be45b66SKalle Valo int ratemode = -1;
4332be45b66SKalle Valo int i;
4342be45b66SKalle Valo
4352be45b66SKalle Valo if ((bitrate != 10) && (bitrate != 20) &&
4362be45b66SKalle Valo (bitrate != 55) && (bitrate != 110))
4372be45b66SKalle Valo return ratemode;
4382be45b66SKalle Valo
4392be45b66SKalle Valo for (i = 0; i < BITRATE_TABLE_SIZE; i++) {
4402be45b66SKalle Valo if ((bitrate_table[i].bitrate == bitrate) &&
4412be45b66SKalle Valo (bitrate_table[i].automatic == automatic)) {
4422be45b66SKalle Valo ratemode = i;
4432be45b66SKalle Valo break;
4442be45b66SKalle Valo }
4452be45b66SKalle Valo }
4462be45b66SKalle Valo return ratemode;
4472be45b66SKalle Valo }
4482be45b66SKalle Valo
orinoco_get_ratemode_cfg(int ratemode,int * bitrate,int * automatic)4492be45b66SKalle Valo void orinoco_get_ratemode_cfg(int ratemode, int *bitrate, int *automatic)
4502be45b66SKalle Valo {
4512be45b66SKalle Valo BUG_ON((ratemode < 0) || (ratemode >= BITRATE_TABLE_SIZE));
4522be45b66SKalle Valo
4532be45b66SKalle Valo *bitrate = bitrate_table[ratemode].bitrate * 100000;
4542be45b66SKalle Valo *automatic = bitrate_table[ratemode].automatic;
4552be45b66SKalle Valo }
4562be45b66SKalle Valo
orinoco_hw_program_rids(struct orinoco_private * priv)4572be45b66SKalle Valo int orinoco_hw_program_rids(struct orinoco_private *priv)
4582be45b66SKalle Valo {
4592be45b66SKalle Valo struct net_device *dev = priv->ndev;
4602be45b66SKalle Valo struct wireless_dev *wdev = netdev_priv(dev);
4612be45b66SKalle Valo struct hermes *hw = &priv->hw;
4622be45b66SKalle Valo int err;
4632be45b66SKalle Valo struct hermes_idstring idbuf;
4642be45b66SKalle Valo
4652be45b66SKalle Valo /* Set the MAC address */
4662be45b66SKalle Valo err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
4672be45b66SKalle Valo HERMES_BYTES_TO_RECLEN(ETH_ALEN),
4682be45b66SKalle Valo dev->dev_addr);
4692be45b66SKalle Valo if (err) {
4702be45b66SKalle Valo printk(KERN_ERR "%s: Error %d setting MAC address\n",
4712be45b66SKalle Valo dev->name, err);
4722be45b66SKalle Valo return err;
4732be45b66SKalle Valo }
4742be45b66SKalle Valo
4752be45b66SKalle Valo /* Set up the link mode */
4762be45b66SKalle Valo err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE,
4772be45b66SKalle Valo priv->port_type);
4782be45b66SKalle Valo if (err) {
4792be45b66SKalle Valo printk(KERN_ERR "%s: Error %d setting port type\n",
4802be45b66SKalle Valo dev->name, err);
4812be45b66SKalle Valo return err;
4822be45b66SKalle Valo }
4832be45b66SKalle Valo /* Set the channel/frequency */
4842be45b66SKalle Valo if (priv->channel != 0 && priv->iw_mode != NL80211_IFTYPE_STATION) {
4852be45b66SKalle Valo err = hermes_write_wordrec(hw, USER_BAP,
4862be45b66SKalle Valo HERMES_RID_CNFOWNCHANNEL,
4872be45b66SKalle Valo priv->channel);
4882be45b66SKalle Valo if (err) {
4892be45b66SKalle Valo printk(KERN_ERR "%s: Error %d setting channel %d\n",
4902be45b66SKalle Valo dev->name, err, priv->channel);
4912be45b66SKalle Valo return err;
4922be45b66SKalle Valo }
4932be45b66SKalle Valo }
4942be45b66SKalle Valo
4952be45b66SKalle Valo if (priv->has_ibss) {
4962be45b66SKalle Valo u16 createibss;
4972be45b66SKalle Valo
4982be45b66SKalle Valo if ((strlen(priv->desired_essid) == 0) && (priv->createibss)) {
4992be45b66SKalle Valo printk(KERN_WARNING "%s: This firmware requires an "
5002be45b66SKalle Valo "ESSID in IBSS-Ad-Hoc mode.\n", dev->name);
5012be45b66SKalle Valo /* With wvlan_cs, in this case, we would crash.
5022be45b66SKalle Valo * hopefully, this driver will behave better...
5032be45b66SKalle Valo * Jean II */
5042be45b66SKalle Valo createibss = 0;
5052be45b66SKalle Valo } else {
5062be45b66SKalle Valo createibss = priv->createibss;
5072be45b66SKalle Valo }
5082be45b66SKalle Valo
5092be45b66SKalle Valo err = hermes_write_wordrec(hw, USER_BAP,
5102be45b66SKalle Valo HERMES_RID_CNFCREATEIBSS,
5112be45b66SKalle Valo createibss);
5122be45b66SKalle Valo if (err) {
5132be45b66SKalle Valo printk(KERN_ERR "%s: Error %d setting CREATEIBSS\n",
5142be45b66SKalle Valo dev->name, err);
5152be45b66SKalle Valo return err;
5162be45b66SKalle Valo }
5172be45b66SKalle Valo }
5182be45b66SKalle Valo
5192be45b66SKalle Valo /* Set the desired BSSID */
5202be45b66SKalle Valo err = __orinoco_hw_set_wap(priv);
5212be45b66SKalle Valo if (err) {
5222be45b66SKalle Valo printk(KERN_ERR "%s: Error %d setting AP address\n",
5232be45b66SKalle Valo dev->name, err);
5242be45b66SKalle Valo return err;
5252be45b66SKalle Valo }
5262be45b66SKalle Valo
5272be45b66SKalle Valo /* Set the desired ESSID */
5282be45b66SKalle Valo idbuf.len = cpu_to_le16(strlen(priv->desired_essid));
5292be45b66SKalle Valo memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val));
5302be45b66SKalle Valo /* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */
5312be45b66SKalle Valo err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID,
5322be45b66SKalle Valo HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid) + 2),
5332be45b66SKalle Valo &idbuf);
5342be45b66SKalle Valo if (err) {
5352be45b66SKalle Valo printk(KERN_ERR "%s: Error %d setting OWNSSID\n",
5362be45b66SKalle Valo dev->name, err);
5372be45b66SKalle Valo return err;
5382be45b66SKalle Valo }
5392be45b66SKalle Valo err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID,
5402be45b66SKalle Valo HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid) + 2),
5412be45b66SKalle Valo &idbuf);
5422be45b66SKalle Valo if (err) {
5432be45b66SKalle Valo printk(KERN_ERR "%s: Error %d setting DESIREDSSID\n",
5442be45b66SKalle Valo dev->name, err);
5452be45b66SKalle Valo return err;
5462be45b66SKalle Valo }
5472be45b66SKalle Valo
5482be45b66SKalle Valo /* Set the station name */
5492be45b66SKalle Valo idbuf.len = cpu_to_le16(strlen(priv->nick));
5502be45b66SKalle Valo memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val));
5512be45b66SKalle Valo err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
5522be45b66SKalle Valo HERMES_BYTES_TO_RECLEN(strlen(priv->nick) + 2),
5532be45b66SKalle Valo &idbuf);
5542be45b66SKalle Valo if (err) {
5552be45b66SKalle Valo printk(KERN_ERR "%s: Error %d setting nickname\n",
5562be45b66SKalle Valo dev->name, err);
5572be45b66SKalle Valo return err;
5582be45b66SKalle Valo }
5592be45b66SKalle Valo
5602be45b66SKalle Valo /* Set AP density */
5612be45b66SKalle Valo if (priv->has_sensitivity) {
5622be45b66SKalle Valo err = hermes_write_wordrec(hw, USER_BAP,
5632be45b66SKalle Valo HERMES_RID_CNFSYSTEMSCALE,
5642be45b66SKalle Valo priv->ap_density);
5652be45b66SKalle Valo if (err) {
5662be45b66SKalle Valo printk(KERN_WARNING "%s: Error %d setting SYSTEMSCALE. "
5672be45b66SKalle Valo "Disabling sensitivity control\n",
5682be45b66SKalle Valo dev->name, err);
5692be45b66SKalle Valo
5702be45b66SKalle Valo priv->has_sensitivity = 0;
5712be45b66SKalle Valo }
5722be45b66SKalle Valo }
5732be45b66SKalle Valo
5742be45b66SKalle Valo /* Set RTS threshold */
5752be45b66SKalle Valo err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD,
5762be45b66SKalle Valo priv->rts_thresh);
5772be45b66SKalle Valo if (err) {
5782be45b66SKalle Valo printk(KERN_ERR "%s: Error %d setting RTS threshold\n",
5792be45b66SKalle Valo dev->name, err);
5802be45b66SKalle Valo return err;
5812be45b66SKalle Valo }
5822be45b66SKalle Valo
5832be45b66SKalle Valo /* Set fragmentation threshold or MWO robustness */
5842be45b66SKalle Valo if (priv->has_mwo)
5852be45b66SKalle Valo err = hermes_write_wordrec(hw, USER_BAP,
5862be45b66SKalle Valo HERMES_RID_CNFMWOROBUST_AGERE,
5872be45b66SKalle Valo priv->mwo_robust);
5882be45b66SKalle Valo else
5892be45b66SKalle Valo err = hermes_write_wordrec(hw, USER_BAP,
5902be45b66SKalle Valo HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
5912be45b66SKalle Valo priv->frag_thresh);
5922be45b66SKalle Valo if (err) {
5932be45b66SKalle Valo printk(KERN_ERR "%s: Error %d setting fragmentation\n",
5942be45b66SKalle Valo dev->name, err);
5952be45b66SKalle Valo return err;
5962be45b66SKalle Valo }
5972be45b66SKalle Valo
5982be45b66SKalle Valo /* Set bitrate */
5992be45b66SKalle Valo err = __orinoco_hw_set_bitrate(priv);
6002be45b66SKalle Valo if (err) {
6012be45b66SKalle Valo printk(KERN_ERR "%s: Error %d setting bitrate\n",
6022be45b66SKalle Valo dev->name, err);
6032be45b66SKalle Valo return err;
6042be45b66SKalle Valo }
6052be45b66SKalle Valo
6062be45b66SKalle Valo /* Set power management */
6072be45b66SKalle Valo if (priv->has_pm) {
6082be45b66SKalle Valo err = hermes_write_wordrec(hw, USER_BAP,
6092be45b66SKalle Valo HERMES_RID_CNFPMENABLED,
6102be45b66SKalle Valo priv->pm_on);
6112be45b66SKalle Valo if (err) {
6122be45b66SKalle Valo printk(KERN_ERR "%s: Error %d setting up PM\n",
6132be45b66SKalle Valo dev->name, err);
6142be45b66SKalle Valo return err;
6152be45b66SKalle Valo }
6162be45b66SKalle Valo
6172be45b66SKalle Valo err = hermes_write_wordrec(hw, USER_BAP,
6182be45b66SKalle Valo HERMES_RID_CNFMULTICASTRECEIVE,
6192be45b66SKalle Valo priv->pm_mcast);
6202be45b66SKalle Valo if (err) {
6212be45b66SKalle Valo printk(KERN_ERR "%s: Error %d setting up PM\n",
6222be45b66SKalle Valo dev->name, err);
6232be45b66SKalle Valo return err;
6242be45b66SKalle Valo }
6252be45b66SKalle Valo err = hermes_write_wordrec(hw, USER_BAP,
6262be45b66SKalle Valo HERMES_RID_CNFMAXSLEEPDURATION,
6272be45b66SKalle Valo priv->pm_period);
6282be45b66SKalle Valo if (err) {
6292be45b66SKalle Valo printk(KERN_ERR "%s: Error %d setting up PM\n",
6302be45b66SKalle Valo dev->name, err);
6312be45b66SKalle Valo return err;
6322be45b66SKalle Valo }
6332be45b66SKalle Valo err = hermes_write_wordrec(hw, USER_BAP,
6342be45b66SKalle Valo HERMES_RID_CNFPMHOLDOVERDURATION,
6352be45b66SKalle Valo priv->pm_timeout);
6362be45b66SKalle Valo if (err) {
6372be45b66SKalle Valo printk(KERN_ERR "%s: Error %d setting up PM\n",
6382be45b66SKalle Valo dev->name, err);
6392be45b66SKalle Valo return err;
6402be45b66SKalle Valo }
6412be45b66SKalle Valo }
6422be45b66SKalle Valo
6432be45b66SKalle Valo /* Set preamble - only for Symbol so far... */
6442be45b66SKalle Valo if (priv->has_preamble) {
6452be45b66SKalle Valo err = hermes_write_wordrec(hw, USER_BAP,
6462be45b66SKalle Valo HERMES_RID_CNFPREAMBLE_SYMBOL,
6472be45b66SKalle Valo priv->preamble);
6482be45b66SKalle Valo if (err) {
6492be45b66SKalle Valo printk(KERN_ERR "%s: Error %d setting preamble\n",
6502be45b66SKalle Valo dev->name, err);
6512be45b66SKalle Valo return err;
6522be45b66SKalle Valo }
6532be45b66SKalle Valo }
6542be45b66SKalle Valo
6552be45b66SKalle Valo /* Set up encryption */
6562be45b66SKalle Valo if (priv->has_wep || priv->has_wpa) {
6572be45b66SKalle Valo err = __orinoco_hw_setup_enc(priv);
6582be45b66SKalle Valo if (err) {
6592be45b66SKalle Valo printk(KERN_ERR "%s: Error %d activating encryption\n",
6602be45b66SKalle Valo dev->name, err);
6612be45b66SKalle Valo return err;
6622be45b66SKalle Valo }
6632be45b66SKalle Valo }
6642be45b66SKalle Valo
6652be45b66SKalle Valo if (priv->iw_mode == NL80211_IFTYPE_MONITOR) {
6662be45b66SKalle Valo /* Enable monitor mode */
6672be45b66SKalle Valo dev->type = ARPHRD_IEEE80211;
6682be45b66SKalle Valo err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST |
6692be45b66SKalle Valo HERMES_TEST_MONITOR, 0, NULL);
6702be45b66SKalle Valo } else {
6712be45b66SKalle Valo /* Disable monitor mode */
6722be45b66SKalle Valo dev->type = ARPHRD_ETHER;
6732be45b66SKalle Valo err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST |
6742be45b66SKalle Valo HERMES_TEST_STOP, 0, NULL);
6752be45b66SKalle Valo }
6762be45b66SKalle Valo if (err)
6772be45b66SKalle Valo return err;
6782be45b66SKalle Valo
6792be45b66SKalle Valo /* Reset promiscuity / multicast*/
6802be45b66SKalle Valo priv->promiscuous = 0;
6812be45b66SKalle Valo priv->mc_count = 0;
6822be45b66SKalle Valo
6832be45b66SKalle Valo /* Record mode change */
6842be45b66SKalle Valo wdev->iftype = priv->iw_mode;
6852be45b66SKalle Valo
6862be45b66SKalle Valo return 0;
6872be45b66SKalle Valo }
6882be45b66SKalle Valo
6892be45b66SKalle Valo /* Get tsc from the firmware */
orinoco_hw_get_tkip_iv(struct orinoco_private * priv,int key,u8 * tsc)6902be45b66SKalle Valo int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key, u8 *tsc)
6912be45b66SKalle Valo {
6922be45b66SKalle Valo struct hermes *hw = &priv->hw;
6932be45b66SKalle Valo int err = 0;
6942be45b66SKalle Valo u8 tsc_arr[4][ORINOCO_SEQ_LEN];
6952be45b66SKalle Valo
6962be45b66SKalle Valo if ((key < 0) || (key >= 4))
6972be45b66SKalle Valo return -EINVAL;
6982be45b66SKalle Valo
6992be45b66SKalle Valo err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_TKIP_IV,
7002be45b66SKalle Valo sizeof(tsc_arr), NULL, &tsc_arr);
7012be45b66SKalle Valo if (!err)
7022be45b66SKalle Valo memcpy(tsc, &tsc_arr[key][0], sizeof(tsc_arr[0]));
7032be45b66SKalle Valo
7042be45b66SKalle Valo return err;
7052be45b66SKalle Valo }
7062be45b66SKalle Valo
__orinoco_hw_set_bitrate(struct orinoco_private * priv)7072be45b66SKalle Valo int __orinoco_hw_set_bitrate(struct orinoco_private *priv)
7082be45b66SKalle Valo {
7092be45b66SKalle Valo struct hermes *hw = &priv->hw;
7102be45b66SKalle Valo int ratemode = priv->bitratemode;
7112be45b66SKalle Valo int err = 0;
7122be45b66SKalle Valo
7132be45b66SKalle Valo if (ratemode >= BITRATE_TABLE_SIZE) {
7142be45b66SKalle Valo printk(KERN_ERR "%s: BUG: Invalid bitrate mode %d\n",
7152be45b66SKalle Valo priv->ndev->name, ratemode);
7162be45b66SKalle Valo return -EINVAL;
7172be45b66SKalle Valo }
7182be45b66SKalle Valo
7192be45b66SKalle Valo switch (priv->firmware_type) {
7202be45b66SKalle Valo case FIRMWARE_TYPE_AGERE:
7212be45b66SKalle Valo err = hermes_write_wordrec(hw, USER_BAP,
7222be45b66SKalle Valo HERMES_RID_CNFTXRATECONTROL,
7232be45b66SKalle Valo bitrate_table[ratemode].agere_txratectrl);
7242be45b66SKalle Valo break;
7252be45b66SKalle Valo case FIRMWARE_TYPE_INTERSIL:
7262be45b66SKalle Valo case FIRMWARE_TYPE_SYMBOL:
7272be45b66SKalle Valo err = hermes_write_wordrec(hw, USER_BAP,
7282be45b66SKalle Valo HERMES_RID_CNFTXRATECONTROL,
7292be45b66SKalle Valo bitrate_table[ratemode].intersil_txratectrl);
7302be45b66SKalle Valo break;
7312be45b66SKalle Valo default:
7322be45b66SKalle Valo BUG();
7332be45b66SKalle Valo }
7342be45b66SKalle Valo
7352be45b66SKalle Valo return err;
7362be45b66SKalle Valo }
7372be45b66SKalle Valo
orinoco_hw_get_act_bitrate(struct orinoco_private * priv,int * bitrate)7382be45b66SKalle Valo int orinoco_hw_get_act_bitrate(struct orinoco_private *priv, int *bitrate)
7392be45b66SKalle Valo {
7402be45b66SKalle Valo struct hermes *hw = &priv->hw;
7412be45b66SKalle Valo int i;
7422be45b66SKalle Valo int err = 0;
7432be45b66SKalle Valo u16 val;
7442be45b66SKalle Valo
7452be45b66SKalle Valo err = hermes_read_wordrec(hw, USER_BAP,
7462be45b66SKalle Valo HERMES_RID_CURRENTTXRATE, &val);
7472be45b66SKalle Valo if (err)
7482be45b66SKalle Valo return err;
7492be45b66SKalle Valo
7502be45b66SKalle Valo switch (priv->firmware_type) {
7512be45b66SKalle Valo case FIRMWARE_TYPE_AGERE: /* Lucent style rate */
7522be45b66SKalle Valo /* Note : in Lucent firmware, the return value of
7532be45b66SKalle Valo * HERMES_RID_CURRENTTXRATE is the bitrate in Mb/s,
7542be45b66SKalle Valo * and therefore is totally different from the
7552be45b66SKalle Valo * encoding of HERMES_RID_CNFTXRATECONTROL.
7562be45b66SKalle Valo * Don't forget that 6Mb/s is really 5.5Mb/s */
7572be45b66SKalle Valo if (val == 6)
7582be45b66SKalle Valo *bitrate = 5500000;
7592be45b66SKalle Valo else
7602be45b66SKalle Valo *bitrate = val * 1000000;
7612be45b66SKalle Valo break;
7622be45b66SKalle Valo case FIRMWARE_TYPE_INTERSIL: /* Intersil style rate */
7632be45b66SKalle Valo case FIRMWARE_TYPE_SYMBOL: /* Symbol style rate */
7642be45b66SKalle Valo for (i = 0; i < BITRATE_TABLE_SIZE; i++)
7652be45b66SKalle Valo if (bitrate_table[i].intersil_txratectrl == val) {
7662be45b66SKalle Valo *bitrate = bitrate_table[i].bitrate * 100000;
7672be45b66SKalle Valo break;
7682be45b66SKalle Valo }
7692be45b66SKalle Valo
7702be45b66SKalle Valo if (i >= BITRATE_TABLE_SIZE) {
7712be45b66SKalle Valo printk(KERN_INFO "%s: Unable to determine current bitrate (0x%04hx)\n",
7722be45b66SKalle Valo priv->ndev->name, val);
7732be45b66SKalle Valo err = -EIO;
7742be45b66SKalle Valo }
7752be45b66SKalle Valo
7762be45b66SKalle Valo break;
7772be45b66SKalle Valo default:
7782be45b66SKalle Valo BUG();
7792be45b66SKalle Valo }
7802be45b66SKalle Valo
7812be45b66SKalle Valo return err;
7822be45b66SKalle Valo }
7832be45b66SKalle Valo
7842be45b66SKalle Valo /* Set fixed AP address */
__orinoco_hw_set_wap(struct orinoco_private * priv)7852be45b66SKalle Valo int __orinoco_hw_set_wap(struct orinoco_private *priv)
7862be45b66SKalle Valo {
7872be45b66SKalle Valo int roaming_flag;
7882be45b66SKalle Valo int err = 0;
7892be45b66SKalle Valo struct hermes *hw = &priv->hw;
7902be45b66SKalle Valo
7912be45b66SKalle Valo switch (priv->firmware_type) {
7922be45b66SKalle Valo case FIRMWARE_TYPE_AGERE:
7932be45b66SKalle Valo /* not supported */
7942be45b66SKalle Valo break;
7952be45b66SKalle Valo case FIRMWARE_TYPE_INTERSIL:
7962be45b66SKalle Valo if (priv->bssid_fixed)
7972be45b66SKalle Valo roaming_flag = 2;
7982be45b66SKalle Valo else
7992be45b66SKalle Valo roaming_flag = 1;
8002be45b66SKalle Valo
8012be45b66SKalle Valo err = hermes_write_wordrec(hw, USER_BAP,
8022be45b66SKalle Valo HERMES_RID_CNFROAMINGMODE,
8032be45b66SKalle Valo roaming_flag);
8042be45b66SKalle Valo break;
8052be45b66SKalle Valo case FIRMWARE_TYPE_SYMBOL:
8062be45b66SKalle Valo err = HERMES_WRITE_RECORD(hw, USER_BAP,
8072be45b66SKalle Valo HERMES_RID_CNFMANDATORYBSSID_SYMBOL,
8082be45b66SKalle Valo &priv->desired_bssid);
8092be45b66SKalle Valo break;
8102be45b66SKalle Valo }
8112be45b66SKalle Valo return err;
8122be45b66SKalle Valo }
8132be45b66SKalle Valo
8142be45b66SKalle Valo /* Change the WEP keys and/or the current keys. Can be called
8152be45b66SKalle Valo * either from __orinoco_hw_setup_enc() or directly from
8162be45b66SKalle Valo * orinoco_ioctl_setiwencode(). In the later case the association
8172be45b66SKalle Valo * with the AP is not broken (if the firmware can handle it),
8182be45b66SKalle Valo * which is needed for 802.1x implementations. */
__orinoco_hw_setup_wepkeys(struct orinoco_private * priv)8192be45b66SKalle Valo int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv)
8202be45b66SKalle Valo {
8212be45b66SKalle Valo struct hermes *hw = &priv->hw;
8222be45b66SKalle Valo int err = 0;
8232be45b66SKalle Valo int i;
8242be45b66SKalle Valo
8252be45b66SKalle Valo switch (priv->firmware_type) {
8262be45b66SKalle Valo case FIRMWARE_TYPE_AGERE:
8272be45b66SKalle Valo {
8282be45b66SKalle Valo struct orinoco_key keys[ORINOCO_MAX_KEYS];
8292be45b66SKalle Valo
8302be45b66SKalle Valo memset(&keys, 0, sizeof(keys));
8312be45b66SKalle Valo for (i = 0; i < ORINOCO_MAX_KEYS; i++) {
8322be45b66SKalle Valo int len = min(priv->keys[i].key_len,
8332be45b66SKalle Valo ORINOCO_MAX_KEY_SIZE);
8342be45b66SKalle Valo memcpy(&keys[i].data, priv->keys[i].key, len);
8352be45b66SKalle Valo if (len > SMALL_KEY_SIZE)
8362be45b66SKalle Valo keys[i].len = cpu_to_le16(LARGE_KEY_SIZE);
8372be45b66SKalle Valo else if (len > 0)
8382be45b66SKalle Valo keys[i].len = cpu_to_le16(SMALL_KEY_SIZE);
8392be45b66SKalle Valo else
8402be45b66SKalle Valo keys[i].len = cpu_to_le16(0);
8412be45b66SKalle Valo }
8422be45b66SKalle Valo
8432be45b66SKalle Valo err = HERMES_WRITE_RECORD(hw, USER_BAP,
8442be45b66SKalle Valo HERMES_RID_CNFWEPKEYS_AGERE,
8452be45b66SKalle Valo &keys);
8462be45b66SKalle Valo if (err)
8472be45b66SKalle Valo return err;
8482be45b66SKalle Valo err = hermes_write_wordrec(hw, USER_BAP,
8492be45b66SKalle Valo HERMES_RID_CNFTXKEY_AGERE,
8502be45b66SKalle Valo priv->tx_key);
8512be45b66SKalle Valo if (err)
8522be45b66SKalle Valo return err;
8532be45b66SKalle Valo break;
8542be45b66SKalle Valo }
8552be45b66SKalle Valo case FIRMWARE_TYPE_INTERSIL:
8562be45b66SKalle Valo case FIRMWARE_TYPE_SYMBOL:
8572be45b66SKalle Valo {
8582be45b66SKalle Valo int keylen;
8592be45b66SKalle Valo
8602be45b66SKalle Valo /* Force uniform key length to work around
8612be45b66SKalle Valo * firmware bugs */
8622be45b66SKalle Valo keylen = priv->keys[priv->tx_key].key_len;
8632be45b66SKalle Valo
8642be45b66SKalle Valo if (keylen > LARGE_KEY_SIZE) {
8652be45b66SKalle Valo printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n",
8662be45b66SKalle Valo priv->ndev->name, priv->tx_key, keylen);
8672be45b66SKalle Valo return -E2BIG;
8682be45b66SKalle Valo } else if (keylen > SMALL_KEY_SIZE)
8692be45b66SKalle Valo keylen = LARGE_KEY_SIZE;
8702be45b66SKalle Valo else if (keylen > 0)
8712be45b66SKalle Valo keylen = SMALL_KEY_SIZE;
8722be45b66SKalle Valo else
8732be45b66SKalle Valo keylen = 0;
8742be45b66SKalle Valo
8752be45b66SKalle Valo /* Write all 4 keys */
8762be45b66SKalle Valo for (i = 0; i < ORINOCO_MAX_KEYS; i++) {
8772be45b66SKalle Valo u8 key[LARGE_KEY_SIZE] = { 0 };
8782be45b66SKalle Valo
8792be45b66SKalle Valo memcpy(key, priv->keys[i].key,
8802be45b66SKalle Valo priv->keys[i].key_len);
8812be45b66SKalle Valo
8822be45b66SKalle Valo err = hw->ops->write_ltv(hw, USER_BAP,
8832be45b66SKalle Valo HERMES_RID_CNFDEFAULTKEY0 + i,
8842be45b66SKalle Valo HERMES_BYTES_TO_RECLEN(keylen),
8852be45b66SKalle Valo key);
8862be45b66SKalle Valo if (err)
8872be45b66SKalle Valo return err;
8882be45b66SKalle Valo }
8892be45b66SKalle Valo
8902be45b66SKalle Valo /* Write the index of the key used in transmission */
8912be45b66SKalle Valo err = hermes_write_wordrec(hw, USER_BAP,
8922be45b66SKalle Valo HERMES_RID_CNFWEPDEFAULTKEYID,
8932be45b66SKalle Valo priv->tx_key);
8942be45b66SKalle Valo if (err)
8952be45b66SKalle Valo return err;
8962be45b66SKalle Valo }
8972be45b66SKalle Valo break;
8982be45b66SKalle Valo }
8992be45b66SKalle Valo
9002be45b66SKalle Valo return 0;
9012be45b66SKalle Valo }
9022be45b66SKalle Valo
__orinoco_hw_setup_enc(struct orinoco_private * priv)9032be45b66SKalle Valo int __orinoco_hw_setup_enc(struct orinoco_private *priv)
9042be45b66SKalle Valo {
9052be45b66SKalle Valo struct hermes *hw = &priv->hw;
9062be45b66SKalle Valo int err = 0;
9072be45b66SKalle Valo int master_wep_flag;
9082be45b66SKalle Valo int auth_flag;
9092be45b66SKalle Valo int enc_flag;
9102be45b66SKalle Valo
9112be45b66SKalle Valo /* Setup WEP keys */
9122be45b66SKalle Valo if (priv->encode_alg == ORINOCO_ALG_WEP)
9132be45b66SKalle Valo __orinoco_hw_setup_wepkeys(priv);
9142be45b66SKalle Valo
9152be45b66SKalle Valo if (priv->wep_restrict)
9162be45b66SKalle Valo auth_flag = HERMES_AUTH_SHARED_KEY;
9172be45b66SKalle Valo else
9182be45b66SKalle Valo auth_flag = HERMES_AUTH_OPEN;
9192be45b66SKalle Valo
9202be45b66SKalle Valo if (priv->wpa_enabled)
9212be45b66SKalle Valo enc_flag = 2;
9222be45b66SKalle Valo else if (priv->encode_alg == ORINOCO_ALG_WEP)
9232be45b66SKalle Valo enc_flag = 1;
9242be45b66SKalle Valo else
9252be45b66SKalle Valo enc_flag = 0;
9262be45b66SKalle Valo
9272be45b66SKalle Valo switch (priv->firmware_type) {
9282be45b66SKalle Valo case FIRMWARE_TYPE_AGERE: /* Agere style WEP */
9292be45b66SKalle Valo if (priv->encode_alg == ORINOCO_ALG_WEP) {
9302be45b66SKalle Valo /* Enable the shared-key authentication. */
9312be45b66SKalle Valo err = hermes_write_wordrec(hw, USER_BAP,
9322be45b66SKalle Valo HERMES_RID_CNFAUTHENTICATION_AGERE,
9332be45b66SKalle Valo auth_flag);
934*1e346cbbSAlexey Kodanev if (err)
935*1e346cbbSAlexey Kodanev return err;
9362be45b66SKalle Valo }
9372be45b66SKalle Valo err = hermes_write_wordrec(hw, USER_BAP,
9382be45b66SKalle Valo HERMES_RID_CNFWEPENABLED_AGERE,
9392be45b66SKalle Valo enc_flag);
9402be45b66SKalle Valo if (err)
9412be45b66SKalle Valo return err;
9422be45b66SKalle Valo
9432be45b66SKalle Valo if (priv->has_wpa) {
9442be45b66SKalle Valo /* Set WPA key management */
9452be45b66SKalle Valo err = hermes_write_wordrec(hw, USER_BAP,
9462be45b66SKalle Valo HERMES_RID_CNFSETWPAAUTHMGMTSUITE_AGERE,
9472be45b66SKalle Valo priv->key_mgmt);
9482be45b66SKalle Valo if (err)
9492be45b66SKalle Valo return err;
9502be45b66SKalle Valo }
9512be45b66SKalle Valo
9522be45b66SKalle Valo break;
9532be45b66SKalle Valo
9542be45b66SKalle Valo case FIRMWARE_TYPE_INTERSIL: /* Intersil style WEP */
9552be45b66SKalle Valo case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */
9562be45b66SKalle Valo if (priv->encode_alg == ORINOCO_ALG_WEP) {
9572be45b66SKalle Valo if (priv->wep_restrict ||
9582be45b66SKalle Valo (priv->firmware_type == FIRMWARE_TYPE_SYMBOL))
9592be45b66SKalle Valo master_wep_flag = HERMES_WEP_PRIVACY_INVOKED |
9602be45b66SKalle Valo HERMES_WEP_EXCL_UNENCRYPTED;
9612be45b66SKalle Valo else
9622be45b66SKalle Valo master_wep_flag = HERMES_WEP_PRIVACY_INVOKED;
9632be45b66SKalle Valo
9642be45b66SKalle Valo err = hermes_write_wordrec(hw, USER_BAP,
9652be45b66SKalle Valo HERMES_RID_CNFAUTHENTICATION,
9662be45b66SKalle Valo auth_flag);
9672be45b66SKalle Valo if (err)
9682be45b66SKalle Valo return err;
9692be45b66SKalle Valo } else
9702be45b66SKalle Valo master_wep_flag = 0;
9712be45b66SKalle Valo
9722be45b66SKalle Valo if (priv->iw_mode == NL80211_IFTYPE_MONITOR)
9732be45b66SKalle Valo master_wep_flag |= HERMES_WEP_HOST_DECRYPT;
9742be45b66SKalle Valo
9752be45b66SKalle Valo /* Master WEP setting : on/off */
9762be45b66SKalle Valo err = hermes_write_wordrec(hw, USER_BAP,
9772be45b66SKalle Valo HERMES_RID_CNFWEPFLAGS_INTERSIL,
9782be45b66SKalle Valo master_wep_flag);
9792be45b66SKalle Valo if (err)
9802be45b66SKalle Valo return err;
9812be45b66SKalle Valo
9822be45b66SKalle Valo break;
9832be45b66SKalle Valo }
9842be45b66SKalle Valo
9852be45b66SKalle Valo return 0;
9862be45b66SKalle Valo }
9872be45b66SKalle Valo
9882be45b66SKalle Valo /* key must be 32 bytes, including the tx and rx MIC keys.
9892be45b66SKalle Valo * rsc must be NULL or up to 8 bytes
9902be45b66SKalle Valo * tsc must be NULL or up to 8 bytes
9912be45b66SKalle Valo */
__orinoco_hw_set_tkip_key(struct orinoco_private * priv,int key_idx,int set_tx,const u8 * key,size_t key_len,const u8 * rsc,size_t rsc_len,const u8 * tsc,size_t tsc_len)9922be45b66SKalle Valo int __orinoco_hw_set_tkip_key(struct orinoco_private *priv, int key_idx,
99370ca8441SKees Cook int set_tx, const u8 *key, size_t key_len,
99470ca8441SKees Cook const u8 *rsc, size_t rsc_len,
99570ca8441SKees Cook const u8 *tsc, size_t tsc_len)
9962be45b66SKalle Valo {
9972be45b66SKalle Valo struct {
9982be45b66SKalle Valo __le16 idx;
9992be45b66SKalle Valo u8 rsc[ORINOCO_SEQ_LEN];
100070ca8441SKees Cook struct {
10012be45b66SKalle Valo u8 key[TKIP_KEYLEN];
10022be45b66SKalle Valo u8 tx_mic[MIC_KEYLEN];
10032be45b66SKalle Valo u8 rx_mic[MIC_KEYLEN];
100470ca8441SKees Cook } tkip;
10052be45b66SKalle Valo u8 tsc[ORINOCO_SEQ_LEN];
10062be45b66SKalle Valo } __packed buf;
10072be45b66SKalle Valo struct hermes *hw = &priv->hw;
10082be45b66SKalle Valo int ret;
10092be45b66SKalle Valo int err;
10102be45b66SKalle Valo int k;
10112be45b66SKalle Valo u16 xmitting;
10122be45b66SKalle Valo
10132be45b66SKalle Valo key_idx &= 0x3;
10142be45b66SKalle Valo
10152be45b66SKalle Valo if (set_tx)
10162be45b66SKalle Valo key_idx |= 0x8000;
10172be45b66SKalle Valo
10182be45b66SKalle Valo buf.idx = cpu_to_le16(key_idx);
101970ca8441SKees Cook if (key_len != sizeof(buf.tkip))
102070ca8441SKees Cook return -EINVAL;
102170ca8441SKees Cook memcpy(&buf.tkip, key, sizeof(buf.tkip));
10222be45b66SKalle Valo
10232be45b66SKalle Valo if (rsc_len > sizeof(buf.rsc))
10242be45b66SKalle Valo rsc_len = sizeof(buf.rsc);
10252be45b66SKalle Valo
10262be45b66SKalle Valo if (tsc_len > sizeof(buf.tsc))
10272be45b66SKalle Valo tsc_len = sizeof(buf.tsc);
10282be45b66SKalle Valo
10292be45b66SKalle Valo memset(buf.rsc, 0, sizeof(buf.rsc));
10302be45b66SKalle Valo memset(buf.tsc, 0, sizeof(buf.tsc));
10312be45b66SKalle Valo
10322be45b66SKalle Valo if (rsc != NULL)
10332be45b66SKalle Valo memcpy(buf.rsc, rsc, rsc_len);
10342be45b66SKalle Valo
10352be45b66SKalle Valo if (tsc != NULL)
10362be45b66SKalle Valo memcpy(buf.tsc, tsc, tsc_len);
10372be45b66SKalle Valo else
10382be45b66SKalle Valo buf.tsc[4] = 0x10;
10392be45b66SKalle Valo
10402be45b66SKalle Valo /* Wait up to 100ms for tx queue to empty */
10412be45b66SKalle Valo for (k = 100; k > 0; k--) {
10422be45b66SKalle Valo udelay(1000);
10432be45b66SKalle Valo ret = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_TXQUEUEEMPTY,
10442be45b66SKalle Valo &xmitting);
10452be45b66SKalle Valo if (ret || !xmitting)
10462be45b66SKalle Valo break;
10472be45b66SKalle Valo }
10482be45b66SKalle Valo
10492be45b66SKalle Valo if (k == 0)
10502be45b66SKalle Valo ret = -ETIMEDOUT;
10512be45b66SKalle Valo
10522be45b66SKalle Valo err = HERMES_WRITE_RECORD(hw, USER_BAP,
10532be45b66SKalle Valo HERMES_RID_CNFADDDEFAULTTKIPKEY_AGERE,
10542be45b66SKalle Valo &buf);
10552be45b66SKalle Valo
10562be45b66SKalle Valo return ret ? ret : err;
10572be45b66SKalle Valo }
10582be45b66SKalle Valo
orinoco_clear_tkip_key(struct orinoco_private * priv,int key_idx)10592be45b66SKalle Valo int orinoco_clear_tkip_key(struct orinoco_private *priv, int key_idx)
10602be45b66SKalle Valo {
10612be45b66SKalle Valo struct hermes *hw = &priv->hw;
10622be45b66SKalle Valo int err;
10632be45b66SKalle Valo
10642be45b66SKalle Valo err = hermes_write_wordrec(hw, USER_BAP,
10652be45b66SKalle Valo HERMES_RID_CNFREMDEFAULTTKIPKEY_AGERE,
10662be45b66SKalle Valo key_idx);
10672be45b66SKalle Valo if (err)
10682be45b66SKalle Valo printk(KERN_WARNING "%s: Error %d clearing TKIP key %d\n",
10692be45b66SKalle Valo priv->ndev->name, err, key_idx);
10702be45b66SKalle Valo return err;
10712be45b66SKalle Valo }
10722be45b66SKalle Valo
__orinoco_hw_set_multicast_list(struct orinoco_private * priv,struct net_device * dev,int mc_count,int promisc)10732be45b66SKalle Valo int __orinoco_hw_set_multicast_list(struct orinoco_private *priv,
10742be45b66SKalle Valo struct net_device *dev,
10752be45b66SKalle Valo int mc_count, int promisc)
10762be45b66SKalle Valo {
10772be45b66SKalle Valo struct hermes *hw = &priv->hw;
10782be45b66SKalle Valo int err = 0;
10792be45b66SKalle Valo
10802be45b66SKalle Valo if (promisc != priv->promiscuous) {
10812be45b66SKalle Valo err = hermes_write_wordrec(hw, USER_BAP,
10822be45b66SKalle Valo HERMES_RID_CNFPROMISCUOUSMODE,
10832be45b66SKalle Valo promisc);
10842be45b66SKalle Valo if (err) {
10852be45b66SKalle Valo printk(KERN_ERR "%s: Error %d setting PROMISCUOUSMODE to 1.\n",
10862be45b66SKalle Valo priv->ndev->name, err);
10872be45b66SKalle Valo } else
10882be45b66SKalle Valo priv->promiscuous = promisc;
10892be45b66SKalle Valo }
10902be45b66SKalle Valo
10912be45b66SKalle Valo /* If we're not in promiscuous mode, then we need to set the
10922be45b66SKalle Valo * group address if either we want to multicast, or if we were
10932be45b66SKalle Valo * multicasting and want to stop */
10942be45b66SKalle Valo if (!promisc && (mc_count || priv->mc_count)) {
10952be45b66SKalle Valo struct netdev_hw_addr *ha;
10962be45b66SKalle Valo struct hermes_multicast mclist;
10972be45b66SKalle Valo int i = 0;
10982be45b66SKalle Valo
10992be45b66SKalle Valo netdev_for_each_mc_addr(ha, dev) {
11002be45b66SKalle Valo if (i == mc_count)
11012be45b66SKalle Valo break;
11022be45b66SKalle Valo memcpy(mclist.addr[i++], ha->addr, ETH_ALEN);
11032be45b66SKalle Valo }
11042be45b66SKalle Valo
11052be45b66SKalle Valo err = hw->ops->write_ltv(hw, USER_BAP,
11062be45b66SKalle Valo HERMES_RID_CNFGROUPADDRESSES,
11072be45b66SKalle Valo HERMES_BYTES_TO_RECLEN(mc_count * ETH_ALEN),
11082be45b66SKalle Valo &mclist);
11092be45b66SKalle Valo if (err)
11102be45b66SKalle Valo printk(KERN_ERR "%s: Error %d setting multicast list.\n",
11112be45b66SKalle Valo priv->ndev->name, err);
11122be45b66SKalle Valo else
11132be45b66SKalle Valo priv->mc_count = mc_count;
11142be45b66SKalle Valo }
11152be45b66SKalle Valo return err;
11162be45b66SKalle Valo }
11172be45b66SKalle Valo
11182be45b66SKalle Valo /* Return : < 0 -> error code ; >= 0 -> length */
orinoco_hw_get_essid(struct orinoco_private * priv,int * active,char buf[IW_ESSID_MAX_SIZE+1])11192be45b66SKalle Valo int orinoco_hw_get_essid(struct orinoco_private *priv, int *active,
11202be45b66SKalle Valo char buf[IW_ESSID_MAX_SIZE + 1])
11212be45b66SKalle Valo {
11222be45b66SKalle Valo struct hermes *hw = &priv->hw;
11232be45b66SKalle Valo int err = 0;
11242be45b66SKalle Valo struct hermes_idstring essidbuf;
11252be45b66SKalle Valo char *p = (char *)(&essidbuf.val);
11262be45b66SKalle Valo int len;
11272be45b66SKalle Valo unsigned long flags;
11282be45b66SKalle Valo
11292be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
11302be45b66SKalle Valo return -EBUSY;
11312be45b66SKalle Valo
11322be45b66SKalle Valo if (strlen(priv->desired_essid) > 0) {
11332be45b66SKalle Valo /* We read the desired SSID from the hardware rather
11342be45b66SKalle Valo than from priv->desired_essid, just in case the
11352be45b66SKalle Valo firmware is allowed to change it on us. I'm not
11362be45b66SKalle Valo sure about this */
11372be45b66SKalle Valo /* My guess is that the OWNSSID should always be whatever
11382be45b66SKalle Valo * we set to the card, whereas CURRENT_SSID is the one that
11392be45b66SKalle Valo * may change... - Jean II */
11402be45b66SKalle Valo u16 rid;
11412be45b66SKalle Valo
11422be45b66SKalle Valo *active = 1;
11432be45b66SKalle Valo
11442be45b66SKalle Valo rid = (priv->port_type == 3) ? HERMES_RID_CNFOWNSSID :
11452be45b66SKalle Valo HERMES_RID_CNFDESIREDSSID;
11462be45b66SKalle Valo
11472be45b66SKalle Valo err = hw->ops->read_ltv(hw, USER_BAP, rid, sizeof(essidbuf),
11482be45b66SKalle Valo NULL, &essidbuf);
11492be45b66SKalle Valo if (err)
11502be45b66SKalle Valo goto fail_unlock;
11512be45b66SKalle Valo } else {
11522be45b66SKalle Valo *active = 0;
11532be45b66SKalle Valo
11542be45b66SKalle Valo err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID,
11552be45b66SKalle Valo sizeof(essidbuf), NULL, &essidbuf);
11562be45b66SKalle Valo if (err)
11572be45b66SKalle Valo goto fail_unlock;
11582be45b66SKalle Valo }
11592be45b66SKalle Valo
11602be45b66SKalle Valo len = le16_to_cpu(essidbuf.len);
11612be45b66SKalle Valo BUG_ON(len > IW_ESSID_MAX_SIZE);
11622be45b66SKalle Valo
11632be45b66SKalle Valo memset(buf, 0, IW_ESSID_MAX_SIZE);
11642be45b66SKalle Valo memcpy(buf, p, len);
11652be45b66SKalle Valo err = len;
11662be45b66SKalle Valo
11672be45b66SKalle Valo fail_unlock:
11682be45b66SKalle Valo orinoco_unlock(priv, &flags);
11692be45b66SKalle Valo
11702be45b66SKalle Valo return err;
11712be45b66SKalle Valo }
11722be45b66SKalle Valo
orinoco_hw_get_freq(struct orinoco_private * priv)11732be45b66SKalle Valo int orinoco_hw_get_freq(struct orinoco_private *priv)
11742be45b66SKalle Valo {
11752be45b66SKalle Valo struct hermes *hw = &priv->hw;
11762be45b66SKalle Valo int err = 0;
11772be45b66SKalle Valo u16 channel;
11782be45b66SKalle Valo int freq = 0;
11792be45b66SKalle Valo unsigned long flags;
11802be45b66SKalle Valo
11812be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
11822be45b66SKalle Valo return -EBUSY;
11832be45b66SKalle Valo
11842be45b66SKalle Valo err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENTCHANNEL,
11852be45b66SKalle Valo &channel);
11862be45b66SKalle Valo if (err)
11872be45b66SKalle Valo goto out;
11882be45b66SKalle Valo
11892be45b66SKalle Valo /* Intersil firmware 1.3.5 returns 0 when the interface is down */
11902be45b66SKalle Valo if (channel == 0) {
11912be45b66SKalle Valo err = -EBUSY;
11922be45b66SKalle Valo goto out;
11932be45b66SKalle Valo }
11942be45b66SKalle Valo
11952be45b66SKalle Valo if ((channel < 1) || (channel > NUM_CHANNELS)) {
11962be45b66SKalle Valo printk(KERN_WARNING "%s: Channel out of range (%d)!\n",
11972be45b66SKalle Valo priv->ndev->name, channel);
11982be45b66SKalle Valo err = -EBUSY;
11992be45b66SKalle Valo goto out;
12002be45b66SKalle Valo
12012be45b66SKalle Valo }
120257fbcce3SJohannes Berg freq = ieee80211_channel_to_frequency(channel, NL80211_BAND_2GHZ);
12032be45b66SKalle Valo
12042be45b66SKalle Valo out:
12052be45b66SKalle Valo orinoco_unlock(priv, &flags);
12062be45b66SKalle Valo
12072be45b66SKalle Valo if (err > 0)
12082be45b66SKalle Valo err = -EBUSY;
12092be45b66SKalle Valo return err ? err : freq;
12102be45b66SKalle Valo }
12112be45b66SKalle Valo
orinoco_hw_get_bitratelist(struct orinoco_private * priv,int * numrates,s32 * rates,int max)12122be45b66SKalle Valo int orinoco_hw_get_bitratelist(struct orinoco_private *priv,
12132be45b66SKalle Valo int *numrates, s32 *rates, int max)
12142be45b66SKalle Valo {
12152be45b66SKalle Valo struct hermes *hw = &priv->hw;
12162be45b66SKalle Valo struct hermes_idstring list;
12172be45b66SKalle Valo unsigned char *p = (unsigned char *)&list.val;
12182be45b66SKalle Valo int err = 0;
12192be45b66SKalle Valo int num;
12202be45b66SKalle Valo int i;
12212be45b66SKalle Valo unsigned long flags;
12222be45b66SKalle Valo
12232be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
12242be45b66SKalle Valo return -EBUSY;
12252be45b66SKalle Valo
12262be45b66SKalle Valo err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES,
12272be45b66SKalle Valo sizeof(list), NULL, &list);
12282be45b66SKalle Valo orinoco_unlock(priv, &flags);
12292be45b66SKalle Valo
12302be45b66SKalle Valo if (err)
12312be45b66SKalle Valo return err;
12322be45b66SKalle Valo
12332be45b66SKalle Valo num = le16_to_cpu(list.len);
12342be45b66SKalle Valo *numrates = num;
12352be45b66SKalle Valo num = min(num, max);
12362be45b66SKalle Valo
12372be45b66SKalle Valo for (i = 0; i < num; i++)
12382be45b66SKalle Valo rates[i] = (p[i] & 0x7f) * 500000; /* convert to bps */
12392be45b66SKalle Valo
12402be45b66SKalle Valo return 0;
12412be45b66SKalle Valo }
12422be45b66SKalle Valo
orinoco_hw_trigger_scan(struct orinoco_private * priv,const struct cfg80211_ssid * ssid)12432be45b66SKalle Valo int orinoco_hw_trigger_scan(struct orinoco_private *priv,
12442be45b66SKalle Valo const struct cfg80211_ssid *ssid)
12452be45b66SKalle Valo {
12462be45b66SKalle Valo struct net_device *dev = priv->ndev;
12472be45b66SKalle Valo struct hermes *hw = &priv->hw;
12482be45b66SKalle Valo unsigned long flags;
12492be45b66SKalle Valo int err = 0;
12502be45b66SKalle Valo
12512be45b66SKalle Valo if (orinoco_lock(priv, &flags) != 0)
12522be45b66SKalle Valo return -EBUSY;
12532be45b66SKalle Valo
12542be45b66SKalle Valo /* Scanning with port 0 disabled would fail */
12552be45b66SKalle Valo if (!netif_running(dev)) {
12562be45b66SKalle Valo err = -ENETDOWN;
12572be45b66SKalle Valo goto out;
12582be45b66SKalle Valo }
12592be45b66SKalle Valo
12602be45b66SKalle Valo /* In monitor mode, the scan results are always empty.
12612be45b66SKalle Valo * Probe responses are passed to the driver as received
12622be45b66SKalle Valo * frames and could be processed in software. */
12632be45b66SKalle Valo if (priv->iw_mode == NL80211_IFTYPE_MONITOR) {
12642be45b66SKalle Valo err = -EOPNOTSUPP;
12652be45b66SKalle Valo goto out;
12662be45b66SKalle Valo }
12672be45b66SKalle Valo
12682be45b66SKalle Valo if (priv->has_hostscan) {
12692be45b66SKalle Valo switch (priv->firmware_type) {
12702be45b66SKalle Valo case FIRMWARE_TYPE_SYMBOL:
12712be45b66SKalle Valo err = hermes_write_wordrec(hw, USER_BAP,
12722be45b66SKalle Valo HERMES_RID_CNFHOSTSCAN_SYMBOL,
12732be45b66SKalle Valo HERMES_HOSTSCAN_SYMBOL_ONCE |
12742be45b66SKalle Valo HERMES_HOSTSCAN_SYMBOL_BCAST);
12752be45b66SKalle Valo break;
12762be45b66SKalle Valo case FIRMWARE_TYPE_INTERSIL: {
12772be45b66SKalle Valo __le16 req[3];
12782be45b66SKalle Valo
12792be45b66SKalle Valo req[0] = cpu_to_le16(0x3fff); /* All channels */
12802be45b66SKalle Valo req[1] = cpu_to_le16(0x0001); /* rate 1 Mbps */
12812be45b66SKalle Valo req[2] = 0; /* Any ESSID */
12822be45b66SKalle Valo err = HERMES_WRITE_RECORD(hw, USER_BAP,
12832be45b66SKalle Valo HERMES_RID_CNFHOSTSCAN, &req);
12842be45b66SKalle Valo break;
12852be45b66SKalle Valo }
12862be45b66SKalle Valo case FIRMWARE_TYPE_AGERE:
12872be45b66SKalle Valo if (ssid->ssid_len > 0) {
12882be45b66SKalle Valo struct hermes_idstring idbuf;
12892be45b66SKalle Valo size_t len = ssid->ssid_len;
12902be45b66SKalle Valo
12912be45b66SKalle Valo idbuf.len = cpu_to_le16(len);
12922be45b66SKalle Valo memcpy(idbuf.val, ssid->ssid, len);
12932be45b66SKalle Valo
12942be45b66SKalle Valo err = hw->ops->write_ltv(hw, USER_BAP,
12952be45b66SKalle Valo HERMES_RID_CNFSCANSSID_AGERE,
12962be45b66SKalle Valo HERMES_BYTES_TO_RECLEN(len + 2),
12972be45b66SKalle Valo &idbuf);
12982be45b66SKalle Valo } else
12992be45b66SKalle Valo err = hermes_write_wordrec(hw, USER_BAP,
13002be45b66SKalle Valo HERMES_RID_CNFSCANSSID_AGERE,
13012be45b66SKalle Valo 0); /* Any ESSID */
13022be45b66SKalle Valo if (err)
13032be45b66SKalle Valo break;
13042be45b66SKalle Valo
13052be45b66SKalle Valo if (priv->has_ext_scan) {
13062be45b66SKalle Valo err = hermes_write_wordrec(hw, USER_BAP,
13072be45b66SKalle Valo HERMES_RID_CNFSCANCHANNELS2GHZ,
13082be45b66SKalle Valo 0x7FFF);
13092be45b66SKalle Valo if (err)
13102be45b66SKalle Valo goto out;
13112be45b66SKalle Valo
13122be45b66SKalle Valo err = hermes_inquire(hw,
13132be45b66SKalle Valo HERMES_INQ_CHANNELINFO);
13142be45b66SKalle Valo } else
13152be45b66SKalle Valo err = hermes_inquire(hw, HERMES_INQ_SCAN);
13162be45b66SKalle Valo
13172be45b66SKalle Valo break;
13182be45b66SKalle Valo }
13192be45b66SKalle Valo } else
13202be45b66SKalle Valo err = hermes_inquire(hw, HERMES_INQ_SCAN);
13212be45b66SKalle Valo
13222be45b66SKalle Valo out:
13232be45b66SKalle Valo orinoco_unlock(priv, &flags);
13242be45b66SKalle Valo
13252be45b66SKalle Valo return err;
13262be45b66SKalle Valo }
13272be45b66SKalle Valo
13282be45b66SKalle Valo /* Disassociate from node with BSSID addr */
orinoco_hw_disassociate(struct orinoco_private * priv,u8 * addr,u16 reason_code)13292be45b66SKalle Valo int orinoco_hw_disassociate(struct orinoco_private *priv,
13302be45b66SKalle Valo u8 *addr, u16 reason_code)
13312be45b66SKalle Valo {
13322be45b66SKalle Valo struct hermes *hw = &priv->hw;
13332be45b66SKalle Valo int err;
13342be45b66SKalle Valo
13352be45b66SKalle Valo struct {
13362be45b66SKalle Valo u8 addr[ETH_ALEN];
13372be45b66SKalle Valo __le16 reason_code;
13382be45b66SKalle Valo } __packed buf;
13392be45b66SKalle Valo
13402be45b66SKalle Valo /* Currently only supported by WPA enabled Agere fw */
13412be45b66SKalle Valo if (!priv->has_wpa)
13422be45b66SKalle Valo return -EOPNOTSUPP;
13432be45b66SKalle Valo
13442be45b66SKalle Valo memcpy(buf.addr, addr, ETH_ALEN);
13452be45b66SKalle Valo buf.reason_code = cpu_to_le16(reason_code);
13462be45b66SKalle Valo err = HERMES_WRITE_RECORD(hw, USER_BAP,
13472be45b66SKalle Valo HERMES_RID_CNFDISASSOCIATE,
13482be45b66SKalle Valo &buf);
13492be45b66SKalle Valo return err;
13502be45b66SKalle Valo }
13512be45b66SKalle Valo
orinoco_hw_get_current_bssid(struct orinoco_private * priv,u8 * addr)13522be45b66SKalle Valo int orinoco_hw_get_current_bssid(struct orinoco_private *priv,
13532be45b66SKalle Valo u8 *addr)
13542be45b66SKalle Valo {
13552be45b66SKalle Valo struct hermes *hw = &priv->hw;
13562be45b66SKalle Valo int err;
13572be45b66SKalle Valo
13582be45b66SKalle Valo err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
13592be45b66SKalle Valo ETH_ALEN, NULL, addr);
13602be45b66SKalle Valo
13612be45b66SKalle Valo return err;
13622be45b66SKalle Valo }
1363