+/* Note: currently, use hostapd ioctl from the Host AP driver for WPA
+ * support. This is to be replaced with Linux wireless extensions once they
+ * get WPA support. */
+
+/* Note II: please leave all this together as it will be easier to remove later,
+ * once wireless extensions add WPA support -mcgrof */
+
+/* PRISM54_HOSTAPD ioctl() cmd: */
+enum {
+ PRISM2_SET_ENCRYPTION = 6,
+ PRISM2_HOSTAPD_SET_GENERIC_ELEMENT = 12,
+ PRISM2_HOSTAPD_MLME = 13,
+ PRISM2_HOSTAPD_SCAN_REQ = 14,
+};
+
+#define PRISM54_SET_WPA SIOCIWFIRSTPRIV+12
+#define PRISM54_HOSTAPD SIOCIWFIRSTPRIV+25
+#define PRISM54_DROP_UNENCRYPTED SIOCIWFIRSTPRIV+26
+
+#define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024
+#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \
+((int) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data))
+
+/* Maximum length for algorithm names (-1 for nul termination)
+ * used in ioctl() */
+#define HOSTAP_CRYPT_ALG_NAME_LEN 16
+
+struct prism2_hostapd_param {
+ u32 cmd;
+ u8 sta_addr[ETH_ALEN];
+ union {
+ struct {
+ u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN];
+ u32 flags;
+ u32 err;
+ u8 idx;
+ u8 seq[8]; /* sequence counter (set: RX, get: TX) */
+ u16 key_len;
+ u8 key[0];
+ } crypt;
+ struct {
+ u8 len;
+ u8 data[0];
+ } generic_elem;
+ struct {
+#define MLME_STA_DEAUTH 0
+#define MLME_STA_DISASSOC 1
+ u16 cmd;
+ u16 reason_code;
+ } mlme;
+ struct {
+ u8 ssid_len;
+ u8 ssid[32];
+ } scan_req;
+ } u;
+};
+
+
+static int
+prism2_ioctl_set_encryption(struct net_device *dev,
+ struct prism2_hostapd_param *param,
+ int param_len)
+{
+ islpci_private *priv = netdev_priv(dev);
+ int rvalue = 0, force = 0;
+ int authen = DOT11_AUTH_OS, invoke = 0, exunencrypt = 0;
+ union oid_res_t r;
+
+ /* with the new API, it's impossible to get a NULL pointer.
+ * New version of iwconfig set the IW_ENCODE_NOKEY flag
+ * when no key is given, but older versions don't. */
+
+ if (param->u.crypt.key_len > 0) {
+ /* we have a key to set */
+ int index = param->u.crypt.idx;
+ int current_index;
+ struct obj_key key = { DOT11_PRIV_TKIP, 0, "" };
+
+ /* get the current key index */
+ rvalue = mgt_get_request(priv, DOT11_OID_DEFKEYID, 0, NULL, &r);
+ current_index = r.u;
+ /* Verify that the key is not marked as invalid */
+ if (!(param->u.crypt.flags & IW_ENCODE_NOKEY)) {
+ key.length = param->u.crypt.key_len > sizeof (param->u.crypt.key) ?
+ sizeof (param->u.crypt.key) : param->u.crypt.key_len;
+ memcpy(key.key, param->u.crypt.key, key.length);
+ if (key.length == 32)
+ /* we want WPA-PSK */
+ key.type = DOT11_PRIV_TKIP;
+ if ((index < 0) || (index > 3))
+ /* no index provided use the current one */
+ index = current_index;
+
+ /* now send the key to the card */
+ rvalue |=
+ mgt_set_request(priv, DOT11_OID_DEFKEYX, index,
+ &key);
+ }
+ /*
+ * If a valid key is set, encryption should be enabled
+ * (user may turn it off later).
+ * This is also how "iwconfig ethX key on" works
+ */
+ if ((index == current_index) && (key.length > 0))
+ force = 1;
+ } else {
+ int index = (param->u.crypt.flags & IW_ENCODE_INDEX) - 1;
+ if ((index >= 0) && (index <= 3)) {
+ /* we want to set the key index */
+ rvalue |=
+ mgt_set_request(priv, DOT11_OID_DEFKEYID, 0,
+ &index);
+ } else {
+ if (!param->u.crypt.flags & IW_ENCODE_MODE) {
+ /* we cannot do anything. Complain. */
+ return -EINVAL;
+ }
+ }
+ }
+ /* now read the flags */
+ if (param->u.crypt.flags & IW_ENCODE_DISABLED) {
+ /* Encoding disabled,
+ * authen = DOT11_AUTH_OS;
+ * invoke = 0;
+ * exunencrypt = 0; */
+ }
+ if (param->u.crypt.flags & IW_ENCODE_OPEN)
+ /* Encode but accept non-encoded packets. No auth */
+ invoke = 1;
+ if ((param->u.crypt.flags & IW_ENCODE_RESTRICTED) || force) {
+ /* Refuse non-encoded packets. Auth */
+ authen = DOT11_AUTH_BOTH;
+ invoke = 1;
+ exunencrypt = 1;
+ }
+ /* do the change if requested */
+ if ((param->u.crypt.flags & IW_ENCODE_MODE) || force) {
+ rvalue |=
+ mgt_set_request(priv, DOT11_OID_AUTHENABLE, 0, &authen);
+ rvalue |=
+ mgt_set_request(priv, DOT11_OID_PRIVACYINVOKED, 0, &invoke);
+ rvalue |=
+ mgt_set_request(priv, DOT11_OID_EXUNENCRYPTED, 0,
+ &exunencrypt);
+ }
+ return rvalue;
+}
+
+static int
+prism2_ioctl_set_generic_element(struct net_device *ndev,
+ struct prism2_hostapd_param *param,
+ int param_len)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ int max_len, len, alen, ret=0;
+ struct obj_attachment *attach;
+
+ len = param->u.generic_elem.len;
+ max_len = param_len - PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN;
+ if (max_len < 0 || max_len < len)
+ return -EINVAL;
+
+ alen = sizeof(*attach) + len;
+ attach = kmalloc(alen, GFP_KERNEL);
+ if (attach == NULL)
+ return -ENOMEM;
+
+ memset(attach, 0, alen);
+#define WLAN_FC_TYPE_MGMT 0
+#define WLAN_FC_STYPE_ASSOC_REQ 0
+#define WLAN_FC_STYPE_REASSOC_REQ 2
+
+ /* Note: endianness is covered by mgt_set_varlen */
+
+ attach->type = (WLAN_FC_TYPE_MGMT << 2) |
+ (WLAN_FC_STYPE_ASSOC_REQ << 4);
+ attach->id = -1;
+ attach->size = len;
+ memcpy(attach->data, param->u.generic_elem.data, len);
+
+ ret = mgt_set_varlen(priv, DOT11_OID_ATTACHMENT, attach, len);
+
+ if (ret == 0) {
+ attach->type = (WLAN_FC_TYPE_MGMT << 2) |
+ (WLAN_FC_STYPE_REASSOC_REQ << 4);
+
+ ret = mgt_set_varlen(priv, DOT11_OID_ATTACHMENT, attach, len);
+
+ if (ret == 0)
+ printk(KERN_DEBUG "%s: WPA IE Attachment was set\n",
+ ndev->name);
+ }
+
+ kfree(attach);
+ return ret;
+
+}
+
+static int
+prism2_ioctl_mlme(struct net_device *dev, struct prism2_hostapd_param *param)
+{
+ return -EOPNOTSUPP;
+}
+
+static int
+prism2_ioctl_scan_req(struct net_device *ndev,
+ struct prism2_hostapd_param *param)
+{
+ islpci_private *priv = netdev_priv(ndev);
+ int i, rvalue;
+ struct obj_bsslist *bsslist;
+ u32 noise = 0;
+ char *extra = "";
+ char *current_ev = "foo";
+ union oid_res_t r;
+
+ if (islpci_get_state(priv) < PRV_STATE_INIT) {
+ /* device is not ready, fail gently */
+ return 0;
+ }
+
+ /* first get the noise value. We will use it to report the link quality */
+ rvalue = mgt_get_request(priv, DOT11_OID_NOISEFLOOR, 0, NULL, &r);
+ noise = r.u;
+
+ /* Ask the device for a list of known bss. We can report at most
+ * IW_MAX_AP=64 to the range struct. But the device won't repport anything
+ * if you change the value of IWMAX_BSS=24.
+ */
+ rvalue |= mgt_get_request(priv, DOT11_OID_BSSLIST, 0, NULL, &r);
+ bsslist = r.ptr;
+
+ /* ok now, scan the list and translate its info */
+ for (i = 0; i < min(IW_MAX_AP, (int) bsslist->nr); i++)
+ current_ev = prism54_translate_bss(ndev, current_ev,
+ extra + IW_SCAN_MAX_DATA,
+ &(bsslist->bsslist[i]),
+ noise);
+ kfree(bsslist);
+
+ return rvalue;
+}
+
+static int
+prism54_hostapd(struct net_device *ndev, struct iw_point *p)
+{
+ struct prism2_hostapd_param *param;
+ int ret = 0;
+ u32 uwrq;
+
+ printk(KERN_DEBUG "prism54_hostapd - len=%d\n", p->length);
+ if (p->length < sizeof(struct prism2_hostapd_param) ||
+ p->length > PRISM2_HOSTAPD_MAX_BUF_SIZE || !p->pointer)
+ return -EINVAL;
+
+ param = (struct prism2_hostapd_param *) kmalloc(p->length, GFP_KERNEL);
+ if (param == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(param, p->pointer, p->length)) {
+ kfree(param);
+ return -EFAULT;
+ }
+
+ switch (param->cmd) {
+ case PRISM2_SET_ENCRYPTION:
+ printk(KERN_DEBUG "%s: Caught WPA supplicant set encryption request\n",
+ ndev->name);
+ ret = prism2_ioctl_set_encryption(ndev, param, p->length);
+ break;
+ case PRISM2_HOSTAPD_SET_GENERIC_ELEMENT:
+ printk(KERN_DEBUG "%s: Caught WPA supplicant set WPA IE request\n",
+ ndev->name);
+ ret = prism2_ioctl_set_generic_element(ndev, param,
+ p->length);
+ break;
+ case PRISM2_HOSTAPD_MLME:
+ printk(KERN_DEBUG "%s: Caught WPA supplicant MLME request\n",
+ ndev->name);
+ ret = prism2_ioctl_mlme(ndev, param);
+ break;
+ case PRISM2_HOSTAPD_SCAN_REQ:
+ printk(KERN_DEBUG "%s: Caught WPA supplicant scan request\n",
+ ndev->name);
+ ret = prism2_ioctl_scan_req(ndev, param);
+ break;
+ case PRISM54_SET_WPA:
+ printk(KERN_DEBUG "%s: Caught WPA supplicant wpa init request\n",
+ ndev->name);
+ uwrq = 1;
+ ret = prism54_set_wpa(ndev, NULL, &uwrq, NULL);
+ break;
+ case PRISM54_DROP_UNENCRYPTED:
+ printk(KERN_DEBUG "%s: Caught WPA drop unencrypted request\n",
+ ndev->name);
+#if 0
+ uwrq = 0x01;
+ mgt_set(priv, DOT11_OID_EXUNENCRYPTED, &uwrq);
+ down_write(&priv->mib_sem);
+ mgt_commit(priv);
+ up_write(&priv->mib_sem);
+#endif
+ /* Not necessary, as set_wpa does it, should we just do it here though? */
+ ret = 0;
+ break;
+ default:
+ printk(KERN_DEBUG "%s: Caught a WPA supplicant request that is not supported\n",
+ ndev->name);
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ if (ret == 0 && copy_to_user(p->pointer, param, p->length))
+ ret = -EFAULT;
+
+ kfree(param);
+
+ return ret;
+}
+