Subject: mac80211: BT3 AMP support needs work and maybe splitting up Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- include/linux/nl80211.h | 58 ++++++++- include/net/cfg80211.h | 23 +++ net/mac80211/Makefile | 2 net/mac80211/bt3-amp.c | 289 +++++++++++++++++++++++++++++++++++++++++++++ net/mac80211/bt3-amp.h | 18 ++ net/mac80211/cfg.c | 8 + net/mac80211/ieee80211_i.h | 22 +++ net/mac80211/iface.c | 72 ++++++----- net/wireless/Kconfig | 4 net/wireless/nl80211.c | 187 ++++++++++++++++++++++++++++- 10 files changed, 649 insertions(+), 34 deletions(-) --- wireless-testing.orig/include/linux/nl80211.h 2009-12-01 18:23:13.000000000 +0100 +++ wireless-testing/include/linux/nl80211.h 2009-12-01 18:28:23.000000000 +0100 @@ -270,6 +270,28 @@ * @NL80211_CMD_SET_WIPHY_NETNS: Set a wiphy's netns. Note that all devices * associated with this wiphy must be down and will follow. * + * @NL80211_CMD_HCI_AMP_ADD: Add an HCI AMP for BT3. %NL80211_ATTR_MAC can be + * used to select a different local address for the HCI AMP, e.g., when + * running in AP mode. Returns %NL80211_HCI_AMP_COOKIE. + * @NL80211_CMD_HCI_AMP_DELETE: Delete an HCI AMP specified by + * %NL80211_HCI_AMP_COOKIE. This is also used as an event to indicate that + * an HCI AMP was deleted. + * @NL80211_CMD_HCI_AMP_HCI_EVENT: Send an HCI event through an HCI AMP + * specified by %NL80211_ATTR_HCI_AMP_COOKIE with contents in + * %NL80211_ATTR_FRAME. This is also used as an event to indicate + * reception of an HCI event on an HCI AMP. + * @NL80211_CMD_HCI_AMP_BIND: Bind an HCI AMP (%NL80211_ATTR_HCI_AMP_COOKIE) to + * a BT3 peer's IEEE 802.11 address (%NL80211_ATTR_MAC). + * %NL80211_ATTR_HCI_AMP_INITIATOR is used to indicate that the local end + * is the BT3 initiator (vs. responder). %NL80211_ATTR_IE can be used to + * add additional IEs to the Beacon and Probe Response frames. This + * command instructs the driver to start beaconing and to start IEEE + * 802.11 authentication/association process. This is also used as an + * event to indicate the result of the AMP link establishment. + * @NL80211_CMD_HCI_AMP_EAPOL: Send an EAPOL frame (%NL80211_ATTR_FRAME) over + * HCI AMP (%NL80211_ATTR_HCI_AMP_COOKIE). This is also used as an event + * to indicate reception of an EAPOL frame over an HCI AMP. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -353,6 +375,12 @@ enum nl80211_commands { NL80211_CMD_DEL_PMKSA, NL80211_CMD_FLUSH_PMKSA, + NL80211_CMD_HCI_AMP_ADD, + NL80211_CMD_HCI_AMP_DELETE, + NL80211_CMD_HCI_AMP_HCI_EVENT, + NL80211_CMD_HCI_AMP_BIND, + NL80211_CMD_HCI_AMP_EAPOL, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -606,6 +634,12 @@ enum nl80211_commands { * @NL80211_ATTR_MAX_NUM_PMKIDS: maximum number of PMKIDs a firmware can * cache, a wiphy attribute. * + * @NL80211_ATTR_HCI_AMP_COOKIE: Identifier for HCI AMP (u32) + * @NL80211_ATTR_HCI_AMP_INITIATOR: Indicator of whether the local end is the + BT Initiator (flag is included) or Responder (flag is not included) + * @NL80211_ATTR_HCI_AMP_RESULT: HCI AMP association result + * (&enum nl80211_hci_amp_result) + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -743,6 +777,10 @@ enum nl80211_attrs { NL80211_ATTR_PMKID, NL80211_ATTR_MAX_NUM_PMKIDS, + NL80211_ATTR_HCI_AMP_COOKIE, + NL80211_ATTR_HCI_AMP_INITIATOR, + NL80211_ATTR_HCI_AMP_RESULT, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -800,6 +838,8 @@ enum nl80211_attrs { * @NL80211_IFTYPE_MESH_POINT: mesh point * @NL80211_IFTYPE_MAX: highest interface type number currently defined * @__NL80211_IFTYPE_AFTER_LAST: internal use + * @NL80211_IFTYPE_HCI_AMP: internal interface type used for BT3 AMP inside + * mac80211 * * These values are used with the %NL80211_ATTR_IFTYPE * to set the type of an interface. @@ -817,7 +857,9 @@ enum nl80211_iftype { /* keep last */ __NL80211_IFTYPE_AFTER_LAST, - NL80211_IFTYPE_MAX = __NL80211_IFTYPE_AFTER_LAST - 1 + NL80211_IFTYPE_MAX = __NL80211_IFTYPE_AFTER_LAST - 1, + /* hidden types (used internally in mac80211) */ + NL80211_IFTYPE_HCI_AMP, }; /** @@ -1442,4 +1484,18 @@ enum nl80211_key_attributes { NL80211_KEY_MAX = __NL80211_KEY_AFTER_LAST - 1 }; +/** + * enum nl80211_hci_amp_result - HCI AMP bind result + * @NL80211_HCI_AMP_SUCCESS: bind completed successfully + * @NL80211_HCI_AMP_FAILED: bind operation failed due to unsuccessful + * association + * @NL80211_HCI_AMP_TIMEOUT: bind operation failed due to a timeout when + * waiting for a management frame + */ +enum nl80211_hci_amp_result { + NL80211_HCI_AMP_SUCCESS, + NL80211_HCI_AMP_FAILED, + NL80211_HCI_AMP_TIMEOUT, +}; + #endif /* __LINUX_NL80211_H */ --- wireless-testing.orig/net/wireless/nl80211.c 2009-12-01 18:23:13.000000000 +0100 +++ wireless-testing/net/wireless/nl80211.c 2009-12-01 18:28:23.000000000 +0100 @@ -141,6 +141,9 @@ static struct nla_policy nl80211_policy[ [NL80211_ATTR_4ADDR] = { .type = NLA_U8 }, [NL80211_ATTR_PMKID] = { .type = NLA_BINARY, .len = WLAN_PMKID_LEN }, + [NL80211_ATTR_HCI_AMP_COOKIE] = { .type = NLA_U32 }, + [NL80211_ATTR_HCI_AMP_INITIATOR] = { .type = NLA_FLAG }, + [NL80211_ATTR_HCI_AMP_RESULT] = { .type = NLA_U32 }, }; /* policy for the attributes */ @@ -460,7 +463,7 @@ static int nl80211_send_wiphy(struct sk_ goto nla_put_failure; i = 0; - while (ifmodes) { + while (ifmodes && i <= NL80211_IFTYPE_MAX) { if (ifmodes & 1) NLA_PUT_FLAG(msg, i); ifmodes >>= 1; @@ -569,6 +572,9 @@ static int nl80211_send_wiphy(struct sk_ CMD(set_pmksa, SET_PMKSA); CMD(del_pmksa, DEL_PMKSA); CMD(flush_pmksa, FLUSH_PMKSA); +#ifdef CONFIG_WIFI_AMP + CMD(hci_amp_add, HCI_AMP_ADD); +#endif /* CONFIG_WIFI_AMP */ if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { i++; NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); @@ -1110,7 +1116,8 @@ static int nl80211_new_interface(struct } if (!rdev->ops->add_virtual_intf || - !(rdev->wiphy.interface_modes & (1 << type))) { + !(rdev->wiphy.interface_modes & (1 << type)) || + type > NL80211_IFTYPE_MAX) { err = -EOPNOTSUPP; goto unlock; } @@ -4322,6 +4329,149 @@ static int nl80211_flush_pmksa(struct sk } +#ifdef CONFIG_WIFI_AMP +static int nl80211_hci_amp_add(struct sk_buff *skb, struct genl_info *info) +{ + static u32 next_cookie; + u32 cookie; + struct sk_buff *msg; + const u8 *addr = NULL; + struct cfg80211_registered_device *rdev; + int err = -ENOBUFS; + + rdev = cfg80211_get_dev_from_info(info); + if (IS_ERR(rdev)) + return PTR_ERR(rdev); + + if (!rdev->ops->hci_amp_add) { + err = -EOPNOTSUPP; + goto out_err; + } + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) { + err = -ENOMEM; + goto out_err; + } + + cookie = next_cookie++; + NLA_PUT_U32(msg, NL80211_ATTR_HCI_AMP_COOKIE, cookie); + + if (info->attrs[NL80211_ATTR_MAC]) + addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + + err = rdev->ops->hci_amp_add(&rdev->wiphy, cookie, addr); + if (err) + goto out_free; + + cfg80211_unlock_rdev(rdev); + + return genlmsg_reply(msg, info); + + nla_put_failure: + out_free: + nlmsg_free(msg); + out_err: + cfg80211_unlock_rdev(rdev); + return err; +} + +static int nl80211_hci_amp_delete(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev; + int err; + u32 cookie; + + if (!info->attrs[NL80211_ATTR_HCI_AMP_COOKIE]) + return -EINVAL; + + rdev = cfg80211_get_dev_from_info(info); + if (IS_ERR(rdev)) + return PTR_ERR(rdev); + + if (!rdev->ops->hci_amp_delete) { + err = -EOPNOTSUPP; + goto out_err; + } + + cookie = nla_get_u32(info->attrs[NL80211_ATTR_HCI_AMP_COOKIE]); + err = rdev->ops->hci_amp_delete(&rdev->wiphy, cookie); + + out_err: + cfg80211_unlock_rdev(rdev); + return err; +} + +static int nl80211_hci_amp_hci_event(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *rdev; + int err; + u32 cookie; + const u8 *frame; + size_t frame_len; + + if (!info->attrs[NL80211_ATTR_HCI_AMP_COOKIE] || + !info->attrs[NL80211_ATTR_FRAME]) + return -EINVAL; + + rdev = cfg80211_get_dev_from_info(info); + if (IS_ERR(rdev)) + return PTR_ERR(rdev); + + if (!rdev->ops->hci_amp_hci_event) { + err = -EOPNOTSUPP; + goto out_err; + } + + cookie = nla_get_u32(info->attrs[NL80211_ATTR_HCI_AMP_COOKIE]); + frame = nla_data(info->attrs[NL80211_ATTR_FRAME]); + frame_len = nla_len(info->attrs[NL80211_ATTR_FRAME]); + err = rdev->ops->hci_amp_hci_event(&rdev->wiphy, cookie, + frame, frame_len); + + out_err: + cfg80211_unlock_rdev(rdev); + return err; +} + +static int nl80211_hci_amp_bind(struct sk_buff *skb, struct genl_info *info) +{ + return -EOPNOTSUPP; +} + +static int nl80211_hci_amp_eapol(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev; + int err; + u32 cookie; + const u8 *frame; + size_t frame_len; + + if (!info->attrs[NL80211_ATTR_HCI_AMP_COOKIE] || + !info->attrs[NL80211_ATTR_FRAME]) + return -EINVAL; + + rdev = cfg80211_get_dev_from_info(info); + if (IS_ERR(rdev)) + return PTR_ERR(rdev); + + if (!rdev->ops->hci_amp_eapol) { + err = -EOPNOTSUPP; + goto out_err; + } + + cookie = nla_get_u32(info->attrs[NL80211_ATTR_HCI_AMP_COOKIE]); + frame = nla_data(info->attrs[NL80211_ATTR_FRAME]); + frame_len = nla_len(info->attrs[NL80211_ATTR_FRAME]); + err = rdev->ops->hci_amp_eapol(&rdev->wiphy, cookie, frame, frame_len); + + out_err: + cfg80211_unlock_rdev(rdev); + return err; +} +#endif /* CONFIG_WIFI_AMP */ + static struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_GET_WIPHY, @@ -4584,7 +4734,38 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, }, - +#ifdef CONFIG_WIFI_AMP + { + .cmd = NL80211_CMD_HCI_AMP_ADD, + .doit = nl80211_hci_amp_add, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_HCI_AMP_DELETE, + .doit = nl80211_hci_amp_delete, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_HCI_AMP_HCI_EVENT, + .doit = nl80211_hci_amp_hci_event, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_HCI_AMP_BIND, + .doit = nl80211_hci_amp_bind, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_HCI_AMP_EAPOL, + .doit = nl80211_hci_amp_eapol, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, +#endif /* CONFIG_WIFI_AMP */ }; static struct genl_multicast_group nl80211_mlme_mcgrp = { .name = "mlme", --- wireless-testing.orig/include/net/cfg80211.h 2009-12-01 18:23:13.000000000 +0100 +++ wireless-testing/include/net/cfg80211.h 2009-12-01 18:28:23.000000000 +0100 @@ -996,6 +996,16 @@ struct cfg80211_pmksa { * @del_pmksa: Delete a cached PMKID. * @flush_pmksa: Flush all cached PMKIDs. * + * @hci_amp_add: Add an HCI AMP for BT3. @cookie is the unique identifier + * allocated for this HCI AMP. + * @hci_amp_delete: Delete an HCI AMP. + * @hci_amp_hci_event: Send an HCI event through an HCI AMP. + * @hci_amp_bind: Bind an HCI AMP to a BT3 peer's IEEE 802.11 address. + * @initiator is used to indicate whether the local end is the BT3 + * initiator (vs. responder). @ie may provide additional IEs to the Beacon + * and Probe Response frames. This call instructs the driver to start + * beaconing and to start IEEE 802.11 authentication/association process. + * @hci_amp_eapol: Send an EAPOL frame over an HCI AMP. */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy); @@ -1123,6 +1133,19 @@ struct cfg80211_ops { struct cfg80211_pmksa *pmksa); int (*flush_pmksa)(struct wiphy *wiphy, struct net_device *netdev); +#ifdef CONFIG_WIFI_AMP + int (*hci_amp_add)(struct wiphy *wiphy, u32 cookie, + const u8 *addr); + int (*hci_amp_delete)(struct wiphy *wiphy, u32 cookie); + int (*hci_amp_hci_event)(struct wiphy *wiphy, u32 cookie, + const u8 *frame, size_t frame_len); + int (*hci_amp_bind)(struct wiphy *wiphy, u32 cookie, + bool initiator, const u8 *addr, const u8 *ie, + size_t ie_len); + int (*hci_amp_eapol)(struct wiphy *wiphy, u32 cookie, + const u8 *frame, size_t frame_len); +#endif /* CONFIG_WIFI_AMP */ + /* some temporary stuff to finish wext */ int (*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev, bool enabled, int timeout); --- wireless-testing.orig/net/mac80211/Makefile 2009-12-01 18:27:46.000000000 +0100 +++ wireless-testing/net/mac80211/Makefile 2009-12-01 18:28:23.000000000 +0100 @@ -43,6 +43,8 @@ mac80211-$(CONFIG_PM) += pm.o mac80211-$(CONFIG_MAC80211_DRIVER_API_TRACER) += driver-trace.o CFLAGS_driver-trace.o := -I$(src) +mac80211-$(CONFIG_WIFI_AMP) += bt3-amp.o + # objects for PID algorithm rc80211_pid-y := rc80211_pid_algo.o rc80211_pid-$(CONFIG_MAC80211_DEBUGFS) += rc80211_pid_debugfs.o --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ wireless-testing/net/mac80211/bt3-amp.c 2009-12-01 18:28:23.000000000 +0100 @@ -0,0 +1,289 @@ +/* + * Copyright 2009, Atheros Communications + * Copyright 2009, Johannes Berg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include "ieee80211_i.h" +#include "driver-ops.h" +#include "bt3-amp.h" + +static int bt3amp_open(struct hci_dev *hdev) +{ + struct ieee80211_sub_if_data *sdata = hdev->driver_data; + int err; + + err = ieee80211_open(sdata); + if (err) + return err; + + sdata->u.amp.running = true; + + /* configure beacon here? */ + + return 0; +} + +static int bt3amp_close(struct hci_dev *hdev) +{ + struct ieee80211_sub_if_data *sdata = hdev->driver_data; + + sdata->u.amp.running = false; + + return ieee80211_close(sdata); +} + +static int bt3amp_flush(struct hci_dev *hdev) +{ + struct ieee80211_sub_if_data *sdata = hdev->driver_data; + struct ieee80211_local *local = sdata->local; + + drv_flush(local, false); + + return 0; +} + +static int bt3amp_send(struct sk_buff *skb) +{ +// struct hci_dev *hdev = (void *)skb->dev; +// struct ieee80211_sub_if_data *sdata = hdev->driver_data; +// struct ieee80211_local *local = sdata->local; + + dev_kfree_skb(skb); + return 0; +} + +static void bt3amp_destruct(struct hci_dev *hdev) +{ + /* + * dummy -- I hope hci_dev lifetime is managed by me, + * cf. ieee80211_hci_amp_delete() below. + */ +} + +static void bt3amp_notify(struct hci_dev *hdev, unsigned int evt) +{ +// struct ieee80211_sub_if_data *sdata = hdev->driver_data; +// struct ieee80211_local *local = sdata->local; +} + +int ieee80211_hci_amp_add(struct wiphy *wiphy, u32 cookie, + const u8 *addr) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + struct hci_dev *hdev; + struct ieee80211_sub_if_data *sdata; + int err; + + sdata = kzalloc(sizeof(*sdata), GFP_KERNEL); + if (!sdata) + return -ENOMEM; + + hdev = hci_alloc_dev(); + if (!hdev) { + err = -ENOMEM; + goto free_amp; + } + + SET_HCIDEV_DEV(hdev, &wiphy->dev); + hdev->owner = THIS_MODULE; + hdev->driver_data = sdata; + hdev->type = HCI_USB; /* UH OH */ + hdev->open = bt3amp_open; + hdev->close = bt3amp_close; + hdev->flush = bt3amp_flush; + hdev->send = bt3amp_send; + hdev->destruct = bt3amp_destruct; + hdev->notify = bt3amp_notify; + + sdata->vif.type = NL80211_IFTYPE_HCI_AMP; + memcpy(sdata->vif.addr, addr, ETH_ALEN); + sdata->local = local; + snprintf(sdata->name, sizeof(sdata->name), "bt3:%s", hdev->name); + + err = hci_register_dev(hdev); + if (!err) + return 0; + + mutex_lock(&local->iflist_mtx); + list_add_tail_rcu(&sdata->list, &local->interfaces); + mutex_unlock(&local->iflist_mtx); + + hci_free_dev(hdev); + free_amp: + kfree(sdata); + return err; +} + +static struct ieee80211_sub_if_data *find_amp(struct ieee80211_local *local, + u32 cookie) +{ + struct ieee80211_sub_if_data *sdata; + + list_for_each_entry(sdata, &local->interfaces, list) { + if (sdata->vif.type != NL80211_IFTYPE_HCI_AMP) + continue; + if (sdata->u.amp.cookie != cookie) + continue; + return sdata; + } + + return NULL; +} + +int ieee80211_hci_amp_delete(struct wiphy *wiphy, u32 cookie) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + struct ieee80211_sub_if_data *sdata; + int err; + + mutex_lock(&local->iflist_mtx); + + sdata = find_amp(local, cookie); + if (!sdata) { + err = -ENODEV; + goto out; + } + + /* XXX: does this call close if open? */ + hci_unregister_dev(sdata->u.amp.hdev); + hci_free_dev(sdata->u.amp.hdev); + + list_del(&sdata->list); + kfree(sdata); + + out: + mutex_unlock(&local->iflist_mtx); + + return err; +} + +int ieee80211_hci_amp_hci_event(struct wiphy *wiphy, u32 cookie, + const u8 *frame, size_t frame_len) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + struct ieee80211_sub_if_data *sdata; + + sdata = find_amp(local, cookie); + if (!sdata) + return -ENODEV; + + return 0; +} + +static enum work_done_result +ieee80211_bt3_auth_done(struct ieee80211_work *wk, struct sk_buff *skb) +{ + struct ieee80211_mgmt *mgmt; + struct ieee80211_work *assoc; + u16 status; + + if (!skb) { +#if TODO + cfg80211_amp_bind_timeout(wk->sdata->u.amp.cookie, + wk->filter_ta); +#endif + return WORK_DONE_DESTROY; + } + + mgmt = (void *)skb->data; + status = le16_to_cpu(mgmt->u.auth.status_code); + + if (status != WLAN_STATUS_SUCCESS) { +#if TODO + cfg80211_amp_bind_failure(wk->sdata->u.amp.cookie, + wk->filter_ta, status); +#endif + return WORK_DONE_DESTROY; + } + + assoc = kzalloc(sizeof(*assoc) + wk->submitter_data, GFP_KERNEL); + if (!assoc) { +#if TODO + cfg80211_amp_bind_failure(wk->sdata->u.amp.cookie, + wk->filter_ta, internal_failure); +#endif + return WORK_DONE_DESTROY; + } + + memcpy(assoc->assoc.ssid, wk->probe_auth.ssid, wk->probe_auth.ssid_len); + assoc->assoc.ssid_len = wk->probe_auth.ssid_len; + + /* XXX: get peer capability? */ + assoc->assoc.capability = WLAN_CAPABILITY_PRIVACY; + assoc->assoc.smps = IEEE80211_SMPS_OFF; + assoc->assoc.wmm_used = false; /* XXX */ + assoc->assoc.use_11n = false; /* XXX */ + assoc->assoc.supp_rates = NULL; + assoc->assoc.supp_rates_len = 0; + assoc->assoc.ht_information_ie = NULL; + + return WORK_DONE_DESTROY; +} + + +int ieee80211_hci_amp_bind(struct wiphy *wiphy, u32 cookie, + bool initiator, const u8 *addr, const u8 *ie, + size_t ie_len) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + struct ieee80211_sub_if_data *sdata; + + sdata = find_amp(local, cookie); + if (!sdata) + return -ENODEV; + + if (!initiator) { + struct ieee80211_work *wk; + + /* responder -- start auth handshake */ + + wk = kzalloc(sizeof(*wk) + ie_len, GFP_KERNEL); + if (!wk) + return -ENOMEM; + + memcpy(wk->filter_ta, addr, ETH_ALEN); + + if (ie && ie_len) { + memcpy(&wk->ie, ie, ie_len); + wk->ie_len = 0; + wk->submitter_data = ie_len; + } + + sprintf(wk->probe_auth.ssid, + "AMP-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x", + addr[0], addr[1], addr[2], + addr[3], addr[4], addr[5]); + wk->probe_auth.ssid_len = 21; + + wk->probe_auth.algorithm = WLAN_AUTH_OPEN; + wk->probe_auth.privacy = true; + + wk->type = IEEE80211_WORK_AUTH; + wk->chan = local->oper_channel; + wk->sdata = sdata; + wk->done = ieee80211_bt3_auth_done; + + ieee80211_add_work(wk); + } + + return 0; +} + +int ieee80211_hci_amp_eapol(struct wiphy *wiphy, u32 cookie, + const u8 *frame, size_t frame_len) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + struct ieee80211_sub_if_data *sdata; + + sdata = find_amp(local, cookie); + if (!sdata) + return -ENODEV; + + return 0; +} --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ wireless-testing/net/mac80211/bt3-amp.h 2009-12-01 18:28:23.000000000 +0100 @@ -0,0 +1,18 @@ +#ifndef BT3_AMP_H +#define BT3_AMP_H + +#include +#include + +int ieee80211_hci_amp_add(struct wiphy *wiphy, u32 cookie, + const u8 *addr); +int ieee80211_hci_amp_delete(struct wiphy *wiphy, u32 cookie); +int ieee80211_hci_amp_hci_event(struct wiphy *wiphy, u32 cookie, + const u8 *frame, size_t frame_len); +int ieee80211_hci_amp_bind(struct wiphy *wiphy, u32 cookie, + bool initiator, const u8 *addr, const u8 *ie, + size_t ie_len); +int ieee80211_hci_amp_eapol(struct wiphy *wiphy, u32 cookie, + const u8 *frame, size_t frame_len); + +#endif /* BT3_AMP_H */ --- wireless-testing.orig/net/mac80211/cfg.c 2009-12-01 18:23:13.000000000 +0100 +++ wireless-testing/net/mac80211/cfg.c 2009-12-01 18:28:23.000000000 +0100 @@ -17,6 +17,7 @@ #include "cfg.h" #include "rate.h" #include "mesh.h" +#include "bt3-amp.h" static bool nl80211_type_check(enum nl80211_iftype type) { @@ -1478,4 +1479,11 @@ struct cfg80211_ops mac80211_config_ops CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd) .set_power_mgmt = ieee80211_set_power_mgmt, .set_bitrate_mask = ieee80211_set_bitrate_mask, +#ifdef CONFIG_WIFI_AMP + .hci_amp_add = ieee80211_hci_amp_add, + .hci_amp_delete = ieee80211_hci_amp_delete, + .hci_amp_hci_event = ieee80211_hci_amp_hci_event, + .hci_amp_bind = ieee80211_hci_amp_bind, + .hci_amp_eapol = ieee80211_hci_amp_eapol, +#endif /* CONFIG_WIFI_AMP */ }; --- wireless-testing.orig/net/wireless/Kconfig 2009-12-01 18:23:13.000000000 +0100 +++ wireless-testing/net/wireless/Kconfig 2009-12-01 18:28:23.000000000 +0100 @@ -44,6 +44,10 @@ config NL80211_TESTMODE Say N. +config WIFI_AMP + def_bool y + depends on CFG80211=BT || BT=y + config CFG80211_DEVELOPER_WARNINGS bool "enable developer warnings" depends on CFG80211 --- wireless-testing.orig/net/mac80211/ieee80211_i.h 2009-12-01 18:27:46.000000000 +0100 +++ wireless-testing/net/mac80211/ieee80211_i.h 2009-12-01 18:28:23.000000000 +0100 @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include "key.h" #include "sta_info.h" @@ -259,10 +261,12 @@ struct ieee80211_work { /* XXX: chan type? -- right now not really needed */ unsigned long timeout; + + unsigned long submitter_data; + enum ieee80211_work_type type; u8 filter_ta[ETH_ALEN]; - union { struct { int tries; @@ -430,6 +434,12 @@ struct ieee80211_if_mesh { do { } while (0) #endif +struct ieee80211_if_bt3amp { + struct hci_dev *hdev; + u32 cookie; + bool running; +}; + /** * enum ieee80211_sub_if_data_flags - virtual interface flags * @@ -501,6 +511,9 @@ struct ieee80211_sub_if_data { #ifdef CONFIG_MAC80211_MESH struct ieee80211_if_mesh mesh; #endif +#ifdef CONFIG_WIFI_AMP + struct ieee80211_if_bt3amp amp; +#endif u32 mntr_flags; } u; @@ -996,8 +1009,15 @@ void ieee80211_remove_interfaces(struct u32 __ieee80211_recalc_idle(struct ieee80211_local *local); void ieee80211_recalc_idle(struct ieee80211_local *local); +int ieee80211_open(struct ieee80211_sub_if_data *sdata); +int ieee80211_close(struct ieee80211_sub_if_data *sdata); + static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata) { +#ifdef CONFIG_WIFI_AMP + if (unlikely(sdata->vif.type == NL80211_IFTYPE_HCI_AMP)) + return sdata->u.amp.running; +#endif return netif_running(sdata->dev); } --- wireless-testing.orig/net/mac80211/iface.c 2009-12-01 18:27:46.000000000 +0100 +++ wireless-testing/net/mac80211/iface.c 2009-12-01 18:28:23.000000000 +0100 @@ -90,9 +90,8 @@ static inline int identical_mac_addr_all type2 == NL80211_IFTYPE_AP_VLAN)); } -static int ieee80211_open(struct net_device *dev) +int ieee80211_open(struct ieee80211_sub_if_data *sdata) { - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_sub_if_data *nsdata; struct ieee80211_local *local = sdata->local; struct sta_info *sta; @@ -103,15 +102,13 @@ static int ieee80211_open(struct net_dev u8 null_addr[ETH_ALEN] = {0}; /* fail early if user set an invalid address */ - if (compare_ether_addr(dev->dev_addr, null_addr) && - !is_valid_ether_addr(dev->dev_addr)) + if (compare_ether_addr(sdata->vif.addr, null_addr) && + !is_valid_ether_addr(sdata->vif.addr)) return -EADDRNOTAVAIL; /* we hold the RTNL here so can safely walk the list */ list_for_each_entry(nsdata, &local->interfaces, list) { - struct net_device *ndev = nsdata->dev; - - if (ndev != dev && ieee80211_sdata_running(nsdata)) { + if (nsdata != sdata && ieee80211_sdata_running(nsdata)) { /* * Allow only a single IBSS interface to be up at any * time. This is restricted because beacon distribution @@ -130,7 +127,7 @@ static int ieee80211_open(struct net_dev * The remaining checks are only performed for interfaces * with the same MAC address. */ - if (compare_ether_addr(dev->dev_addr, ndev->dev_addr)) + if (compare_ether_addr(sdata->vif.addr, nsdata->vif.addr)) continue; /* @@ -200,18 +197,18 @@ static int ieee80211_open(struct net_dev * No need to check running since we do not allow * it to start up with this invalid address. */ - if (compare_ether_addr(null_addr, ndev->dev_addr) == 0) { - memcpy(ndev->dev_addr, + if (compare_ether_addr(null_addr, nsdata->vif.addr) == 0) { + memcpy(nsdata->vif.addr, local->hw.wiphy->perm_addr, ETH_ALEN); - memcpy(ndev->perm_addr, ndev->dev_addr, ETH_ALEN); + memcpy(ndev->perm_addr, nsdata->vif.addr, ETH_ALEN); } } /* * Validate the MAC address for this device. */ - if (!is_valid_ether_addr(dev->dev_addr)) { + if (!is_valid_ether_addr(sdata->vif.addr)) { if (!local->open_count) drv_stop(local); return -EADDRNOTAVAIL; @@ -271,9 +268,9 @@ static int ieee80211_open(struct net_dev ieee80211_enable_keys(sdata); if (sdata->vif.type == NL80211_IFTYPE_STATION) - netif_carrier_off(dev); - else - netif_carrier_on(dev); + netif_carrier_off(sdata->dev); + else if (sdata->vif.type != NL80211_IFTYPE_HCI_AMP) + netif_carrier_on(sdata->dev); } if (sdata->vif.type == NL80211_IFTYPE_WDS) { @@ -330,7 +327,8 @@ static int ieee80211_open(struct net_dev if (sdata->vif.type == NL80211_IFTYPE_STATION) ieee80211_queue_work(&local->hw, &sdata->u.mgd.work); - netif_start_queue(dev); + if (sdata->vif.type != NL80211_IFTYPE_HCI_AMP) + netif_start_queue(sdata->dev); return 0; err_del_interface: @@ -345,9 +343,15 @@ static int ieee80211_open(struct net_dev return res; } -static int ieee80211_stop(struct net_device *dev) +static int ieee80211_dev_open(struct net_device *dev) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + return ieee80211_open(sdata); +} + +int ieee80211_close(struct ieee80211_sub_if_data *sdata) +{ struct ieee80211_local *local = sdata->local; struct ieee80211_if_init_conf conf; struct sta_info *sta; @@ -359,7 +363,8 @@ static int ieee80211_stop(struct net_dev /* * Stop TX on this interface first. */ - netif_stop_queue(dev); + if (sdata->vif.type != NL80211_IFTYPE_HCI_AMP) + netif_stop_queue(sdata->dev); /* * Purge work for this interface. @@ -412,14 +417,16 @@ static int ieee80211_stop(struct net_dev if (sdata->vif.type == NL80211_IFTYPE_AP) local->fif_pspoll--; - netif_addr_lock_bh(dev); - spin_lock_bh(&local->filter_lock); - __dev_addr_unsync(&local->mc_list, &local->mc_count, - &dev->mc_list, &dev->mc_count); - spin_unlock_bh(&local->filter_lock); - netif_addr_unlock_bh(dev); + if (sdata->vif.type != NL80211_IFTYPE_HCI_AMP) { + netif_addr_lock_bh(sdata->dev); + spin_lock_bh(&local->filter_lock); + __dev_addr_unsync(&local->mc_list, &local->mc_count, + &sdata->dev->mc_list, &sdata->dev->mc_count); + spin_unlock_bh(&local->filter_lock); + netif_addr_unlock_bh(sdata->dev); - ieee80211_configure_filter(local); + ieee80211_configure_filter(local); + } del_timer_sync(&local->dynamic_ps_timer); cancel_work_sync(&local->dynamic_ps_enable_work); @@ -574,6 +581,13 @@ static int ieee80211_stop(struct net_dev return 0; } +static int ieee80211_dev_stop(struct net_device *dev) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + return ieee80211_close(sdata); +} + static void ieee80211_set_multicast_list(struct net_device *dev) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -666,8 +680,8 @@ static void ieee80211_teardown_sdata(str } static const struct net_device_ops ieee80211_dataif_ops = { - .ndo_open = ieee80211_open, - .ndo_stop = ieee80211_stop, + .ndo_open = ieee80211_dev_open, + .ndo_stop = ieee80211_dev_stop, .ndo_uninit = ieee80211_teardown_sdata, .ndo_start_xmit = ieee80211_subif_start_xmit, .ndo_set_multicast_list = ieee80211_set_multicast_list, @@ -676,8 +690,8 @@ static const struct net_device_ops ieee8 }; static const struct net_device_ops ieee80211_monitorif_ops = { - .ndo_open = ieee80211_open, - .ndo_stop = ieee80211_stop, + .ndo_open = ieee80211_dev_open, + .ndo_stop = ieee80211_dev_stop, .ndo_uninit = ieee80211_teardown_sdata, .ndo_start_xmit = ieee80211_monitor_start_xmit, .ndo_set_multicast_list = ieee80211_set_multicast_list,