linux/drivers/net/wireless/intersil/orinoco/hw.c
<<
>>
Prefs
   1/* Encapsulate basic setting changes and retrieval on Hermes hardware
   2 *
   3 * See copyright notice in main.c
   4 */
   5#include <linux/kernel.h>
   6#include <linux/device.h>
   7#include <linux/if_arp.h>
   8#include <linux/ieee80211.h>
   9#include <linux/wireless.h>
  10#include <net/cfg80211.h>
  11#include "hermes.h"
  12#include "hermes_rid.h"
  13#include "orinoco.h"
  14
  15#include "hw.h"
  16
  17#define SYMBOL_MAX_VER_LEN      (14)
  18
  19/* Symbol firmware has a bug allocating buffers larger than this */
  20#define TX_NICBUF_SIZE_BUG      1585
  21
  22/********************************************************************/
  23/* Data tables                                                      */
  24/********************************************************************/
  25
  26/* This tables gives the actual meanings of the bitrate IDs returned
  27 * by the firmware. */
  28static const struct {
  29        int bitrate; /* in 100s of kilobits */
  30        int automatic;
  31        u16 agere_txratectrl;
  32        u16 intersil_txratectrl;
  33} bitrate_table[] = {
  34        {110, 1,  3, 15}, /* Entry 0 is the default */
  35        {10,  0,  1,  1},
  36        {10,  1,  1,  1},
  37        {20,  0,  2,  2},
  38        {20,  1,  6,  3},
  39        {55,  0,  4,  4},
  40        {55,  1,  7,  7},
  41        {110, 0,  5,  8},
  42};
  43#define BITRATE_TABLE_SIZE ARRAY_SIZE(bitrate_table)
  44
  45/* Firmware version encoding */
  46struct comp_id {
  47        u16 id, variant, major, minor;
  48} __packed;
  49
  50static inline enum fwtype determine_firmware_type(struct comp_id *nic_id)
  51{
  52        if (nic_id->id < 0x8000)
  53                return FIRMWARE_TYPE_AGERE;
  54        else if (nic_id->id == 0x8000 && nic_id->major == 0)
  55                return FIRMWARE_TYPE_SYMBOL;
  56        else
  57                return FIRMWARE_TYPE_INTERSIL;
  58}
  59
  60/* Set priv->firmware type, determine firmware properties
  61 * This function can be called before we have registerred with netdev,
  62 * so all errors go out with dev_* rather than printk
  63 *
  64 * If non-NULL stores a firmware description in fw_name.
  65 * If non-NULL stores a HW version in hw_ver
  66 *
  67 * These are output via generic cfg80211 ethtool support.
  68 */
  69int determine_fw_capabilities(struct orinoco_private *priv,
  70                              char *fw_name, size_t fw_name_len,
  71                              u32 *hw_ver)
  72{
  73        struct device *dev = priv->dev;
  74        struct hermes *hw = &priv->hw;
  75        int err;
  76        struct comp_id nic_id, sta_id;
  77        unsigned int firmver;
  78        char tmp[SYMBOL_MAX_VER_LEN + 1] __attribute__((aligned(2)));
  79
  80        /* Get the hardware version */
  81        err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_NICID, &nic_id);
  82        if (err) {
  83                dev_err(dev, "Cannot read hardware identity: error %d\n",
  84                        err);
  85                return err;
  86        }
  87
  88        le16_to_cpus(&nic_id.id);
  89        le16_to_cpus(&nic_id.variant);
  90        le16_to_cpus(&nic_id.major);
  91        le16_to_cpus(&nic_id.minor);
  92        dev_info(dev, "Hardware identity %04x:%04x:%04x:%04x\n",
  93                 nic_id.id, nic_id.variant, nic_id.major, nic_id.minor);
  94
  95        if (hw_ver)
  96                *hw_ver = (((nic_id.id & 0xff) << 24) |
  97                           ((nic_id.variant & 0xff) << 16) |
  98                           ((nic_id.major & 0xff) << 8) |
  99                           (nic_id.minor & 0xff));
 100
 101        priv->firmware_type = determine_firmware_type(&nic_id);
 102
 103        /* Get the firmware version */
 104        err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_STAID, &sta_id);
 105        if (err) {
 106                dev_err(dev, "Cannot read station identity: error %d\n",
 107                        err);
 108                return err;
 109        }
 110
 111        le16_to_cpus(&sta_id.id);
 112        le16_to_cpus(&sta_id.variant);
 113        le16_to_cpus(&sta_id.major);
 114        le16_to_cpus(&sta_id.minor);
 115        dev_info(dev, "Station identity  %04x:%04x:%04x:%04x\n",
 116                 sta_id.id, sta_id.variant, sta_id.major, sta_id.minor);
 117
 118        switch (sta_id.id) {
 119        case 0x15:
 120                dev_err(dev, "Primary firmware is active\n");
 121                return -ENODEV;
 122        case 0x14b:
 123                dev_err(dev, "Tertiary firmware is active\n");
 124                return -ENODEV;
 125        case 0x1f:      /* Intersil, Agere, Symbol Spectrum24 */
 126        case 0x21:      /* Symbol Spectrum24 Trilogy */
 127                break;
 128        default:
 129                dev_notice(dev, "Unknown station ID, please report\n");
 130                break;
 131        }
 132
 133        /* Default capabilities */
 134        priv->has_sensitivity = 1;
 135        priv->has_mwo = 0;
 136        priv->has_preamble = 0;
 137        priv->has_port3 = 1;
 138        priv->has_ibss = 1;
 139        priv->has_wep = 0;
 140        priv->has_big_wep = 0;
 141        priv->has_alt_txcntl = 0;
 142        priv->has_ext_scan = 0;
 143        priv->has_wpa = 0;
 144        priv->do_fw_download = 0;
 145
 146        /* Determine capabilities from the firmware version */
 147        switch (priv->firmware_type) {
 148        case FIRMWARE_TYPE_AGERE:
 149                /* Lucent Wavelan IEEE, Lucent Orinoco, Cabletron RoamAbout,
 150                   ELSA, Melco, HP, IBM, Dell 1150, Compaq 110/210 */
 151                if (fw_name)
 152                        snprintf(fw_name, fw_name_len, "Lucent/Agere %d.%02d",
 153                                 sta_id.major, sta_id.minor);
 154
 155                firmver = ((unsigned long)sta_id.major << 16) | sta_id.minor;
 156
 157                priv->has_ibss = (firmver >= 0x60006);
 158                priv->has_wep = (firmver >= 0x40020);
 159                priv->has_big_wep = 1; /* FIXME: this is wrong - how do we tell
 160                                          Gold cards from the others? */
 161                priv->has_mwo = (firmver >= 0x60000);
 162                priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */
 163                priv->ibss_port = 1;
 164                priv->has_hostscan = (firmver >= 0x8000a);
 165                priv->do_fw_download = 1;
 166                priv->broken_monitor = (firmver >= 0x80000);
 167                priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */
 168                priv->has_ext_scan = (firmver >= 0x90000); /* All 9.x ? */
 169                priv->has_wpa = (firmver >= 0x9002a);
 170                /* Tested with Agere firmware :
 171                 *      1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II
 172                 * Tested CableTron firmware : 4.32 => Anton */
 173                break;
 174        case FIRMWARE_TYPE_SYMBOL:
 175                /* Symbol , 3Com AirConnect, Intel, Ericsson WLAN */
 176                /* Intel MAC : 00:02:B3:* */
 177                /* 3Com MAC : 00:50:DA:* */
 178                memset(tmp, 0, sizeof(tmp));
 179                /* Get the Symbol firmware version */
 180                err = hw->ops->read_ltv(hw, USER_BAP,
 181                                        HERMES_RID_SECONDARYVERSION_SYMBOL,
 182                                        SYMBOL_MAX_VER_LEN, NULL, &tmp);
 183                if (err) {
 184                        dev_warn(dev, "Error %d reading Symbol firmware info. "
 185                                 "Wildly guessing capabilities...\n", err);
 186                        firmver = 0;
 187                        tmp[0] = '\0';
 188                } else {
 189                        /* The firmware revision is a string, the format is
 190                         * something like : "V2.20-01".
 191                         * Quick and dirty parsing... - Jean II
 192                         */
 193                        firmver = ((tmp[1] - '0') << 16)
 194                                | ((tmp[3] - '0') << 12)
 195                                | ((tmp[4] - '0') << 8)
 196                                | ((tmp[6] - '0') << 4)
 197                                | (tmp[7] - '0');
 198
 199                        tmp[SYMBOL_MAX_VER_LEN] = '\0';
 200                }
 201
 202                if (fw_name)
 203                        snprintf(fw_name, fw_name_len, "Symbol %s", tmp);
 204
 205                priv->has_ibss = (firmver >= 0x20000);
 206                priv->has_wep = (firmver >= 0x15012);
 207                priv->has_big_wep = (firmver >= 0x20000);
 208                priv->has_pm = (firmver >= 0x20000 && firmver < 0x22000) ||
 209                               (firmver >= 0x29000 && firmver < 0x30000) ||
 210                               firmver >= 0x31000;
 211                priv->has_preamble = (firmver >= 0x20000);
 212                priv->ibss_port = 4;
 213
 214                /* Symbol firmware is found on various cards, but
 215                 * there has been no attempt to check firmware
 216                 * download on non-spectrum_cs based cards.
 217                 *
 218                 * Given that the Agere firmware download works
 219                 * differently, we should avoid doing a firmware
 220                 * download with the Symbol algorithm on non-spectrum
 221                 * cards.
 222                 *
 223                 * For now we can identify a spectrum_cs based card
 224                 * because it has a firmware reset function.
 225                 */
 226                priv->do_fw_download = (priv->stop_fw != NULL);
 227
 228                priv->broken_disableport = (firmver == 0x25013) ||
 229                                (firmver >= 0x30000 && firmver <= 0x31000);
 230                priv->has_hostscan = (firmver >= 0x31001) ||
 231                                     (firmver >= 0x29057 && firmver < 0x30000);
 232                /* Tested with Intel firmware : 0x20015 => Jean II */
 233                /* Tested with 3Com firmware : 0x15012 & 0x22001 => Jean II */
 234                break;
 235        case FIRMWARE_TYPE_INTERSIL:
 236                /* D-Link, Linksys, Adtron, ZoomAir, and many others...
 237                 * Samsung, Compaq 100/200 and Proxim are slightly
 238                 * different and less well tested */
 239                /* D-Link MAC : 00:40:05:* */
 240                /* Addtron MAC : 00:90:D1:* */
 241                if (fw_name)
 242                        snprintf(fw_name, fw_name_len, "Intersil %d.%d.%d",
 243                                 sta_id.major, sta_id.minor, sta_id.variant);
 244
 245                firmver = ((unsigned long)sta_id.major << 16) |
 246                        ((unsigned long)sta_id.minor << 8) | sta_id.variant;
 247
 248                priv->has_ibss = (firmver >= 0x000700); /* FIXME */
 249                priv->has_big_wep = priv->has_wep = (firmver >= 0x000800);
 250                priv->has_pm = (firmver >= 0x000700);
 251                priv->has_hostscan = (firmver >= 0x010301);
 252
 253                if (firmver >= 0x000800)
 254                        priv->ibss_port = 0;
 255                else {
 256                        dev_notice(dev, "Intersil firmware earlier than v0.8.x"
 257                                   " - several features not supported\n");
 258                        priv->ibss_port = 1;
 259                }
 260                break;
 261        }
 262        if (fw_name)
 263                dev_info(dev, "Firmware determined as %s\n", fw_name);
 264
 265#ifndef CONFIG_HERMES_PRISM
 266        if (priv->firmware_type == FIRMWARE_TYPE_INTERSIL) {
 267                dev_err(dev, "Support for Prism chipset is not enabled\n");
 268                return -ENODEV;
 269        }
 270#endif
 271
 272        return 0;
 273}
 274
 275/* Read settings from EEPROM into our private structure.
 276 * MAC address gets dropped into callers buffer
 277 * Can be called before netdev registration.
 278 */
 279int orinoco_hw_read_card_settings(struct orinoco_private *priv, u8 *dev_addr)
 280{
 281        struct device *dev = priv->dev;
 282        struct hermes_idstring nickbuf;
 283        struct hermes *hw = &priv->hw;
 284        int len;
 285        int err;
 286        u16 reclen;
 287
 288        /* Get the MAC address */
 289        err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
 290                                ETH_ALEN, NULL, dev_addr);
 291        if (err) {
 292                dev_warn(dev, "Failed to read MAC address!\n");
 293                goto out;
 294        }
 295
 296        dev_dbg(dev, "MAC address %pM\n", dev_addr);
 297
 298        /* Get the station name */
 299        err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
 300                                sizeof(nickbuf), &reclen, &nickbuf);
 301        if (err) {
 302                dev_err(dev, "failed to read station name\n");
 303                goto out;
 304        }
 305        if (nickbuf.len)
 306                len = min(IW_ESSID_MAX_SIZE, (int)le16_to_cpu(nickbuf.len));
 307        else
 308                len = min(IW_ESSID_MAX_SIZE, 2 * reclen);
 309        memcpy(priv->nick, &nickbuf.val, len);
 310        priv->nick[len] = '\0';
 311
 312        dev_dbg(dev, "Station name \"%s\"\n", priv->nick);
 313
 314        /* Get allowed channels */
 315        err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CHANNELLIST,
 316                                  &priv->channel_mask);
 317        if (err) {
 318                dev_err(dev, "Failed to read channel list!\n");
 319                goto out;
 320        }
 321
 322        /* Get initial AP density */
 323        err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE,
 324                                  &priv->ap_density);
 325        if (err || priv->ap_density < 1 || priv->ap_density > 3)
 326                priv->has_sensitivity = 0;
 327
 328        /* Get initial RTS threshold */
 329        err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD,
 330                                  &priv->rts_thresh);
 331        if (err) {
 332                dev_err(dev, "Failed to read RTS threshold!\n");
 333                goto out;
 334        }
 335
 336        /* Get initial fragmentation settings */
 337        if (priv->has_mwo)
 338                err = hermes_read_wordrec(hw, USER_BAP,
 339                                          HERMES_RID_CNFMWOROBUST_AGERE,
 340                                          &priv->mwo_robust);
 341        else
 342                err = hermes_read_wordrec(hw, USER_BAP,
 343                                          HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
 344                                          &priv->frag_thresh);
 345        if (err) {
 346                dev_err(dev, "Failed to read fragmentation settings!\n");
 347                goto out;
 348        }
 349
 350        /* Power management setup */
 351        if (priv->has_pm) {
 352                priv->pm_on = 0;
 353                priv->pm_mcast = 1;
 354                err = hermes_read_wordrec(hw, USER_BAP,
 355                                          HERMES_RID_CNFMAXSLEEPDURATION,
 356                                          &priv->pm_period);
 357                if (err) {
 358                        dev_err(dev, "Failed to read power management "
 359                                "period!\n");
 360                        goto out;
 361                }
 362                err = hermes_read_wordrec(hw, USER_BAP,
 363                                          HERMES_RID_CNFPMHOLDOVERDURATION,
 364                                          &priv->pm_timeout);
 365                if (err) {
 366                        dev_err(dev, "Failed to read power management "
 367                                "timeout!\n");
 368                        goto out;
 369                }
 370        }
 371
 372        /* Preamble setup */
 373        if (priv->has_preamble) {
 374                err = hermes_read_wordrec(hw, USER_BAP,
 375                                          HERMES_RID_CNFPREAMBLE_SYMBOL,
 376                                          &priv->preamble);
 377                if (err) {
 378                        dev_err(dev, "Failed to read preamble setup\n");
 379                        goto out;
 380                }
 381        }
 382
 383        /* Retry settings */
 384        err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_SHORTRETRYLIMIT,
 385                                  &priv->short_retry_limit);
 386        if (err) {
 387                dev_err(dev, "Failed to read short retry limit\n");
 388                goto out;
 389        }
 390
 391        err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_LONGRETRYLIMIT,
 392                                  &priv->long_retry_limit);
 393        if (err) {
 394                dev_err(dev, "Failed to read long retry limit\n");
 395                goto out;
 396        }
 397
 398        err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_MAXTRANSMITLIFETIME,
 399                                  &priv->retry_lifetime);
 400        if (err) {
 401                dev_err(dev, "Failed to read max retry lifetime\n");
 402                goto out;
 403        }
 404
 405out:
 406        return err;
 407}
 408
 409/* Can be called before netdev registration */
 410int orinoco_hw_allocate_fid(struct orinoco_private *priv)
 411{
 412        struct device *dev = priv->dev;
 413        struct hermes *hw = &priv->hw;
 414        int err;
 415
 416        err = hw->ops->allocate(hw, priv->nicbuf_size, &priv->txfid);
 417        if (err == -EIO && priv->nicbuf_size > TX_NICBUF_SIZE_BUG) {
 418                /* Try workaround for old Symbol firmware bug */
 419                priv->nicbuf_size = TX_NICBUF_SIZE_BUG;
 420                err = hw->ops->allocate(hw, priv->nicbuf_size, &priv->txfid);
 421
 422                dev_warn(dev, "Firmware ALLOC bug detected "
 423                         "(old Symbol firmware?). Work around %s\n",
 424                         err ? "failed!" : "ok.");
 425        }
 426
 427        return err;
 428}
 429
 430int orinoco_get_bitratemode(int bitrate, int automatic)
 431{
 432        int ratemode = -1;
 433        int i;
 434
 435        if ((bitrate != 10) && (bitrate != 20) &&
 436            (bitrate != 55) && (bitrate != 110))
 437                return ratemode;
 438
 439        for (i = 0; i < BITRATE_TABLE_SIZE; i++) {
 440                if ((bitrate_table[i].bitrate == bitrate) &&
 441                    (bitrate_table[i].automatic == automatic)) {
 442                        ratemode = i;
 443                        break;
 444                }
 445        }
 446        return ratemode;
 447}
 448
 449void orinoco_get_ratemode_cfg(int ratemode, int *bitrate, int *automatic)
 450{
 451        BUG_ON((ratemode < 0) || (ratemode >= BITRATE_TABLE_SIZE));
 452
 453        *bitrate = bitrate_table[ratemode].bitrate * 100000;
 454        *automatic = bitrate_table[ratemode].automatic;
 455}
 456
 457int orinoco_hw_program_rids(struct orinoco_private *priv)
 458{
 459        struct net_device *dev = priv->ndev;
 460        struct wireless_dev *wdev = netdev_priv(dev);
 461        struct hermes *hw = &priv->hw;
 462        int err;
 463        struct hermes_idstring idbuf;
 464
 465        /* Set the MAC address */
 466        err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
 467                                 HERMES_BYTES_TO_RECLEN(ETH_ALEN),
 468                                 dev->dev_addr);
 469        if (err) {
 470                printk(KERN_ERR "%s: Error %d setting MAC address\n",
 471                       dev->name, err);
 472                return err;
 473        }
 474
 475        /* Set up the link mode */
 476        err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE,
 477                                   priv->port_type);
 478        if (err) {
 479                printk(KERN_ERR "%s: Error %d setting port type\n",
 480                       dev->name, err);
 481                return err;
 482        }
 483        /* Set the channel/frequency */
 484        if (priv->channel != 0 && priv->iw_mode != NL80211_IFTYPE_STATION) {
 485                err = hermes_write_wordrec(hw, USER_BAP,
 486                                           HERMES_RID_CNFOWNCHANNEL,
 487                                           priv->channel);
 488                if (err) {
 489                        printk(KERN_ERR "%s: Error %d setting channel %d\n",
 490                               dev->name, err, priv->channel);
 491                        return err;
 492                }
 493        }
 494
 495        if (priv->has_ibss) {
 496                u16 createibss;
 497
 498                if ((strlen(priv->desired_essid) == 0) && (priv->createibss)) {
 499                        printk(KERN_WARNING "%s: This firmware requires an "
 500                               "ESSID in IBSS-Ad-Hoc mode.\n", dev->name);
 501                        /* With wvlan_cs, in this case, we would crash.
 502                         * hopefully, this driver will behave better...
 503                         * Jean II */
 504                        createibss = 0;
 505                } else {
 506                        createibss = priv->createibss;
 507                }
 508
 509                err = hermes_write_wordrec(hw, USER_BAP,
 510                                           HERMES_RID_CNFCREATEIBSS,
 511                                           createibss);
 512                if (err) {
 513                        printk(KERN_ERR "%s: Error %d setting CREATEIBSS\n",
 514                               dev->name, err);
 515                        return err;
 516                }
 517        }
 518
 519        /* Set the desired BSSID */
 520        err = __orinoco_hw_set_wap(priv);
 521        if (err) {
 522                printk(KERN_ERR "%s: Error %d setting AP address\n",
 523                       dev->name, err);
 524                return err;
 525        }
 526
 527        /* Set the desired ESSID */
 528        idbuf.len = cpu_to_le16(strlen(priv->desired_essid));
 529        memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val));
 530        /* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */
 531        err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID,
 532                        HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid) + 2),
 533                        &idbuf);
 534        if (err) {
 535                printk(KERN_ERR "%s: Error %d setting OWNSSID\n",
 536                       dev->name, err);
 537                return err;
 538        }
 539        err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID,
 540                        HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid) + 2),
 541                        &idbuf);
 542        if (err) {
 543                printk(KERN_ERR "%s: Error %d setting DESIREDSSID\n",
 544                       dev->name, err);
 545                return err;
 546        }
 547
 548        /* Set the station name */
 549        idbuf.len = cpu_to_le16(strlen(priv->nick));
 550        memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val));
 551        err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
 552                                 HERMES_BYTES_TO_RECLEN(strlen(priv->nick) + 2),
 553                                 &idbuf);
 554        if (err) {
 555                printk(KERN_ERR "%s: Error %d setting nickname\n",
 556                       dev->name, err);
 557                return err;
 558        }
 559
 560        /* Set AP density */
 561        if (priv->has_sensitivity) {
 562                err = hermes_write_wordrec(hw, USER_BAP,
 563                                           HERMES_RID_CNFSYSTEMSCALE,
 564                                           priv->ap_density);
 565                if (err) {
 566                        printk(KERN_WARNING "%s: Error %d setting SYSTEMSCALE. "
 567                               "Disabling sensitivity control\n",
 568                               dev->name, err);
 569
 570                        priv->has_sensitivity = 0;
 571                }
 572        }
 573
 574        /* Set RTS threshold */
 575        err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD,
 576                                   priv->rts_thresh);
 577        if (err) {
 578                printk(KERN_ERR "%s: Error %d setting RTS threshold\n",
 579                       dev->name, err);
 580                return err;
 581        }
 582
 583        /* Set fragmentation threshold or MWO robustness */
 584        if (priv->has_mwo)
 585                err = hermes_write_wordrec(hw, USER_BAP,
 586                                           HERMES_RID_CNFMWOROBUST_AGERE,
 587                                           priv->mwo_robust);
 588        else
 589                err = hermes_write_wordrec(hw, USER_BAP,
 590                                           HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
 591                                           priv->frag_thresh);
 592        if (err) {
 593                printk(KERN_ERR "%s: Error %d setting fragmentation\n",
 594                       dev->name, err);
 595                return err;
 596        }
 597
 598        /* Set bitrate */
 599        err = __orinoco_hw_set_bitrate(priv);
 600        if (err) {
 601                printk(KERN_ERR "%s: Error %d setting bitrate\n",
 602                       dev->name, err);
 603                return err;
 604        }
 605
 606        /* Set power management */
 607        if (priv->has_pm) {
 608                err = hermes_write_wordrec(hw, USER_BAP,
 609                                           HERMES_RID_CNFPMENABLED,
 610                                           priv->pm_on);
 611                if (err) {
 612                        printk(KERN_ERR "%s: Error %d setting up PM\n",
 613                               dev->name, err);
 614                        return err;
 615                }
 616
 617                err = hermes_write_wordrec(hw, USER_BAP,
 618                                           HERMES_RID_CNFMULTICASTRECEIVE,
 619                                           priv->pm_mcast);
 620                if (err) {
 621                        printk(KERN_ERR "%s: Error %d setting up PM\n",
 622                               dev->name, err);
 623                        return err;
 624                }
 625                err = hermes_write_wordrec(hw, USER_BAP,
 626                                           HERMES_RID_CNFMAXSLEEPDURATION,
 627                                           priv->pm_period);
 628                if (err) {
 629                        printk(KERN_ERR "%s: Error %d setting up PM\n",
 630                               dev->name, err);
 631                        return err;
 632                }
 633                err = hermes_write_wordrec(hw, USER_BAP,
 634                                           HERMES_RID_CNFPMHOLDOVERDURATION,
 635                                           priv->pm_timeout);
 636                if (err) {
 637                        printk(KERN_ERR "%s: Error %d setting up PM\n",
 638                               dev->name, err);
 639                        return err;
 640                }
 641        }
 642
 643        /* Set preamble - only for Symbol so far... */
 644        if (priv->has_preamble) {
 645                err = hermes_write_wordrec(hw, USER_BAP,
 646                                           HERMES_RID_CNFPREAMBLE_SYMBOL,
 647                                           priv->preamble);
 648                if (err) {
 649                        printk(KERN_ERR "%s: Error %d setting preamble\n",
 650                               dev->name, err);
 651                        return err;
 652                }
 653        }
 654
 655        /* Set up encryption */
 656        if (priv->has_wep || priv->has_wpa) {
 657                err = __orinoco_hw_setup_enc(priv);
 658                if (err) {
 659                        printk(KERN_ERR "%s: Error %d activating encryption\n",
 660                               dev->name, err);
 661                        return err;
 662                }
 663        }
 664
 665        if (priv->iw_mode == NL80211_IFTYPE_MONITOR) {
 666                /* Enable monitor mode */
 667                dev->type = ARPHRD_IEEE80211;
 668                err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST |
 669                                            HERMES_TEST_MONITOR, 0, NULL);
 670        } else {
 671                /* Disable monitor mode */
 672                dev->type = ARPHRD_ETHER;
 673                err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST |
 674                                            HERMES_TEST_STOP, 0, NULL);
 675        }
 676        if (err)
 677                return err;
 678
 679        /* Reset promiscuity / multicast*/
 680        priv->promiscuous = 0;
 681        priv->mc_count = 0;
 682
 683        /* Record mode change */
 684        wdev->iftype = priv->iw_mode;
 685
 686        return 0;
 687}
 688
 689/* Get tsc from the firmware */
 690int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key, u8 *tsc)
 691{
 692        struct hermes *hw = &priv->hw;
 693        int err = 0;
 694        u8 tsc_arr[4][ORINOCO_SEQ_LEN];
 695
 696        if ((key < 0) || (key >= 4))
 697                return -EINVAL;
 698
 699        err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_TKIP_IV,
 700                                sizeof(tsc_arr), NULL, &tsc_arr);
 701        if (!err)
 702                memcpy(tsc, &tsc_arr[key][0], sizeof(tsc_arr[0]));
 703
 704        return err;
 705}
 706
 707int __orinoco_hw_set_bitrate(struct orinoco_private *priv)
 708{
 709        struct hermes *hw = &priv->hw;
 710        int ratemode = priv->bitratemode;
 711        int err = 0;
 712
 713        if (ratemode >= BITRATE_TABLE_SIZE) {
 714                printk(KERN_ERR "%s: BUG: Invalid bitrate mode %d\n",
 715                       priv->ndev->name, ratemode);
 716                return -EINVAL;
 717        }
 718
 719        switch (priv->firmware_type) {
 720        case FIRMWARE_TYPE_AGERE:
 721                err = hermes_write_wordrec(hw, USER_BAP,
 722                                HERMES_RID_CNFTXRATECONTROL,
 723                                bitrate_table[ratemode].agere_txratectrl);
 724                break;
 725        case FIRMWARE_TYPE_INTERSIL:
 726        case FIRMWARE_TYPE_SYMBOL:
 727                err = hermes_write_wordrec(hw, USER_BAP,
 728                                HERMES_RID_CNFTXRATECONTROL,
 729                                bitrate_table[ratemode].intersil_txratectrl);
 730                break;
 731        default:
 732                BUG();
 733        }
 734
 735        return err;
 736}
 737
 738int orinoco_hw_get_act_bitrate(struct orinoco_private *priv, int *bitrate)
 739{
 740        struct hermes *hw = &priv->hw;
 741        int i;
 742        int err = 0;
 743        u16 val;
 744
 745        err = hermes_read_wordrec(hw, USER_BAP,
 746                                  HERMES_RID_CURRENTTXRATE, &val);
 747        if (err)
 748                return err;
 749
 750        switch (priv->firmware_type) {
 751        case FIRMWARE_TYPE_AGERE: /* Lucent style rate */
 752                /* Note : in Lucent firmware, the return value of
 753                 * HERMES_RID_CURRENTTXRATE is the bitrate in Mb/s,
 754                 * and therefore is totally different from the
 755                 * encoding of HERMES_RID_CNFTXRATECONTROL.
 756                 * Don't forget that 6Mb/s is really 5.5Mb/s */
 757                if (val == 6)
 758                        *bitrate = 5500000;
 759                else
 760                        *bitrate = val * 1000000;
 761                break;
 762        case FIRMWARE_TYPE_INTERSIL: /* Intersil style rate */
 763        case FIRMWARE_TYPE_SYMBOL: /* Symbol style rate */
 764                for (i = 0; i < BITRATE_TABLE_SIZE; i++)
 765                        if (bitrate_table[i].intersil_txratectrl == val) {
 766                                *bitrate = bitrate_table[i].bitrate * 100000;
 767                                break;
 768                        }
 769
 770                if (i >= BITRATE_TABLE_SIZE) {
 771                        printk(KERN_INFO "%s: Unable to determine current bitrate (0x%04hx)\n",
 772                               priv->ndev->name, val);
 773                        err = -EIO;
 774                }
 775
 776                break;
 777        default:
 778                BUG();
 779        }
 780
 781        return err;
 782}
 783
 784/* Set fixed AP address */
 785int __orinoco_hw_set_wap(struct orinoco_private *priv)
 786{
 787        int roaming_flag;
 788        int err = 0;
 789        struct hermes *hw = &priv->hw;
 790
 791        switch (priv->firmware_type) {
 792        case FIRMWARE_TYPE_AGERE:
 793                /* not supported */
 794                break;
 795        case FIRMWARE_TYPE_INTERSIL:
 796                if (priv->bssid_fixed)
 797                        roaming_flag = 2;
 798                else
 799                        roaming_flag = 1;
 800
 801                err = hermes_write_wordrec(hw, USER_BAP,
 802                                           HERMES_RID_CNFROAMINGMODE,
 803                                           roaming_flag);
 804                break;
 805        case FIRMWARE_TYPE_SYMBOL:
 806                err = HERMES_WRITE_RECORD(hw, USER_BAP,
 807                                          HERMES_RID_CNFMANDATORYBSSID_SYMBOL,
 808                                          &priv->desired_bssid);
 809                break;
 810        }
 811        return err;
 812}
 813
 814/* Change the WEP keys and/or the current keys.  Can be called
 815 * either from __orinoco_hw_setup_enc() or directly from
 816 * orinoco_ioctl_setiwencode().  In the later case the association
 817 * with the AP is not broken (if the firmware can handle it),
 818 * which is needed for 802.1x implementations. */
 819int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv)
 820{
 821        struct hermes *hw = &priv->hw;
 822        int err = 0;
 823        int i;
 824
 825        switch (priv->firmware_type) {
 826        case FIRMWARE_TYPE_AGERE:
 827        {
 828                struct orinoco_key keys[ORINOCO_MAX_KEYS];
 829
 830                memset(&keys, 0, sizeof(keys));
 831                for (i = 0; i < ORINOCO_MAX_KEYS; i++) {
 832                        int len = min(priv->keys[i].key_len,
 833                                      ORINOCO_MAX_KEY_SIZE);
 834                        memcpy(&keys[i].data, priv->keys[i].key, len);
 835                        if (len > SMALL_KEY_SIZE)
 836                                keys[i].len = cpu_to_le16(LARGE_KEY_SIZE);
 837                        else if (len > 0)
 838                                keys[i].len = cpu_to_le16(SMALL_KEY_SIZE);
 839                        else
 840                                keys[i].len = cpu_to_le16(0);
 841                }
 842
 843                err = HERMES_WRITE_RECORD(hw, USER_BAP,
 844                                          HERMES_RID_CNFWEPKEYS_AGERE,
 845                                          &keys);
 846                if (err)
 847                        return err;
 848                err = hermes_write_wordrec(hw, USER_BAP,
 849                                           HERMES_RID_CNFTXKEY_AGERE,
 850                                           priv->tx_key);
 851                if (err)
 852                        return err;
 853                break;
 854        }
 855        case FIRMWARE_TYPE_INTERSIL:
 856        case FIRMWARE_TYPE_SYMBOL:
 857                {
 858                        int keylen;
 859
 860                        /* Force uniform key length to work around
 861                         * firmware bugs */
 862                        keylen = priv->keys[priv->tx_key].key_len;
 863
 864                        if (keylen > LARGE_KEY_SIZE) {
 865                                printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n",
 866                                       priv->ndev->name, priv->tx_key, keylen);
 867                                return -E2BIG;
 868                        } else if (keylen > SMALL_KEY_SIZE)
 869                                keylen = LARGE_KEY_SIZE;
 870                        else if (keylen > 0)
 871                                keylen = SMALL_KEY_SIZE;
 872                        else
 873                                keylen = 0;
 874
 875                        /* Write all 4 keys */
 876                        for (i = 0; i < ORINOCO_MAX_KEYS; i++) {
 877                                u8 key[LARGE_KEY_SIZE] = { 0 };
 878
 879                                memcpy(key, priv->keys[i].key,
 880                                       priv->keys[i].key_len);
 881
 882                                err = hw->ops->write_ltv(hw, USER_BAP,
 883                                                HERMES_RID_CNFDEFAULTKEY0 + i,
 884                                                HERMES_BYTES_TO_RECLEN(keylen),
 885                                                key);
 886                                if (err)
 887                                        return err;
 888                        }
 889
 890                        /* Write the index of the key used in transmission */
 891                        err = hermes_write_wordrec(hw, USER_BAP,
 892                                                HERMES_RID_CNFWEPDEFAULTKEYID,
 893                                                priv->tx_key);
 894                        if (err)
 895                                return err;
 896                }
 897                break;
 898        }
 899
 900        return 0;
 901}
 902
 903int __orinoco_hw_setup_enc(struct orinoco_private *priv)
 904{
 905        struct hermes *hw = &priv->hw;
 906        int err = 0;
 907        int master_wep_flag;
 908        int auth_flag;
 909        int enc_flag;
 910
 911        /* Setup WEP keys */
 912        if (priv->encode_alg == ORINOCO_ALG_WEP)
 913                __orinoco_hw_setup_wepkeys(priv);
 914
 915        if (priv->wep_restrict)
 916                auth_flag = HERMES_AUTH_SHARED_KEY;
 917        else
 918                auth_flag = HERMES_AUTH_OPEN;
 919
 920        if (priv->wpa_enabled)
 921                enc_flag = 2;
 922        else if (priv->encode_alg == ORINOCO_ALG_WEP)
 923                enc_flag = 1;
 924        else
 925                enc_flag = 0;
 926
 927        switch (priv->firmware_type) {
 928        case FIRMWARE_TYPE_AGERE: /* Agere style WEP */
 929                if (priv->encode_alg == ORINOCO_ALG_WEP) {
 930                        /* Enable the shared-key authentication. */
 931                        err = hermes_write_wordrec(hw, USER_BAP,
 932                                        HERMES_RID_CNFAUTHENTICATION_AGERE,
 933                                        auth_flag);
 934                }
 935                err = hermes_write_wordrec(hw, USER_BAP,
 936                                           HERMES_RID_CNFWEPENABLED_AGERE,
 937                                           enc_flag);
 938                if (err)
 939                        return err;
 940
 941                if (priv->has_wpa) {
 942                        /* Set WPA key management */
 943                        err = hermes_write_wordrec(hw, USER_BAP,
 944                                  HERMES_RID_CNFSETWPAAUTHMGMTSUITE_AGERE,
 945                                  priv->key_mgmt);
 946                        if (err)
 947                                return err;
 948                }
 949
 950                break;
 951
 952        case FIRMWARE_TYPE_INTERSIL: /* Intersil style WEP */
 953        case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */
 954                if (priv->encode_alg == ORINOCO_ALG_WEP) {
 955                        if (priv->wep_restrict ||
 956                            (priv->firmware_type == FIRMWARE_TYPE_SYMBOL))
 957                                master_wep_flag = HERMES_WEP_PRIVACY_INVOKED |
 958                                                  HERMES_WEP_EXCL_UNENCRYPTED;
 959                        else
 960                                master_wep_flag = HERMES_WEP_PRIVACY_INVOKED;
 961
 962                        err = hermes_write_wordrec(hw, USER_BAP,
 963                                                   HERMES_RID_CNFAUTHENTICATION,
 964                                                   auth_flag);
 965                        if (err)
 966                                return err;
 967                } else
 968                        master_wep_flag = 0;
 969
 970                if (priv->iw_mode == NL80211_IFTYPE_MONITOR)
 971                        master_wep_flag |= HERMES_WEP_HOST_DECRYPT;
 972
 973                /* Master WEP setting : on/off */
 974                err = hermes_write_wordrec(hw, USER_BAP,
 975                                           HERMES_RID_CNFWEPFLAGS_INTERSIL,
 976                                           master_wep_flag);
 977                if (err)
 978                        return err;
 979
 980                break;
 981        }
 982
 983        return 0;
 984}
 985
 986/* key must be 32 bytes, including the tx and rx MIC keys.
 987 * rsc must be NULL or up to 8 bytes
 988 * tsc must be NULL or up to 8 bytes
 989 */
 990int __orinoco_hw_set_tkip_key(struct orinoco_private *priv, int key_idx,
 991                              int set_tx, const u8 *key, const u8 *rsc,
 992                              size_t rsc_len, const u8 *tsc, size_t tsc_len)
 993{
 994        struct {
 995                __le16 idx;
 996                u8 rsc[ORINOCO_SEQ_LEN];
 997                u8 key[TKIP_KEYLEN];
 998                u8 tx_mic[MIC_KEYLEN];
 999                u8 rx_mic[MIC_KEYLEN];
1000                u8 tsc[ORINOCO_SEQ_LEN];
1001        } __packed buf;
1002        struct hermes *hw = &priv->hw;
1003        int ret;
1004        int err;
1005        int k;
1006        u16 xmitting;
1007
1008        key_idx &= 0x3;
1009
1010        if (set_tx)
1011                key_idx |= 0x8000;
1012
1013        buf.idx = cpu_to_le16(key_idx);
1014        memcpy(buf.key, key,
1015               sizeof(buf.key) + sizeof(buf.tx_mic) + sizeof(buf.rx_mic));
1016
1017        if (rsc_len > sizeof(buf.rsc))
1018                rsc_len = sizeof(buf.rsc);
1019
1020        if (tsc_len > sizeof(buf.tsc))
1021                tsc_len = sizeof(buf.tsc);
1022
1023        memset(buf.rsc, 0, sizeof(buf.rsc));
1024        memset(buf.tsc, 0, sizeof(buf.tsc));
1025
1026        if (rsc != NULL)
1027                memcpy(buf.rsc, rsc, rsc_len);
1028
1029        if (tsc != NULL)
1030                memcpy(buf.tsc, tsc, tsc_len);
1031        else
1032                buf.tsc[4] = 0x10;
1033
1034        /* Wait up to 100ms for tx queue to empty */
1035        for (k = 100; k > 0; k--) {
1036                udelay(1000);
1037                ret = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_TXQUEUEEMPTY,
1038                                          &xmitting);
1039                if (ret || !xmitting)
1040                        break;
1041        }
1042
1043        if (k == 0)
1044                ret = -ETIMEDOUT;
1045
1046        err = HERMES_WRITE_RECORD(hw, USER_BAP,
1047                                  HERMES_RID_CNFADDDEFAULTTKIPKEY_AGERE,
1048                                  &buf);
1049
1050        return ret ? ret : err;
1051}
1052
1053int orinoco_clear_tkip_key(struct orinoco_private *priv, int key_idx)
1054{
1055        struct hermes *hw = &priv->hw;
1056        int err;
1057
1058        err = hermes_write_wordrec(hw, USER_BAP,
1059                                   HERMES_RID_CNFREMDEFAULTTKIPKEY_AGERE,
1060                                   key_idx);
1061        if (err)
1062                printk(KERN_WARNING "%s: Error %d clearing TKIP key %d\n",
1063                       priv->ndev->name, err, key_idx);
1064        return err;
1065}
1066
1067int __orinoco_hw_set_multicast_list(struct orinoco_private *priv,
1068                                    struct net_device *dev,
1069                                    int mc_count, int promisc)
1070{
1071        struct hermes *hw = &priv->hw;
1072        int err = 0;
1073
1074        if (promisc != priv->promiscuous) {
1075                err = hermes_write_wordrec(hw, USER_BAP,
1076                                           HERMES_RID_CNFPROMISCUOUSMODE,
1077                                           promisc);
1078                if (err) {
1079                        printk(KERN_ERR "%s: Error %d setting PROMISCUOUSMODE to 1.\n",
1080                               priv->ndev->name, err);
1081                } else
1082                        priv->promiscuous = promisc;
1083        }
1084
1085        /* If we're not in promiscuous mode, then we need to set the
1086         * group address if either we want to multicast, or if we were
1087         * multicasting and want to stop */
1088        if (!promisc && (mc_count || priv->mc_count)) {
1089                struct netdev_hw_addr *ha;
1090                struct hermes_multicast mclist;
1091                int i = 0;
1092
1093                netdev_for_each_mc_addr(ha, dev) {
1094                        if (i == mc_count)
1095                                break;
1096                        memcpy(mclist.addr[i++], ha->addr, ETH_ALEN);
1097                }
1098
1099                err = hw->ops->write_ltv(hw, USER_BAP,
1100                                   HERMES_RID_CNFGROUPADDRESSES,
1101                                   HERMES_BYTES_TO_RECLEN(mc_count * ETH_ALEN),
1102                                   &mclist);
1103                if (err)
1104                        printk(KERN_ERR "%s: Error %d setting multicast list.\n",
1105                               priv->ndev->name, err);
1106                else
1107                        priv->mc_count = mc_count;
1108        }
1109        return err;
1110}
1111
1112/* Return : < 0 -> error code ; >= 0 -> length */
1113int orinoco_hw_get_essid(struct orinoco_private *priv, int *active,
1114                         char buf[IW_ESSID_MAX_SIZE + 1])
1115{
1116        struct hermes *hw = &priv->hw;
1117        int err = 0;
1118        struct hermes_idstring essidbuf;
1119        char *p = (char *)(&essidbuf.val);
1120        int len;
1121        unsigned long flags;
1122
1123        if (orinoco_lock(priv, &flags) != 0)
1124                return -EBUSY;
1125
1126        if (strlen(priv->desired_essid) > 0) {
1127                /* We read the desired SSID from the hardware rather
1128                   than from priv->desired_essid, just in case the
1129                   firmware is allowed to change it on us. I'm not
1130                   sure about this */
1131                /* My guess is that the OWNSSID should always be whatever
1132                 * we set to the card, whereas CURRENT_SSID is the one that
1133                 * may change... - Jean II */
1134                u16 rid;
1135
1136                *active = 1;
1137
1138                rid = (priv->port_type == 3) ? HERMES_RID_CNFOWNSSID :
1139                        HERMES_RID_CNFDESIREDSSID;
1140
1141                err = hw->ops->read_ltv(hw, USER_BAP, rid, sizeof(essidbuf),
1142                                        NULL, &essidbuf);
1143                if (err)
1144                        goto fail_unlock;
1145        } else {
1146                *active = 0;
1147
1148                err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID,
1149                                        sizeof(essidbuf), NULL, &essidbuf);
1150                if (err)
1151                        goto fail_unlock;
1152        }
1153
1154        len = le16_to_cpu(essidbuf.len);
1155        BUG_ON(len > IW_ESSID_MAX_SIZE);
1156
1157        memset(buf, 0, IW_ESSID_MAX_SIZE);
1158        memcpy(buf, p, len);
1159        err = len;
1160
1161 fail_unlock:
1162        orinoco_unlock(priv, &flags);
1163
1164        return err;
1165}
1166
1167int orinoco_hw_get_freq(struct orinoco_private *priv)
1168{
1169        struct hermes *hw = &priv->hw;
1170        int err = 0;
1171        u16 channel;
1172        int freq = 0;
1173        unsigned long flags;
1174
1175        if (orinoco_lock(priv, &flags) != 0)
1176                return -EBUSY;
1177
1178        err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENTCHANNEL,
1179                                  &channel);
1180        if (err)
1181                goto out;
1182
1183        /* Intersil firmware 1.3.5 returns 0 when the interface is down */
1184        if (channel == 0) {
1185                err = -EBUSY;
1186                goto out;
1187        }
1188
1189        if ((channel < 1) || (channel > NUM_CHANNELS)) {
1190                printk(KERN_WARNING "%s: Channel out of range (%d)!\n",
1191                       priv->ndev->name, channel);
1192                err = -EBUSY;
1193                goto out;
1194
1195        }
1196        freq = ieee80211_channel_to_frequency(channel, NL80211_BAND_2GHZ);
1197
1198 out:
1199        orinoco_unlock(priv, &flags);
1200
1201        if (err > 0)
1202                err = -EBUSY;
1203        return err ? err : freq;
1204}
1205
1206int orinoco_hw_get_bitratelist(struct orinoco_private *priv,
1207                               int *numrates, s32 *rates, int max)
1208{
1209        struct hermes *hw = &priv->hw;
1210        struct hermes_idstring list;
1211        unsigned char *p = (unsigned char *)&list.val;
1212        int err = 0;
1213        int num;
1214        int i;
1215        unsigned long flags;
1216
1217        if (orinoco_lock(priv, &flags) != 0)
1218                return -EBUSY;
1219
1220        err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES,
1221                                sizeof(list), NULL, &list);
1222        orinoco_unlock(priv, &flags);
1223
1224        if (err)
1225                return err;
1226
1227        num = le16_to_cpu(list.len);
1228        *numrates = num;
1229        num = min(num, max);
1230
1231        for (i = 0; i < num; i++)
1232                rates[i] = (p[i] & 0x7f) * 500000; /* convert to bps */
1233
1234        return 0;
1235}
1236
1237int orinoco_hw_trigger_scan(struct orinoco_private *priv,
1238                            const struct cfg80211_ssid *ssid)
1239{
1240        struct net_device *dev = priv->ndev;
1241        struct hermes *hw = &priv->hw;
1242        unsigned long flags;
1243        int err = 0;
1244
1245        if (orinoco_lock(priv, &flags) != 0)
1246                return -EBUSY;
1247
1248        /* Scanning with port 0 disabled would fail */
1249        if (!netif_running(dev)) {
1250                err = -ENETDOWN;
1251                goto out;
1252        }
1253
1254        /* In monitor mode, the scan results are always empty.
1255         * Probe responses are passed to the driver as received
1256         * frames and could be processed in software. */
1257        if (priv->iw_mode == NL80211_IFTYPE_MONITOR) {
1258                err = -EOPNOTSUPP;
1259                goto out;
1260        }
1261
1262        if (priv->has_hostscan) {
1263                switch (priv->firmware_type) {
1264                case FIRMWARE_TYPE_SYMBOL:
1265                        err = hermes_write_wordrec(hw, USER_BAP,
1266                                                HERMES_RID_CNFHOSTSCAN_SYMBOL,
1267                                                HERMES_HOSTSCAN_SYMBOL_ONCE |
1268                                                HERMES_HOSTSCAN_SYMBOL_BCAST);
1269                        break;
1270                case FIRMWARE_TYPE_INTERSIL: {
1271                        __le16 req[3];
1272
1273                        req[0] = cpu_to_le16(0x3fff);   /* All channels */
1274                        req[1] = cpu_to_le16(0x0001);   /* rate 1 Mbps */
1275                        req[2] = 0;                     /* Any ESSID */
1276                        err = HERMES_WRITE_RECORD(hw, USER_BAP,
1277                                                  HERMES_RID_CNFHOSTSCAN, &req);
1278                        break;
1279                }
1280                case FIRMWARE_TYPE_AGERE:
1281                        if (ssid->ssid_len > 0) {
1282                                struct hermes_idstring idbuf;
1283                                size_t len = ssid->ssid_len;
1284
1285                                idbuf.len = cpu_to_le16(len);
1286                                memcpy(idbuf.val, ssid->ssid, len);
1287
1288                                err = hw->ops->write_ltv(hw, USER_BAP,
1289                                               HERMES_RID_CNFSCANSSID_AGERE,
1290                                               HERMES_BYTES_TO_RECLEN(len + 2),
1291                                               &idbuf);
1292                        } else
1293                                err = hermes_write_wordrec(hw, USER_BAP,
1294                                                   HERMES_RID_CNFSCANSSID_AGERE,
1295                                                   0);  /* Any ESSID */
1296                        if (err)
1297                                break;
1298
1299                        if (priv->has_ext_scan) {
1300                                err = hermes_write_wordrec(hw, USER_BAP,
1301                                                HERMES_RID_CNFSCANCHANNELS2GHZ,
1302                                                0x7FFF);
1303                                if (err)
1304                                        goto out;
1305
1306                                err = hermes_inquire(hw,
1307                                                     HERMES_INQ_CHANNELINFO);
1308                        } else
1309                                err = hermes_inquire(hw, HERMES_INQ_SCAN);
1310
1311                        break;
1312                }
1313        } else
1314                err = hermes_inquire(hw, HERMES_INQ_SCAN);
1315
1316 out:
1317        orinoco_unlock(priv, &flags);
1318
1319        return err;
1320}
1321
1322/* Disassociate from node with BSSID addr */
1323int orinoco_hw_disassociate(struct orinoco_private *priv,
1324                            u8 *addr, u16 reason_code)
1325{
1326        struct hermes *hw = &priv->hw;
1327        int err;
1328
1329        struct {
1330                u8 addr[ETH_ALEN];
1331                __le16 reason_code;
1332        } __packed buf;
1333
1334        /* Currently only supported by WPA enabled Agere fw */
1335        if (!priv->has_wpa)
1336                return -EOPNOTSUPP;
1337
1338        memcpy(buf.addr, addr, ETH_ALEN);
1339        buf.reason_code = cpu_to_le16(reason_code);
1340        err = HERMES_WRITE_RECORD(hw, USER_BAP,
1341                                  HERMES_RID_CNFDISASSOCIATE,
1342                                  &buf);
1343        return err;
1344}
1345
1346int orinoco_hw_get_current_bssid(struct orinoco_private *priv,
1347                                 u8 *addr)
1348{
1349        struct hermes *hw = &priv->hw;
1350        int err;
1351
1352        err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
1353                                ETH_ALEN, NULL, addr);
1354
1355        return err;
1356}
1357