static int wlcompat_private_ioctl(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { int *value = (int *) wrqu->name; switch (info->cmd) { case WLCOMPAT_SET_MONITOR: { if (wl_ioctl(dev, WLC_SET_MONITOR, value, sizeof(int)) < 0) return -EINVAL; break; } case WLCOMPAT_GET_MONITOR: { if (wl_ioctl(dev, WLC_GET_MONITOR, extra, sizeof(int)) < 0) return -EINVAL; break; } case WLCOMPAT_SET_TXPWR_LIMIT: { int val; if (wl_get_val(dev, "qtxpower", &val, sizeof(int)) < 0) return -EINVAL; if (*extra > 0) val |= WL_TXPWR_OVERRIDE; else val &= ~WL_TXPWR_OVERRIDE; if (wl_set_val(dev, "qtxpower", &val, sizeof(int)) < 0) return -EINVAL; break; } case WLCOMPAT_GET_TXPWR_LIMIT: { if (wl_get_val(dev, "qtxpower", value, sizeof(int)) < 0) return -EINVAL; *value = ((*value & WL_TXPWR_OVERRIDE) == WL_TXPWR_OVERRIDE ? 1 : 0); break; } case WLCOMPAT_SET_ANTDIV: { if (wl_ioctl(dev, WLC_SET_ANTDIV, value, sizeof(int)) < 0) return -EINVAL; break; } case WLCOMPAT_GET_ANTDIV: { if (wl_ioctl(dev, WLC_GET_ANTDIV, extra, sizeof(int)) < 0) return -EINVAL; break; } case WLCOMPAT_SET_TXANT: { if (wl_ioctl(dev, WLC_SET_TXANT, value, sizeof(int)) < 0) return -EINVAL; break; } case WLCOMPAT_GET_TXANT: { if (wl_ioctl(dev, WLC_GET_TXANT, extra, sizeof(int)) < 0) return -EINVAL; break; } default: { return -EINVAL; } } return 0; }
int wl_set_int(char *name, char *var, int val) { return wl_set_val(name, var, &val, sizeof(val)); }
static int wlcompat_ioctl(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { switch (info->cmd) { case SIOCGIWNAME: strcpy(wrqu->name, "IEEE 802.11-DS"); break; case SIOCGIWFREQ: { channel_info_t ci; if (wl_ioctl(dev,WLC_GET_CHANNEL, &ci, sizeof(ci)) < 0) return -EINVAL; wrqu->freq.m = ci.target_channel; wrqu->freq.e = 0; break; } case SIOCSIWFREQ: { if (wrqu->freq.m == -1) { wrqu->freq.m = 0; if (wl_ioctl(dev, WLC_SET_CHANNEL, &wrqu->freq.m, sizeof(int)) < 0) return -EINVAL; } else { if (wrqu->freq.e == 1) { int channel = 0; int f = wrqu->freq.m / 100000; while ((channel < NUM_CHANNELS + 1) && (f != channel_frequency[channel])) channel++; if (channel == NUM_CHANNELS) // channel not found return -EINVAL; wrqu->freq.e = 0; wrqu->freq.m = channel + 1; } if ((wrqu->freq.e == 0) && (wrqu->freq.m < 1000)) { if (wl_ioctl(dev, WLC_SET_CHANNEL, &wrqu->freq.m, sizeof(int)) < 0) return -EINVAL; } else { return -EINVAL; } } break; } case SIOCSIWAP: { int ap = 0; if (wrqu->ap_addr.sa_family != ARPHRD_ETHER) return -EINVAL; if (wl_ioctl(dev, WLC_GET_AP, &ap, sizeof(ap)) < 0) return -EINVAL; if (wl_ioctl(dev, (ap ? WLC_SET_BSSID : WLC_REASSOC), wrqu->ap_addr.sa_data, 6) < 0) return -EINVAL; break; } case SIOCGIWAP: { wrqu->ap_addr.sa_family = ARPHRD_ETHER; if (wl_ioctl(dev,WLC_GET_BSSID,wrqu->ap_addr.sa_data,6) < 0) return -EINVAL; break; } case SIOCGIWESSID: { wlc_ssid_t ssid; if (wl_ioctl(dev,WLC_GET_SSID, &ssid, sizeof(wlc_ssid_t)) < 0) return -EINVAL; wrqu->essid.flags = wrqu->data.flags = 1; wrqu->essid.length = wrqu->data.length = ssid.SSID_len + 1; memcpy(extra,ssid.SSID,ssid.SSID_len + 1); break; } case SIOCSIWESSID: { wlc_ssid_t ssid; memset(&ssid, 0, sizeof(ssid)); ssid.SSID_len = strlen(extra); if (ssid.SSID_len > WLC_ESSID_MAX_SIZE) ssid.SSID_len = WLC_ESSID_MAX_SIZE; memcpy(ssid.SSID, extra, ssid.SSID_len); if (wl_ioctl(dev, WLC_SET_SSID, &ssid, sizeof(ssid)) < 0) return -EINVAL; break; } case SIOCGIWRTS: { if (wl_ioctl(dev,WLC_GET_RTS,&(wrqu->rts.value),sizeof(int)) < 0) return -EINVAL; break; } case SIOCSIWRTS: { if (wl_ioctl(dev,WLC_SET_RTS,&(wrqu->rts.value),sizeof(int)) < 0) return -EINVAL; break; } case SIOCGIWFRAG: { if (wl_ioctl(dev,WLC_GET_FRAG,&(wrqu->frag.value),sizeof(int)) < 0) return -EINVAL; break; } case SIOCSIWFRAG: { if (wl_ioctl(dev,WLC_SET_FRAG,&(wrqu->frag.value),sizeof(int)) < 0) return -EINVAL; break; } case SIOCGIWTXPOW: { int radio; wl_ioctl(dev, WLC_GET_RADIO, &radio, sizeof(int)); if (wl_get_val(dev, "qtxpower", &(wrqu->txpower.value), sizeof(int)) < 0) return -EINVAL; wrqu->txpower.value &= ~WL_TXPWR_OVERRIDE; wrqu->txpower.value /= 4; wrqu->txpower.fixed = 0; wrqu->txpower.disabled = radio; wrqu->txpower.flags = IW_TXPOW_DBM; break; } case SIOCSIWTXPOW: { /* This is weird: WLC_SET_RADIO with 1 as argument disables the radio */ int radio = wrqu->txpower.disabled; wl_ioctl(dev, WLC_SET_RADIO, &radio, sizeof(int)); if (!wrqu->txpower.disabled && (wrqu->txpower.value > 0)) { int value; if (wl_get_val(dev, "qtxpower", &value, sizeof(int)) < 0) return -EINVAL; value &= WL_TXPWR_OVERRIDE; wrqu->txpower.value *= 4; wrqu->txpower.value |= value; if (wrqu->txpower.flags != IW_TXPOW_DBM) return -EINVAL; if (wrqu->txpower.value > 0) if (wl_set_val(dev, "qtxpower", &(wrqu->txpower.value), sizeof(int)) < 0) return -EINVAL; } break; } case SIOCSIWENCODE: { int val = 0, wep = 1, wrestrict = 1; int index = (wrqu->data.flags & IW_ENCODE_INDEX) - 1; if (index < 0) index = get_primary_key(dev); if (wrqu->data.flags & IW_ENCODE_DISABLED) { wep = 0; if (wl_ioctl(dev, WLC_SET_WSEC, &wep, sizeof(val)) < 0) return -EINVAL; return 0; } if (wl_ioctl(dev, WLC_SET_WSEC, &wep, sizeof(val)) < 0) return -EINVAL; if (wrqu->data.flags & IW_ENCODE_OPEN) wrestrict = 0; if (wrqu->data.pointer && (wrqu->data.length > 0) && (wrqu->data.length <= 16)) { wl_wsec_key_t key; memset(&key, 0, sizeof(key)); key.flags = WL_PRIMARY_KEY; key.len = wrqu->data.length; key.index = index; memcpy(key.data, wrqu->data.pointer, wrqu->data.length); if (wl_ioctl(dev, WLC_SET_KEY, &key, sizeof(key)) < 0) return -EINVAL; } if (index >= 0) wl_ioctl(dev, WLC_SET_KEY_PRIMARY, &index, sizeof(index)); if (wrestrict >= 0) wl_ioctl(dev, WLC_SET_WEP_RESTRICT, &wrestrict, sizeof(wrestrict)); break; } case SIOCGIWENCODE: { int val; if (wl_ioctl(dev, WLC_GET_WEP, &val, sizeof(val)) < 0) return -EINVAL; if (val > 0) { int key = get_primary_key(dev); wrqu->data.flags = IW_ENCODE_ENABLED; if (key-- > 0) { int *info_addr; wkey *wep_key; info_addr = (int *) dev->priv; wep_key = (wkey *) ((*info_addr) + 0x2752 + (key * 0x110)); wrqu->data.flags |= key + 1; wrqu->data.length = wep_key->len; memset(extra, 0, 16); memcpy(extra, wep_key->data, 16); } else { wrqu->data.flags |= IW_ENCODE_NOKEY; } } else { wrqu->data.flags = IW_ENCODE_DISABLED; } break; } case SIOCGIWRANGE: { return wlcompat_ioctl_getiwrange(dev, extra); break; } case SIOCSIWMODE: { int ap = -1, infra = -1, passive = 0, wet = 0; switch (wrqu->mode) { case IW_MODE_MONITOR: passive = 1; break; case IW_MODE_ADHOC: infra = 0; ap = 0; break; case IW_MODE_MASTER: infra = 1; ap = 1; break; case IW_MODE_INFRA: infra = 1; ap = 0; break; case IW_MODE_REPEAT: infra = 1; ap = 0; wet = 1; break; default: return -EINVAL; } wl_ioctl(dev, WLC_SET_PASSIVE, &passive, sizeof(passive)); wl_ioctl(dev, WLC_SET_MONITOR, &passive, sizeof(passive)); wl_ioctl(dev, WLC_SET_WET, &wet, sizeof(wet)); if (ap >= 0) wl_ioctl(dev, WLC_SET_AP, &ap, sizeof(ap)); if (infra >= 0) wl_ioctl(dev, WLC_SET_INFRA, &infra, sizeof(infra)); break; } case SIOCGIWMODE: { int ap, infra, wet, passive; if (wl_ioctl(dev, WLC_GET_AP, &ap, sizeof(ap)) < 0) return -EINVAL; if (wl_ioctl(dev, WLC_GET_INFRA, &infra, sizeof(infra)) < 0) return -EINVAL; if (wl_ioctl(dev, WLC_GET_PASSIVE, &passive, sizeof(passive)) < 0) return -EINVAL; if (wl_ioctl(dev, WLC_GET_WET, &wet, sizeof(wet)) < 0) return -EINVAL; if (passive) { wrqu->mode = IW_MODE_MONITOR; } else if (!infra) { wrqu->mode = IW_MODE_ADHOC; } else { if (ap) { wrqu->mode = IW_MODE_MASTER; } else { if (wet) { wrqu->mode = IW_MODE_REPEAT; } else { wrqu->mode = IW_MODE_INFRA; } } } break; } default: { if (info->cmd >= SIOCIWFIRSTPRIV) return wlcompat_private_ioctl(dev, info, wrqu, extra); return -EINVAL; } } return 0; }