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 | 24 ++++++ net/mac80211/Makefile | 2 net/mac80211/bt3-amp.c | 39 ++++++++++ net/mac80211/bt3-amp.h | 15 +++ net/mac80211/cfg.c | 8 ++ net/wireless/Kconfig | 4 + net/wireless/nl80211.c | 186 +++++++++++++++++++++++++++++++++++++++++++++++- 8 files changed, 333 insertions(+), 3 deletions(-) --- wireless-testing.orig/include/linux/nl80211.h 2009-11-20 10:09:33.000000000 +0100 +++ wireless-testing/include/linux/nl80211.h 2009-11-20 11:41:24.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 */ @@ -349,6 +371,12 @@ enum nl80211_commands { NL80211_CMD_GET_SURVEY, NL80211_CMD_NEW_SURVEY_RESULTS, + 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 */ @@ -603,6 +631,12 @@ enum nl80211_commands { * multiple virtual interfaces it might not always be the mode that * actually ends up being used. * + * @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 */ @@ -739,6 +773,10 @@ enum nl80211_attrs { NL80211_ATTR_SMPS_MODE, + 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, @@ -796,6 +834,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. @@ -813,7 +853,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, }; /** @@ -1463,4 +1505,18 @@ enum nl80211_smps_mode { NL80211_SMPS_MAX = __NL80211_SMPS_NUM - 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-11-20 10:09:33.000000000 +0100 +++ wireless-testing/net/wireless/nl80211.c 2009-11-20 11:41:24.000000000 +0100 @@ -140,6 +140,9 @@ static struct nla_policy nl80211_policy[ [NL80211_ATTR_PID] = { .type = NLA_U32 }, [NL80211_ATTR_4ADDR] = { .type = NLA_U8 }, [NL80211_ATTR_SMPS_MODE] = { .type = NLA_U32 }, + [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 */ @@ -456,7 +459,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; @@ -562,6 +565,9 @@ static int nl80211_send_wiphy(struct sk_ CMD(deauth, DEAUTHENTICATE); CMD(disassoc, DISASSOCIATE); CMD(join_ibss, JOIN_IBSS); +#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); @@ -1123,7 +1129,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; } @@ -4242,6 +4249,149 @@ static int nl80211_wiphy_netns(struct sk return err; } +#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, @@ -4486,6 +4636,38 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .dumpit = nl80211_dump_survey, }, +#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-11-20 10:09:33.000000000 +0100 +++ wireless-testing/include/net/cfg80211.h 2009-11-20 11:41:24.000000000 +0100 @@ -976,6 +976,17 @@ struct cfg80211_bitrate_mask { * @dump_survey: get site survey information. * * @testmode_cmd: run a test mode command + * + * @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); @@ -1100,6 +1111,19 @@ struct cfg80211_ops { int (*set_smps)(struct wiphy *wiphy, struct net_device *netdev, enum nl80211_smps_mode smps_mode); +#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-11-20 09:23:34.000000000 +0100 +++ wireless-testing/net/mac80211/Makefile 2009-11-20 11:41:24.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-11-20 11:41:25.000000000 +0100 @@ -0,0 +1,39 @@ +/* + * Copyright 2009, Atheros Communications + * + * 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 + +int ieee80211_hci_amp_add(struct wiphy *wiphy, u32 cookie, + const u8 *addr) +{ + return -1; +} + +int ieee80211_hci_amp_delete(struct wiphy *wiphy, u32 cookie) +{ + return -1; +} + +int ieee80211_hci_amp_hci_event(struct wiphy *wiphy, u32 cookie, + const u8 *frame, size_t frame_len) +{ + return -1; +} + +int ieee80211_hci_amp_bind(struct wiphy *wiphy, u32 cookie, + bool initiator, const u8 *addr, const u8 *ie, + size_t ie_len) +{ + return -1; +} + +int ieee80211_hci_amp_eapol(struct wiphy *wiphy, u32 cookie, + const u8 *frame, size_t frame_len) +{ + return -1; +} --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ wireless-testing/net/mac80211/bt3-amp.h 2009-11-20 11:41:25.000000000 +0100 @@ -0,0 +1,15 @@ +#ifndef BT3_AMP_H +#define BT3_AMP_H + +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-11-20 10:09:33.000000000 +0100 +++ wireless-testing/net/mac80211/cfg.c 2009-11-20 11:41:25.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) { @@ -1497,4 +1498,11 @@ struct cfg80211_ops mac80211_config_ops .set_power_mgmt = ieee80211_set_power_mgmt, .set_bitrate_mask = ieee80211_set_bitrate_mask, .set_smps = ieee80211_set_smps, +#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-11-20 09:23:34.000000000 +0100 +++ wireless-testing/net/wireless/Kconfig 2009-11-20 11:41:25.000000000 +0100 @@ -44,6 +44,10 @@ config NL80211_TESTMODE Say N. +config WIFI_AMP + def_bool y + depends on CFG80211=BT || BT=n + config CFG80211_DEVELOPER_WARNINGS bool "enable developer warnings" depends on CFG80211