/* * wpa2 support requires the hypervisor version 2.0 or later
*/ staticinlineint wpa2_capable(void)
{ return 0 <= ps3_compare_firmware_version(2, 0, 0);
}
staticinlineint precise_ie(void)
{ return 0 <= ps3_compare_firmware_version(2, 2, 0);
} /* * post_eurus_cmd helpers
*/ struct eurus_cmd_arg_info { int pre_arg; /* command requires arg1, arg2 at POST COMMAND */ int post_arg; /* command requires arg1, arg2 at GET_RESULT */
};
/* * synthesize WPA/RSN IE data * See WiFi WPA specification and IEEE 802.11-2007 7.3.2.25 * for the format
*/ static size_t gelic_wl_synthesize_ie(u8 *buf, struct gelic_eurus_scan_info *scan)
{
const u8 *oui_header;
u8 *start = buf; int rsn; int ccmp;
pr_debug("%s: <- sec=%16x\n", __func__, scan->security); switch (be16_to_cpu(scan->security) & GELIC_EURUS_SCAN_SEC_MASK) { case GELIC_EURUS_SCAN_SEC_WPA:
rsn = 0; break; case GELIC_EURUS_SCAN_SEC_WPA2:
rsn = 1; break; default: /* WEP or none. No IE returned */ return 0;
}
switch (be16_to_cpu(scan->security) & GELIC_EURUS_SCAN_SEC_WPA_MASK) { case GELIC_EURUS_SCAN_SEC_WPA_TKIP:
ccmp = 0; break; case GELIC_EURUS_SCAN_SEC_WPA_AES:
ccmp = 1; break; default: if (rsn) {
ccmp = 1;
pr_info("%s: no cipher info. defaulted to CCMP\n",
__func__);
} else {
ccmp = 0;
pr_info("%s: no cipher info. defaulted to TKIP\n",
__func__);
}
}
if (rsn)
oui_header = rsn_oui; else
oui_header = wpa_oui;
/* element id */ if (rsn)
*buf++ = WLAN_EID_RSN; else
*buf++ = WLAN_EID_VENDOR_SPECIFIC;
/* length filed; set later */
buf++;
/* wpa special header */ if (!rsn) {
memcpy(buf, wpa_oui, OUI_LEN);
buf += OUI_LEN;
*buf++ = 0x01;
}
/* version */
*buf++ = 0x01; /* version 1.0 */
*buf++ = 0x00;
/* group cipher */
memcpy(buf, oui_header, OUI_LEN);
buf += OUI_LEN;
/* * translate the scan informations from hypervisor to a * independent format
*/ staticchar *gelic_wl_translate_scan(struct net_device *netdev, struct iw_request_info *info, char *ev, char *stop, struct gelic_wl_scan_info *network)
{ struct iw_event iwe; struct gelic_eurus_scan_info *scan = network->hwinfo; char *tmp;
u8 rate; unsignedint i, j, len;
u8 buf[64]; /* arbitrary size large enough */
pr_debug("%s: <-\n", __func__);
/* first entry should be AP's mac address */
iwe.cmd = SIOCGIWAP;
iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
memcpy(iwe.u.ap_addr.sa_data, &scan->bssid[2], ETH_ALEN);
ev = iwe_stream_add_event(info, ev, stop, &iwe, IW_EV_ADDR_LEN);
/* RSN */
memset(&iwe, 0, sizeof(iwe)); if (be16_to_cpu(scan->size) <= sizeof(*scan)) { /* If wpa[2] capable station, synthesize IE and put it */
len = gelic_wl_synthesize_ie(buf, scan); if (len) {
iwe.cmd = IWEVGENIE;
iwe.u.data.length = len;
ev = iwe_stream_add_point(info, ev, stop, &iwe, buf);
}
} else { /* this scan info has IE data */ struct ie_info ie_info;
size_t data_len;
staticint gelic_wl_get_scan(struct net_device *netdev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{ struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); struct gelic_wl_scan_info *scan_info; char *ev = extra; char *stop = ev + wrqu->data.length; int ret = 0; unsignedlong this_time = jiffies;
pr_debug("%s: <-\n", __func__); if (mutex_lock_interruptible(&wl->scan_lock)) return -EAGAIN;
switch (wl->scan_stat) { case GELIC_WL_SCAN_STAT_SCANNING: /* If a scan in progress, caller should call me again */
ret = -EAGAIN; goto out; case GELIC_WL_SCAN_STAT_INIT: /* last scan request failed or never issued */
ret = -ENODEV; goto out; case GELIC_WL_SCAN_STAT_GOT_LIST: /* ok, use current list */ break;
}
list_for_each_entry(scan_info, &wl->network_list, list) { if (wl->scan_age == 0 ||
time_after(scan_info->last_scanned + wl->scan_age,
this_time))
ev = gelic_wl_translate_scan(netdev, info,
ev, stop,
scan_info); else
pr_debug("%s:entry too old\n", __func__);
if (stop - ev <= IW_EV_ADDR_LEN) {
ret = -E2BIG; goto out;
}
}
staticint gelic_wl_set_auth(struct net_device *netdev, struct iw_request_info *info, union iwreq_data *data, char *extra)
{ struct iw_param *param = &data->param; struct gelic_wl_info *wl = port_wl(netdev_port(netdev)); unsignedlong irqflag; int ret = 0;
pr_debug("%s: <- %d\n", __func__, param->flags & IW_AUTH_INDEX);
spin_lock_irqsave(&wl->lock, irqflag); switch (param->flags & IW_AUTH_INDEX) { case IW_AUTH_WPA_VERSION: if (param->value & IW_AUTH_WPA_VERSION_DISABLED) {
pr_debug("%s: NO WPA selected\n", __func__);
wl->wpa_level = GELIC_WL_WPA_LEVEL_NONE;
wl->group_cipher_method = GELIC_WL_CIPHER_WEP;
wl->pairwise_cipher_method = GELIC_WL_CIPHER_WEP;
} if (param->value & IW_AUTH_WPA_VERSION_WPA) {
pr_debug("%s: WPA version 1 selected\n", __func__);
wl->wpa_level = GELIC_WL_WPA_LEVEL_WPA;
wl->group_cipher_method = GELIC_WL_CIPHER_TKIP;
wl->pairwise_cipher_method = GELIC_WL_CIPHER_TKIP;
wl->auth_method = GELIC_EURUS_AUTH_OPEN;
} if (param->value & IW_AUTH_WPA_VERSION_WPA2) { /* * As the hypervisor may not tell the cipher * information of the AP if it is WPA2, * you will not decide suitable cipher from * its beacon. * You should have knowledge about the AP's * cipher information in other method prior to * the association.
*/ if (!precise_ie())
pr_info("%s: WPA2 may not work\n", __func__); if (wpa2_capable()) {
wl->wpa_level = GELIC_WL_WPA_LEVEL_WPA2;
wl->group_cipher_method = GELIC_WL_CIPHER_AES;
wl->pairwise_cipher_method =
GELIC_WL_CIPHER_AES;
wl->auth_method = GELIC_EURUS_AUTH_OPEN;
} else
ret = -EINVAL;
} break;
case IW_AUTH_CIPHER_PAIRWISE: if (param->value &
(IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_WEP40)) {
pr_debug("%s: WEP selected\n", __func__);
wl->pairwise_cipher_method = GELIC_WL_CIPHER_WEP;
} if (param->value & IW_AUTH_CIPHER_TKIP) {
pr_debug("%s: TKIP selected\n", __func__);
wl->pairwise_cipher_method = GELIC_WL_CIPHER_TKIP;
} if (param->value & IW_AUTH_CIPHER_CCMP) {
pr_debug("%s: CCMP selected\n", __func__);
wl->pairwise_cipher_method = GELIC_WL_CIPHER_AES;
} if (param->value & IW_AUTH_CIPHER_NONE) {
pr_debug("%s: no auth selected\n", __func__);
wl->pairwise_cipher_method = GELIC_WL_CIPHER_NONE;
} break; case IW_AUTH_CIPHER_GROUP: if (param->value &
(IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_WEP40)) {
pr_debug("%s: WEP selected\n", __func__);
wl->group_cipher_method = GELIC_WL_CIPHER_WEP;
} if (param->value & IW_AUTH_CIPHER_TKIP) {
pr_debug("%s: TKIP selected\n", __func__);
wl->group_cipher_method = GELIC_WL_CIPHER_TKIP;
} if (param->value & IW_AUTH_CIPHER_CCMP) {
pr_debug("%s: CCMP selected\n", __func__);
wl->group_cipher_method = GELIC_WL_CIPHER_AES;
} if (param->value & IW_AUTH_CIPHER_NONE) {
pr_debug("%s: no auth selected\n", __func__);
wl->group_cipher_method = GELIC_WL_CIPHER_NONE;
} break; case IW_AUTH_80211_AUTH_ALG: if (param->value & IW_AUTH_ALG_SHARED_KEY) {
pr_debug("%s: shared key specified\n", __func__);
wl->auth_method = GELIC_EURUS_AUTH_SHARED;
} elseif (param->value & IW_AUTH_ALG_OPEN_SYSTEM) {
pr_debug("%s: open system specified\n", __func__);
wl->auth_method = GELIC_EURUS_AUTH_OPEN;
} else
ret = -EINVAL; break;
if (flags & IW_ENCODE_NOKEY) { /* if just IW_ENCODE_NOKEY, change current key index */ if (!flags && index_specified) {
wl->current_key = key_index; goto done;
}
/* * scanning helpers
*/ staticint gelic_wl_start_scan(struct gelic_wl_info *wl, int always_scan,
u8 *essid, size_t essid_len)
{ struct gelic_eurus_cmd *cmd; int ret = 0; void *buf = NULL;
size_t len;
pr_debug("%s: <- always=%d\n", __func__, always_scan); if (mutex_lock_interruptible(&wl->scan_lock)) return -ERESTARTSYS;
/* * If already a scan in progress, do not trigger more
*/ if (wl->scan_stat == GELIC_WL_SCAN_STAT_SCANNING) {
pr_debug("%s: scanning now\n", __func__); goto out;
}
init_completion(&wl->scan_done); /* * If we have already a bss list, don't try to get new * unless we are doing an ESSID scan
*/ if ((!essid_len && !always_scan)
&& wl->scan_stat == GELIC_WL_SCAN_STAT_GOT_LIST) {
pr_debug("%s: already has the list\n", __func__);
complete(&wl->scan_done); goto out;
}
/* ESSID scan ? */ if (essid_len && essid) {
buf = (void *)__get_free_page(GFP_KERNEL); if (!buf) {
ret = -ENOMEM; goto out;
}
len = IW_ESSID_MAX_SIZE; /* hypervisor always requires 32 */
memset(buf, 0, len);
memcpy(buf, essid, essid_len);
pr_debug("%s: essid scan='%s'\n", __func__, (char *)buf);
} else
len = 0;
if (wl->scan_stat != GELIC_WL_SCAN_STAT_SCANNING) { /* * stop() may be called while scanning, ignore result
*/
pr_debug("%s: scan complete when stat != scanning(%d)\n",
__func__, wl->scan_stat); goto out;
}
/* OK, bss list retrieved */
wl->scan_stat = GELIC_WL_SCAN_STAT_GOT_LIST;
/* mark all entries are old */
list_for_each_entry_safe(target, tmp, &wl->network_list, list) {
target->valid = 0; /* expire too old entries */ if (time_before(target->last_scanned + wl->scan_age,
this_time)) {
kfree(target->hwinfo);
target->hwinfo = NULL;
list_move_tail(&target->list, &wl->network_free_list);
}
}
/* put them in the network_list */ for (i = 0, scan_info_size = 0, scan_info = buf;
scan_info_size < data_len;
i++, scan_info_size += be16_to_cpu(scan_info->size),
scan_info = (void *)scan_info + be16_to_cpu(scan_info->size)) {
pr_debug("%s:size=%d bssid=%pM scan_info=%p\n", __func__,
be16_to_cpu(scan_info->size),
&scan_info->bssid[2], scan_info);
/* * The wireless firmware may return invalid channel 0 and/or * invalid rate if the AP emits zero length SSID ie. As this * scan information is useless, ignore it
*/ if (!be16_to_cpu(scan_info->channel) || !scan_info->rate[0]) {
pr_debug("%s: invalid scan info\n", __func__); continue;
}
found = 0;
oldest = NULL;
list_for_each_entry(target, &wl->network_list, list) { if (ether_addr_equal(&target->hwinfo->bssid[2],
&scan_info->bssid[2])) {
found = 1;
pr_debug("%s: same BBS found scanned list\n",
__func__); break;
} if (!oldest ||
(target->last_scanned < oldest->last_scanned))
oldest = target;
}
if (!found) { /* not found in the list */ if (list_empty(&wl->network_free_list)) { /* expire oldest */
target = oldest;
} else {
target = list_entry(wl->network_free_list.next, struct gelic_wl_scan_info,
list);
}
}
/* copy hw scan info */
target->essid_len = strnlen(scan_info->essid, sizeof(scan_info->essid));
target->rate_len = 0; for (r = 0; r < 12; r++) if (scan_info->rate[r])
target->rate_len++; if (8 < target->rate_len)
pr_info("%s: AP returns %d rates\n", __func__,
target->rate_len);
target->rate_ext_len = 0; for (r = 0; r < 16; r++) if (scan_info->ext_rate[r])
target->rate_ext_len++;
list_move_tail(&target->list, &wl->network_list);
}
memset(&data, 0, sizeof(data));
wireless_send_event(port_to_netdev(wl_port(wl)), SIOCGIWSCAN, &data,
NULL);
out:
free_page((unsignedlong)buf);
complete(&wl->scan_done);
mutex_unlock(&wl->scan_lock);
pr_debug("%s:end\n", __func__);
}
/* * Select an appropriate bss from current scan list regarding * current settings from userspace. * The caller must hold wl->scan_lock, * and on the state of wl->scan_state == GELIC_WL_SCAN_GOT_LIST
*/ staticvoid update_best(struct gelic_wl_scan_info **best, struct gelic_wl_scan_info *candid, int *best_weight, int *weight)
{ if (*best_weight < ++(*weight)) {
*best_weight = *weight;
*best = candid;
}
}
/* * Setup WEP configuration to the chip * The caller must hold wl->scan_lock, * and on the state of wl->scan_state == GELIC_WL_SCAN_GOT_LIST
*/ staticint gelic_wl_do_wep_setup(struct gelic_wl_info *wl)
{ unsignedint i; struct gelic_eurus_wep_cfg *wep; struct gelic_eurus_cmd *cmd; int wep104 = 0; int have_key = 0; int ret = 0;
pr_debug("%s: <-\n", __func__); /* we can assume no one should uses the buffer */
wep = (struct gelic_eurus_wep_cfg *)__get_free_page(GFP_KERNEL); if (!wep) return -ENOMEM;
memset(wep, 0, sizeof(*wep));
if (wl->group_cipher_method == GELIC_WL_CIPHER_WEP) {
pr_debug("%s: WEP mode\n", __func__); for (i = 0; i < GELIC_WEP_KEYS; i++) { if (!test_bit(i, &wl->key_enabled)) continue;
#ifdef DEBUG staticconstchar *wpasecstr(enum gelic_eurus_wpa_security sec)
{ switch (sec) { case GELIC_EURUS_WPA_SEC_NONE: return"NONE"; case GELIC_EURUS_WPA_SEC_WPA_TKIP_TKIP: return"WPA_TKIP_TKIP"; case GELIC_EURUS_WPA_SEC_WPA_TKIP_AES: return"WPA_TKIP_AES"; case GELIC_EURUS_WPA_SEC_WPA_AES_AES: return"WPA_AES_AES"; case GELIC_EURUS_WPA_SEC_WPA2_TKIP_TKIP: return"WPA2_TKIP_TKIP"; case GELIC_EURUS_WPA_SEC_WPA2_TKIP_AES: return"WPA2_TKIP_AES"; case GELIC_EURUS_WPA_SEC_WPA2_AES_AES: return"WPA2_AES_AES";
} return"";
}; #endif
staticint gelic_wl_do_wpa_setup(struct gelic_wl_info *wl)
{ struct gelic_eurus_wpa_cfg *wpa; struct gelic_eurus_cmd *cmd;
u16 security; int ret = 0;
pr_debug("%s: <-\n", __func__); /* we can assume no one should uses the buffer */
wpa = (struct gelic_eurus_wpa_cfg *)__get_free_page(GFP_KERNEL); if (!wpa) return -ENOMEM;
memset(wpa, 0, sizeof(*wpa));
if (!test_bit(GELIC_WL_STAT_WPA_PSK_SET, &wl->stat))
pr_info("%s: PSK not configured yet\n", __func__);
/* WEP/WPA */ switch (wl->wpa_level) { case GELIC_WL_WPA_LEVEL_NONE: /* If WEP or no security, setup WEP config */
ret = gelic_wl_do_wep_setup(wl); break; case GELIC_WL_WPA_LEVEL_WPA: case GELIC_WL_WPA_LEVEL_WPA2:
ret = gelic_wl_do_wpa_setup(wl); break;
}
if (ret) {
pr_debug("%s: WEP/WPA setup failed %d\n", __func__,
ret);
ret = -EPERM;
gelic_wl_send_iwap_event(wl, NULL); goto out;
}
/* * If we fall here in the middle of association, * associate_bss() should be waiting for complation of * wl->assoc_done. * As it waits with timeout, just leave assoc_done * uncompleted, then it terminates with timeout
*/ if (!mutex_trylock(&wl->assoc_stat_lock)) {
pr_debug("%s: already locked\n", __func__);
lock = 0;
} else {
pr_debug("%s: obtain lock\n", __func__);
lock = 1;
}
/* * Wait for bss scan completion * If we have scan list already, gelic_wl_start_scan() * returns OK and raises the complete. Thus, * it's ok to wait unconditionally here
*/
wait_for_completion(&wl->scan_done);
pr_debug("%s: scan done\n", __func__);
mutex_lock(&wl->scan_lock); if (wl->scan_stat != GELIC_WL_SCAN_STAT_GOT_LIST) {
gelic_wl_send_iwap_event(wl, NULL);
pr_info("%s: no scan list. association failed\n", __func__); goto scan_lock_out;
}
/* find best matching bss */
best_bss = gelic_wl_find_best_bss(wl); if (!best_bss) {
gelic_wl_send_iwap_event(wl, NULL);
pr_info("%s: no bss matched. association failed\n", __func__); goto scan_lock_out;
}
/* ok, do association */
ret = gelic_wl_associate_bss(wl, best_bss); if (ret)
pr_info("%s: association failed %d\n", __func__, ret);
scan_lock_out:
mutex_unlock(&wl->scan_lock);
out:
mutex_unlock(&wl->assoc_stat_lock);
} /* * Interrupt handler * Called from the ethernet interrupt handler * Processes wireless specific virtual interrupts only
*/ void gelic_wl_interrupt(struct net_device *netdev, u64 status)
{ struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
if (status & GELIC_CARD_WLAN_COMMAND_COMPLETED) {
pr_debug("%s:cmd complete\n", __func__);
complete(&wl->cmd_done_intr);
}
wl->eurus_cmd_queue = create_singlethread_workqueue("gelic_cmd"); if (!wl->eurus_cmd_queue) goto fail_cmd_workqueue;
wl->event_queue = create_singlethread_workqueue("gelic_event"); if (!wl->event_queue) goto fail_event_workqueue;
INIT_LIST_HEAD(&wl->network_free_list);
INIT_LIST_HEAD(&wl->network_list); for (i = 0; i < GELIC_WL_BSS_MAX_ENT; i++)
list_add_tail(&wl->networks[i].list,
&wl->network_free_list);
init_completion(&wl->cmd_done_intr);
scan_info = wl->networks; for (i = 0; i < GELIC_WL_BSS_MAX_ENT; i++, scan_info++)
kfree(scan_info->hwinfo);
kfree(wl->networks);
free_netdev(port_to_netdev(wl_port(wl)));
pr_debug("%s: ->\n", __func__);
}
staticint gelic_wl_try_associate(struct net_device *netdev)
{ struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); int ret = -1; unsignedint i;
pr_debug("%s: <-\n", __func__);
/* check constraits for start association */ /* for no access restriction AP */ if (wl->group_cipher_method == GELIC_WL_CIPHER_NONE) { if (test_bit(GELIC_WL_STAT_CONFIGURED,
&wl->stat)) goto do_associate; else {
pr_debug("%s: no wep, not configured\n", __func__); return ret;
}
}
/* for WEP, one of four keys should be set */ if (wl->group_cipher_method == GELIC_WL_CIPHER_WEP) { /* one of keys set */ for (i = 0; i < GELIC_WEP_KEYS; i++) { if (test_bit(i, &wl->key_enabled)) goto do_associate;
}
pr_debug("%s: WEP, but no key specified\n", __func__); return ret;
}
/* for WPA[2], psk should be set */ if ((wl->group_cipher_method == GELIC_WL_CIPHER_TKIP) ||
(wl->group_cipher_method == GELIC_WL_CIPHER_AES)) { if (test_bit(GELIC_WL_STAT_WPA_PSK_SET,
&wl->stat)) goto do_associate; else {
pr_debug("%s: AES/TKIP, but PSK not configured\n",
__func__); return ret;
}
}
do_associate:
ret = schedule_delayed_work(&wl->assoc_work, 0);
pr_debug("%s: start association work %d\n", __func__, ret); return ret;
}
/* * If scann process is running on chip, * further requests will be rejected
*/ if (wl->scan_stat == GELIC_WL_SCAN_STAT_SCANNING)
wait_for_completion_timeout(&wl->scan_done, HZ);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.