/* Change the WEP keys and/or the current keys. Can be called * either from __orinoco_hw_setup_enc() or directly from * orinoco_ioctl_setiwencode(). In the later case the association * with the AP is not broken (if the firmware can handle it), * which is needed for 802.1x implementations. */ int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv) { hermes_t *hw = &priv->hw; int err = 0; switch (priv->firmware_type) { case FIRMWARE_TYPE_AGERE: err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNFWEPKEYS_AGERE, &priv->keys); if (err) return err; err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFTXKEY_AGERE, priv->tx_key); if (err) return err; break; case FIRMWARE_TYPE_INTERSIL: case FIRMWARE_TYPE_SYMBOL: { int keylen; int i; /* Force uniform key length to work around * firmware bugs */ keylen = le16_to_cpu(priv->keys[priv->tx_key].len); if (keylen > LARGE_KEY_SIZE) { printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n", priv->ndev->name, priv->tx_key, keylen); return -E2BIG; } /* Write all 4 keys */ for (i = 0; i < ORINOCO_MAX_KEYS; i++) { err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFDEFAULTKEY0 + i, HERMES_BYTES_TO_RECLEN(keylen), priv->keys[i].data); if (err) return err; } /* Write the index of the key used in transmission */ err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFWEPDEFAULTKEYID, priv->tx_key); if (err) return err; } break; } return 0; }
int __orinoco_hw_set_multicast_list(struct orinoco_private *priv, struct dev_addr_list *mc_list, int mc_count, int promisc) { hermes_t *hw = &priv->hw; int err = 0; if (promisc != priv->promiscuous) { err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPROMISCUOUSMODE, promisc); if (err) { printk(KERN_ERR "%s: Error %d setting PROMISCUOUSMODE to 1.\n", priv->ndev->name, err); } else priv->promiscuous = promisc; } /* If we're not in promiscuous mode, then we need to set the * group address if either we want to multicast, or if we were * multicasting and want to stop */ if (!promisc && (mc_count || priv->mc_count)) { struct dev_mc_list *p = mc_list; struct hermes_multicast mclist; int i; for (i = 0; i < mc_count; i++) { /* paranoia: is list shorter than mc_count? */ BUG_ON(!p); /* paranoia: bad address size in list? */ BUG_ON(p->dmi_addrlen != ETH_ALEN); memcpy(mclist.addr[i], p->dmi_addr, ETH_ALEN); p = p->next; } if (p) printk(KERN_WARNING "%s: Multicast list is " "longer than mc_count\n", priv->ndev->name); err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFGROUPADDRESSES, HERMES_BYTES_TO_RECLEN(mc_count * ETH_ALEN), &mclist); if (err) printk(KERN_ERR "%s: Error %d setting multicast list.\n", priv->ndev->name, err); else priv->mc_count = mc_count; } return err; }
int __orinoco_hw_set_multicast_list(struct orinoco_private *priv, struct net_device *dev, int mc_count, int promisc) { hermes_t *hw = &priv->hw; int err = 0; if (promisc != priv->promiscuous) { err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPROMISCUOUSMODE, promisc); if (err) { printk(KERN_ERR "%s: Error %d setting PROMISCUOUSMODE to 1.\n", priv->ndev->name, err); } else priv->promiscuous = promisc; } /* If we're not in promiscuous mode, then we need to set the * group address if either we want to multicast, or if we were * multicasting and want to stop */ if (!promisc && (mc_count || priv->mc_count)) { struct dev_mc_list *p; struct hermes_multicast mclist; int i = 0; netdev_for_each_mc_addr(p, dev) { if (i == mc_count) break; memcpy(mclist.addr[i++], p->dmi_addr, ETH_ALEN); } err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFGROUPADDRESSES, HERMES_BYTES_TO_RECLEN(mc_count * ETH_ALEN), &mclist); if (err) printk(KERN_ERR "%s: Error %d setting multicast list.\n", priv->ndev->name, err); else priv->mc_count = mc_count; } return err; }
/* Change the WEP keys and/or the current keys. Can be called * either from __orinoco_hw_setup_enc() or directly from * orinoco_ioctl_setiwencode(). In the later case the association * with the AP is not broken (if the firmware can handle it), * which is needed for 802.1x implementations. */ int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv) { hermes_t *hw = &priv->hw; int err = 0; int i; switch (priv->firmware_type) { case FIRMWARE_TYPE_AGERE: { struct orinoco_key keys[ORINOCO_MAX_KEYS]; memset(&keys, 0, sizeof(keys)); for (i = 0; i < ORINOCO_MAX_KEYS; i++) { int len = min(priv->keys[i].key_len, ORINOCO_MAX_KEY_SIZE); memcpy(&keys[i].data, priv->keys[i].key, len); if (len > SMALL_KEY_SIZE) keys[i].len = cpu_to_le16(LARGE_KEY_SIZE); else if (len > 0) keys[i].len = cpu_to_le16(SMALL_KEY_SIZE); else keys[i].len = cpu_to_le16(0); } err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNFWEPKEYS_AGERE, &keys); if (err) return err; err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFTXKEY_AGERE, priv->tx_key); if (err) return err; break; } case FIRMWARE_TYPE_INTERSIL: case FIRMWARE_TYPE_SYMBOL: { int keylen; /* Force uniform key length to work around * firmware bugs */ keylen = priv->keys[priv->tx_key].key_len; if (keylen > LARGE_KEY_SIZE) { printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n", priv->ndev->name, priv->tx_key, keylen); return -E2BIG; } else if (keylen > SMALL_KEY_SIZE) keylen = LARGE_KEY_SIZE; else if (keylen > 0) keylen = SMALL_KEY_SIZE; else keylen = 0; /* Write all 4 keys */ for (i = 0; i < ORINOCO_MAX_KEYS; i++) { u8 key[LARGE_KEY_SIZE] = { 0 }; memcpy(key, priv->keys[i].key, priv->keys[i].key_len); err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFDEFAULTKEY0 + i, HERMES_BYTES_TO_RECLEN(keylen), key); if (err) return err; } /* Write the index of the key used in transmission */ err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFWEPDEFAULTKEYID, priv->tx_key); if (err) return err; } break; } return 0; }
int orinoco_hw_program_rids(struct orinoco_private *priv) { struct net_device *dev = priv->ndev; struct wireless_dev *wdev = netdev_priv(dev); hermes_t *hw = &priv->hw; int err; struct hermes_idstring idbuf; /* Set the MAC address */ err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, HERMES_BYTES_TO_RECLEN(ETH_ALEN), dev->dev_addr); if (err) { printk(KERN_ERR "%s: Error %d setting MAC address\n", dev->name, err); return err; } /* Set up the link mode */ err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE, priv->port_type); if (err) { printk(KERN_ERR "%s: Error %d setting port type\n", dev->name, err); return err; } /* Set the channel/frequency */ if (priv->channel != 0 && priv->iw_mode != NL80211_IFTYPE_STATION) { err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFOWNCHANNEL, priv->channel); if (err) { printk(KERN_ERR "%s: Error %d setting channel %d\n", dev->name, err, priv->channel); return err; } } if (priv->has_ibss) { u16 createibss; if ((strlen(priv->desired_essid) == 0) && (priv->createibss)) { printk(KERN_WARNING "%s: This firmware requires an " "ESSID in IBSS-Ad-Hoc mode.\n", dev->name); /* With wvlan_cs, in this case, we would crash. * hopefully, this driver will behave better... * Jean II */ createibss = 0; } else { createibss = priv->createibss; } err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFCREATEIBSS, createibss); if (err) { printk(KERN_ERR "%s: Error %d setting CREATEIBSS\n", dev->name, err); return err; } } /* Set the desired BSSID */ err = __orinoco_hw_set_wap(priv); if (err) { printk(KERN_ERR "%s: Error %d setting AP address\n", dev->name, err); return err; } /* Set the desired ESSID */ idbuf.len = cpu_to_le16(strlen(priv->desired_essid)); memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val)); /* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */ err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID, HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2), &idbuf); if (err) { printk(KERN_ERR "%s: Error %d setting OWNSSID\n", dev->name, err); return err; } err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID, HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2), &idbuf); if (err) { printk(KERN_ERR "%s: Error %d setting DESIREDSSID\n", dev->name, err); return err; } /* Set the station name */ idbuf.len = cpu_to_le16(strlen(priv->nick)); memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val)); err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME, HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2), &idbuf); if (err) { printk(KERN_ERR "%s: Error %d setting nickname\n", dev->name, err); return err; } /* Set AP density */ if (priv->has_sensitivity) { err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, priv->ap_density); if (err) { printk(KERN_WARNING "%s: Error %d setting SYSTEMSCALE. " "Disabling sensitivity control\n", dev->name, err); priv->has_sensitivity = 0; } } /* Set RTS threshold */ err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD, priv->rts_thresh); if (err) { printk(KERN_ERR "%s: Error %d setting RTS threshold\n", dev->name, err); return err; } /* Set fragmentation threshold or MWO robustness */ if (priv->has_mwo) err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFMWOROBUST_AGERE, priv->mwo_robust); else err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFFRAGMENTATIONTHRESHOLD, priv->frag_thresh); if (err) { printk(KERN_ERR "%s: Error %d setting fragmentation\n", dev->name, err); return err; } /* Set bitrate */ err = __orinoco_hw_set_bitrate(priv); if (err) { printk(KERN_ERR "%s: Error %d setting bitrate\n", dev->name, err); return err; } /* Set power management */ if (priv->has_pm) { err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPMENABLED, priv->pm_on); if (err) { printk(KERN_ERR "%s: Error %d setting up PM\n", dev->name, err); return err; } err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFMULTICASTRECEIVE, priv->pm_mcast); if (err) { printk(KERN_ERR "%s: Error %d setting up PM\n", dev->name, err); return err; } err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFMAXSLEEPDURATION, priv->pm_period); if (err) { printk(KERN_ERR "%s: Error %d setting up PM\n", dev->name, err); return err; } err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPMHOLDOVERDURATION, priv->pm_timeout); if (err) { printk(KERN_ERR "%s: Error %d setting up PM\n", dev->name, err); return err; } } /* Set preamble - only for Symbol so far... */ if (priv->has_preamble) { err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPREAMBLE_SYMBOL, priv->preamble); if (err) { printk(KERN_ERR "%s: Error %d setting preamble\n", dev->name, err); return err; } } /* Set up encryption */ if (priv->has_wep || priv->has_wpa) { err = __orinoco_hw_setup_enc(priv); if (err) { printk(KERN_ERR "%s: Error %d activating encryption\n", dev->name, err); return err; } } if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { /* Enable monitor mode */ dev->type = ARPHRD_IEEE80211; err = hermes_docmd_wait(hw, HERMES_CMD_TEST | HERMES_TEST_MONITOR, 0, NULL); } else { /* Disable monitor mode */ dev->type = ARPHRD_ETHER; err = hermes_docmd_wait(hw, HERMES_CMD_TEST | HERMES_TEST_STOP, 0, NULL); } if (err) return err; /* Reset promiscuity / multicast*/ priv->promiscuous = 0; priv->mc_count = 0; /* Record mode change */ wdev->iftype = priv->iw_mode; return 0; }
int orinoco_hw_trigger_scan(struct orinoco_private *priv, const struct cfg80211_ssid *ssid) { struct net_device *dev = priv->ndev; hermes_t *hw = &priv->hw; unsigned long flags; int err = 0; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; /* Scanning with port 0 disabled would fail */ if (!netif_running(dev)) { err = -ENETDOWN; goto out; } /* In monitor mode, the scan results are always empty. * Probe responses are passed to the driver as received * frames and could be processed in software. */ if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { err = -EOPNOTSUPP; goto out; } if (priv->has_hostscan) { switch (priv->firmware_type) { case FIRMWARE_TYPE_SYMBOL: err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFHOSTSCAN_SYMBOL, HERMES_HOSTSCAN_SYMBOL_ONCE | HERMES_HOSTSCAN_SYMBOL_BCAST); break; case FIRMWARE_TYPE_INTERSIL: { __le16 req[3]; req[0] = cpu_to_le16(0x3fff); /* All channels */ req[1] = cpu_to_le16(0x0001); /* rate 1 Mbps */ req[2] = 0; /* Any ESSID */ err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNFHOSTSCAN, &req); break; } case FIRMWARE_TYPE_AGERE: if (ssid->ssid_len > 0) { struct hermes_idstring idbuf; size_t len = ssid->ssid_len; idbuf.len = cpu_to_le16(len); memcpy(idbuf.val, ssid->ssid, len); err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFSCANSSID_AGERE, HERMES_BYTES_TO_RECLEN(len + 2), &idbuf); } else err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFSCANSSID_AGERE, 0); /* Any ESSID */ if (err) break; if (priv->has_ext_scan) { err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFSCANCHANNELS2GHZ, 0x7FFF); if (err) goto out; err = hermes_inquire(hw, HERMES_INQ_CHANNELINFO); } else err = hermes_inquire(hw, HERMES_INQ_SCAN); break; } } else err = hermes_inquire(hw, HERMES_INQ_SCAN); out: orinoco_unlock(priv, &flags); return err; }