From 0c28ec587a2f061b93a98ac02a53b4152cbe48f4 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 15 Sep 2011 11:53:01 +0300 Subject: cfg80211: add cfg80211_find_vendor_ie() function Add function to find vendor-specific ie (along with vendor-specific ie struct definition and P2P OUI values) Signed-off-by: Eliad Peller Reviewed-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 10 ++++++++++ include/net/cfg80211.h | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+) (limited to 'include') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 72f3933938c0..b5e0a5c344fd 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -777,6 +777,13 @@ struct ieee80211_mmie { u8 mic[8]; } __attribute__ ((packed)); +struct ieee80211_vendor_ie { + u8 element_id; + u8 len; + u8 oui[3]; + u8 oui_type; +} __packed; + /* Control frames */ struct ieee80211_rts { __le16 frame_control; @@ -1470,6 +1477,9 @@ enum ieee80211_sa_query_action { #define WLAN_PMKID_LEN 16 +#define WLAN_OUI_WFA 0x506f9a +#define WLAN_OUI_TYPE_WFA_P2P 9 + /* * WMM/802.11e Tspec Element */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index b42136a61f3a..9518b5cfb822 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2458,6 +2458,24 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb); */ const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len); +/** + * cfg80211_find_vendor_ie - find vendor specific information element in data + * + * @oui: vendor OUI + * @oui_type: vendor-specific OUI type + * @ies: data consisting of IEs + * @len: length of data + * + * This function will return %NULL if the vendor specific element ID + * could not be found or if the element is invalid (claims to be + * longer than the given data), or a pointer to the first byte + * of the requested element, that is the byte containing the + * element ID. There are no checks on the element length + * other than having to fit into the given data. + */ +const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type, + const u8 *ies, int len); + /** * DOC: Regulatory enforcement infrastructure * -- cgit v1.2.3 From 3861b2c5d90b219ee772b5a1d1a32ee630564121 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Fri, 16 Sep 2011 12:33:58 +0200 Subject: bcma: cc: export more control functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/bcma/driver_chipcommon_pmu.c | 38 ++++++++++++++++++++++------- include/linux/bcma/bcma_driver_chipcommon.h | 9 +++++++ 2 files changed, 38 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/bcma/driver_chipcommon_pmu.c b/drivers/bcma/driver_chipcommon_pmu.c index 4bc10aa57bd4..2968d809d49f 100644 --- a/drivers/bcma/driver_chipcommon_pmu.c +++ b/drivers/bcma/driver_chipcommon_pmu.c @@ -18,20 +18,40 @@ static u32 bcma_chipco_pll_read(struct bcma_drv_cc *cc, u32 offset) return bcma_cc_read32(cc, BCMA_CC_PLLCTL_DATA); } -static void bcma_chipco_chipctl_maskset(struct bcma_drv_cc *cc, - u32 offset, u32 mask, u32 set) +void bcma_chipco_pll_write(struct bcma_drv_cc *cc, u32 offset, u32 value) { - u32 value; + bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset); + bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR); + bcma_cc_write32(cc, BCMA_CC_PLLCTL_DATA, value); +} +EXPORT_SYMBOL_GPL(bcma_chipco_pll_write); - bcma_cc_read32(cc, BCMA_CC_CHIPCTL_ADDR); +void bcma_chipco_pll_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask, + u32 set) +{ + bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset); + bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR); + bcma_cc_maskset32(cc, BCMA_CC_PLLCTL_DATA, mask, set); +} +EXPORT_SYMBOL_GPL(bcma_chipco_pll_maskset); + +void bcma_chipco_chipctl_maskset(struct bcma_drv_cc *cc, + u32 offset, u32 mask, u32 set) +{ bcma_cc_write32(cc, BCMA_CC_CHIPCTL_ADDR, offset); bcma_cc_read32(cc, BCMA_CC_CHIPCTL_ADDR); - value = bcma_cc_read32(cc, BCMA_CC_CHIPCTL_DATA); - value &= mask; - value |= set; - bcma_cc_write32(cc, BCMA_CC_CHIPCTL_DATA, value); - bcma_cc_read32(cc, BCMA_CC_CHIPCTL_DATA); + bcma_cc_maskset32(cc, BCMA_CC_CHIPCTL_DATA, mask, set); +} +EXPORT_SYMBOL_GPL(bcma_chipco_chipctl_maskset); + +void bcma_chipco_regctl_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask, + u32 set) +{ + bcma_cc_write32(cc, BCMA_CC_REGCTL_ADDR, offset); + bcma_cc_read32(cc, BCMA_CC_REGCTL_ADDR); + bcma_cc_maskset32(cc, BCMA_CC_REGCTL_DATA, mask, set); } +EXPORT_SYMBOL_GPL(bcma_chipco_regctl_maskset); static void bcma_pmu_pll_init(struct bcma_drv_cc *cc) { diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h index a7ae33d06f24..1526d965ed06 100644 --- a/include/linux/bcma/bcma_driver_chipcommon.h +++ b/include/linux/bcma/bcma_driver_chipcommon.h @@ -378,4 +378,13 @@ u32 bcma_chipco_gpio_polarity(struct bcma_drv_cc *cc, u32 mask, u32 value); /* PMU support */ extern void bcma_pmu_init(struct bcma_drv_cc *cc); +extern void bcma_chipco_pll_write(struct bcma_drv_cc *cc, u32 offset, + u32 value); +extern void bcma_chipco_pll_maskset(struct bcma_drv_cc *cc, u32 offset, + u32 mask, u32 set); +extern void bcma_chipco_chipctl_maskset(struct bcma_drv_cc *cc, + u32 offset, u32 mask, u32 set); +extern void bcma_chipco_regctl_maskset(struct bcma_drv_cc *cc, + u32 offset, u32 mask, u32 set); + #endif /* LINUX_BCMA_DRIVER_CC_H_ */ -- cgit v1.2.3 From c9df56b48e4ff003eaebd680ec7a45342dcd03ea Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Fri, 16 Sep 2011 18:56:23 +0300 Subject: cfg80211/nl80211: Add PMKSA caching candidate event When the driver (or most likely firmware) decides which AP to use for roaming based on internal scan result processing, user space needs to be notified of PMKSA caching candidates to allow RSN pre-authentication to be used. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- include/linux/nl80211.h | 33 +++++++++++++++++++++++++++++++++ include/net/cfg80211.h | 11 +++++++++++ net/wireless/mlme.c | 11 +++++++++++ net/wireless/nl80211.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ net/wireless/nl80211.h | 4 ++++ 5 files changed, 105 insertions(+) (limited to 'include') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index f17307590e61..460b12a8ef66 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -499,6 +499,9 @@ * this command may also be sent by the driver as an MLME event to * inform userspace of the new replay counter. * + * @NL80211_CMD_PMKSA_CANDIDATE: This is used as an event to inform userspace + * of PMKSA caching dandidates. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -623,6 +626,8 @@ enum nl80211_commands { NL80211_CMD_SET_REKEY_OFFLOAD, + NL80211_CMD_PMKSA_CANDIDATE, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1070,6 +1075,9 @@ enum nl80211_commands { * @NL80211_ATTR_ROAM_SUPPORT: Indicates whether the firmware is capable of * roaming to another AP in the same ESS if the signal lever is low. * + * @NL80211_ATTR_PMKSA_CANDIDATE: Nested attribute containing the PMKSA caching + * candidate information, see &enum nl80211_pmksa_candidate_attr. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1288,6 +1296,8 @@ enum nl80211_attrs { NL80211_ATTR_SCHED_SCAN_MATCH, NL80211_ATTR_MAX_MATCH_SETS, + NL80211_ATTR_PMKSA_CANDIDATE, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -2558,4 +2568,27 @@ enum nl80211_sta_wme_attr { NL80211_STA_WME_MAX = __NL80211_STA_WME_AFTER_LAST - 1 }; +/** + * enum nl80211_pmksa_candidate_attr - attributes for PMKSA caching candidates + * @__NL80211_PMKSA_CANDIDATE_INVALID: invalid number for nested attributes + * @NL80211_PMKSA_CANDIDATE_INDEX: candidate index (u32; the smaller, the higher + * priority) + * @NL80211_PMKSA_CANDIDATE_BSSID: candidate BSSID (6 octets) + * @NL80211_PMKSA_CANDIDATE_PREAUTH: RSN pre-authentication supported (flag) + * @NUM_NL80211_PMKSA_CANDIDATE: number of PMKSA caching candidate attributes + * (internal) + * @MAX_NL80211_PMKSA_CANDIDATE: highest PMKSA caching candidate attribute + * (internal) + */ +enum nl80211_pmksa_candidate_attr { + __NL80211_PMKSA_CANDIDATE_INVALID, + NL80211_PMKSA_CANDIDATE_INDEX, + NL80211_PMKSA_CANDIDATE_BSSID, + NL80211_PMKSA_CANDIDATE_PREAUTH, + + /* keep last */ + NUM_NL80211_PMKSA_CANDIDATE, + MAX_NL80211_PMKSA_CANDIDATE = NUM_NL80211_PMKSA_CANDIDATE - 1 +}; + #endif /* __LINUX_NL80211_H */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 9518b5cfb822..6ac4bddeeeca 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3136,6 +3136,17 @@ void cfg80211_cqm_pktloss_notify(struct net_device *dev, void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid, const u8 *replay_ctr, gfp_t gfp); +/** + * cfg80211_pmksa_candidate_notify - notify about PMKSA caching candidate + * @dev: network device + * @index: candidate index (the smaller the index, the higher the priority) + * @bssid: BSSID of AP + * @preauth: Whether AP advertises support for RSN pre-authentication + * @gfp: allocation flags + */ +void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, + const u8 *bssid, bool preauth, gfp_t gfp); + /* Logging, debugging and troubleshooting/diagnostic helpers. */ /* wiphy_printk helpers, similar to dev_printk */ diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 832f6574e4ed..61adea540e02 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -1095,3 +1095,14 @@ void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid, nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp); } EXPORT_SYMBOL(cfg80211_gtk_rekey_notify); + +void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, + const u8 *bssid, bool preauth, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp); +} +EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 430b432bc3f0..3c6427abdf34 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -7270,6 +7270,52 @@ void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev, nlmsg_free(msg); } +void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, int index, + const u8 *bssid, bool preauth, gfp_t gfp) +{ + struct sk_buff *msg; + struct nlattr *attr; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PMKSA_CANDIDATE); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + + attr = nla_nest_start(msg, NL80211_ATTR_PMKSA_CANDIDATE); + if (!attr) + goto nla_put_failure; + + NLA_PUT_U32(msg, NL80211_PMKSA_CANDIDATE_INDEX, index); + NLA_PUT(msg, NL80211_PMKSA_CANDIDATE_BSSID, ETH_ALEN, bssid); + if (preauth) + NLA_PUT_FLAG(msg, NL80211_PMKSA_CANDIDATE_PREAUTH); + + nla_nest_end(msg, attr); + + if (genlmsg_end(msg, hdr) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + void nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *peer, diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 5d69c56400ae..f24a1fbeaf19 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -113,4 +113,8 @@ void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *bssid, const u8 *replay_ctr, gfp_t gfp); +void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, int index, + const u8 *bssid, bool preauth, gfp_t gfp); + #endif /* __NET_WIRELESS_NL80211_H */ -- cgit v1.2.3 From a7ce1c9446a7f7513211e4698d07357d20452909 Mon Sep 17 00:00:00 2001 From: Alexander Simon Date: Sun, 18 Sep 2011 00:16:45 +0200 Subject: mac80211: fix indentation Signed-off-by: Alexander Simon Signed-off-by: John W. Linville --- include/net/mac80211.h | 2 +- net/mac80211/ibss.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 9edba09547e4..c0f63fd0c52b 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -171,7 +171,7 @@ enum ieee80211_bss_change { BSS_CHANGED_ERP_CTS_PROT = 1<<1, BSS_CHANGED_ERP_PREAMBLE = 1<<2, BSS_CHANGED_ERP_SLOT = 1<<3, - BSS_CHANGED_HT = 1<<4, + BSS_CHANGED_HT = 1<<4, BSS_CHANGED_BASIC_RATES = 1<<5, BSS_CHANGED_BEACON_INT = 1<<6, BSS_CHANGED_BSSID = 1<<7, diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 56c24cabf26d..836b2752ecd6 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -417,7 +417,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, * must be callable in atomic context. */ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, - u8 *bssid,u8 *addr, u32 supp_rates, + u8 *bssid, u8 *addr, u32 supp_rates, gfp_t gfp) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; -- cgit v1.2.3 From 8b3fe7b591b3c50061a8701f8eda14033420577b Mon Sep 17 00:00:00 2001 From: Ilan Elias Date: Sun, 18 Sep 2011 11:19:33 +0300 Subject: NFC: Add dev_up and dev_down control operations Add 2 new nfc control operations: dev_up to turn on the nfc device dev_down to turn off the nfc device Signed-off-by: Ilan Elias Signed-off-by: John W. Linville --- drivers/nfc/pn533.c | 2 ++ include/linux/nfc.h | 6 +++++ include/net/nfc.h | 4 +++ net/nfc/core.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++ net/nfc/netlink.c | 56 ++++++++++++++++++++++++++++++++++++++ net/nfc/nfc.h | 4 +++ 6 files changed, 149 insertions(+) (limited to 'include') diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index f81a93e5b59d..c78eb6afd0cb 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -1432,6 +1432,8 @@ static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata, } struct nfc_ops pn533_nfc_ops = { + .dev_up = NULL, + .dev_down = NULL, .start_poll = pn533_start_poll, .stop_poll = pn533_stop_poll, .activate_target = pn533_activate_target, diff --git a/include/linux/nfc.h b/include/linux/nfc.h index c525e0b5876b..36cb955b05cc 100644 --- a/include/linux/nfc.h +++ b/include/linux/nfc.h @@ -39,6 +39,10 @@ * * @NFC_CMD_GET_DEVICE: request information about a device (requires * %NFC_ATTR_DEVICE_INDEX) or dump request to get a list of all nfc devices + * @NFC_CMD_DEV_UP: turn on the nfc device + * (requires %NFC_ATTR_DEVICE_INDEX) + * @NFC_CMD_DEV_DOWN: turn off the nfc device + * (requires %NFC_ATTR_DEVICE_INDEX) * @NFC_CMD_START_POLL: start polling for targets using the given protocols * (requires %NFC_ATTR_DEVICE_INDEX and %NFC_ATTR_PROTOCOLS) * @NFC_CMD_STOP_POLL: stop polling for targets (requires @@ -56,6 +60,8 @@ enum nfc_commands { NFC_CMD_UNSPEC, NFC_CMD_GET_DEVICE, + NFC_CMD_DEV_UP, + NFC_CMD_DEV_DOWN, NFC_CMD_START_POLL, NFC_CMD_STOP_POLL, NFC_CMD_GET_TARGET, diff --git a/include/net/nfc.h b/include/net/nfc.h index 87b51fe15b70..6a7f602aa841 100644 --- a/include/net/nfc.h +++ b/include/net/nfc.h @@ -48,6 +48,8 @@ typedef void (*data_exchange_cb_t)(void *context, struct sk_buff *skb, int err); struct nfc_ops { + int (*dev_up)(struct nfc_dev *dev); + int (*dev_down)(struct nfc_dev *dev); int (*start_poll)(struct nfc_dev *dev, u32 protocols); void (*stop_poll)(struct nfc_dev *dev); int (*activate_target)(struct nfc_dev *dev, u32 target_idx, @@ -78,7 +80,9 @@ struct nfc_dev { int targets_generation; spinlock_t targets_lock; struct device dev; + bool dev_up; bool polling; + bool remote_activated; struct nfc_genl_data genl_data; u32 supported_protocols; diff --git a/net/nfc/core.c b/net/nfc/core.c index 284e2f6a14ff..47e02c1b8c02 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -52,6 +52,80 @@ int nfc_printk(const char *level, const char *format, ...) } EXPORT_SYMBOL(nfc_printk); +/** + * nfc_dev_up - turn on the NFC device + * + * @dev: The nfc device to be turned on + * + * The device remains up until the nfc_dev_down function is called. + */ +int nfc_dev_up(struct nfc_dev *dev) +{ + int rc = 0; + + nfc_dbg("dev_name=%s", dev_name(&dev->dev)); + + device_lock(&dev->dev); + + if (!device_is_registered(&dev->dev)) { + rc = -ENODEV; + goto error; + } + + if (dev->dev_up) { + rc = -EALREADY; + goto error; + } + + if (dev->ops->dev_up) + rc = dev->ops->dev_up(dev); + + if (!rc) + dev->dev_up = true; + +error: + device_unlock(&dev->dev); + return rc; +} + +/** + * nfc_dev_down - turn off the NFC device + * + * @dev: The nfc device to be turned off + */ +int nfc_dev_down(struct nfc_dev *dev) +{ + int rc = 0; + + nfc_dbg("dev_name=%s", dev_name(&dev->dev)); + + device_lock(&dev->dev); + + if (!device_is_registered(&dev->dev)) { + rc = -ENODEV; + goto error; + } + + if (!dev->dev_up) { + rc = -EALREADY; + goto error; + } + + if (dev->polling || dev->remote_activated) { + rc = -EBUSY; + goto error; + } + + if (dev->ops->dev_down) + dev->ops->dev_down(dev); + + dev->dev_up = false; + +error: + device_unlock(&dev->dev); + return rc; +} + /** * nfc_start_poll - start polling for nfc targets * @@ -144,6 +218,8 @@ int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol) } rc = dev->ops->activate_target(dev, target_idx, protocol); + if (!rc) + dev->remote_activated = true; error: device_unlock(&dev->dev); @@ -170,6 +246,7 @@ int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx) } dev->ops->deactivate_target(dev, target_idx); + dev->remote_activated = false; error: device_unlock(&dev->dev); diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index ccdff7953f7d..03f8818e1f16 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -367,6 +367,52 @@ out_putdev: return rc; } +static int nfc_genl_dev_up(struct sk_buff *skb, struct genl_info *info) +{ + struct nfc_dev *dev; + int rc; + u32 idx; + + nfc_dbg("entry"); + + if (!info->attrs[NFC_ATTR_DEVICE_INDEX]) + return -EINVAL; + + idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); + + dev = nfc_get_device(idx); + if (!dev) + return -ENODEV; + + rc = nfc_dev_up(dev); + + nfc_put_device(dev); + return rc; +} + +static int nfc_genl_dev_down(struct sk_buff *skb, struct genl_info *info) +{ + struct nfc_dev *dev; + int rc; + u32 idx; + + nfc_dbg("entry"); + + if (!info->attrs[NFC_ATTR_DEVICE_INDEX]) + return -EINVAL; + + idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); + + dev = nfc_get_device(idx); + if (!dev) + return -ENODEV; + + rc = nfc_dev_down(dev); + + nfc_put_device(dev); + return rc; +} + static int nfc_genl_start_poll(struct sk_buff *skb, struct genl_info *info) { struct nfc_dev *dev; @@ -440,6 +486,16 @@ static struct genl_ops nfc_genl_ops[] = { .done = nfc_genl_dump_devices_done, .policy = nfc_genl_policy, }, + { + .cmd = NFC_CMD_DEV_UP, + .doit = nfc_genl_dev_up, + .policy = nfc_genl_policy, + }, + { + .cmd = NFC_CMD_DEV_DOWN, + .doit = nfc_genl_dev_down, + .policy = nfc_genl_policy, + }, { .cmd = NFC_CMD_START_POLL, .doit = nfc_genl_start_poll, diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h index aaf9832298f3..1a877de8e230 100644 --- a/net/nfc/nfc.h +++ b/net/nfc/nfc.h @@ -101,6 +101,10 @@ static inline void nfc_device_iter_exit(struct class_dev_iter *iter) class_dev_iter_exit(iter); } +int nfc_dev_up(struct nfc_dev *dev); + +int nfc_dev_down(struct nfc_dev *dev); + int nfc_start_poll(struct nfc_dev *dev, u32 protocols); int nfc_stop_poll(struct nfc_dev *dev); -- cgit v1.2.3 From 55eb94f9e923cba376cdf48ea5ab28d81116bead Mon Sep 17 00:00:00 2001 From: Ilan Elias Date: Sun, 18 Sep 2011 11:19:34 +0300 Subject: NFC: move nfc.h from include/net to include/net/nfc The file nfc.h was moved from include/net to include/net/nfc, since new NFC header files will be added to include/net/nfc. Signed-off-by: Ilan Elias Signed-off-by: John W. Linville --- MAINTAINERS | 2 +- drivers/nfc/pn533.c | 2 +- include/net/nfc.h | 165 -------------------------------------------------- include/net/nfc/nfc.h | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++ net/nfc/nfc.h | 2 +- 5 files changed, 168 insertions(+), 168 deletions(-) delete mode 100644 include/net/nfc.h create mode 100644 include/net/nfc/nfc.h (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index ce0e60c7b07a..1789ce22ea8c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4508,7 +4508,7 @@ L: linux-wireless@vger.kernel.org S: Maintained F: net/nfc/ F: include/linux/nfc.h -F: include/net/nfc.h +F: include/net/nfc/ F: drivers/nfc/ NFS, SUNRPC, AND LOCKD CLIENTS diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index c78eb6afd0cb..7bcb1febef0d 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #define VERSION "0.1" diff --git a/include/net/nfc.h b/include/net/nfc.h deleted file mode 100644 index 6a7f602aa841..000000000000 --- a/include/net/nfc.h +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) 2011 Instituto Nokia de Tecnologia - * - * Authors: - * Lauro Ramos Venancio - * Aloisio Almeida Jr - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef __NET_NFC_H -#define __NET_NFC_H - -#include -#include - -#define nfc_dev_info(dev, fmt, arg...) dev_info((dev), "NFC: " fmt "\n", ## arg) -#define nfc_dev_err(dev, fmt, arg...) dev_err((dev), "NFC: " fmt "\n", ## arg) -#define nfc_dev_dbg(dev, fmt, arg...) dev_dbg((dev), fmt "\n", ## arg) - -struct nfc_dev; - -/** - * data_exchange_cb_t - Definition of nfc_data_exchange callback - * - * @context: nfc_data_exchange cb_context parameter - * @skb: response data - * @err: If an error has occurred during data exchange, it is the - * error number. Zero means no error. - * - * When a rx or tx package is lost or corrupted or the target gets out - * of the operating field, err is -EIO. - */ -typedef void (*data_exchange_cb_t)(void *context, struct sk_buff *skb, - int err); - -struct nfc_ops { - int (*dev_up)(struct nfc_dev *dev); - int (*dev_down)(struct nfc_dev *dev); - int (*start_poll)(struct nfc_dev *dev, u32 protocols); - void (*stop_poll)(struct nfc_dev *dev); - int (*activate_target)(struct nfc_dev *dev, u32 target_idx, - u32 protocol); - void (*deactivate_target)(struct nfc_dev *dev, u32 target_idx); - int (*data_exchange)(struct nfc_dev *dev, u32 target_idx, - struct sk_buff *skb, data_exchange_cb_t cb, - void *cb_context); -}; - -struct nfc_target { - u32 idx; - u32 supported_protocols; - u16 sens_res; - u8 sel_res; -}; - -struct nfc_genl_data { - u32 poll_req_pid; - struct mutex genl_data_mutex; -}; - -struct nfc_dev { - unsigned idx; - unsigned target_idx; - struct nfc_target *targets; - int n_targets; - int targets_generation; - spinlock_t targets_lock; - struct device dev; - bool dev_up; - bool polling; - bool remote_activated; - struct nfc_genl_data genl_data; - u32 supported_protocols; - - int tx_headroom; - int tx_tailroom; - - struct nfc_ops *ops; -}; -#define to_nfc_dev(_dev) container_of(_dev, struct nfc_dev, dev) - -extern struct class nfc_class; - -struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops, - u32 supported_protocols, - int tx_headroom, - int tx_tailroom); - -/** - * nfc_free_device - free nfc device - * - * @dev: The nfc device to free - */ -static inline void nfc_free_device(struct nfc_dev *dev) -{ - put_device(&dev->dev); -} - -int nfc_register_device(struct nfc_dev *dev); - -void nfc_unregister_device(struct nfc_dev *dev); - -/** - * nfc_set_parent_dev - set the parent device - * - * @nfc_dev: The nfc device whose parent is being set - * @dev: The parent device - */ -static inline void nfc_set_parent_dev(struct nfc_dev *nfc_dev, - struct device *dev) -{ - nfc_dev->dev.parent = dev; -} - -/** - * nfc_set_drvdata - set driver specifc data - * - * @dev: The nfc device - * @data: Pointer to driver specifc data - */ -static inline void nfc_set_drvdata(struct nfc_dev *dev, void *data) -{ - dev_set_drvdata(&dev->dev, data); -} - -/** - * nfc_get_drvdata - get driver specifc data - * - * @dev: The nfc device - */ -static inline void *nfc_get_drvdata(struct nfc_dev *dev) -{ - return dev_get_drvdata(&dev->dev); -} - -/** - * nfc_device_name - get the nfc device name - * - * @dev: The nfc device whose name to return - */ -static inline const char *nfc_device_name(struct nfc_dev *dev) -{ - return dev_name(&dev->dev); -} - -struct sk_buff *nfc_alloc_skb(unsigned int size, gfp_t gfp); - -int nfc_targets_found(struct nfc_dev *dev, struct nfc_target *targets, - int ntargets); - -#endif /* __NET_NFC_H */ diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h new file mode 100644 index 000000000000..6a7f602aa841 --- /dev/null +++ b/include/net/nfc/nfc.h @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2011 Instituto Nokia de Tecnologia + * + * Authors: + * Lauro Ramos Venancio + * Aloisio Almeida Jr + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __NET_NFC_H +#define __NET_NFC_H + +#include +#include + +#define nfc_dev_info(dev, fmt, arg...) dev_info((dev), "NFC: " fmt "\n", ## arg) +#define nfc_dev_err(dev, fmt, arg...) dev_err((dev), "NFC: " fmt "\n", ## arg) +#define nfc_dev_dbg(dev, fmt, arg...) dev_dbg((dev), fmt "\n", ## arg) + +struct nfc_dev; + +/** + * data_exchange_cb_t - Definition of nfc_data_exchange callback + * + * @context: nfc_data_exchange cb_context parameter + * @skb: response data + * @err: If an error has occurred during data exchange, it is the + * error number. Zero means no error. + * + * When a rx or tx package is lost or corrupted or the target gets out + * of the operating field, err is -EIO. + */ +typedef void (*data_exchange_cb_t)(void *context, struct sk_buff *skb, + int err); + +struct nfc_ops { + int (*dev_up)(struct nfc_dev *dev); + int (*dev_down)(struct nfc_dev *dev); + int (*start_poll)(struct nfc_dev *dev, u32 protocols); + void (*stop_poll)(struct nfc_dev *dev); + int (*activate_target)(struct nfc_dev *dev, u32 target_idx, + u32 protocol); + void (*deactivate_target)(struct nfc_dev *dev, u32 target_idx); + int (*data_exchange)(struct nfc_dev *dev, u32 target_idx, + struct sk_buff *skb, data_exchange_cb_t cb, + void *cb_context); +}; + +struct nfc_target { + u32 idx; + u32 supported_protocols; + u16 sens_res; + u8 sel_res; +}; + +struct nfc_genl_data { + u32 poll_req_pid; + struct mutex genl_data_mutex; +}; + +struct nfc_dev { + unsigned idx; + unsigned target_idx; + struct nfc_target *targets; + int n_targets; + int targets_generation; + spinlock_t targets_lock; + struct device dev; + bool dev_up; + bool polling; + bool remote_activated; + struct nfc_genl_data genl_data; + u32 supported_protocols; + + int tx_headroom; + int tx_tailroom; + + struct nfc_ops *ops; +}; +#define to_nfc_dev(_dev) container_of(_dev, struct nfc_dev, dev) + +extern struct class nfc_class; + +struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops, + u32 supported_protocols, + int tx_headroom, + int tx_tailroom); + +/** + * nfc_free_device - free nfc device + * + * @dev: The nfc device to free + */ +static inline void nfc_free_device(struct nfc_dev *dev) +{ + put_device(&dev->dev); +} + +int nfc_register_device(struct nfc_dev *dev); + +void nfc_unregister_device(struct nfc_dev *dev); + +/** + * nfc_set_parent_dev - set the parent device + * + * @nfc_dev: The nfc device whose parent is being set + * @dev: The parent device + */ +static inline void nfc_set_parent_dev(struct nfc_dev *nfc_dev, + struct device *dev) +{ + nfc_dev->dev.parent = dev; +} + +/** + * nfc_set_drvdata - set driver specifc data + * + * @dev: The nfc device + * @data: Pointer to driver specifc data + */ +static inline void nfc_set_drvdata(struct nfc_dev *dev, void *data) +{ + dev_set_drvdata(&dev->dev, data); +} + +/** + * nfc_get_drvdata - get driver specifc data + * + * @dev: The nfc device + */ +static inline void *nfc_get_drvdata(struct nfc_dev *dev) +{ + return dev_get_drvdata(&dev->dev); +} + +/** + * nfc_device_name - get the nfc device name + * + * @dev: The nfc device whose name to return + */ +static inline const char *nfc_device_name(struct nfc_dev *dev) +{ + return dev_name(&dev->dev); +} + +struct sk_buff *nfc_alloc_skb(unsigned int size, gfp_t gfp); + +int nfc_targets_found(struct nfc_dev *dev, struct nfc_target *targets, + int ntargets); + +#endif /* __NET_NFC_H */ diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h index 1a877de8e230..b6753f45624e 100644 --- a/net/nfc/nfc.h +++ b/net/nfc/nfc.h @@ -24,7 +24,7 @@ #ifndef __LOCAL_NFC_H #define __LOCAL_NFC_H -#include +#include #include __attribute__((format (printf, 2, 3))) -- cgit v1.2.3 From 6a2968aaf50c7a22fced77a5e24aa636281efca8 Mon Sep 17 00:00:00 2001 From: Ilan Elias Date: Sun, 18 Sep 2011 11:19:35 +0300 Subject: NFC: basic NCI protocol implementation The NFC Controller Interface (NCI) is a standard communication protocol between an NFC Controller (NFCC) and a Device Host (DH), defined by the NFC Forum. Signed-off-by: Ilan Elias Signed-off-by: John W. Linville --- include/net/nfc/nci.h | 313 ++++++++++++++++++ include/net/nfc/nci_core.h | 183 +++++++++++ net/nfc/Kconfig | 2 + net/nfc/Makefile | 1 + net/nfc/nci/Kconfig | 10 + net/nfc/nci/Makefile | 7 + net/nfc/nci/core.c | 790 +++++++++++++++++++++++++++++++++++++++++++++ net/nfc/nci/data.c | 245 ++++++++++++++ net/nfc/nci/lib.c | 94 ++++++ net/nfc/nci/ntf.c | 258 +++++++++++++++ net/nfc/nci/rsp.c | 226 +++++++++++++ 11 files changed, 2129 insertions(+) create mode 100644 include/net/nfc/nci.h create mode 100644 include/net/nfc/nci_core.h create mode 100644 net/nfc/nci/Kconfig create mode 100644 net/nfc/nci/Makefile create mode 100644 net/nfc/nci/core.c create mode 100644 net/nfc/nci/data.c create mode 100644 net/nfc/nci/lib.c create mode 100644 net/nfc/nci/ntf.c create mode 100644 net/nfc/nci/rsp.c (limited to 'include') diff --git a/include/net/nfc/nci.h b/include/net/nfc/nci.h new file mode 100644 index 000000000000..39b85bc0804f --- /dev/null +++ b/include/net/nfc/nci.h @@ -0,0 +1,313 @@ +/* + * The NFC Controller Interface is the communication protocol between an + * NFC Controller (NFCC) and a Device Host (DH). + * + * Copyright (C) 2011 Texas Instruments, Inc. + * + * Written by Ilan Elias + * + * Acknowledgements: + * This file is based on hci.h, which was written + * by Maxim Krasnyansky. + * + * 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __NCI_H +#define __NCI_H + +/* NCI constants */ +#define NCI_MAX_NUM_MAPPING_CONFIGS 10 +#define NCI_MAX_NUM_RF_CONFIGS 10 +#define NCI_MAX_NUM_CONN 10 + +/* NCI Status Codes */ +#define NCI_STATUS_OK 0x00 +#define NCI_STATUS_REJECTED 0x01 +#define NCI_STATUS_MESSAGE_CORRUPTED 0x02 +#define NCI_STATUS_BUFFER_FULL 0x03 +#define NCI_STATUS_FAILED 0x04 +#define NCI_STATUS_NOT_INITIALIZED 0x05 +#define NCI_STATUS_SYNTAX_ERROR 0x06 +#define NCI_STATUS_SEMANTIC_ERROR 0x07 +#define NCI_STATUS_UNKNOWN_GID 0x08 +#define NCI_STATUS_UNKNOWN_OID 0x09 +#define NCI_STATUS_INVALID_PARAM 0x0a +#define NCI_STATUS_MESSAGE_SIZE_EXCEEDED 0x0b +/* Discovery Specific Status Codes */ +#define NCI_STATUS_DISCOVERY_ALREADY_STARTED 0xa0 +#define NCI_STATUS_DISCOVERY_TARGET_ACTIVATION_FAILED 0xa1 +/* RF Interface Specific Status Codes */ +#define NCI_STATUS_RF_TRANSMISSION_ERROR 0xb0 +#define NCI_STATUS_RF_PROTOCOL_ERROR 0xb1 +#define NCI_STATUS_RF_TIMEOUT_ERROR 0xb2 +#define NCI_STATUS_RF_LINK_LOSS_ERROR 0xb3 +/* NFCEE Interface Specific Status Codes */ +#define NCI_STATUS_MAX_ACTIVE_NFCEE_INTERFACES_REACHED 0xc0 +#define NCI_STATUS_NFCEE_INTERFACE_ACTIVATION_FAILED 0xc1 +#define NCI_STATUS_NFCEE_TRANSMISSION_ERROR 0xc2 +#define NCI_STATUS_NFCEE_PROTOCOL_ERROR 0xc3 +#define NCI_STATUS_NFCEE_TIMEOUT_ERROR 0xc4 + +/* NCI RF Technology and Mode */ +#define NCI_NFC_A_PASSIVE_POLL_MODE 0x00 +#define NCI_NFC_B_PASSIVE_POLL_MODE 0x01 +#define NCI_NFC_F_PASSIVE_POLL_MODE 0x02 +#define NCI_NFC_A_ACTIVE_POLL_MODE 0x03 +#define NCI_NFC_F_ACTIVE_POLL_MODE 0x05 +#define NCI_NFC_A_PASSIVE_LISTEN_MODE 0x80 +#define NCI_NFC_B_PASSIVE_LISTEN_MODE 0x81 +#define NCI_NFC_F_PASSIVE_LISTEN_MODE 0x82 +#define NCI_NFC_A_ACTIVE_LISTEN_MODE 0x83 +#define NCI_NFC_F_ACTIVE_LISTEN_MODE 0x85 + +/* NCI RF Protocols */ +#define NCI_RF_PROTOCOL_UNKNOWN 0x00 +#define NCI_RF_PROTOCOL_T1T 0x01 +#define NCI_RF_PROTOCOL_T2T 0x02 +#define NCI_RF_PROTOCOL_T3T 0x03 +#define NCI_RF_PROTOCOL_ISO_DEP 0x04 +#define NCI_RF_PROTOCOL_NFC_DEP 0x05 + +/* NCI RF Interfaces */ +#define NCI_RF_INTERFACE_RFU 0x00 +#define NCI_RF_INTERFACE_FRAME 0x01 +#define NCI_RF_INTERFACE_ISO_DEP 0x02 +#define NCI_RF_INTERFACE_NFC_DEP 0x03 + +/* NCI RF_DISCOVER_MAP_CMD modes */ +#define NCI_DISC_MAP_MODE_POLL 0x01 +#define NCI_DISC_MAP_MODE_LISTEN 0x02 +#define NCI_DISC_MAP_MODE_BOTH 0x03 + +/* NCI Discovery Types */ +#define NCI_DISCOVERY_TYPE_POLL_A_PASSIVE 0x00 +#define NCI_DISCOVERY_TYPE_POLL_B_PASSIVE 0x01 +#define NCI_DISCOVERY_TYPE_POLL_F_PASSIVE 0x02 +#define NCI_DISCOVERY_TYPE_POLL_A_ACTIVE 0x03 +#define NCI_DISCOVERY_TYPE_POLL_F_ACTIVE 0x05 +#define NCI_DISCOVERY_TYPE_WAKEUP_A_PASSIVE 0x06 +#define NCI_DISCOVERY_TYPE_WAKEUP_B_PASSIVE 0x07 +#define NCI_DISCOVERY_TYPE_WAKEUP_A_ACTIVE 0x09 +#define NCI_DISCOVERY_TYPE_LISTEN_A_PASSIVE 0x80 +#define NCI_DISCOVERY_TYPE_LISTEN_B_PASSIVE 0x81 +#define NCI_DISCOVERY_TYPE_LISTEN_F_PASSIVE 0x82 +#define NCI_DISCOVERY_TYPE_LISTEN_A_ACTIVE 0x83 +#define NCI_DISCOVERY_TYPE_LISTEN_F_ACTIVE 0x85 + +/* NCI Deactivation Type */ +#define NCI_DEACTIVATE_TYPE_IDLE_MODE 0x00 +#define NCI_DEACTIVATE_TYPE_SLEEP_MODE 0x01 +#define NCI_DEACTIVATE_TYPE_SLEEP_AF_MODE 0x02 +#define NCI_DEACTIVATE_TYPE_RF_LINK_LOSS 0x03 +#define NCI_DEACTIVATE_TYPE_DISCOVERY_ERROR 0x04 + +/* Message Type (MT) */ +#define NCI_MT_DATA_PKT 0x00 +#define NCI_MT_CMD_PKT 0x01 +#define NCI_MT_RSP_PKT 0x02 +#define NCI_MT_NTF_PKT 0x03 + +#define nci_mt(hdr) (((hdr)[0]>>5)&0x07) +#define nci_mt_set(hdr, mt) ((hdr)[0] |= (__u8)(((mt)&0x07)<<5)) + +/* Packet Boundary Flag (PBF) */ +#define NCI_PBF_LAST 0x00 +#define NCI_PBF_CONT 0x01 + +#define nci_pbf(hdr) (__u8)(((hdr)[0]>>4)&0x01) +#define nci_pbf_set(hdr, pbf) ((hdr)[0] |= (__u8)(((pbf)&0x01)<<4)) + +/* Control Opcode manipulation */ +#define nci_opcode_pack(gid, oid) (__u16)((((__u16)((gid)&0x0f))<<8)|\ + ((__u16)((oid)&0x3f))) +#define nci_opcode(hdr) nci_opcode_pack(hdr[0], hdr[1]) +#define nci_opcode_gid(op) (__u8)(((op)&0x0f00)>>8) +#define nci_opcode_oid(op) (__u8)((op)&0x003f) + +/* Payload Length */ +#define nci_plen(hdr) (__u8)((hdr)[2]) + +/* Connection ID */ +#define nci_conn_id(hdr) (__u8)(((hdr)[0])&0x0f) + +/* GID values */ +#define NCI_GID_CORE 0x0 +#define NCI_GID_RF_MGMT 0x1 +#define NCI_GID_NFCEE_MGMT 0x2 +#define NCI_GID_PROPRIETARY 0xf + +/* ---- NCI Packet structures ---- */ +#define NCI_CTRL_HDR_SIZE 3 +#define NCI_DATA_HDR_SIZE 3 + +struct nci_ctrl_hdr { + __u8 gid; /* MT & PBF & GID */ + __u8 oid; + __u8 plen; +} __packed; + +struct nci_data_hdr { + __u8 conn_id; /* MT & PBF & ConnID */ + __u8 rfu; + __u8 plen; +} __packed; + +/* ------------------------ */ +/* ----- NCI Commands ---- */ +/* ------------------------ */ +#define NCI_OP_CORE_RESET_CMD nci_opcode_pack(NCI_GID_CORE, 0x00) + +#define NCI_OP_CORE_INIT_CMD nci_opcode_pack(NCI_GID_CORE, 0x01) + +#define NCI_OP_CORE_SET_CONFIG_CMD nci_opcode_pack(NCI_GID_CORE, 0x02) + +#define NCI_OP_CORE_CONN_CREATE_CMD nci_opcode_pack(NCI_GID_CORE, 0x04) +struct nci_core_conn_create_cmd { + __u8 target_handle; + __u8 num_target_specific_params; +} __packed; + +#define NCI_OP_CORE_CONN_CLOSE_CMD nci_opcode_pack(NCI_GID_CORE, 0x06) + +#define NCI_OP_RF_DISCOVER_MAP_CMD nci_opcode_pack(NCI_GID_RF_MGMT, 0x00) +struct disc_map_config { + __u8 rf_protocol; + __u8 mode; + __u8 rf_interface_type; +} __packed; + +struct nci_rf_disc_map_cmd { + __u8 num_mapping_configs; + struct disc_map_config mapping_configs + [NCI_MAX_NUM_MAPPING_CONFIGS]; +} __packed; + +#define NCI_OP_RF_DISCOVER_CMD nci_opcode_pack(NCI_GID_RF_MGMT, 0x03) +struct disc_config { + __u8 type; + __u8 frequency; +} __packed; + +struct nci_rf_disc_cmd { + __u8 num_disc_configs; + struct disc_config disc_configs[NCI_MAX_NUM_RF_CONFIGS]; +} __packed; + +#define NCI_OP_RF_DEACTIVATE_CMD nci_opcode_pack(NCI_GID_RF_MGMT, 0x06) +struct nci_rf_deactivate_cmd { + __u8 type; +} __packed; + +/* ----------------------- */ +/* ---- NCI Responses ---- */ +/* ----------------------- */ +#define NCI_OP_CORE_RESET_RSP nci_opcode_pack(NCI_GID_CORE, 0x00) +struct nci_core_reset_rsp { + __u8 status; + __u8 nci_ver; +} __packed; + +#define NCI_OP_CORE_INIT_RSP nci_opcode_pack(NCI_GID_CORE, 0x01) +struct nci_core_init_rsp_1 { + __u8 status; + __le32 nfcc_features; + __u8 num_supported_rf_interfaces; + __u8 supported_rf_interfaces[0]; /* variable size array */ + /* continuted in nci_core_init_rsp_2 */ +} __packed; + +struct nci_core_init_rsp_2 { + __u8 max_logical_connections; + __le16 max_routing_table_size; + __u8 max_control_packet_payload_length; + __le16 rf_sending_buffer_size; + __le16 rf_receiving_buffer_size; + __le16 manufacturer_id; +} __packed; + +#define NCI_OP_CORE_SET_CONFIG_RSP nci_opcode_pack(NCI_GID_CORE, 0x02) + +#define NCI_OP_CORE_CONN_CREATE_RSP nci_opcode_pack(NCI_GID_CORE, 0x04) +struct nci_core_conn_create_rsp { + __u8 status; + __u8 max_pkt_payload_size; + __u8 initial_num_credits; + __u8 conn_id; +} __packed; + +#define NCI_OP_CORE_CONN_CLOSE_RSP nci_opcode_pack(NCI_GID_CORE, 0x06) + +#define NCI_OP_RF_DISCOVER_MAP_RSP nci_opcode_pack(NCI_GID_RF_MGMT, 0x00) + +#define NCI_OP_RF_DISCOVER_RSP nci_opcode_pack(NCI_GID_RF_MGMT, 0x03) + +#define NCI_OP_RF_DEACTIVATE_RSP nci_opcode_pack(NCI_GID_RF_MGMT, 0x06) + +/* --------------------------- */ +/* ---- NCI Notifications ---- */ +/* --------------------------- */ +#define NCI_OP_CORE_CONN_CREDITS_NTF nci_opcode_pack(NCI_GID_CORE, 0x07) +struct conn_credit_entry { + __u8 conn_id; + __u8 credits; +} __packed; + +struct nci_core_conn_credit_ntf { + __u8 num_entries; + struct conn_credit_entry conn_entries[NCI_MAX_NUM_CONN]; +} __packed; + +#define NCI_OP_RF_FIELD_INFO_NTF nci_opcode_pack(NCI_GID_CORE, 0x08) +struct nci_rf_field_info_ntf { + __u8 rf_field_status; +} __packed; + +#define NCI_OP_RF_ACTIVATE_NTF nci_opcode_pack(NCI_GID_RF_MGMT, 0x05) +struct rf_tech_specific_params_nfca_poll { + __u16 sens_res; + __u8 nfcid1_len; /* 0, 4, 7, or 10 Bytes */ + __u8 nfcid1[10]; + __u8 sel_res_len; /* 0 or 1 Bytes */ + __u8 sel_res; +} __packed; + +struct activation_params_nfca_poll_iso_dep { + __u8 rats_res_len; + __u8 rats_res[20]; +}; + +struct nci_rf_activate_ntf { + __u8 target_handle; + __u8 rf_protocol; + __u8 rf_tech_and_mode; + __u8 rf_tech_specific_params_len; + + union { + struct rf_tech_specific_params_nfca_poll nfca_poll; + } rf_tech_specific_params; + + __u8 rf_interface_type; + __u8 activation_params_len; + + union { + struct activation_params_nfca_poll_iso_dep nfca_poll_iso_dep; + } activation_params; + +} __packed; + +#define NCI_OP_RF_DEACTIVATE_NTF nci_opcode_pack(NCI_GID_RF_MGMT, 0x06) + +#endif /* __NCI_H */ diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h new file mode 100644 index 000000000000..2563f3a95e67 --- /dev/null +++ b/include/net/nfc/nci_core.h @@ -0,0 +1,183 @@ +/* + * The NFC Controller Interface is the communication protocol between an + * NFC Controller (NFCC) and a Device Host (DH). + * + * Copyright (C) 2011 Texas Instruments, Inc. + * + * Written by Ilan Elias + * + * Acknowledgements: + * This file is based on hci_core.h, which was written + * by Maxim Krasnyansky. + * + * 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __NCI_CORE_H +#define __NCI_CORE_H + +#include +#include + +#include +#include + +/* NCI device state */ +enum { + NCI_INIT, + NCI_UP, + NCI_DISCOVERY, + NCI_POLL_ACTIVE, +}; + +/* NCI timeouts */ +#define NCI_RESET_TIMEOUT 5000 +#define NCI_INIT_TIMEOUT 5000 +#define NCI_RF_DISC_TIMEOUT 5000 +#define NCI_RF_DEACTIVATE_TIMEOUT 5000 +#define NCI_CMD_TIMEOUT 5000 + +struct nci_dev; + +struct nci_ops { + int (*open)(struct nci_dev *ndev); + int (*close)(struct nci_dev *ndev); + int (*send)(struct sk_buff *skb); +}; + +#define NCI_MAX_SUPPORTED_RF_INTERFACES 4 + +/* NCI Core structures */ +struct nci_dev { + struct nfc_dev *nfc_dev; + struct nci_ops *ops; + + int tx_headroom; + int tx_tailroom; + + unsigned long flags; + + atomic_t cmd_cnt; + atomic_t credits_cnt; + + struct timer_list cmd_timer; + + struct workqueue_struct *cmd_wq; + struct work_struct cmd_work; + + struct workqueue_struct *rx_wq; + struct work_struct rx_work; + + struct workqueue_struct *tx_wq; + struct work_struct tx_work; + + struct sk_buff_head cmd_q; + struct sk_buff_head rx_q; + struct sk_buff_head tx_q; + + struct mutex req_lock; + struct completion req_completion; + __u32 req_status; + __u32 req_result; + + void *driver_data; + + __u32 poll_prots; + __u32 target_available_prots; + __u32 target_active_prot; + + /* received during NCI_OP_CORE_RESET_RSP */ + __u8 nci_ver; + + /* received during NCI_OP_CORE_INIT_RSP */ + __u32 nfcc_features; + __u8 num_supported_rf_interfaces; + __u8 supported_rf_interfaces + [NCI_MAX_SUPPORTED_RF_INTERFACES]; + __u8 max_logical_connections; + __u16 max_routing_table_size; + __u8 max_control_packet_payload_length; + __u16 rf_sending_buffer_size; + __u16 rf_receiving_buffer_size; + __u16 manufacturer_id; + + /* received during NCI_OP_CORE_CONN_CREATE_RSP for static conn 0 */ + __u8 max_pkt_payload_size; + __u8 initial_num_credits; + __u8 conn_id; + + /* stored during nci_data_exchange */ + data_exchange_cb_t data_exchange_cb; + void *data_exchange_cb_context; + struct sk_buff *rx_data_reassembly; +}; + +/* ----- NCI Devices ----- */ +struct nci_dev *nci_allocate_device(struct nci_ops *ops, + __u32 supported_protocols, + int tx_headroom, + int tx_tailroom); +void nci_free_device(struct nci_dev *ndev); +int nci_register_device(struct nci_dev *ndev); +void nci_unregister_device(struct nci_dev *ndev); +int nci_recv_frame(struct sk_buff *skb); + +static inline struct sk_buff *nci_skb_alloc(struct nci_dev *ndev, + unsigned int len, + gfp_t how) +{ + struct sk_buff *skb; + + skb = alloc_skb(len + ndev->tx_headroom + ndev->tx_tailroom, how); + if (skb) + skb_reserve(skb, ndev->tx_headroom); + + return skb; +} + +static inline void nci_set_parent_dev(struct nci_dev *ndev, struct device *dev) +{ + nfc_set_parent_dev(ndev->nfc_dev, dev); +} + +static inline void nci_set_drvdata(struct nci_dev *ndev, void *data) +{ + ndev->driver_data = data; +} + +static inline void *nci_get_drvdata(struct nci_dev *ndev) +{ + return ndev->driver_data; +} + +void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb); +void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb); +void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb); +int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload); +int nci_send_data(struct nci_dev *ndev, __u8 conn_id, struct sk_buff *skb); +void nci_data_exchange_complete(struct nci_dev *ndev, struct sk_buff *skb, + int err); + +/* ----- NCI requests ----- */ +#define NCI_REQ_DONE 0 +#define NCI_REQ_PEND 1 +#define NCI_REQ_CANCELED 2 + +void nci_req_complete(struct nci_dev *ndev, int result); + +/* ----- NCI status code ----- */ +int nci_to_errno(__u8 code); + +#endif /* __NCI_CORE_H */ diff --git a/net/nfc/Kconfig b/net/nfc/Kconfig index 33e095b124b3..58cddadf8e8e 100644 --- a/net/nfc/Kconfig +++ b/net/nfc/Kconfig @@ -13,4 +13,6 @@ menuconfig NFC To compile this support as a module, choose M here: the module will be called nfc. +source "net/nfc/nci/Kconfig" + source "drivers/nfc/Kconfig" diff --git a/net/nfc/Makefile b/net/nfc/Makefile index 16250c353851..fbb550f2377b 100644 --- a/net/nfc/Makefile +++ b/net/nfc/Makefile @@ -3,5 +3,6 @@ # obj-$(CONFIG_NFC) += nfc.o +obj-$(CONFIG_NFC_NCI) += nci/ nfc-objs := core.o netlink.o af_nfc.o rawsock.o diff --git a/net/nfc/nci/Kconfig b/net/nfc/nci/Kconfig new file mode 100644 index 000000000000..decdc49b26d8 --- /dev/null +++ b/net/nfc/nci/Kconfig @@ -0,0 +1,10 @@ +config NFC_NCI + depends on NFC && EXPERIMENTAL + tristate "NCI protocol support (EXPERIMENTAL)" + default n + help + NCI (NFC Controller Interface) is a communication protocol between + an NFC Controller (NFCC) and a Device Host (DH). + + Say Y here to compile NCI support into the kernel or say M to + compile it as module (nci). diff --git a/net/nfc/nci/Makefile b/net/nfc/nci/Makefile new file mode 100644 index 000000000000..cdb3a2e44471 --- /dev/null +++ b/net/nfc/nci/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the Linux NFC NCI layer. +# + +obj-$(CONFIG_NFC_NCI) += nci.o + +nci-objs := core.o data.o lib.o ntf.o rsp.o \ No newline at end of file diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c new file mode 100644 index 000000000000..895e5fdf464a --- /dev/null +++ b/net/nfc/nci/core.c @@ -0,0 +1,790 @@ +/* + * The NFC Controller Interface is the communication protocol between an + * NFC Controller (NFCC) and a Device Host (DH). + * + * Copyright (C) 2011 Texas Instruments, Inc. + * + * Written by Ilan Elias + * + * Acknowledgements: + * This file is based on hci_core.c, which was written + * by Maxim Krasnyansky. + * + * 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include + +#include "../nfc.h" +#include +#include +#include + +static void nci_cmd_work(struct work_struct *work); +static void nci_rx_work(struct work_struct *work); +static void nci_tx_work(struct work_struct *work); + +/* ---- NCI requests ---- */ + +void nci_req_complete(struct nci_dev *ndev, int result) +{ + if (ndev->req_status == NCI_REQ_PEND) { + ndev->req_result = result; + ndev->req_status = NCI_REQ_DONE; + complete(&ndev->req_completion); + } +} + +static void nci_req_cancel(struct nci_dev *ndev, int err) +{ + if (ndev->req_status == NCI_REQ_PEND) { + ndev->req_result = err; + ndev->req_status = NCI_REQ_CANCELED; + complete(&ndev->req_completion); + } +} + +/* Execute request and wait for completion. */ +static int __nci_request(struct nci_dev *ndev, + void (*req)(struct nci_dev *ndev, unsigned long opt), + unsigned long opt, + __u32 timeout) +{ + int rc = 0; + unsigned long completion_rc; + + ndev->req_status = NCI_REQ_PEND; + + init_completion(&ndev->req_completion); + req(ndev, opt); + completion_rc = wait_for_completion_interruptible_timeout( + &ndev->req_completion, + timeout); + + nfc_dbg("wait_for_completion return %ld", completion_rc); + + if (completion_rc > 0) { + switch (ndev->req_status) { + case NCI_REQ_DONE: + rc = nci_to_errno(ndev->req_result); + break; + + case NCI_REQ_CANCELED: + rc = -ndev->req_result; + break; + + default: + rc = -ETIMEDOUT; + break; + } + } else { + nfc_err("wait_for_completion_interruptible_timeout failed %ld", + completion_rc); + + rc = ((completion_rc == 0) ? (-ETIMEDOUT) : (completion_rc)); + } + + ndev->req_status = ndev->req_result = 0; + + return rc; +} + +static inline int nci_request(struct nci_dev *ndev, + void (*req)(struct nci_dev *ndev, unsigned long opt), + unsigned long opt, __u32 timeout) +{ + int rc; + + if (!test_bit(NCI_UP, &ndev->flags)) + return -ENETDOWN; + + /* Serialize all requests */ + mutex_lock(&ndev->req_lock); + rc = __nci_request(ndev, req, opt, timeout); + mutex_unlock(&ndev->req_lock); + + return rc; +} + +static void nci_reset_req(struct nci_dev *ndev, unsigned long opt) +{ + nci_send_cmd(ndev, NCI_OP_CORE_RESET_CMD, 0, NULL); +} + +static void nci_init_req(struct nci_dev *ndev, unsigned long opt) +{ + nci_send_cmd(ndev, NCI_OP_CORE_INIT_CMD, 0, NULL); +} + +static void nci_init_complete_req(struct nci_dev *ndev, unsigned long opt) +{ + struct nci_rf_disc_map_cmd cmd; + struct nci_core_conn_create_cmd conn_cmd; + int i; + + /* create static rf connection */ + conn_cmd.target_handle = 0; + conn_cmd.num_target_specific_params = 0; + nci_send_cmd(ndev, NCI_OP_CORE_CONN_CREATE_CMD, 2, &conn_cmd); + + /* set rf mapping configurations */ + cmd.num_mapping_configs = 0; + + /* by default mapping is set to NCI_RF_INTERFACE_FRAME */ + for (i = 0; i < ndev->num_supported_rf_interfaces; i++) { + if (ndev->supported_rf_interfaces[i] == + NCI_RF_INTERFACE_ISO_DEP) { + cmd.mapping_configs[cmd.num_mapping_configs] + .rf_protocol = NCI_RF_PROTOCOL_ISO_DEP; + cmd.mapping_configs[cmd.num_mapping_configs] + .mode = NCI_DISC_MAP_MODE_BOTH; + cmd.mapping_configs[cmd.num_mapping_configs] + .rf_interface_type = NCI_RF_INTERFACE_ISO_DEP; + cmd.num_mapping_configs++; + } else if (ndev->supported_rf_interfaces[i] == + NCI_RF_INTERFACE_NFC_DEP) { + cmd.mapping_configs[cmd.num_mapping_configs] + .rf_protocol = NCI_RF_PROTOCOL_NFC_DEP; + cmd.mapping_configs[cmd.num_mapping_configs] + .mode = NCI_DISC_MAP_MODE_BOTH; + cmd.mapping_configs[cmd.num_mapping_configs] + .rf_interface_type = NCI_RF_INTERFACE_NFC_DEP; + cmd.num_mapping_configs++; + } + + if (cmd.num_mapping_configs == NCI_MAX_NUM_MAPPING_CONFIGS) + break; + } + + nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_MAP_CMD, + (1 + (cmd.num_mapping_configs*sizeof(struct disc_map_config))), + &cmd); +} + +static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt) +{ + struct nci_rf_disc_cmd cmd; + __u32 protocols = opt; + + cmd.num_disc_configs = 0; + + if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && + (protocols & NFC_PROTO_JEWEL_MASK + || protocols & NFC_PROTO_MIFARE_MASK + || protocols & NFC_PROTO_ISO14443_MASK + || protocols & NFC_PROTO_NFC_DEP_MASK)) { + cmd.disc_configs[cmd.num_disc_configs].type = + NCI_DISCOVERY_TYPE_POLL_A_PASSIVE; + cmd.disc_configs[cmd.num_disc_configs].frequency = 1; + cmd.num_disc_configs++; + } + + if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && + (protocols & NFC_PROTO_ISO14443_MASK)) { + cmd.disc_configs[cmd.num_disc_configs].type = + NCI_DISCOVERY_TYPE_POLL_B_PASSIVE; + cmd.disc_configs[cmd.num_disc_configs].frequency = 1; + cmd.num_disc_configs++; + } + + if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && + (protocols & NFC_PROTO_FELICA_MASK + || protocols & NFC_PROTO_NFC_DEP_MASK)) { + cmd.disc_configs[cmd.num_disc_configs].type = + NCI_DISCOVERY_TYPE_POLL_F_PASSIVE; + cmd.disc_configs[cmd.num_disc_configs].frequency = 1; + cmd.num_disc_configs++; + } + + nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_CMD, + (1 + (cmd.num_disc_configs*sizeof(struct disc_config))), + &cmd); +} + +static void nci_rf_deactivate_req(struct nci_dev *ndev, unsigned long opt) +{ + struct nci_rf_deactivate_cmd cmd; + + cmd.type = NCI_DEACTIVATE_TYPE_IDLE_MODE; + + nci_send_cmd(ndev, NCI_OP_RF_DEACTIVATE_CMD, + sizeof(struct nci_rf_deactivate_cmd), + &cmd); +} + +static int nci_open_device(struct nci_dev *ndev) +{ + int rc = 0; + + mutex_lock(&ndev->req_lock); + + if (test_bit(NCI_UP, &ndev->flags)) { + rc = -EALREADY; + goto done; + } + + if (ndev->ops->open(ndev)) { + rc = -EIO; + goto done; + } + + atomic_set(&ndev->cmd_cnt, 1); + + set_bit(NCI_INIT, &ndev->flags); + + rc = __nci_request(ndev, nci_reset_req, 0, + msecs_to_jiffies(NCI_RESET_TIMEOUT)); + + if (!rc) { + rc = __nci_request(ndev, nci_init_req, 0, + msecs_to_jiffies(NCI_INIT_TIMEOUT)); + } + + if (!rc) { + rc = __nci_request(ndev, nci_init_complete_req, 0, + msecs_to_jiffies(NCI_INIT_TIMEOUT)); + } + + clear_bit(NCI_INIT, &ndev->flags); + + if (!rc) { + set_bit(NCI_UP, &ndev->flags); + } else { + /* Init failed, cleanup */ + skb_queue_purge(&ndev->cmd_q); + skb_queue_purge(&ndev->rx_q); + skb_queue_purge(&ndev->tx_q); + + ndev->ops->close(ndev); + ndev->flags = 0; + } + +done: + mutex_unlock(&ndev->req_lock); + return rc; +} + +static int nci_close_device(struct nci_dev *ndev) +{ + nci_req_cancel(ndev, ENODEV); + mutex_lock(&ndev->req_lock); + + if (!test_and_clear_bit(NCI_UP, &ndev->flags)) { + del_timer_sync(&ndev->cmd_timer); + mutex_unlock(&ndev->req_lock); + return 0; + } + + /* Drop RX and TX queues */ + skb_queue_purge(&ndev->rx_q); + skb_queue_purge(&ndev->tx_q); + + /* Flush RX and TX wq */ + flush_workqueue(ndev->rx_wq); + flush_workqueue(ndev->tx_wq); + + /* Reset device */ + skb_queue_purge(&ndev->cmd_q); + atomic_set(&ndev->cmd_cnt, 1); + + set_bit(NCI_INIT, &ndev->flags); + __nci_request(ndev, nci_reset_req, 0, + msecs_to_jiffies(NCI_RESET_TIMEOUT)); + clear_bit(NCI_INIT, &ndev->flags); + + /* Flush cmd wq */ + flush_workqueue(ndev->cmd_wq); + + /* After this point our queues are empty + * and no works are scheduled. */ + ndev->ops->close(ndev); + + /* Clear flags */ + ndev->flags = 0; + + mutex_unlock(&ndev->req_lock); + + return 0; +} + +/* NCI command timer function */ +static void nci_cmd_timer(unsigned long arg) +{ + struct nci_dev *ndev = (void *) arg; + + nfc_dbg("entry"); + + atomic_set(&ndev->cmd_cnt, 1); + queue_work(ndev->cmd_wq, &ndev->cmd_work); +} + +static int nci_dev_up(struct nfc_dev *nfc_dev) +{ + struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + + nfc_dbg("entry"); + + return nci_open_device(ndev); +} + +static int nci_dev_down(struct nfc_dev *nfc_dev) +{ + struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + + nfc_dbg("entry"); + + return nci_close_device(ndev); +} + +static int nci_start_poll(struct nfc_dev *nfc_dev, __u32 protocols) +{ + struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + int rc; + + nfc_dbg("entry"); + + if (test_bit(NCI_DISCOVERY, &ndev->flags)) { + nfc_err("unable to start poll, since poll is already active"); + return -EBUSY; + } + + if (test_bit(NCI_POLL_ACTIVE, &ndev->flags)) { + nfc_dbg("target already active, first deactivate..."); + + rc = nci_request(ndev, nci_rf_deactivate_req, 0, + msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); + if (rc) + return -EBUSY; + } + + rc = nci_request(ndev, nci_rf_discover_req, protocols, + msecs_to_jiffies(NCI_RF_DISC_TIMEOUT)); + + if (!rc) + ndev->poll_prots = protocols; + + return rc; +} + +static void nci_stop_poll(struct nfc_dev *nfc_dev) +{ + struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + + nfc_dbg("entry"); + + if (!test_bit(NCI_DISCOVERY, &ndev->flags)) { + nfc_err("unable to stop poll, since poll is not active"); + return; + } + + nci_request(ndev, nci_rf_deactivate_req, 0, + msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); +} + +static int nci_activate_target(struct nfc_dev *nfc_dev, __u32 target_idx, + __u32 protocol) +{ + struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + + nfc_dbg("entry, target_idx %d, protocol 0x%x", target_idx, protocol); + + if (!test_bit(NCI_POLL_ACTIVE, &ndev->flags)) { + nfc_err("there is no available target to activate"); + return -EINVAL; + } + + if (ndev->target_active_prot) { + nfc_err("there is already an active target"); + return -EBUSY; + } + + if (!(ndev->target_available_prots & (1 << protocol))) { + nfc_err("target does not support the requested protocol 0x%x", + protocol); + return -EINVAL; + } + + ndev->target_active_prot = protocol; + ndev->target_available_prots = 0; + + return 0; +} + +static void nci_deactivate_target(struct nfc_dev *nfc_dev, __u32 target_idx) +{ + struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + + nfc_dbg("entry, target_idx %d", target_idx); + + if (!ndev->target_active_prot) { + nfc_err("unable to deactivate target, no active target"); + return; + } + + ndev->target_active_prot = 0; + + if (test_bit(NCI_POLL_ACTIVE, &ndev->flags)) { + nci_request(ndev, nci_rf_deactivate_req, 0, + msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); + } +} + +static int nci_data_exchange(struct nfc_dev *nfc_dev, __u32 target_idx, + struct sk_buff *skb, + data_exchange_cb_t cb, + void *cb_context) +{ + struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + + nfc_dbg("entry, target_idx %d, len %d", target_idx, skb->len); + + if (!ndev->target_active_prot) { + nfc_err("unable to exchange data, no active target"); + return -EINVAL; + } + + /* store cb and context to be used on receiving data */ + ndev->data_exchange_cb = cb; + ndev->data_exchange_cb_context = cb_context; + + return nci_send_data(ndev, ndev->conn_id, skb); +} + +static struct nfc_ops nci_nfc_ops = { + .dev_up = nci_dev_up, + .dev_down = nci_dev_down, + .start_poll = nci_start_poll, + .stop_poll = nci_stop_poll, + .activate_target = nci_activate_target, + .deactivate_target = nci_deactivate_target, + .data_exchange = nci_data_exchange, +}; + +/* ---- Interface to NCI drivers ---- */ + +/** + * nci_allocate_device - allocate a new nci device + * + * @ops: device operations + * @supported_protocols: NFC protocols supported by the device + */ +struct nci_dev *nci_allocate_device(struct nci_ops *ops, + __u32 supported_protocols, + int tx_headroom, + int tx_tailroom) +{ + struct nci_dev *ndev = NULL; + + nfc_dbg("entry, supported_protocols 0x%x", supported_protocols); + + if (!ops->open || !ops->close || !ops->send) + goto exit; + + if (!supported_protocols) + goto exit; + + ndev = kzalloc(sizeof(struct nci_dev), GFP_KERNEL); + if (!ndev) + goto exit; + + ndev->ops = ops; + ndev->tx_headroom = tx_headroom; + ndev->tx_tailroom = tx_tailroom; + + ndev->nfc_dev = nfc_allocate_device(&nci_nfc_ops, + supported_protocols, + tx_headroom + NCI_DATA_HDR_SIZE, + tx_tailroom); + if (!ndev->nfc_dev) + goto free_exit; + + nfc_set_drvdata(ndev->nfc_dev, ndev); + + goto exit; + +free_exit: + kfree(ndev); + +exit: + return ndev; +} +EXPORT_SYMBOL(nci_allocate_device); + +/** + * nci_free_device - deallocate nci device + * + * @ndev: The nci device to deallocate + */ +void nci_free_device(struct nci_dev *ndev) +{ + nfc_dbg("entry"); + + nfc_free_device(ndev->nfc_dev); + kfree(ndev); +} +EXPORT_SYMBOL(nci_free_device); + +/** + * nci_register_device - register a nci device in the nfc subsystem + * + * @dev: The nci device to register + */ +int nci_register_device(struct nci_dev *ndev) +{ + int rc; + struct device *dev = &ndev->nfc_dev->dev; + char name[32]; + + nfc_dbg("entry"); + + rc = nfc_register_device(ndev->nfc_dev); + if (rc) + goto exit; + + ndev->flags = 0; + + INIT_WORK(&ndev->cmd_work, nci_cmd_work); + snprintf(name, sizeof(name), "%s_nci_cmd_wq", dev_name(dev)); + ndev->cmd_wq = create_singlethread_workqueue(name); + if (!ndev->cmd_wq) { + rc = -ENOMEM; + goto unreg_exit; + } + + INIT_WORK(&ndev->rx_work, nci_rx_work); + snprintf(name, sizeof(name), "%s_nci_rx_wq", dev_name(dev)); + ndev->rx_wq = create_singlethread_workqueue(name); + if (!ndev->rx_wq) { + rc = -ENOMEM; + goto destroy_cmd_wq_exit; + } + + INIT_WORK(&ndev->tx_work, nci_tx_work); + snprintf(name, sizeof(name), "%s_nci_tx_wq", dev_name(dev)); + ndev->tx_wq = create_singlethread_workqueue(name); + if (!ndev->tx_wq) { + rc = -ENOMEM; + goto destroy_rx_wq_exit; + } + + skb_queue_head_init(&ndev->cmd_q); + skb_queue_head_init(&ndev->rx_q); + skb_queue_head_init(&ndev->tx_q); + + setup_timer(&ndev->cmd_timer, nci_cmd_timer, + (unsigned long) ndev); + + mutex_init(&ndev->req_lock); + + goto exit; + +destroy_rx_wq_exit: + destroy_workqueue(ndev->rx_wq); + +destroy_cmd_wq_exit: + destroy_workqueue(ndev->cmd_wq); + +unreg_exit: + nfc_unregister_device(ndev->nfc_dev); + +exit: + return rc; +} +EXPORT_SYMBOL(nci_register_device); + +/** + * nci_unregister_device - unregister a nci device in the nfc subsystem + * + * @dev: The nci device to unregister + */ +void nci_unregister_device(struct nci_dev *ndev) +{ + nfc_dbg("entry"); + + nci_close_device(ndev); + + destroy_workqueue(ndev->cmd_wq); + destroy_workqueue(ndev->rx_wq); + destroy_workqueue(ndev->tx_wq); + + nfc_unregister_device(ndev->nfc_dev); +} +EXPORT_SYMBOL(nci_unregister_device); + +/** + * nci_recv_frame - receive frame from NCI drivers + * + * @skb: The sk_buff to receive + */ +int nci_recv_frame(struct sk_buff *skb) +{ + struct nci_dev *ndev = (struct nci_dev *) skb->dev; + + nfc_dbg("entry, len %d", skb->len); + + if (!ndev || (!test_bit(NCI_UP, &ndev->flags) + && !test_bit(NCI_INIT, &ndev->flags))) { + kfree_skb(skb); + return -ENXIO; + } + + /* Queue frame for rx worker thread */ + skb_queue_tail(&ndev->rx_q, skb); + queue_work(ndev->rx_wq, &ndev->rx_work); + + return 0; +} +EXPORT_SYMBOL(nci_recv_frame); + +static int nci_send_frame(struct sk_buff *skb) +{ + struct nci_dev *ndev = (struct nci_dev *) skb->dev; + + nfc_dbg("entry, len %d", skb->len); + + if (!ndev) { + kfree_skb(skb); + return -ENODEV; + } + + /* Get rid of skb owner, prior to sending to the driver. */ + skb_orphan(skb); + + return ndev->ops->send(skb); +} + +/* Send NCI command */ +int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload) +{ + struct nci_ctrl_hdr *hdr; + struct sk_buff *skb; + + nfc_dbg("entry, opcode 0x%x, plen %d", opcode, plen); + + skb = nci_skb_alloc(ndev, (NCI_CTRL_HDR_SIZE + plen), GFP_KERNEL); + if (!skb) { + nfc_err("no memory for command"); + return -ENOMEM; + } + + hdr = (struct nci_ctrl_hdr *) skb_put(skb, NCI_CTRL_HDR_SIZE); + hdr->gid = nci_opcode_gid(opcode); + hdr->oid = nci_opcode_oid(opcode); + hdr->plen = plen; + + nci_mt_set((__u8 *)hdr, NCI_MT_CMD_PKT); + nci_pbf_set((__u8 *)hdr, NCI_PBF_LAST); + + if (plen) + memcpy(skb_put(skb, plen), payload, plen); + + skb->dev = (void *) ndev; + + skb_queue_tail(&ndev->cmd_q, skb); + queue_work(ndev->cmd_wq, &ndev->cmd_work); + + return 0; +} + +/* ---- NCI TX Data worker thread ---- */ + +static void nci_tx_work(struct work_struct *work) +{ + struct nci_dev *ndev = container_of(work, struct nci_dev, tx_work); + struct sk_buff *skb; + + nfc_dbg("entry, credits_cnt %d", atomic_read(&ndev->credits_cnt)); + + /* Send queued tx data */ + while (atomic_read(&ndev->credits_cnt)) { + skb = skb_dequeue(&ndev->tx_q); + if (!skb) + return; + + atomic_dec(&ndev->credits_cnt); + + nfc_dbg("NCI TX: MT=data, PBF=%d, conn_id=%d, plen=%d", + nci_pbf(skb->data), + nci_conn_id(skb->data), + nci_plen(skb->data)); + + nci_send_frame(skb); + } +} + +/* ----- NCI RX worker thread (data & control) ----- */ + +static void nci_rx_work(struct work_struct *work) +{ + struct nci_dev *ndev = container_of(work, struct nci_dev, rx_work); + struct sk_buff *skb; + + while ((skb = skb_dequeue(&ndev->rx_q))) { + /* Process frame */ + switch (nci_mt(skb->data)) { + case NCI_MT_RSP_PKT: + nci_rsp_packet(ndev, skb); + break; + + case NCI_MT_NTF_PKT: + nci_ntf_packet(ndev, skb); + break; + + case NCI_MT_DATA_PKT: + nci_rx_data_packet(ndev, skb); + break; + + default: + nfc_err("unknown MT 0x%x", nci_mt(skb->data)); + kfree_skb(skb); + break; + } + } +} + +/* ----- NCI TX CMD worker thread ----- */ + +static void nci_cmd_work(struct work_struct *work) +{ + struct nci_dev *ndev = container_of(work, struct nci_dev, cmd_work); + struct sk_buff *skb; + + nfc_dbg("entry, cmd_cnt %d", atomic_read(&ndev->cmd_cnt)); + + /* Send queued command */ + if (atomic_read(&ndev->cmd_cnt)) { + skb = skb_dequeue(&ndev->cmd_q); + if (!skb) + return; + + atomic_dec(&ndev->cmd_cnt); + + nfc_dbg("NCI TX: MT=cmd, PBF=%d, GID=0x%x, OID=0x%x, plen=%d", + nci_pbf(skb->data), + nci_opcode_gid(nci_opcode(skb->data)), + nci_opcode_oid(nci_opcode(skb->data)), + nci_plen(skb->data)); + + nci_send_frame(skb); + + mod_timer(&ndev->cmd_timer, + jiffies + msecs_to_jiffies(NCI_CMD_TIMEOUT)); + } +} diff --git a/net/nfc/nci/data.c b/net/nfc/nci/data.c new file mode 100644 index 000000000000..141790ada4aa --- /dev/null +++ b/net/nfc/nci/data.c @@ -0,0 +1,245 @@ +/* + * The NFC Controller Interface is the communication protocol between an + * NFC Controller (NFCC) and a Device Host (DH). + * + * Copyright (C) 2011 Texas Instruments, Inc. + * + * Written by Ilan Elias + * + * 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include + +#include "../nfc.h" +#include +#include +#include + +/* Complete data exchange transaction and forward skb to nfc core */ +void nci_data_exchange_complete(struct nci_dev *ndev, + struct sk_buff *skb, + int err) +{ + data_exchange_cb_t cb = ndev->data_exchange_cb; + void *cb_context = ndev->data_exchange_cb_context; + + nfc_dbg("entry, len %d, err %d", ((skb) ? (skb->len) : (0)), err); + + if (cb) { + ndev->data_exchange_cb = NULL; + ndev->data_exchange_cb_context = 0; + + /* forward skb to nfc core */ + cb(cb_context, skb, err); + } else if (skb) { + nfc_err("no rx callback, dropping rx data..."); + + /* no waiting callback, free skb */ + kfree_skb(skb); + } +} + +/* ----------------- NCI TX Data ----------------- */ + +static inline void nci_push_data_hdr(struct nci_dev *ndev, + __u8 conn_id, + struct sk_buff *skb, + __u8 pbf) +{ + struct nci_data_hdr *hdr; + int plen = skb->len; + + hdr = (struct nci_data_hdr *) skb_push(skb, NCI_DATA_HDR_SIZE); + hdr->conn_id = conn_id; + hdr->rfu = 0; + hdr->plen = plen; + + nci_mt_set((__u8 *)hdr, NCI_MT_DATA_PKT); + nci_pbf_set((__u8 *)hdr, pbf); + + skb->dev = (void *) ndev; +} + +static int nci_queue_tx_data_frags(struct nci_dev *ndev, + __u8 conn_id, + struct sk_buff *skb) { + int total_len = skb->len; + unsigned char *data = skb->data; + unsigned long flags; + struct sk_buff_head frags_q; + struct sk_buff *skb_frag; + int frag_len; + int rc = 0; + + nfc_dbg("entry, conn_id 0x%x, total_len %d", conn_id, total_len); + + __skb_queue_head_init(&frags_q); + + while (total_len) { + frag_len = min_t(int, total_len, ndev->max_pkt_payload_size); + + skb_frag = nci_skb_alloc(ndev, + (NCI_DATA_HDR_SIZE + frag_len), + GFP_KERNEL); + if (skb_frag == NULL) { + rc = -ENOMEM; + goto free_exit; + } + skb_reserve(skb_frag, NCI_DATA_HDR_SIZE); + + /* first, copy the data */ + memcpy(skb_put(skb_frag, frag_len), data, frag_len); + + /* second, set the header */ + nci_push_data_hdr(ndev, conn_id, skb_frag, + ((total_len == frag_len) ? (NCI_PBF_LAST) : (NCI_PBF_CONT))); + + __skb_queue_tail(&frags_q, skb_frag); + + data += frag_len; + total_len -= frag_len; + + nfc_dbg("frag_len %d, remaining total_len %d", + frag_len, total_len); + } + + /* queue all fragments atomically */ + spin_lock_irqsave(&ndev->tx_q.lock, flags); + + while ((skb_frag = __skb_dequeue(&frags_q)) != NULL) + __skb_queue_tail(&ndev->tx_q, skb_frag); + + spin_unlock_irqrestore(&ndev->tx_q.lock, flags); + + /* free the original skb */ + kfree_skb(skb); + + goto exit; + +free_exit: + while ((skb_frag = __skb_dequeue(&frags_q)) != NULL) + kfree_skb(skb_frag); + +exit: + return rc; +} + +/* Send NCI data */ +int nci_send_data(struct nci_dev *ndev, __u8 conn_id, struct sk_buff *skb) +{ + int rc = 0; + + nfc_dbg("entry, conn_id 0x%x, plen %d", conn_id, skb->len); + + /* check if the packet need to be fragmented */ + if (skb->len <= ndev->max_pkt_payload_size) { + /* no need to fragment packet */ + nci_push_data_hdr(ndev, conn_id, skb, NCI_PBF_LAST); + + skb_queue_tail(&ndev->tx_q, skb); + } else { + /* fragment packet and queue the fragments */ + rc = nci_queue_tx_data_frags(ndev, conn_id, skb); + if (rc) { + nfc_err("failed to fragment tx data packet"); + goto free_exit; + } + } + + queue_work(ndev->tx_wq, &ndev->tx_work); + + goto exit; + +free_exit: + kfree_skb(skb); + +exit: + return rc; +} + +/* ----------------- NCI RX Data ----------------- */ + +static void nci_add_rx_data_frag(struct nci_dev *ndev, + struct sk_buff *skb, + __u8 pbf) +{ + int reassembly_len; + int err = 0; + + if (ndev->rx_data_reassembly) { + reassembly_len = ndev->rx_data_reassembly->len; + + /* first, make enough room for the already accumulated data */ + if (skb_cow_head(skb, reassembly_len)) { + nfc_err("error adding room for accumulated rx data"); + + kfree_skb(skb); + skb = 0; + + kfree_skb(ndev->rx_data_reassembly); + ndev->rx_data_reassembly = 0; + + err = -ENOMEM; + goto exit; + } + + /* second, combine the two fragments */ + memcpy(skb_push(skb, reassembly_len), + ndev->rx_data_reassembly->data, + reassembly_len); + + /* third, free old reassembly */ + kfree_skb(ndev->rx_data_reassembly); + ndev->rx_data_reassembly = 0; + } + + if (pbf == NCI_PBF_CONT) { + /* need to wait for next fragment, store skb and exit */ + ndev->rx_data_reassembly = skb; + return; + } + +exit: + nci_data_exchange_complete(ndev, skb, err); +} + +/* Rx Data packet */ +void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb) +{ + __u8 pbf = nci_pbf(skb->data); + + nfc_dbg("entry, len %d", skb->len); + + nfc_dbg("NCI RX: MT=data, PBF=%d, conn_id=%d, plen=%d", + nci_pbf(skb->data), + nci_conn_id(skb->data), + nci_plen(skb->data)); + + /* strip the nci data header */ + skb_pull(skb, NCI_DATA_HDR_SIZE); + + if (ndev->target_active_prot == NFC_PROTO_MIFARE) { + /* frame I/F => remove the status byte */ + nfc_dbg("NFC_PROTO_MIFARE => remove the status byte"); + skb_trim(skb, (skb->len - 1)); + } + + nci_add_rx_data_frag(ndev, skb, pbf); +} diff --git a/net/nfc/nci/lib.c b/net/nfc/nci/lib.c new file mode 100644 index 000000000000..b19dc2fa90e1 --- /dev/null +++ b/net/nfc/nci/lib.c @@ -0,0 +1,94 @@ +/* + * The NFC Controller Interface is the communication protocol between an + * NFC Controller (NFCC) and a Device Host (DH). + * + * Copyright (C) 2011 Texas Instruments, Inc. + * + * Written by Ilan Elias + * + * Acknowledgements: + * This file is based on lib.c, which was written + * by Maxim Krasnyansky. + * + * 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include + +#include + +/* NCI status codes to Unix errno mapping */ +int nci_to_errno(__u8 code) +{ + switch (code) { + case NCI_STATUS_OK: + return 0; + + case NCI_STATUS_REJECTED: + return -EBUSY; + + case NCI_STATUS_MESSAGE_CORRUPTED: + return -EBADMSG; + + case NCI_STATUS_BUFFER_FULL: + return -ENOBUFS; + + case NCI_STATUS_NOT_INITIALIZED: + return -EHOSTDOWN; + + case NCI_STATUS_SYNTAX_ERROR: + case NCI_STATUS_SEMANTIC_ERROR: + case NCI_STATUS_INVALID_PARAM: + case NCI_STATUS_RF_PROTOCOL_ERROR: + case NCI_STATUS_NFCEE_PROTOCOL_ERROR: + return -EPROTO; + + case NCI_STATUS_UNKNOWN_GID: + case NCI_STATUS_UNKNOWN_OID: + return -EBADRQC; + + case NCI_STATUS_MESSAGE_SIZE_EXCEEDED: + return -EMSGSIZE; + + case NCI_STATUS_DISCOVERY_ALREADY_STARTED: + return -EALREADY; + + case NCI_STATUS_DISCOVERY_TARGET_ACTIVATION_FAILED: + case NCI_STATUS_NFCEE_INTERFACE_ACTIVATION_FAILED: + return -ECONNREFUSED; + + case NCI_STATUS_RF_TRANSMISSION_ERROR: + case NCI_STATUS_NFCEE_TRANSMISSION_ERROR: + return -ECOMM; + + case NCI_STATUS_RF_TIMEOUT_ERROR: + case NCI_STATUS_NFCEE_TIMEOUT_ERROR: + return -ETIMEDOUT; + + case NCI_STATUS_RF_LINK_LOSS_ERROR: + return -ENOLINK; + + case NCI_STATUS_MAX_ACTIVE_NFCEE_INTERFACES_REACHED: + return -EDQUOT; + + case NCI_STATUS_FAILED: + default: + return -ENOSYS; + } +} +EXPORT_SYMBOL(nci_to_errno); diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c new file mode 100644 index 000000000000..8dd75352ab6c --- /dev/null +++ b/net/nfc/nci/ntf.c @@ -0,0 +1,258 @@ +/* + * The NFC Controller Interface is the communication protocol between an + * NFC Controller (NFCC) and a Device Host (DH). + * + * Copyright (C) 2011 Texas Instruments, Inc. + * + * Written by Ilan Elias + * + * Acknowledgements: + * This file is based on hci_event.c, which was written + * by Maxim Krasnyansky. + * + * 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include + +#include "../nfc.h" +#include +#include +#include + +/* Handle NCI Notification packets */ + +static void nci_core_conn_credits_ntf_packet(struct nci_dev *ndev, + struct sk_buff *skb) +{ + struct nci_core_conn_credit_ntf *ntf = (void *) skb->data; + int i; + + nfc_dbg("entry, num_entries %d", ntf->num_entries); + + if (ntf->num_entries > NCI_MAX_NUM_CONN) + ntf->num_entries = NCI_MAX_NUM_CONN; + + /* update the credits */ + for (i = 0; i < ntf->num_entries; i++) { + nfc_dbg("entry[%d]: conn_id %d, credits %d", i, + ntf->conn_entries[i].conn_id, + ntf->conn_entries[i].credits); + + if (ntf->conn_entries[i].conn_id == ndev->conn_id) { + /* found static rf connection */ + atomic_add(ntf->conn_entries[i].credits, + &ndev->credits_cnt); + } + } + + /* trigger the next tx */ + if (!skb_queue_empty(&ndev->tx_q)) + queue_work(ndev->tx_wq, &ndev->tx_work); +} + +static void nci_rf_field_info_ntf_packet(struct nci_dev *ndev, + struct sk_buff *skb) +{ + struct nci_rf_field_info_ntf *ntf = (void *) skb->data; + + nfc_dbg("entry, rf_field_status %d", ntf->rf_field_status); +} + +static int nci_rf_activate_nfca_passive_poll(struct nci_dev *ndev, + struct nci_rf_activate_ntf *ntf, __u8 *data) +{ + struct rf_tech_specific_params_nfca_poll *nfca_poll; + struct activation_params_nfca_poll_iso_dep *nfca_poll_iso_dep; + + nfca_poll = &ntf->rf_tech_specific_params.nfca_poll; + nfca_poll_iso_dep = &ntf->activation_params.nfca_poll_iso_dep; + + nfca_poll->sens_res = __le16_to_cpu(*((__u16 *)data)); + data += 2; + + nfca_poll->nfcid1_len = *data++; + + nfc_dbg("sens_res 0x%x, nfcid1_len %d", + nfca_poll->sens_res, + nfca_poll->nfcid1_len); + + memcpy(nfca_poll->nfcid1, data, nfca_poll->nfcid1_len); + data += nfca_poll->nfcid1_len; + + nfca_poll->sel_res_len = *data++; + + if (nfca_poll->sel_res_len != 0) + nfca_poll->sel_res = *data++; + + ntf->rf_interface_type = *data++; + ntf->activation_params_len = *data++; + + nfc_dbg("sel_res_len %d, sel_res 0x%x, rf_interface_type %d, activation_params_len %d", + nfca_poll->sel_res_len, + nfca_poll->sel_res, + ntf->rf_interface_type, + ntf->activation_params_len); + + switch (ntf->rf_interface_type) { + case NCI_RF_INTERFACE_ISO_DEP: + nfca_poll_iso_dep->rats_res_len = *data++; + if (nfca_poll_iso_dep->rats_res_len > 0) { + memcpy(nfca_poll_iso_dep->rats_res, + data, + nfca_poll_iso_dep->rats_res_len); + } + break; + + case NCI_RF_INTERFACE_FRAME: + /* no activation params */ + break; + + default: + nfc_err("unsupported rf_interface_type 0x%x", + ntf->rf_interface_type); + return -EPROTO; + } + + return 0; +} + +static void nci_target_found(struct nci_dev *ndev, + struct nci_rf_activate_ntf *ntf) +{ + struct nfc_target nfc_tgt; + + if (ntf->rf_protocol == NCI_RF_PROTOCOL_T2T) /* T2T MifareUL */ + nfc_tgt.supported_protocols = NFC_PROTO_MIFARE_MASK; + else if (ntf->rf_protocol == NCI_RF_PROTOCOL_ISO_DEP) /* 4A */ + nfc_tgt.supported_protocols = NFC_PROTO_ISO14443_MASK; + + nfc_tgt.sens_res = ntf->rf_tech_specific_params.nfca_poll.sens_res; + nfc_tgt.sel_res = ntf->rf_tech_specific_params.nfca_poll.sel_res; + + if (!(nfc_tgt.supported_protocols & ndev->poll_prots)) { + nfc_dbg("the target found does not have the desired protocol"); + return; + } + + nfc_dbg("new target found, supported_protocols 0x%x", + nfc_tgt.supported_protocols); + + ndev->target_available_prots = nfc_tgt.supported_protocols; + + nfc_targets_found(ndev->nfc_dev, &nfc_tgt, 1); +} + +static void nci_rf_activate_ntf_packet(struct nci_dev *ndev, + struct sk_buff *skb) +{ + struct nci_rf_activate_ntf ntf; + __u8 *data = skb->data; + int rc = -1; + + clear_bit(NCI_DISCOVERY, &ndev->flags); + set_bit(NCI_POLL_ACTIVE, &ndev->flags); + + ntf.target_handle = *data++; + ntf.rf_protocol = *data++; + ntf.rf_tech_and_mode = *data++; + ntf.rf_tech_specific_params_len = *data++; + + nfc_dbg("target_handle %d, rf_protocol 0x%x, rf_tech_and_mode 0x%x, rf_tech_specific_params_len %d", + ntf.target_handle, + ntf.rf_protocol, + ntf.rf_tech_and_mode, + ntf.rf_tech_specific_params_len); + + switch (ntf.rf_tech_and_mode) { + case NCI_NFC_A_PASSIVE_POLL_MODE: + rc = nci_rf_activate_nfca_passive_poll(ndev, &ntf, + data); + break; + + default: + nfc_err("unsupported rf_tech_and_mode 0x%x", + ntf.rf_tech_and_mode); + return; + } + + if (!rc) + nci_target_found(ndev, &ntf); +} + +static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev, + struct sk_buff *skb) +{ + __u8 type = skb->data[0]; + + nfc_dbg("entry, type 0x%x", type); + + clear_bit(NCI_POLL_ACTIVE, &ndev->flags); + ndev->target_active_prot = 0; + + /* drop tx data queue */ + skb_queue_purge(&ndev->tx_q); + + /* drop partial rx data packet */ + if (ndev->rx_data_reassembly) { + kfree_skb(ndev->rx_data_reassembly); + ndev->rx_data_reassembly = 0; + } + + /* complete the data exchange transaction, if exists */ + if (ndev->data_exchange_cb) + nci_data_exchange_complete(ndev, NULL, -EIO); +} + +void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb) +{ + __u16 ntf_opcode = nci_opcode(skb->data); + + nfc_dbg("NCI RX: MT=ntf, PBF=%d, GID=0x%x, OID=0x%x, plen=%d", + nci_pbf(skb->data), + nci_opcode_gid(ntf_opcode), + nci_opcode_oid(ntf_opcode), + nci_plen(skb->data)); + + /* strip the nci control header */ + skb_pull(skb, NCI_CTRL_HDR_SIZE); + + switch (ntf_opcode) { + case NCI_OP_CORE_CONN_CREDITS_NTF: + nci_core_conn_credits_ntf_packet(ndev, skb); + break; + + case NCI_OP_RF_FIELD_INFO_NTF: + nci_rf_field_info_ntf_packet(ndev, skb); + break; + + case NCI_OP_RF_ACTIVATE_NTF: + nci_rf_activate_ntf_packet(ndev, skb); + break; + + case NCI_OP_RF_DEACTIVATE_NTF: + nci_rf_deactivate_ntf_packet(ndev, skb); + break; + + default: + nfc_err("unknown ntf opcode 0x%x", ntf_opcode); + break; + } + + kfree_skb(skb); +} diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c new file mode 100644 index 000000000000..0403d4cd0917 --- /dev/null +++ b/net/nfc/nci/rsp.c @@ -0,0 +1,226 @@ +/* + * The NFC Controller Interface is the communication protocol between an + * NFC Controller (NFCC) and a Device Host (DH). + * + * Copyright (C) 2011 Texas Instruments, Inc. + * + * Written by Ilan Elias + * + * Acknowledgements: + * This file is based on hci_event.c, which was written + * by Maxim Krasnyansky. + * + * 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include + +#include "../nfc.h" +#include +#include + +/* Handle NCI Response packets */ + +static void nci_core_reset_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) +{ + struct nci_core_reset_rsp *rsp = (void *) skb->data; + + nfc_dbg("entry, status 0x%x", rsp->status); + + if (rsp->status == NCI_STATUS_OK) + ndev->nci_ver = rsp->nci_ver; + + nfc_dbg("nci_ver 0x%x", ndev->nci_ver); + + nci_req_complete(ndev, rsp->status); +} + +static void nci_core_init_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) +{ + struct nci_core_init_rsp_1 *rsp_1 = (void *) skb->data; + struct nci_core_init_rsp_2 *rsp_2; + + nfc_dbg("entry, status 0x%x", rsp_1->status); + + if (rsp_1->status != NCI_STATUS_OK) + return; + + ndev->nfcc_features = __le32_to_cpu(rsp_1->nfcc_features); + ndev->num_supported_rf_interfaces = rsp_1->num_supported_rf_interfaces; + + if (ndev->num_supported_rf_interfaces > + NCI_MAX_SUPPORTED_RF_INTERFACES) { + ndev->num_supported_rf_interfaces = + NCI_MAX_SUPPORTED_RF_INTERFACES; + } + + memcpy(ndev->supported_rf_interfaces, + rsp_1->supported_rf_interfaces, + ndev->num_supported_rf_interfaces); + + rsp_2 = (void *) (skb->data + 6 + ndev->num_supported_rf_interfaces); + + ndev->max_logical_connections = + rsp_2->max_logical_connections; + ndev->max_routing_table_size = + __le16_to_cpu(rsp_2->max_routing_table_size); + ndev->max_control_packet_payload_length = + rsp_2->max_control_packet_payload_length; + ndev->rf_sending_buffer_size = + __le16_to_cpu(rsp_2->rf_sending_buffer_size); + ndev->rf_receiving_buffer_size = + __le16_to_cpu(rsp_2->rf_receiving_buffer_size); + ndev->manufacturer_id = + __le16_to_cpu(rsp_2->manufacturer_id); + + nfc_dbg("nfcc_features 0x%x", + ndev->nfcc_features); + nfc_dbg("num_supported_rf_interfaces %d", + ndev->num_supported_rf_interfaces); + nfc_dbg("supported_rf_interfaces[0] 0x%x", + ndev->supported_rf_interfaces[0]); + nfc_dbg("supported_rf_interfaces[1] 0x%x", + ndev->supported_rf_interfaces[1]); + nfc_dbg("supported_rf_interfaces[2] 0x%x", + ndev->supported_rf_interfaces[2]); + nfc_dbg("supported_rf_interfaces[3] 0x%x", + ndev->supported_rf_interfaces[3]); + nfc_dbg("max_logical_connections %d", + ndev->max_logical_connections); + nfc_dbg("max_routing_table_size %d", + ndev->max_routing_table_size); + nfc_dbg("max_control_packet_payload_length %d", + ndev->max_control_packet_payload_length); + nfc_dbg("rf_sending_buffer_size %d", + ndev->rf_sending_buffer_size); + nfc_dbg("rf_receiving_buffer_size %d", + ndev->rf_receiving_buffer_size); + nfc_dbg("manufacturer_id 0x%x", + ndev->manufacturer_id); + + nci_req_complete(ndev, rsp_1->status); +} + +static void nci_core_conn_create_rsp_packet(struct nci_dev *ndev, + struct sk_buff *skb) +{ + struct nci_core_conn_create_rsp *rsp = (void *) skb->data; + + nfc_dbg("entry, status 0x%x", rsp->status); + + if (rsp->status != NCI_STATUS_OK) + return; + + ndev->max_pkt_payload_size = rsp->max_pkt_payload_size; + ndev->initial_num_credits = rsp->initial_num_credits; + ndev->conn_id = rsp->conn_id; + + atomic_set(&ndev->credits_cnt, ndev->initial_num_credits); + + nfc_dbg("max_pkt_payload_size %d", ndev->max_pkt_payload_size); + nfc_dbg("initial_num_credits %d", ndev->initial_num_credits); + nfc_dbg("conn_id %d", ndev->conn_id); +} + +static void nci_rf_disc_map_rsp_packet(struct nci_dev *ndev, + struct sk_buff *skb) +{ + __u8 status = skb->data[0]; + + nfc_dbg("entry, status 0x%x", status); + + nci_req_complete(ndev, status); +} + +static void nci_rf_disc_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) +{ + __u8 status = skb->data[0]; + + nfc_dbg("entry, status 0x%x", status); + + if (status == NCI_STATUS_OK) + set_bit(NCI_DISCOVERY, &ndev->flags); + + nci_req_complete(ndev, status); +} + +static void nci_rf_deactivate_rsp_packet(struct nci_dev *ndev, + struct sk_buff *skb) +{ + __u8 status = skb->data[0]; + + nfc_dbg("entry, status 0x%x", status); + + clear_bit(NCI_DISCOVERY, &ndev->flags); + + nci_req_complete(ndev, status); +} + +void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) +{ + __u16 rsp_opcode = nci_opcode(skb->data); + + /* we got a rsp, stop the cmd timer */ + del_timer(&ndev->cmd_timer); + + nfc_dbg("NCI RX: MT=rsp, PBF=%d, GID=0x%x, OID=0x%x, plen=%d", + nci_pbf(skb->data), + nci_opcode_gid(rsp_opcode), + nci_opcode_oid(rsp_opcode), + nci_plen(skb->data)); + + /* strip the nci control header */ + skb_pull(skb, NCI_CTRL_HDR_SIZE); + + switch (rsp_opcode) { + case NCI_OP_CORE_RESET_RSP: + nci_core_reset_rsp_packet(ndev, skb); + break; + + case NCI_OP_CORE_INIT_RSP: + nci_core_init_rsp_packet(ndev, skb); + break; + + case NCI_OP_CORE_CONN_CREATE_RSP: + nci_core_conn_create_rsp_packet(ndev, skb); + break; + + case NCI_OP_RF_DISCOVER_MAP_RSP: + nci_rf_disc_map_rsp_packet(ndev, skb); + break; + + case NCI_OP_RF_DISCOVER_RSP: + nci_rf_disc_rsp_packet(ndev, skb); + break; + + case NCI_OP_RF_DEACTIVATE_RSP: + nci_rf_deactivate_rsp_packet(ndev, skb); + break; + + default: + nfc_err("unknown rsp opcode 0x%x", rsp_opcode); + break; + } + + kfree_skb(skb); + + /* trigger the next cmd */ + atomic_set(&ndev->cmd_cnt, 1); + if (!skb_queue_empty(&ndev->cmd_q)) + queue_work(ndev->cmd_wq, &ndev->cmd_work); +} -- cgit v1.2.3 From 52087a792c1513b85de674a4fc67fb92855474c3 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 17 Aug 2011 16:23:00 +0300 Subject: Bluetooth: make use of connection number to optimize the scheduler This checks if there is any existing connection according to its type before start iterating in the list and immediately stop iterating when reaching the number of connections. Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 16 ++++++++++++++++ net/bluetooth/hci_core.c | 15 +++++++++++++++ 2 files changed, 31 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 8f441b8b2963..e2ba4d6b4190 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -395,6 +395,22 @@ static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c) } } +static inline unsigned int hci_conn_num(struct hci_dev *hdev, __u8 type) +{ + struct hci_conn_hash *h = &hdev->conn_hash; + switch (type) { + case ACL_LINK: + return h->acl_num; + case LE_LINK: + return h->le_num; + case SCO_LINK: + case ESCO_LINK: + return h->sco_num; + default: + return 0; + } +} + static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev, __u16 handle) { diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 56943add45cc..1d2068322728 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2074,6 +2074,9 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int min = c->sent; conn = c; } + + if (hci_conn_num(hdev, type) == num) + break; } if (conn) { @@ -2131,6 +2134,9 @@ static inline void hci_sched_acl(struct hci_dev *hdev) BT_DBG("%s", hdev->name); + if (!hci_conn_num(hdev, ACL_LINK)) + return; + if (!test_bit(HCI_RAW, &hdev->flags)) { /* ACL tx timeout must be longer than maximum * link supervision timeout (40.9 seconds) */ @@ -2162,6 +2168,9 @@ static inline void hci_sched_sco(struct hci_dev *hdev) BT_DBG("%s", hdev->name); + if (!hci_conn_num(hdev, SCO_LINK)) + return; + while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, "e))) { while (quote-- && (skb = skb_dequeue(&conn->data_q))) { BT_DBG("skb %p len %d", skb, skb->len); @@ -2182,6 +2191,9 @@ static inline void hci_sched_esco(struct hci_dev *hdev) BT_DBG("%s", hdev->name); + if (!hci_conn_num(hdev, ESCO_LINK)) + return; + while (hdev->sco_cnt && (conn = hci_low_sent(hdev, ESCO_LINK, "e))) { while (quote-- && (skb = skb_dequeue(&conn->data_q))) { BT_DBG("skb %p len %d", skb, skb->len); @@ -2202,6 +2214,9 @@ static inline void hci_sched_le(struct hci_dev *hdev) BT_DBG("%s", hdev->name); + if (!hci_conn_num(hdev, LE_LINK)) + return; + if (!test_bit(HCI_RAW, &hdev->flags)) { /* LE tx timeout must be longer than maximum * link supervision timeout (40.9 seconds) */ -- cgit v1.2.3 From d26a23454813908a1bf0e2fd8c73233b22c6dbd7 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Fri, 19 Aug 2011 21:06:51 -0300 Subject: Bluetooth: Add a flag to indicate that SMP is going on Add HCI_CONN_LE_SMP_PEND flag to indicate that SMP is pending for that connection. This allows to have information that an SMP procedure is going on for that connection. We use the HCI_CONN_ENCRYPT_PEND to indicate that encryption (HCI_LE_Start_Encryption) is pending for that connection. While a SMP procedure is going on we hold an reference to the connection, to avoid disconnections. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/l2cap_core.c | 4 +++- net/bluetooth/smp.c | 44 +++++++++++++++++++++++++++++++--------- 3 files changed, 38 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index e2ba4d6b4190..605829b2c63e 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -348,6 +348,7 @@ enum { HCI_CONN_RSWITCH_PEND, HCI_CONN_MODE_CHANGE_PEND, HCI_CONN_SCO_SETUP_PEND, + HCI_CONN_LE_SMP_PEND, }; static inline void hci_conn_hash_init(struct hci_dev *hdev) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index b3bdb482bbe6..8136752d824b 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -986,8 +986,10 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) del_timer_sync(&conn->info_timer); - if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend)) + if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend)) { del_timer(&conn->security_timer); + hci_conn_put(hcon); + } hcon->l2cap_data = NULL; kfree(conn); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 20c82c7000ac..f0c67f62a08e 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -248,6 +248,9 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) BT_DBG("conn %p", conn); + if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend)) + hci_conn_hold(conn->hcon); + conn->preq[0] = SMP_CMD_PAIRING_REQ; memcpy(&conn->preq[1], req, sizeof(*req)); skb_pull(skb, sizeof(*req)); @@ -397,6 +400,9 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) memset(stk + conn->smp_key_size, 0, SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size); + if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend)) + return SMP_UNSPECIFIED; + hci_le_start_enc(hcon, ediv, rand, stk); hcon->enc_key_size = conn->smp_key_size; } else { @@ -430,9 +436,11 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) BT_DBG("conn %p", conn); - if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend)) + if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend)) return 0; + hci_conn_hold(hcon); + skb_pull(skb, sizeof(*rp)); memset(&cp, 0, sizeof(cp)); @@ -443,8 +451,6 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); - set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend); - return 0; } @@ -461,19 +467,13 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) if (IS_ERR(hcon->hdev->tfm)) return 1; - if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend)) - return 0; - if (sec_level == BT_SECURITY_LOW) return 1; if (hcon->sec_level >= sec_level) return 1; - authreq = seclevel_to_authreq(sec_level); - if (hcon->link_mode & HCI_LM_MASTER) { - struct smp_cmd_pairing cp; struct link_key *key; key = hci_find_link_key_type(hcon->hdev, conn->dst, @@ -481,12 +481,28 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) if (key) { struct key_master_id *master = (void *) key->data; + if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, + &hcon->pend)) + goto done; + hci_le_start_enc(hcon, master->ediv, master->rand, key->val); hcon->enc_key_size = key->pin_len; goto done; } + } + + if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend)) + return 0; + + /* While SMP is going on */ + hci_conn_hold(hcon); + + authreq = seclevel_to_authreq(sec_level); + + if (hcon->link_mode & HCI_LM_MASTER) { + struct smp_cmd_pairing cp; build_pairing_cmd(conn, &cp, NULL, authreq); conn->preq[0] = SMP_CMD_PAIRING_REQ; @@ -501,7 +517,6 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) done: hcon->pending_sec_level = sec_level; - set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend); return 0; } @@ -619,6 +634,9 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) if (IS_ERR(conn->hcon->hdev->tfm)) return PTR_ERR(conn->hcon->hdev->tfm); + if (!test_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend)) + return 0; + rsp = (void *) &conn->prsp[1]; /* The responder sends its keys first */ @@ -689,5 +707,11 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) *keydist &= ~SMP_DIST_SIGN; } + if (conn->hcon->out || force) { + clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend); + del_timer(&conn->security_timer); + hci_conn_put(conn->hcon); + } + return 0; } -- cgit v1.2.3 From 454d48ff70c24930c6b0f9cb64f290fca2dfb271 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Fri, 19 Aug 2011 21:06:52 -0300 Subject: Bluetooth: Use the same timeouts for both ACL and LE links Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 605829b2c63e..88566f2e0a6f 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -492,7 +492,7 @@ static inline void hci_conn_put(struct hci_conn *conn) { if (atomic_dec_and_test(&conn->refcnt)) { unsigned long timeo; - if (conn->type == ACL_LINK) { + if (conn->type == ACL_LINK || conn->type == LE_LINK) { del_timer(&conn->idle_timer); if (conn->state == BT_CONNECTED) { timeo = msecs_to_jiffies(conn->disc_timeout); -- cgit v1.2.3 From cfafccf730d363accacbd165542095ce6f7d2de8 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Fri, 19 Aug 2011 21:06:56 -0300 Subject: Bluetooth: Add link_type information to the mgmt Connected event One piece of information that was lost when using the mgmt interface, was the type of the connection. Using HCI events we used to know the type of the connection based on the type of the event, e.g. HCI_LE_Connection_Complete for LE links. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 2 +- include/net/bluetooth/mgmt.h | 1 + net/bluetooth/hci_event.c | 4 ++-- net/bluetooth/mgmt.c | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 88566f2e0a6f..4b17cd7fb164 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -855,7 +855,7 @@ int mgmt_powered(u16 index, u8 powered); int mgmt_discoverable(u16 index, u8 discoverable); int mgmt_connectable(u16 index, u8 connectable); int mgmt_new_key(u16 index, struct link_key *key, u8 persistent); -int mgmt_connected(u16 index, bdaddr_t *bdaddr); +int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 link_type); int mgmt_disconnected(u16 index, bdaddr_t *bdaddr); int mgmt_disconnect_failed(u16 index); int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 5428fd32ccec..1c914ddc6d7a 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -249,6 +249,7 @@ struct mgmt_ev_new_key { #define MGMT_EV_CONNECTED 0x000B struct mgmt_ev_connected { bdaddr_t bdaddr; + __u8 link_type; } __packed; #define MGMT_EV_DISCONNECTED 0x000C diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 7ef4eb4435fb..e54d08222605 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1412,7 +1412,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s conn->state = BT_CONFIG; hci_conn_hold(conn); conn->disc_timeout = HCI_DISCONN_TIMEOUT; - mgmt_connected(hdev->id, &ev->bdaddr); + mgmt_connected(hdev->id, &ev->bdaddr, conn->type); } else conn->state = BT_CONNECTED; @@ -2816,7 +2816,7 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff goto unlock; } - mgmt_connected(hdev->id, &ev->bdaddr); + mgmt_connected(hdev->id, &ev->bdaddr, conn->type); conn->sec_level = BT_SECURITY_LOW; conn->handle = __le16_to_cpu(ev->handle); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 1ce8d80ce38d..dac7d39b810b 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2012,11 +2012,12 @@ int mgmt_new_key(u16 index, struct link_key *key, u8 persistent) return err; } -int mgmt_connected(u16 index, bdaddr_t *bdaddr) +int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 link_type) { struct mgmt_ev_connected ev; bacpy(&ev.bdaddr, bdaddr); + ev.link_type = link_type; return mgmt_event(MGMT_EV_CONNECTED, index, &ev, sizeof(ev), NULL); } -- cgit v1.2.3 From f6422ec624a19ba144b4b5cdbbc5ee41cc6f6400 Mon Sep 17 00:00:00 2001 From: Antti Julku Date: Wed, 22 Jun 2011 13:11:56 +0300 Subject: Bluetooth: Add mgmt command for fast connectable mode Add command to management interface for enabling/disabling the fast connectable mode. Signed-off-by: Antti Julku Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci.h | 10 ++++++++ include/net/bluetooth/mgmt.h | 5 ++++ net/bluetooth/mgmt.c | 60 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index be30aabe7b88..aaf79af72432 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -716,6 +716,16 @@ struct hci_rp_read_bd_addr { bdaddr_t bdaddr; } __packed; +#define HCI_OP_WRITE_PAGE_SCAN_ACTIVITY 0x0c1c +struct hci_cp_write_page_scan_activity { + __le16 interval; + __le16 window; +} __packed; + +#define HCI_OP_WRITE_PAGE_SCAN_TYPE 0x0c47 + #define PAGE_SCAN_TYPE_STANDARD 0x00 + #define PAGE_SCAN_TYPE_INTERLACED 0x01 + #define HCI_OP_LE_SET_EVENT_MASK 0x2001 struct hci_cp_le_set_event_mask { __u8 mask[8]; diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 1c914ddc6d7a..48522e6386bf 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -211,6 +211,11 @@ struct mgmt_cp_unblock_device { bdaddr_t bdaddr; } __packed; +#define MGMT_OP_SET_FAST_CONNECTABLE 0x001F +struct mgmt_cp_set_fast_connectable { + __u8 enable; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index dac7d39b810b..545f84dbae85 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1760,6 +1760,62 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data, return err; } +static int set_fast_connectable(struct sock *sk, u16 index, + unsigned char *data, u16 len) +{ + struct hci_dev *hdev; + struct mgmt_cp_set_fast_connectable *cp = (void *) data; + struct hci_cp_write_page_scan_activity acp; + u8 type; + int err; + + BT_DBG("hci%u", index); + + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, + EINVAL); + + hdev = hci_dev_get(index); + if (!hdev) + return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, + ENODEV); + + hci_dev_lock(hdev); + + if (cp->enable) { + type = PAGE_SCAN_TYPE_INTERLACED; + acp.interval = 0x0024; /* 22.5 msec page scan interval */ + } else { + type = PAGE_SCAN_TYPE_STANDARD; /* default */ + acp.interval = 0x0800; /* default 1.28 sec page scan */ + } + + acp.window = 0x0012; /* default 11.25 msec page scan window */ + + err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY, + sizeof(acp), &acp); + if (err < 0) { + err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, + -err); + goto done; + } + + err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type); + if (err < 0) { + err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, + -err); + goto done; + } + + err = cmd_complete(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, + NULL, 0); +done: + hci_dev_unlock(hdev); + hci_dev_put(hdev); + + return err; +} + int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) { unsigned char *buf; @@ -1880,6 +1936,10 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_UNBLOCK_DEVICE: err = unblock_device(sk, index, buf + sizeof(*hdr), len); break; + case MGMT_OP_SET_FAST_CONNECTABLE: + err = set_fast_connectable(sk, index, buf + sizeof(*hdr), + len); + break; default: BT_DBG("Unknown op %u", opcode); err = cmd_status(sk, index, opcode, 0x01); -- cgit v1.2.3 From 1c1def09c446aae441410b70e6439ffe44dee866 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Mon, 5 Sep 2011 14:31:30 -0300 Subject: Bluetooth: Move SMP fields to a separate structure The objective is to make the core to have as little as possible information about SMP procedures and logic. Now, all the SMP specific information is hidden from the core. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 2 - include/net/bluetooth/l2cap.h | 8 +-- include/net/bluetooth/smp.h | 10 ++++ net/bluetooth/hci_core.c | 8 --- net/bluetooth/smp.c | 108 +++++++++++++++++++-------------------- 5 files changed, 65 insertions(+), 71 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 4b17cd7fb164..ee1ee1bfae9e 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -195,8 +195,6 @@ struct hci_dev { __u16 init_last_cmd; - struct crypto_blkcipher *tfm; - struct inquiry_cache inq_cache; struct hci_conn_hash conn_hash; struct list_head blacklist; diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 4f34ad25e75c..7f878b9d5642 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -409,14 +409,8 @@ struct l2cap_conn { __u8 disc_reason; - __u8 preq[7]; /* SMP Pairing Request */ - __u8 prsp[7]; /* SMP Pairing Response */ - __u8 prnd[16]; /* SMP Pairing Random */ - __u8 pcnf[16]; /* SMP Pairing Confirm */ - __u8 tk[16]; /* SMP Temporary Key */ - __u8 smp_key_size; - struct timer_list security_timer; + struct smp_chan *smp_chan; struct list_head chan_l; rwlock_t chan_lock; diff --git a/include/net/bluetooth/smp.h b/include/net/bluetooth/smp.h index 46c457612300..a9ba72c1dc79 100644 --- a/include/net/bluetooth/smp.h +++ b/include/net/bluetooth/smp.h @@ -115,6 +115,16 @@ struct smp_cmd_security_req { #define SMP_MIN_ENC_KEY_SIZE 7 #define SMP_MAX_ENC_KEY_SIZE 16 +struct smp_chan { + u8 preq[7]; /* SMP Pairing Request */ + u8 prsp[7]; /* SMP Pairing Response */ + u8 prnd[16]; /* SMP Pairing Random */ + u8 pcnf[16]; /* SMP Pairing Confirm */ + u8 tk[16]; /* SMP Temporary Key */ + u8 smp_key_size; + struct crypto_blkcipher *tfm; +}; + /* SMP Commands */ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level); int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 1d2068322728..b4e7cde35365 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1523,11 +1523,6 @@ int hci_register_dev(struct hci_dev *hdev) if (!hdev->workqueue) goto nomem; - hdev->tfm = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(hdev->tfm)) - BT_INFO("Failed to load transform for ecb(aes): %ld", - PTR_ERR(hdev->tfm)); - hci_register_sysfs(hdev); hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev, @@ -1576,9 +1571,6 @@ int hci_unregister_dev(struct hci_dev *hdev) !test_bit(HCI_SETUP, &hdev->flags)) mgmt_index_removed(hdev->id); - if (!IS_ERR(hdev->tfm)) - crypto_free_blkcipher(hdev->tfm); - hci_notify(hdev, HCI_DEV_UNREG); if (hdev->rfkill) { diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index f0c67f62a08e..b5e1b4a300cc 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -232,11 +232,13 @@ static void build_pairing_cmd(struct l2cap_conn *conn, static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size) { + struct smp_chan *smp = conn->smp_chan; + if ((max_key_size > SMP_MAX_ENC_KEY_SIZE) || (max_key_size < SMP_MIN_ENC_KEY_SIZE)) return SMP_ENC_KEY_SIZE; - conn->smp_key_size = max_key_size; + smp->smp_key_size = max_key_size; return 0; } @@ -244,6 +246,7 @@ static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size) static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_pairing rsp, *req = (void *) skb->data; + struct smp_chan *smp = conn->smp_chan; u8 key_size; BT_DBG("conn %p", conn); @@ -251,8 +254,8 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend)) hci_conn_hold(conn->hcon); - conn->preq[0] = SMP_CMD_PAIRING_REQ; - memcpy(&conn->preq[1], req, sizeof(*req)); + smp->preq[0] = SMP_CMD_PAIRING_REQ; + memcpy(&smp->preq[1], req, sizeof(*req)); skb_pull(skb, sizeof(*req)); if (req->oob_flag) @@ -266,10 +269,10 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) return SMP_ENC_KEY_SIZE; /* Just works */ - memset(conn->tk, 0, sizeof(conn->tk)); + memset(smp->tk, 0, sizeof(smp->tk)); - conn->prsp[0] = SMP_CMD_PAIRING_RSP; - memcpy(&conn->prsp[1], &rsp, sizeof(rsp)); + smp->prsp[0] = SMP_CMD_PAIRING_RSP; + memcpy(&smp->prsp[1], &rsp, sizeof(rsp)); smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp); @@ -280,7 +283,9 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_pairing *req, *rsp = (void *) skb->data; struct smp_cmd_pairing_confirm cp; - struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm; + struct smp_chan *smp = conn->smp_chan; + struct crypto_blkcipher *tfm = smp->tfm; + int ret; u8 res[16], key_size; @@ -288,7 +293,7 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) skb_pull(skb, sizeof(*rsp)); - req = (void *) &conn->preq[1]; + req = (void *) &smp->preq[1]; key_size = min(req->max_key_size, rsp->max_key_size); if (check_enc_key_size(conn, key_size)) @@ -298,16 +303,16 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) return SMP_OOB_NOT_AVAIL; /* Just works */ - memset(conn->tk, 0, sizeof(conn->tk)); + memset(smp->tk, 0, sizeof(smp->tk)); - conn->prsp[0] = SMP_CMD_PAIRING_RSP; - memcpy(&conn->prsp[1], rsp, sizeof(*rsp)); + smp->prsp[0] = SMP_CMD_PAIRING_RSP; + memcpy(&smp->prsp[1], rsp, sizeof(*rsp)); - ret = smp_rand(conn->prnd); + ret = smp_rand(smp->prnd); if (ret) return SMP_UNSPECIFIED; - ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp, 0, + ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, 0, conn->src, conn->hcon->dst_type, conn->dst, res); if (ret) return SMP_UNSPECIFIED; @@ -321,17 +326,18 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb) { - struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm; + struct smp_chan *smp = conn->smp_chan; + struct crypto_blkcipher *tfm = smp->tfm; BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); - memcpy(conn->pcnf, skb->data, sizeof(conn->pcnf)); - skb_pull(skb, sizeof(conn->pcnf)); + memcpy(smp->pcnf, skb->data, sizeof(smp->pcnf)); + skb_pull(skb, sizeof(smp->pcnf)); if (conn->hcon->out) { u8 random[16]; - swap128(conn->prnd, random); + swap128(smp->prnd, random); smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random), random); } else { @@ -339,11 +345,11 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb) int ret; u8 res[16]; - ret = smp_rand(conn->prnd); + ret = smp_rand(smp->prnd); if (ret) return SMP_UNSPECIFIED; - ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp, + ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, conn->hcon->dst_type, conn->dst, 0, conn->src, res); if (ret) @@ -360,7 +366,8 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb) static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) { struct hci_conn *hcon = conn->hcon; - struct crypto_blkcipher *tfm = hcon->hdev->tfm; + struct smp_chan *smp = conn->smp_chan; + struct crypto_blkcipher *tfm = smp->tfm; int ret; u8 key[16], res[16], random[16], confirm[16]; @@ -368,11 +375,11 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) skb_pull(skb, sizeof(random)); if (conn->hcon->out) - ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp, 0, + ret = smp_c1(tfm, smp->tk, random, smp->preq, smp->prsp, 0, conn->src, conn->hcon->dst_type, conn->dst, res); else - ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp, + ret = smp_c1(tfm, smp->tk, random, smp->preq, smp->prsp, conn->hcon->dst_type, conn->dst, 0, conn->src, res); if (ret) @@ -382,7 +389,7 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) swap128(res, confirm); - if (memcmp(conn->pcnf, confirm, sizeof(conn->pcnf)) != 0) { + if (memcmp(smp->pcnf, confirm, sizeof(smp->pcnf)) != 0) { BT_ERR("Pairing failed (confirmation values mismatch)"); return SMP_CONFIRM_FAILED; } @@ -394,17 +401,17 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) memset(rand, 0, sizeof(rand)); ediv = 0; - smp_s1(tfm, conn->tk, random, conn->prnd, key); + smp_s1(tfm, smp->tk, random, smp->prnd, key); swap128(key, stk); - memset(stk + conn->smp_key_size, 0, - SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size); + memset(stk + smp->smp_key_size, 0, + SMP_MAX_ENC_KEY_SIZE - smp->smp_key_size); if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend)) return SMP_UNSPECIFIED; hci_le_start_enc(hcon, ediv, rand, stk); - hcon->enc_key_size = conn->smp_key_size; + hcon->enc_key_size = smp->smp_key_size; } else { u8 stk[16], r[16], rand[8]; __le16 ediv; @@ -412,16 +419,16 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) memset(rand, 0, sizeof(rand)); ediv = 0; - swap128(conn->prnd, r); + swap128(smp->prnd, r); smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r); - smp_s1(tfm, conn->tk, conn->prnd, random, key); + smp_s1(tfm, smp->tk, smp->prnd, random, key); swap128(key, stk); - memset(stk + conn->smp_key_size, 0, - SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size); + memset(stk + smp->smp_key_size, 0, + SMP_MAX_ENC_KEY_SIZE - smp->smp_key_size); - hci_add_ltk(conn->hcon->hdev, 0, conn->dst, conn->smp_key_size, + hci_add_ltk(conn->hcon->hdev, 0, conn->dst, smp->smp_key_size, ediv, rand, stk); } @@ -433,6 +440,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) struct smp_cmd_security_req *rp = (void *) skb->data; struct smp_cmd_pairing cp; struct hci_conn *hcon = conn->hcon; + struct smp_chan *smp = conn->smp_chan; BT_DBG("conn %p", conn); @@ -446,8 +454,8 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) memset(&cp, 0, sizeof(cp)); build_pairing_cmd(conn, &cp, NULL, rp->auth_req); - conn->preq[0] = SMP_CMD_PAIRING_REQ; - memcpy(&conn->preq[1], &cp, sizeof(cp)); + smp->preq[0] = SMP_CMD_PAIRING_REQ; + memcpy(&smp->preq[1], &cp, sizeof(cp)); smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); @@ -457,6 +465,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) { struct hci_conn *hcon = conn->hcon; + struct smp_chan *smp = conn->smp_chan; __u8 authreq; BT_DBG("conn %p hcon %p level 0x%2.2x", conn, hcon, sec_level); @@ -464,9 +473,6 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) if (!lmp_host_le_capable(hcon->hdev)) return 1; - if (IS_ERR(hcon->hdev->tfm)) - return 1; - if (sec_level == BT_SECURITY_LOW) return 1; @@ -505,8 +511,8 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) struct smp_cmd_pairing cp; build_pairing_cmd(conn, &cp, NULL, authreq); - conn->preq[0] = SMP_CMD_PAIRING_REQ; - memcpy(&conn->preq[1], &cp, sizeof(cp)); + smp->preq[0] = SMP_CMD_PAIRING_REQ; + memcpy(&smp->preq[1], &cp, sizeof(cp)); smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); } else { @@ -524,10 +530,11 @@ done: static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_encrypt_info *rp = (void *) skb->data; + struct smp_chan *smp = conn->smp_chan; skb_pull(skb, sizeof(*rp)); - memcpy(conn->tk, rp->ltk, sizeof(conn->tk)); + memcpy(smp->tk, rp->ltk, sizeof(smp->tk)); return 0; } @@ -535,11 +542,12 @@ static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb) static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_master_ident *rp = (void *) skb->data; + struct smp_chan *smp = conn->smp_chan; skb_pull(skb, sizeof(*rp)); - hci_add_ltk(conn->hcon->hdev, 1, conn->src, conn->smp_key_size, - rp->ediv, rp->rand, conn->tk); + hci_add_ltk(conn->hcon->hdev, 1, conn->src, smp->smp_key_size, + rp->ediv, rp->rand, smp->tk); smp_distribute_keys(conn, 1); @@ -558,12 +566,6 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) goto done; } - if (IS_ERR(conn->hcon->hdev->tfm)) { - err = PTR_ERR(conn->hcon->hdev->tfm); - reason = SMP_PAIRING_NOTSUPP; - goto done; - } - skb_pull(skb, sizeof(code)); switch (code) { @@ -627,23 +629,21 @@ done: int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) { struct smp_cmd_pairing *req, *rsp; + struct smp_chan *smp = conn->smp_chan; __u8 *keydist; BT_DBG("conn %p force %d", conn, force); - if (IS_ERR(conn->hcon->hdev->tfm)) - return PTR_ERR(conn->hcon->hdev->tfm); - if (!test_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend)) return 0; - rsp = (void *) &conn->prsp[1]; + rsp = (void *) &smp->prsp[1]; /* The responder sends its keys first */ if (!force && conn->hcon->out && (rsp->resp_key_dist & 0x07)) return 0; - req = (void *) &conn->preq[1]; + req = (void *) &smp->preq[1]; if (conn->hcon->out) { keydist = &rsp->init_key_dist; @@ -667,7 +667,7 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc); - hci_add_ltk(conn->hcon->hdev, 1, conn->dst, conn->smp_key_size, + hci_add_ltk(conn->hcon->hdev, 1, conn->dst, smp->smp_key_size, ediv, ident.rand, enc.ltk); ident.ediv = cpu_to_le16(ediv); -- cgit v1.2.3 From 8aab47574a7f5b46a4cdbc6fd820ab34e6c5dbf9 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Mon, 5 Sep 2011 14:31:31 -0300 Subject: Bluetooth: Move SMP crypto functions to a workqueue The function crypto_blkcipher_setkey() called by smp_e() can sleep, so all the crypto work has to be moved to hci_dev workqueue. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/smp.h | 9 +- net/bluetooth/l2cap_core.c | 2 +- net/bluetooth/smp.c | 277 ++++++++++++++++++++++++++++---------------- 3 files changed, 185 insertions(+), 103 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/smp.h b/include/net/bluetooth/smp.h index a9ba72c1dc79..15b97d549441 100644 --- a/include/net/bluetooth/smp.h +++ b/include/net/bluetooth/smp.h @@ -116,13 +116,18 @@ struct smp_cmd_security_req { #define SMP_MAX_ENC_KEY_SIZE 16 struct smp_chan { + struct l2cap_conn *conn; u8 preq[7]; /* SMP Pairing Request */ u8 prsp[7]; /* SMP Pairing Response */ - u8 prnd[16]; /* SMP Pairing Random */ + u8 prnd[16]; /* SMP Pairing Random (local) */ + u8 rrnd[16]; /* SMP Pairing Random (remote) */ u8 pcnf[16]; /* SMP Pairing Confirm */ u8 tk[16]; /* SMP Temporary Key */ u8 smp_key_size; struct crypto_blkcipher *tfm; + struct work_struct confirm; + struct work_struct random; + }; /* SMP Commands */ @@ -130,4 +135,6 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level); int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb); int smp_distribute_keys(struct l2cap_conn *conn, __u8 force); +void smp_chan_destroy(struct l2cap_conn *conn); + #endif /* __SMP_H */ diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index e699837c3b8c..4bfb7d22d171 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -991,7 +991,7 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend)) { del_timer(&conn->security_timer); - hci_conn_put(hcon); + smp_chan_destroy(conn); } hcon->l2cap_data = NULL; diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index b5e1b4a300cc..03489e5815ef 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -243,16 +243,170 @@ static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size) return 0; } +static void confirm_work(struct work_struct *work) +{ + struct smp_chan *smp = container_of(work, struct smp_chan, confirm); + struct l2cap_conn *conn = smp->conn; + struct crypto_blkcipher *tfm; + struct smp_cmd_pairing_confirm cp; + int ret; + u8 res[16], reason; + + BT_DBG("conn %p", conn); + + tfm = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm)) { + reason = SMP_UNSPECIFIED; + goto error; + } + + smp->tfm = tfm; + + if (conn->hcon->out) + ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, 0, + conn->src, conn->hcon->dst_type, conn->dst, + res); + else + ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, + conn->hcon->dst_type, conn->dst, 0, conn->src, + res); + if (ret) { + reason = SMP_UNSPECIFIED; + goto error; + } + + swap128(res, cp.confirm_val); + smp_send_cmd(smp->conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp); + + return; + +error: + smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason); + smp_chan_destroy(conn); +} + +static void random_work(struct work_struct *work) +{ + struct smp_chan *smp = container_of(work, struct smp_chan, random); + struct l2cap_conn *conn = smp->conn; + struct hci_conn *hcon = conn->hcon; + struct crypto_blkcipher *tfm = smp->tfm; + u8 reason, confirm[16], res[16], key[16]; + int ret; + + if (IS_ERR_OR_NULL(tfm)) { + reason = SMP_UNSPECIFIED; + goto error; + } + + BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); + + if (hcon->out) + ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp, 0, + conn->src, hcon->dst_type, conn->dst, + res); + else + ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp, + hcon->dst_type, conn->dst, 0, conn->src, + res); + if (ret) { + reason = SMP_UNSPECIFIED; + goto error; + } + + swap128(res, confirm); + + if (memcmp(smp->pcnf, confirm, sizeof(smp->pcnf)) != 0) { + BT_ERR("Pairing failed (confirmation values mismatch)"); + reason = SMP_CONFIRM_FAILED; + goto error; + } + + if (hcon->out) { + u8 stk[16], rand[8]; + __le16 ediv; + + memset(rand, 0, sizeof(rand)); + ediv = 0; + + smp_s1(tfm, smp->tk, smp->rrnd, smp->prnd, key); + swap128(key, stk); + + memset(stk + smp->smp_key_size, 0, + SMP_MAX_ENC_KEY_SIZE - smp->smp_key_size); + + if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend)) { + reason = SMP_UNSPECIFIED; + goto error; + } + + hci_le_start_enc(hcon, ediv, rand, stk); + hcon->enc_key_size = smp->smp_key_size; + } else { + u8 stk[16], r[16], rand[8]; + __le16 ediv; + + memset(rand, 0, sizeof(rand)); + ediv = 0; + + swap128(smp->prnd, r); + smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r); + + smp_s1(tfm, smp->tk, smp->prnd, smp->rrnd, key); + swap128(key, stk); + + memset(stk + smp->smp_key_size, 0, + SMP_MAX_ENC_KEY_SIZE - smp->smp_key_size); + + hci_add_ltk(hcon->hdev, 0, conn->dst, smp->smp_key_size, + ediv, rand, stk); + } + + return; + +error: + smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason); + smp_chan_destroy(conn); +} + +static struct smp_chan *smp_chan_create(struct l2cap_conn *conn) +{ + struct smp_chan *smp; + + smp = kzalloc(sizeof(struct smp_chan), GFP_ATOMIC); + if (!smp) + return NULL; + + INIT_WORK(&smp->confirm, confirm_work); + INIT_WORK(&smp->random, random_work); + + smp->conn = conn; + conn->smp_chan = smp; + + hci_conn_hold(conn->hcon); + + return smp; +} + +void smp_chan_destroy(struct l2cap_conn *conn) +{ + kfree(conn->smp_chan); + hci_conn_put(conn->hcon); +} + static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_pairing rsp, *req = (void *) skb->data; - struct smp_chan *smp = conn->smp_chan; + struct smp_chan *smp; u8 key_size; + int ret; BT_DBG("conn %p", conn); if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend)) - hci_conn_hold(conn->hcon); + smp = smp_chan_create(conn); + + smp = conn->smp_chan; smp->preq[0] = SMP_CMD_PAIRING_REQ; memcpy(&smp->preq[1], req, sizeof(*req)); @@ -271,6 +425,10 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) /* Just works */ memset(smp->tk, 0, sizeof(smp->tk)); + ret = smp_rand(smp->prnd); + if (ret) + return SMP_UNSPECIFIED; + smp->prsp[0] = SMP_CMD_PAIRING_RSP; memcpy(&smp->prsp[1], &rsp, sizeof(rsp)); @@ -282,12 +440,10 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_pairing *req, *rsp = (void *) skb->data; - struct smp_cmd_pairing_confirm cp; struct smp_chan *smp = conn->smp_chan; - struct crypto_blkcipher *tfm = smp->tfm; - + struct hci_dev *hdev = conn->hcon->hdev; + u8 key_size; int ret; - u8 res[16], key_size; BT_DBG("conn %p", conn); @@ -305,21 +461,14 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) /* Just works */ memset(smp->tk, 0, sizeof(smp->tk)); - smp->prsp[0] = SMP_CMD_PAIRING_RSP; - memcpy(&smp->prsp[1], rsp, sizeof(*rsp)); - ret = smp_rand(smp->prnd); if (ret) return SMP_UNSPECIFIED; - ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, 0, - conn->src, conn->hcon->dst_type, conn->dst, res); - if (ret) - return SMP_UNSPECIFIED; - - swap128(res, cp.confirm_val); + smp->prsp[0] = SMP_CMD_PAIRING_RSP; + memcpy(&smp->prsp[1], rsp, sizeof(*rsp)); - smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp); + queue_work(hdev->workqueue, &smp->confirm); return 0; } @@ -327,7 +476,7 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_chan *smp = conn->smp_chan; - struct crypto_blkcipher *tfm = smp->tfm; + struct hci_dev *hdev = conn->hcon->hdev; BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); @@ -341,23 +490,7 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb) smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random), random); } else { - struct smp_cmd_pairing_confirm cp; - int ret; - u8 res[16]; - - ret = smp_rand(smp->prnd); - if (ret) - return SMP_UNSPECIFIED; - - ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, - conn->hcon->dst_type, conn->dst, - 0, conn->src, res); - if (ret) - return SMP_CONFIRM_FAILED; - - swap128(res, cp.confirm_val); - - smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp); + queue_work(hdev->workqueue, &smp->confirm); } return 0; @@ -365,72 +498,15 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb) static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) { - struct hci_conn *hcon = conn->hcon; struct smp_chan *smp = conn->smp_chan; - struct crypto_blkcipher *tfm = smp->tfm; - int ret; - u8 key[16], res[16], random[16], confirm[16]; - - swap128(skb->data, random); - skb_pull(skb, sizeof(random)); + struct hci_dev *hdev = conn->hcon->hdev; - if (conn->hcon->out) - ret = smp_c1(tfm, smp->tk, random, smp->preq, smp->prsp, 0, - conn->src, conn->hcon->dst_type, conn->dst, - res); - else - ret = smp_c1(tfm, smp->tk, random, smp->preq, smp->prsp, - conn->hcon->dst_type, conn->dst, 0, conn->src, - res); - if (ret) - return SMP_UNSPECIFIED; - - BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); - - swap128(res, confirm); - - if (memcmp(smp->pcnf, confirm, sizeof(smp->pcnf)) != 0) { - BT_ERR("Pairing failed (confirmation values mismatch)"); - return SMP_CONFIRM_FAILED; - } - - if (conn->hcon->out) { - u8 stk[16], rand[8]; - __le16 ediv; - - memset(rand, 0, sizeof(rand)); - ediv = 0; - - smp_s1(tfm, smp->tk, random, smp->prnd, key); - swap128(key, stk); - - memset(stk + smp->smp_key_size, 0, - SMP_MAX_ENC_KEY_SIZE - smp->smp_key_size); - - if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend)) - return SMP_UNSPECIFIED; - - hci_le_start_enc(hcon, ediv, rand, stk); - hcon->enc_key_size = smp->smp_key_size; - } else { - u8 stk[16], r[16], rand[8]; - __le16 ediv; - - memset(rand, 0, sizeof(rand)); - ediv = 0; - - swap128(smp->prnd, r); - smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r); - - smp_s1(tfm, smp->tk, smp->prnd, random, key); - swap128(key, stk); + BT_DBG("conn %p", conn); - memset(stk + smp->smp_key_size, 0, - SMP_MAX_ENC_KEY_SIZE - smp->smp_key_size); + swap128(skb->data, smp->rrnd); + skb_pull(skb, sizeof(smp->rrnd)); - hci_add_ltk(conn->hcon->hdev, 0, conn->dst, smp->smp_key_size, - ediv, rand, stk); - } + queue_work(hdev->workqueue, &smp->random); return 0; } @@ -440,14 +516,14 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) struct smp_cmd_security_req *rp = (void *) skb->data; struct smp_cmd_pairing cp; struct hci_conn *hcon = conn->hcon; - struct smp_chan *smp = conn->smp_chan; + struct smp_chan *smp; BT_DBG("conn %p", conn); if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend)) return 0; - hci_conn_hold(hcon); + smp = smp_chan_create(conn); skb_pull(skb, sizeof(*rp)); @@ -502,8 +578,7 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend)) return 0; - /* While SMP is going on */ - hci_conn_hold(hcon); + smp = smp_chan_create(conn); authreq = seclevel_to_authreq(sec_level); @@ -710,7 +785,7 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) if (conn->hcon->out || force) { clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend); del_timer(&conn->security_timer); - hci_conn_put(conn->hcon); + smp_chan_destroy(conn); } return 0; -- cgit v1.2.3 From 5e762444b0d3e56bbd66f5092434c4a1ba698313 Mon Sep 17 00:00:00 2001 From: Antti Julku Date: Thu, 25 Aug 2011 16:48:02 +0300 Subject: Bluetooth: Add mgmt events for blacklisting Add management interface events for blocking/unblocking a device. Sender of the block device command gets cmd complete and other mgmt sockets get the event. Event is also sent to mgmt sockets when blocking is done with ioctl, e.g when blocking a device with hciconfig. This makes it possible for bluetoothd to track status of blocked devices when a third party block or unblocks a device. Event sending is handled in mgmt_device_blocked function which gets called from hci_blacklist_add in hci_core.c. A pending command is added in mgmt_block_device, so that it can found when sending the event - the event is not sent to the socket from which the pending command came. Locks were moved out from hci_core.c to hci_sock.c and mgmt.c, because locking is needed also for mgmt_pending_add in mgmt.c. Signed-off-by: Antti Julku Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 2 ++ include/net/bluetooth/mgmt.h | 10 +++++++ net/bluetooth/hci_core.c | 34 ++++++---------------- net/bluetooth/hci_sock.c | 18 ++++++++++-- net/bluetooth/mgmt.c | 62 ++++++++++++++++++++++++++++++++++++---- 5 files changed, 92 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index ee1ee1bfae9e..5b924423cf20 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -873,6 +873,8 @@ int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 *dev_class, s8 rssi, u8 *eir); int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name); int mgmt_discovering(u16 index, u8 discovering); +int mgmt_device_blocked(u16 index, bdaddr_t *bdaddr); +int mgmt_device_unblocked(u16 index, bdaddr_t *bdaddr); /* HCI info for socket */ #define hci_pi(sk) ((struct hci_pinfo *) sk) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 48522e6386bf..d66da0f94f95 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -307,3 +307,13 @@ struct mgmt_ev_remote_name { } __packed; #define MGMT_EV_DISCOVERING 0x0014 + +#define MGMT_EV_DEVICE_BLOCKED 0x0015 +struct mgmt_ev_device_blocked { + bdaddr_t bdaddr; +} __packed; + +#define MGMT_EV_DEVICE_UNBLOCKED 0x0016 +struct mgmt_ev_device_unblocked { + bdaddr_t bdaddr; +} __packed; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index b4e7cde35365..b84458dcc226 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1312,59 +1312,41 @@ int hci_blacklist_clear(struct hci_dev *hdev) int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct bdaddr_list *entry; - int err; if (bacmp(bdaddr, BDADDR_ANY) == 0) return -EBADF; - hci_dev_lock_bh(hdev); - - if (hci_blacklist_lookup(hdev, bdaddr)) { - err = -EEXIST; - goto err; - } + if (hci_blacklist_lookup(hdev, bdaddr)) + return -EEXIST; entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL); - if (!entry) { - err = -ENOMEM; - goto err; - } + if (!entry) + return -ENOMEM; bacpy(&entry->bdaddr, bdaddr); list_add(&entry->list, &hdev->blacklist); - err = 0; - -err: - hci_dev_unlock_bh(hdev); - return err; + return mgmt_device_blocked(hdev->id, bdaddr); } int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct bdaddr_list *entry; - int err = 0; - - hci_dev_lock_bh(hdev); if (bacmp(bdaddr, BDADDR_ANY) == 0) { - hci_blacklist_clear(hdev); - goto done; + return hci_blacklist_clear(hdev); } entry = hci_blacklist_lookup(hdev, bdaddr); if (!entry) { - err = -ENOENT; - goto done; + return -ENOENT; } list_del(&entry->list); kfree(entry); -done: - hci_dev_unlock_bh(hdev); - return err; + return mgmt_device_unblocked(hdev->id, bdaddr); } static void hci_clear_adv_cache(unsigned long arg) diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index ff02cf5e77cc..f6afe3d76a66 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -183,21 +183,35 @@ static int hci_sock_release(struct socket *sock) static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg) { bdaddr_t bdaddr; + int err; if (copy_from_user(&bdaddr, arg, sizeof(bdaddr))) return -EFAULT; - return hci_blacklist_add(hdev, &bdaddr); + hci_dev_lock_bh(hdev); + + err = hci_blacklist_add(hdev, &bdaddr); + + hci_dev_unlock_bh(hdev); + + return err; } static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg) { bdaddr_t bdaddr; + int err; if (copy_from_user(&bdaddr, arg, sizeof(bdaddr))) return -EFAULT; - return hci_blacklist_del(hdev, &bdaddr); + hci_dev_lock_bh(hdev); + + err = hci_blacklist_del(hdev, &bdaddr); + + hci_dev_unlock_bh(hdev); + + return err; } /* Ioctls that require bound socket */ diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 6493e807634f..579f7261a7fe 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1698,13 +1698,12 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data, u16 len) { struct hci_dev *hdev; - struct mgmt_cp_block_device *cp; + struct pending_cmd *cmd; + struct mgmt_cp_block_device *cp = (void *) data; int err; BT_DBG("hci%u", index); - cp = (void *) data; - if (len != sizeof(*cp)) return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, EINVAL); @@ -1714,6 +1713,14 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data, return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, ENODEV); + hci_dev_lock_bh(hdev); + + cmd = mgmt_pending_add(sk, MGMT_OP_BLOCK_DEVICE, index, NULL, 0); + if (!cmd) { + err = -ENOMEM; + goto failed; + } + err = hci_blacklist_add(hdev, &cp->bdaddr); if (err < 0) @@ -1721,6 +1728,11 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data, else err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE, NULL, 0); + + mgmt_pending_remove(cmd); + +failed: + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; @@ -1730,13 +1742,12 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data, u16 len) { struct hci_dev *hdev; - struct mgmt_cp_unblock_device *cp; + struct pending_cmd *cmd; + struct mgmt_cp_unblock_device *cp = (void *) data; int err; BT_DBG("hci%u", index); - cp = (void *) data; - if (len != sizeof(*cp)) return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, EINVAL); @@ -1746,6 +1757,14 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data, return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, ENODEV); + hci_dev_lock_bh(hdev); + + cmd = mgmt_pending_add(sk, MGMT_OP_UNBLOCK_DEVICE, index, NULL, 0); + if (!cmd) { + err = -ENOMEM; + goto failed; + } + err = hci_blacklist_del(hdev, &cp->bdaddr); if (err < 0) @@ -1753,6 +1772,11 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data, else err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE, NULL, 0); + + mgmt_pending_remove(cmd); + +failed: + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; @@ -2356,3 +2380,29 @@ int mgmt_discovering(u16 index, u8 discovering) return mgmt_event(MGMT_EV_DISCOVERING, index, &discovering, sizeof(discovering), NULL); } + +int mgmt_device_blocked(u16 index, bdaddr_t *bdaddr) +{ + struct pending_cmd *cmd; + struct mgmt_ev_device_blocked ev; + + cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, index); + + bacpy(&ev.bdaddr, bdaddr); + + return mgmt_event(MGMT_EV_DEVICE_BLOCKED, index, &ev, sizeof(ev), + cmd ? cmd->sk : NULL); +} + +int mgmt_device_unblocked(u16 index, bdaddr_t *bdaddr) +{ + struct pending_cmd *cmd; + struct mgmt_ev_device_unblocked ev; + + cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, index); + + bacpy(&ev.bdaddr, bdaddr); + + return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, index, &ev, sizeof(ev), + cmd ? cmd->sk : NULL); +} -- cgit v1.2.3 From 37a41b4affa33bb237d3692bf51f1b5ebcaf29d8 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 21 Sep 2011 14:06:11 +0300 Subject: mac80211: add ieee80211_vif param to tsf functions TSF can be kept per vif. Add ieee80211_vif param to set/get/reset_tsf, and move the debugfs entries to the per-vif directory. Update all the drivers that implement these callbacks. Signed-off-by: Eliad Peller Signed-off-by: John W. Linville --- drivers/net/wireless/adm8211.c | 3 +- drivers/net/wireless/ath/ath5k/mac80211-ops.c | 6 +-- drivers/net/wireless/ath/ath9k/htc_drv_main.c | 9 ++-- drivers/net/wireless/ath/ath9k/main.c | 8 ++-- drivers/net/wireless/ath/carl9170/main.c | 3 +- drivers/net/wireless/b43/main.c | 5 ++- drivers/net/wireless/iwlegacy/iwl-core.c | 3 +- drivers/net/wireless/iwlegacy/iwl-core.h | 3 +- drivers/net/wireless/rt2x00/rt2400pci.c | 5 ++- drivers/net/wireless/rt2x00/rt2500pci.c | 3 +- drivers/net/wireless/rt2x00/rt2800lib.c | 2 +- drivers/net/wireless/rt2x00/rt2800lib.h | 2 +- drivers/net/wireless/rt2x00/rt61pci.c | 2 +- drivers/net/wireless/rt2x00/rt73usb.c | 2 +- drivers/net/wireless/rtl818x/rtl8180/dev.c | 5 ++- drivers/net/wireless/rtl818x/rtl8187/dev.c | 2 +- drivers/net/wireless/rtlwifi/core.c | 8 ++-- drivers/net/wireless/wl12xx/main.c | 3 +- drivers/net/wireless/zd1211rw/zd_mac.c | 2 +- drivers/staging/brcm80211/brcmsmac/mac80211_if.c | 12 ++++-- drivers/staging/winbond/wbusb.c | 2 +- include/net/mac80211.h | 7 ++-- net/mac80211/debugfs.c | 52 ------------------------ net/mac80211/debugfs_netdev.c | 48 +++++++++++++++++++++- net/mac80211/driver-ops.h | 22 ++++++---- net/mac80211/driver-trace.h | 26 +++++++----- net/mac80211/ibss.c | 4 +- 27 files changed, 137 insertions(+), 112 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c index 43ebc44fc82c..3b752d9fb3cd 100644 --- a/drivers/net/wireless/adm8211.c +++ b/drivers/net/wireless/adm8211.c @@ -1249,7 +1249,8 @@ static int adm8211_hw_reset(struct ieee80211_hw *dev) return 0; } -static u64 adm8211_get_tsft(struct ieee80211_hw *dev) +static u64 adm8211_get_tsft(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) { struct adm8211_priv *priv = dev->priv; u32 tsftl; diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c index 0560234ec3f6..bba4f6fcf7e2 100644 --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -602,7 +602,7 @@ ath5k_conf_tx(struct ieee80211_hw *hw, u16 queue, static u64 -ath5k_get_tsf(struct ieee80211_hw *hw) +ath5k_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath5k_hw *ah = hw->priv; @@ -611,7 +611,7 @@ ath5k_get_tsf(struct ieee80211_hw *hw) static void -ath5k_set_tsf(struct ieee80211_hw *hw, u64 tsf) +ath5k_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u64 tsf) { struct ath5k_hw *ah = hw->priv; @@ -620,7 +620,7 @@ ath5k_set_tsf(struct ieee80211_hw *hw, u64 tsf) static void -ath5k_reset_tsf(struct ieee80211_hw *hw) +ath5k_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath5k_hw *ah = hw->priv; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 495fdf680a6c..17dbbd9d2f53 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1563,7 +1563,8 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, mutex_unlock(&priv->mutex); } -static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw) +static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct ath9k_htc_priv *priv = hw->priv; u64 tsf; @@ -1577,7 +1578,8 @@ static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw) return tsf; } -static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf) +static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u64 tsf) { struct ath9k_htc_priv *priv = hw->priv; @@ -1588,7 +1590,8 @@ static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf) mutex_unlock(&priv->mutex); } -static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw) +static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct ath9k_htc_priv *priv = hw->priv; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index ee39702da5d8..fb803e209760 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -2143,7 +2143,7 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, ath9k_ps_restore(sc); } -static u64 ath9k_get_tsf(struct ieee80211_hw *hw) +static u64 ath9k_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath_softc *sc = hw->priv; u64 tsf; @@ -2157,7 +2157,9 @@ static u64 ath9k_get_tsf(struct ieee80211_hw *hw) return tsf; } -static void ath9k_set_tsf(struct ieee80211_hw *hw, u64 tsf) +static void ath9k_set_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u64 tsf) { struct ath_softc *sc = hw->priv; @@ -2168,7 +2170,7 @@ static void ath9k_set_tsf(struct ieee80211_hw *hw, u64 tsf) mutex_unlock(&sc->mutex); } -static void ath9k_reset_tsf(struct ieee80211_hw *hw) +static void ath9k_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath_softc *sc = hw->priv; diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index af351ecd87c4..8b780d6d470f 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1078,7 +1078,8 @@ out: mutex_unlock(&ar->mutex); } -static u64 carl9170_op_get_tsf(struct ieee80211_hw *hw) +static u64 carl9170_op_get_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct ar9170 *ar = hw->priv; struct carl9170_tsf_rsp tsf; diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 56fa3a3648c4..559bcd6688ec 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -3599,7 +3599,7 @@ static int b43_op_get_stats(struct ieee80211_hw *hw, return 0; } -static u64 b43_op_get_tsf(struct ieee80211_hw *hw) +static u64 b43_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev; @@ -3618,7 +3618,8 @@ static u64 b43_op_get_tsf(struct ieee80211_hw *hw) return tsf; } -static void b43_op_set_tsf(struct ieee80211_hw *hw, u64 tsf) +static void b43_op_set_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u64 tsf) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev; diff --git a/drivers/net/wireless/iwlegacy/iwl-core.c b/drivers/net/wireless/iwlegacy/iwl-core.c index 35cd2537e7fd..c4921911c6d1 100644 --- a/drivers/net/wireless/iwlegacy/iwl-core.c +++ b/drivers/net/wireless/iwlegacy/iwl-core.c @@ -2220,7 +2220,8 @@ out: } EXPORT_SYMBOL(iwl_legacy_mac_config); -void iwl_legacy_mac_reset_tsf(struct ieee80211_hw *hw) +void iwl_legacy_mac_reset_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct iwl_priv *priv = hw->priv; unsigned long flags; diff --git a/drivers/net/wireless/iwlegacy/iwl-core.h b/drivers/net/wireless/iwlegacy/iwl-core.h index 84da79376ef8..b2df01c8f8f5 100644 --- a/drivers/net/wireless/iwlegacy/iwl-core.h +++ b/drivers/net/wireless/iwlegacy/iwl-core.h @@ -620,7 +620,8 @@ static inline const struct ieee80211_supported_band *iwl_get_hw_mode( /* mac80211 handlers */ int iwl_legacy_mac_config(struct ieee80211_hw *hw, u32 changed); -void iwl_legacy_mac_reset_tsf(struct ieee80211_hw *hw); +void iwl_legacy_mac_reset_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); void iwl_legacy_mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c index daa32fc9398b..7e9272b8f01d 100644 --- a/drivers/net/wireless/rt2x00/rt2400pci.c +++ b/drivers/net/wireless/rt2x00/rt2400pci.c @@ -1239,7 +1239,7 @@ static void rt2400pci_fill_rxdone(struct queue_entry *entry, * call, we must decrease the higher 32bits with 1 to get * to correct value. */ - tsf = rt2x00dev->ops->hw->get_tsf(rt2x00dev->hw); + tsf = rt2x00dev->ops->hw->get_tsf(rt2x00dev->hw, NULL); rx_low = rt2x00_get_field32(word4, RXD_W4_RX_END_TIME); rx_high = upper_32_bits(tsf); @@ -1673,7 +1673,8 @@ static int rt2400pci_conf_tx(struct ieee80211_hw *hw, u16 queue, return 0; } -static u64 rt2400pci_get_tsf(struct ieee80211_hw *hw) +static u64 rt2400pci_get_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = hw->priv; u64 tsf; diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c index b46c3b8866fa..dcc0e1fcca77 100644 --- a/drivers/net/wireless/rt2x00/rt2500pci.c +++ b/drivers/net/wireless/rt2x00/rt2500pci.c @@ -1966,7 +1966,8 @@ static int rt2500pci_probe_hw(struct rt2x00_dev *rt2x00dev) /* * IEEE80211 stack callback functions. */ -static u64 rt2500pci_get_tsf(struct ieee80211_hw *hw) +static u64 rt2500pci_get_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = hw->priv; u64 tsf; diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 31c98509f7e6..9688dd0a7ebd 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -4466,7 +4466,7 @@ int rt2800_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, } EXPORT_SYMBOL_GPL(rt2800_conf_tx); -u64 rt2800_get_tsf(struct ieee80211_hw *hw) +u64 rt2800_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = hw->priv; u64 tsf; diff --git a/drivers/net/wireless/rt2x00/rt2800lib.h b/drivers/net/wireless/rt2x00/rt2800lib.h index 7a2511f6785c..6de128e9c612 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.h +++ b/drivers/net/wireless/rt2x00/rt2800lib.h @@ -199,7 +199,7 @@ void rt2800_get_tkip_seq(struct ieee80211_hw *hw, u8 hw_key_idx, u32 *iv32, int rt2800_set_rts_threshold(struct ieee80211_hw *hw, u32 value); int rt2800_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, const struct ieee80211_tx_queue_params *params); -u64 rt2800_get_tsf(struct ieee80211_hw *hw); +u64 rt2800_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif); int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c index 058ef4b19d1d..6b6a8e2dcddc 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.c +++ b/drivers/net/wireless/rt2x00/rt61pci.c @@ -2940,7 +2940,7 @@ static int rt61pci_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, return 0; } -static u64 rt61pci_get_tsf(struct ieee80211_hw *hw) +static u64 rt61pci_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = hw->priv; u64 tsf; diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index 0baeb894f093..6f51e39f5595 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -2279,7 +2279,7 @@ static int rt73usb_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, return 0; } -static u64 rt73usb_get_tsf(struct ieee80211_hw *hw) +static u64 rt73usb_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = hw->priv; u64 tsf; diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c index 66b29dc07cc3..0082015ff664 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -669,7 +669,8 @@ static void rtl8180_stop(struct ieee80211_hw *dev) rtl8180_free_tx_ring(dev, i); } -static u64 rtl8180_get_tsf(struct ieee80211_hw *dev) +static u64 rtl8180_get_tsf(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) { struct rtl8180_priv *priv = dev->priv; @@ -701,7 +702,7 @@ static void rtl8180_beacon_work(struct work_struct *work) * TODO: make hardware update beacon timestamp */ mgmt = (struct ieee80211_mgmt *)skb->data; - mgmt->u.beacon.timestamp = cpu_to_le64(rtl8180_get_tsf(dev)); + mgmt->u.beacon.timestamp = cpu_to_le64(rtl8180_get_tsf(dev, vif)); /* TODO: use actual beacon queue */ skb_set_queue_mapping(skb, 0); diff --git a/drivers/net/wireless/rtl818x/rtl8187/dev.c b/drivers/net/wireless/rtl818x/rtl8187/dev.c index 1e0be14d10d4..f5afa155ce91 100644 --- a/drivers/net/wireless/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187/dev.c @@ -1277,7 +1277,7 @@ static int rtl8187_conf_tx(struct ieee80211_hw *dev, u16 queue, return 0; } -static u64 rtl8187_get_tsf(struct ieee80211_hw *dev) +static u64 rtl8187_get_tsf(struct ieee80211_hw *dev, struct ieee80211_vif *vif) { struct rtl8187_priv *priv = dev->priv; diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c index 04c4e9eb6ee6..21e40f62a8d7 100644 --- a/drivers/net/wireless/rtlwifi/core.c +++ b/drivers/net/wireless/rtlwifi/core.c @@ -775,7 +775,7 @@ out: mutex_unlock(&rtlpriv->locks.conf_mutex); } -static u64 rtl_op_get_tsf(struct ieee80211_hw *hw) +static u64 rtl_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rtl_priv *rtlpriv = rtl_priv(hw); u64 tsf; @@ -784,7 +784,8 @@ static u64 rtl_op_get_tsf(struct ieee80211_hw *hw) return tsf; } -static void rtl_op_set_tsf(struct ieee80211_hw *hw, u64 tsf) +static void rtl_op_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u64 tsf) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); @@ -794,7 +795,8 @@ static void rtl_op_set_tsf(struct ieee80211_hw *hw, u64 tsf) rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_CORRECT_TSF, (u8 *) (&bibss)); } -static void rtl_op_reset_tsf(struct ieee80211_hw *hw) +static void rtl_op_reset_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 tmp = 0; diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index a51dd0ed6d2d..7d409b0f3357 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -3815,7 +3815,8 @@ out: return ret; } -static u64 wl1271_op_get_tsf(struct ieee80211_hw *hw) +static u64 wl1271_op_get_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct wl1271 *wl = hw->priv; diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index cabfae1e70b1..0a70149df3fc 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -1332,7 +1332,7 @@ static void zd_op_bss_info_changed(struct ieee80211_hw *hw, } } -static u64 zd_op_get_tsf(struct ieee80211_hw *hw) +static u64 zd_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct zd_mac *mac = zd_hw_mac(hw); return zd_chip_get_tsf(&mac->chip); diff --git a/drivers/staging/brcm80211/brcmsmac/mac80211_if.c b/drivers/staging/brcm80211/brcmsmac/mac80211_if.c index d6de44e430d3..315dd91800b6 100644 --- a/drivers/staging/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/staging/brcm80211/brcmsmac/mac80211_if.c @@ -133,7 +133,8 @@ static int brcms_ops_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set); static void brcms_ops_sw_scan_start(struct ieee80211_hw *hw); static void brcms_ops_sw_scan_complete(struct ieee80211_hw *hw); -static void brcms_ops_set_tsf(struct ieee80211_hw *hw, u64 tsf); +static void brcms_ops_set_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u64 tsf); static int brcms_ops_get_stats(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats); static void brcms_ops_sta_notify(struct ieee80211_hw *hw, @@ -142,7 +143,8 @@ static void brcms_ops_sta_notify(struct ieee80211_hw *hw, struct ieee80211_sta *sta); static int brcms_ops_conf_tx(struct ieee80211_hw *hw, u16 queue, const struct ieee80211_tx_queue_params *params); -static u64 brcms_ops_get_tsf(struct ieee80211_hw *hw); +static u64 brcms_ops_get_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); static int brcms_ops_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta); static int brcms_ops_sta_remove(struct ieee80211_hw *hw, @@ -516,7 +518,8 @@ static void brcms_ops_sw_scan_complete(struct ieee80211_hw *hw) return; } -static void brcms_ops_set_tsf(struct ieee80211_hw *hw, u64 tsf) +static void brcms_ops_set_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u64 tsf) { wiphy_err(hw->wiphy, "%s: Enter\n", __func__); return; @@ -565,7 +568,8 @@ brcms_ops_conf_tx(struct ieee80211_hw *hw, u16 queue, return 0; } -static u64 brcms_ops_get_tsf(struct ieee80211_hw *hw) +static u64 brcms_ops_get_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { wiphy_err(hw->wiphy, "%s: Enter\n", __func__); return 0; diff --git a/drivers/staging/winbond/wbusb.c b/drivers/staging/winbond/wbusb.c index 3724e1e67ec2..a2e8bd452ed9 100644 --- a/drivers/staging/winbond/wbusb.c +++ b/drivers/staging/winbond/wbusb.c @@ -277,7 +277,7 @@ static int wbsoft_config(struct ieee80211_hw *dev, u32 changed) return 0; } -static u64 wbsoft_get_tsf(struct ieee80211_hw *dev) +static u64 wbsoft_get_tsf(struct ieee80211_hw *dev, struct ieee80211_vif *vif) { printk("wbsoft_get_tsf called\n"); return 0; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index c0f63fd0c52b..90dfcc99b466 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2000,9 +2000,10 @@ struct ieee80211_ops { enum sta_notify_cmd, struct ieee80211_sta *sta); int (*conf_tx)(struct ieee80211_hw *hw, u16 queue, const struct ieee80211_tx_queue_params *params); - u64 (*get_tsf)(struct ieee80211_hw *hw); - void (*set_tsf)(struct ieee80211_hw *hw, u64 tsf); - void (*reset_tsf)(struct ieee80211_hw *hw); + u64 (*get_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); + void (*set_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u64 tsf); + void (*reset_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); int (*tx_last_beacon)(struct ieee80211_hw *hw); int (*ampdu_action)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index c9141168fd43..883996b2f99f 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -78,57 +78,6 @@ DEBUGFS_READONLY_FILE(wep_iv, "%#08x", DEBUGFS_READONLY_FILE(rate_ctrl_alg, "%s", local->rate_ctrl ? local->rate_ctrl->ops->name : "hw/driver"); -static ssize_t tsf_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_local *local = file->private_data; - u64 tsf; - - tsf = drv_get_tsf(local); - - return mac80211_format_buffer(user_buf, count, ppos, "0x%016llx\n", - (unsigned long long) tsf); -} - -static ssize_t tsf_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_local *local = file->private_data; - unsigned long long tsf; - char buf[100]; - size_t len; - - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) - return -EFAULT; - buf[len] = '\0'; - - if (strncmp(buf, "reset", 5) == 0) { - if (local->ops->reset_tsf) { - drv_reset_tsf(local); - wiphy_info(local->hw.wiphy, "debugfs reset TSF\n"); - } - } else { - tsf = simple_strtoul(buf, NULL, 0); - if (local->ops->set_tsf) { - drv_set_tsf(local, tsf); - wiphy_info(local->hw.wiphy, - "debugfs set TSF to %#018llx\n", tsf); - - } - } - - return count; -} - -static const struct file_operations tsf_ops = { - .read = tsf_read, - .write = tsf_write, - .open = mac80211_open_file_generic, - .llseek = default_llseek, -}; - static ssize_t reset_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { @@ -447,7 +396,6 @@ void debugfs_hw_add(struct ieee80211_local *local) DEBUGFS_ADD(frequency); DEBUGFS_ADD(total_ps_buffered); DEBUGFS_ADD(wep_iv); - DEBUGFS_ADD(tsf); DEBUGFS_ADD(queues); DEBUGFS_ADD_MODE(reset, 0200); DEBUGFS_ADD(noack); diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index dd0462917518..9352819a986b 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -21,6 +21,7 @@ #include "rate.h" #include "debugfs.h" #include "debugfs_netdev.h" +#include "driver-ops.h" static ssize_t ieee80211_if_read( struct ieee80211_sub_if_data *sdata, @@ -331,6 +332,46 @@ static ssize_t ieee80211_if_fmt_num_buffered_multicast( } __IEEE80211_IF_FILE(num_buffered_multicast, NULL); +/* IBSS attributes */ +static ssize_t ieee80211_if_fmt_tsf( + const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) +{ + struct ieee80211_local *local = sdata->local; + u64 tsf; + + tsf = drv_get_tsf(local, (struct ieee80211_sub_if_data *)sdata); + + return scnprintf(buf, buflen, "0x%016llx\n", (unsigned long long) tsf); +} + +static ssize_t ieee80211_if_parse_tsf( + struct ieee80211_sub_if_data *sdata, const char *buf, int buflen) +{ + struct ieee80211_local *local = sdata->local; + unsigned long long tsf; + int ret; + + if (strncmp(buf, "reset", 5) == 0) { + if (local->ops->reset_tsf) { + drv_reset_tsf(local, sdata); + wiphy_info(local->hw.wiphy, "debugfs reset TSF\n"); + } + } else { + ret = kstrtoull(buf, 10, &tsf); + if (ret < 0) + return -EINVAL; + if (local->ops->set_tsf) { + drv_set_tsf(local, sdata, tsf); + wiphy_info(local->hw.wiphy, + "debugfs set TSF to %#018llx\n", tsf); + } + } + + return buflen; +} +__IEEE80211_IF_FILE_W(tsf); + + /* WDS attributes */ IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC); @@ -421,6 +462,11 @@ static void add_ap_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD_MODE(tkip_mic_test, 0200); } +static void add_ibss_files(struct ieee80211_sub_if_data *sdata) +{ + DEBUGFS_ADD_MODE(tsf, 0600); +} + static void add_wds_files(struct ieee80211_sub_if_data *sdata) { DEBUGFS_ADD(drop_unencrypted); @@ -515,7 +561,7 @@ static void add_files(struct ieee80211_sub_if_data *sdata) add_sta_files(sdata); break; case NL80211_IFTYPE_ADHOC: - /* XXX */ + add_ibss_files(sdata); break; case NL80211_IFTYPE_AP: add_ap_files(sdata); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 9001ff331f0a..5e5d97389bc9 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -427,36 +427,40 @@ static inline int drv_conf_tx(struct ieee80211_local *local, u16 queue, return ret; } -static inline u64 drv_get_tsf(struct ieee80211_local *local) +static inline u64 drv_get_tsf(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) { u64 ret = -1ULL; might_sleep(); - trace_drv_get_tsf(local); + trace_drv_get_tsf(local, sdata); if (local->ops->get_tsf) - ret = local->ops->get_tsf(&local->hw); + ret = local->ops->get_tsf(&local->hw, &sdata->vif); trace_drv_return_u64(local, ret); return ret; } -static inline void drv_set_tsf(struct ieee80211_local *local, u64 tsf) +static inline void drv_set_tsf(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + u64 tsf) { might_sleep(); - trace_drv_set_tsf(local, tsf); + trace_drv_set_tsf(local, sdata, tsf); if (local->ops->set_tsf) - local->ops->set_tsf(&local->hw, tsf); + local->ops->set_tsf(&local->hw, &sdata->vif, tsf); trace_drv_return_void(local); } -static inline void drv_reset_tsf(struct ieee80211_local *local) +static inline void drv_reset_tsf(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) { might_sleep(); - trace_drv_reset_tsf(local); + trace_drv_reset_tsf(local, sdata); if (local->ops->reset_tsf) - local->ops->reset_tsf(&local->hw); + local->ops->reset_tsf(&local->hw, &sdata->vif); trace_drv_return_void(local); } diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index f47b00dc7afd..07d94ff55798 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -726,35 +726,41 @@ TRACE_EVENT(drv_conf_tx, ) ); -DEFINE_EVENT(local_only_evt, drv_get_tsf, - TP_PROTO(struct ieee80211_local *local), - TP_ARGS(local) +DEFINE_EVENT(local_sdata_evt, drv_get_tsf, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) ); TRACE_EVENT(drv_set_tsf, - TP_PROTO(struct ieee80211_local *local, u64 tsf), + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + u64 tsf), - TP_ARGS(local, tsf), + TP_ARGS(local, sdata, tsf), TP_STRUCT__entry( LOCAL_ENTRY + VIF_ENTRY __field(u64, tsf) ), TP_fast_assign( LOCAL_ASSIGN; + VIF_ASSIGN; __entry->tsf = tsf; ), TP_printk( - LOCAL_PR_FMT " tsf:%llu", - LOCAL_PR_ARG, (unsigned long long)__entry->tsf + LOCAL_PR_FMT VIF_PR_FMT " tsf:%llu", + LOCAL_PR_ARG, VIF_PR_ARG, (unsigned long long)__entry->tsf ) ); -DEFINE_EVENT(local_only_evt, drv_reset_tsf, - TP_PROTO(struct ieee80211_local *local), - TP_ARGS(local) +DEFINE_EVENT(local_sdata_evt, drv_reset_tsf, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) ); DEFINE_EVENT(local_only_evt, drv_tx_last_beacon, diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 836b2752ecd6..7809895df8b0 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -81,7 +81,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, lockdep_assert_held(&ifibss->mtx); /* Reset own TSF to allow time synchronization work. */ - drv_reset_tsf(local); + drv_reset_tsf(local, sdata); skb = ifibss->skb; rcu_assign_pointer(ifibss->presp, NULL); @@ -382,7 +382,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, * second best option: get current TSF * (will return -1 if not supported) */ - rx_timestamp = drv_get_tsf(local); + rx_timestamp = drv_get_tsf(local, sdata); } #ifdef CONFIG_MAC80211_IBSS_DEBUG -- cgit v1.2.3 From 38f04c6b1b682f1879441e2925403ad9aff9e229 Mon Sep 17 00:00:00 2001 From: Ilan Elias Date: Thu, 22 Sep 2011 11:36:19 +0300 Subject: NFC: protect nci_data_exchange transactions Protect 'cb' and 'cb_context' arguments in nci_data_exchange. In fact, this implements a queue with max length of 1 data exchange transactions in parallel. Signed-off-by: Ilan Elias Acked-by: Lauro Ramos Venancio Signed-off-by: John W. Linville --- include/net/nfc/nci_core.h | 1 + net/nfc/nci/core.c | 10 +++++++++- net/nfc/nci/data.c | 2 ++ net/nfc/nci/ntf.c | 2 +- 4 files changed, 13 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index 2563f3a95e67..b8b4bbd7e0fc 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -40,6 +40,7 @@ enum { NCI_UP, NCI_DISCOVERY, NCI_POLL_ACTIVE, + NCI_DATA_EXCHANGE, }; /* NCI timeouts */ diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 9f17e8ec0ab9..1e6b20f2bc99 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -453,6 +453,7 @@ static int nci_data_exchange(struct nfc_dev *nfc_dev, __u32 target_idx, void *cb_context) { struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + int rc; nfc_dbg("entry, target_idx %d, len %d", target_idx, skb->len); @@ -461,11 +462,18 @@ static int nci_data_exchange(struct nfc_dev *nfc_dev, __u32 target_idx, return -EINVAL; } + if (test_and_set_bit(NCI_DATA_EXCHANGE, &ndev->flags)) + return -EBUSY; + /* store cb and context to be used on receiving data */ ndev->data_exchange_cb = cb; ndev->data_exchange_cb_context = cb_context; - return nci_send_data(ndev, ndev->conn_id, skb); + rc = nci_send_data(ndev, ndev->conn_id, skb); + if (rc) + clear_bit(NCI_DATA_EXCHANGE, &ndev->flags); + + return rc; } static struct nfc_ops nci_nfc_ops = { diff --git a/net/nfc/nci/data.c b/net/nfc/nci/data.c index 141790ada4aa..e5ed90fc1a9c 100644 --- a/net/nfc/nci/data.c +++ b/net/nfc/nci/data.c @@ -54,6 +54,8 @@ void nci_data_exchange_complete(struct nci_dev *ndev, /* no waiting callback, free skb */ kfree_skb(skb); } + + clear_bit(NCI_DATA_EXCHANGE, &ndev->flags); } /* ----------------- NCI TX Data ----------------- */ diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c index 8dd75352ab6c..96633f5cda4f 100644 --- a/net/nfc/nci/ntf.c +++ b/net/nfc/nci/ntf.c @@ -215,7 +215,7 @@ static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev, } /* complete the data exchange transaction, if exists */ - if (ndev->data_exchange_cb) + if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags)) nci_data_exchange_complete(ndev, NULL, -EIO); } -- cgit v1.2.3 From e9f935e3e8dc0bddd0df6d148165d95925422502 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Sun, 25 Sep 2011 14:53:30 +0530 Subject: nl80211/cfg80211: Add support to disable CCK rate for management frame Add a new nl80211 attribute to specify whether to send the management frames in CCK rate or not. As of now the wpa_supplicant is disabling CCK rate at P2P init itself. So this patch helps to send P2P probe request/probe response/action frames being sent at non CCK rate in 2GHz without disabling 11b rates. This attribute is used with NL80211_CMD_TRIGGER_SCAN and NL80211_CMD_FRAME commands to disable CCK rate for management frame transmission. Cc: Jouni Malinen Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- include/linux/nl80211.h | 13 +++++++++++++ include/net/cfg80211.h | 5 ++++- net/mac80211/cfg.c | 3 ++- net/wireless/core.h | 3 ++- net/wireless/mlme.c | 5 +++-- net/wireless/nl80211.c | 9 ++++++++- 6 files changed, 32 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 460b12a8ef66..c73582fb9d20 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -238,6 +238,8 @@ * * @NL80211_CMD_GET_SCAN: get scan results * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters + * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the + * probe requests at CCK rate or not. * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to * NL80211_CMD_GET_SCAN and on the "scan" multicast group) * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons, @@ -432,6 +434,8 @@ * specified using %NL80211_ATTR_DURATION. When called, this operation * returns a cookie (%NL80211_ATTR_COOKIE) that will be included with the * TX status event pertaining to the TX request. + * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the + * management frames at CCK rate or not in 2GHz band. * @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this * command may be used with the corresponding cookie to cancel the wait * time if it is known that it is no longer necessary. @@ -1078,6 +1082,13 @@ enum nl80211_commands { * @NL80211_ATTR_PMKSA_CANDIDATE: Nested attribute containing the PMKSA caching * candidate information, see &enum nl80211_pmksa_candidate_attr. * + * @NL80211_ATTR_TX_NO_CCK_RATE: Indicates whether to use CCK rate or not + * for management frames transmission. In order to avoid p2p probe/action + * frames are being transmitted at CCK rate in 2GHz band, the user space + * applications use this attribute. + * This attribute is used with %NL80211_CMD_TRIGGER_SCAN and + * %NL80211_CMD_FRAME commands. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1298,6 +1309,8 @@ enum nl80211_attrs { NL80211_ATTR_PMKSA_CANDIDATE, + NL80211_ATTR_TX_NO_CCK_RATE, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ccfdf3f63ce5..c1dd56b7cce5 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -860,6 +860,7 @@ struct cfg80211_ssid { * @wiphy: the wiphy this was for * @dev: the interface * @aborted: (internal) scan request was notified as aborted + * @no_cck: used to send probe requests at non CCK rate in 2GHz band */ struct cfg80211_scan_request { struct cfg80211_ssid *ssids; @@ -874,6 +875,7 @@ struct cfg80211_scan_request { struct wiphy *wiphy; struct net_device *dev; bool aborted; + bool no_cck; /* keep last */ struct ieee80211_channel *channels[0]; @@ -1560,7 +1562,8 @@ struct cfg80211_ops { struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, - const u8 *buf, size_t len, u64 *cookie); + const u8 *buf, size_t len, bool no_cck, + u64 *cookie); int (*mgmt_tx_cancel_wait)(struct wiphy *wiphy, struct net_device *dev, u64 cookie); diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index b57ddf941e59..9cba0104e291 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1869,7 +1869,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, - const u8 *buf, size_t len, u64 *cookie) + const u8 *buf, size_t len, bool no_cck, + u64 *cookie) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; diff --git a/net/wireless/core.h b/net/wireless/core.h index cb87b8bbceb7..b9ec3061ed72 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -375,7 +375,8 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, - const u8 *buf, size_t len, u64 *cookie); + const u8 *buf, size_t len, bool no_cck, + u64 *cookie); /* SME */ int __cfg80211_connect(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 61adea540e02..21fc9702f81c 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -900,7 +900,8 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, - const u8 *buf, size_t len, u64 *cookie) + const u8 *buf, size_t len, bool no_cck, + u64 *cookie) { struct wireless_dev *wdev = dev->ieee80211_ptr; const struct ieee80211_mgmt *mgmt; @@ -991,7 +992,7 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, /* Transmit the Action frame as requested by user space */ return rdev->ops->mgmt_tx(&rdev->wiphy, dev, chan, offchan, channel_type, channel_type_valid, - wait, buf, len, cookie); + wait, buf, len, no_cck, cookie); } bool cfg80211_rx_mgmt(struct net_device *dev, int freq, const u8 *buf, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a3e26951fd8b..48c1bf1a142d 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -191,6 +191,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { .len = IEEE80211_MAX_DATA_LEN }, [NL80211_ATTR_ROAM_SUPPORT] = { .type = NLA_FLAG }, [NL80211_ATTR_SCHED_SCAN_MATCH] = { .type = NLA_NESTED }, + [NL80211_ATTR_TX_NO_CCK_RATE] = { .type = NLA_FLAG }, }; /* policy for the key attributes */ @@ -3620,6 +3621,9 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) } } + request->no_cck = + nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); + request->dev = dev; request->wiphy = &rdev->wiphy; @@ -5171,6 +5175,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) struct sk_buff *msg; unsigned int wait = 0; bool offchan; + bool no_cck; if (!info->attrs[NL80211_ATTR_FRAME] || !info->attrs[NL80211_ATTR_WIPHY_FREQ]) @@ -5207,6 +5212,8 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK]; + no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); + freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); chan = rdev_freq_to_chan(rdev, freq, channel_type); if (chan == NULL) @@ -5227,7 +5234,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) channel_type_valid, wait, nla_data(info->attrs[NL80211_ATTR_FRAME]), nla_len(info->attrs[NL80211_ATTR_FRAME]), - &cookie); + no_cck, &cookie); if (err) goto free_msg; -- cgit v1.2.3 From aad14ceb45f5ff12da2ab5b37a596e6f81566515 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Sun, 25 Sep 2011 14:53:31 +0530 Subject: mac80211: Send the management frame at requested rate Whenever the scan request or tx_mgmt is requesting not to use CCK rate for managemet frames through NL80211_ATTR_TX_NO_CCK_RATE attribute, then mac80211 should select appropriate least non-CCK rate. This could help to send P2P probes and P2P action frames at non 11b rates without diabling 11b rates globally. Cc: Jouni Malinen Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- include/net/mac80211.h | 4 ++++ net/mac80211/cfg.c | 3 +++ net/mac80211/ieee80211_i.h | 2 +- net/mac80211/mlme.c | 2 +- net/mac80211/rate.c | 29 ++++++++++++++++++++++++++++- net/mac80211/scan.c | 3 ++- net/mac80211/util.c | 8 ++++++-- net/mac80211/work.c | 2 +- 8 files changed, 46 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 90dfcc99b466..1e83afae3c64 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -363,6 +363,9 @@ struct ieee80211_bss_conf { * @IEEE80211_TX_INTFL_TKIP_MIC_FAILURE: Marks this packet to be used for TKIP * testing. It will be sent out with incorrect Michael MIC key to allow * TKIP countermeasures to be tested. + * @IEEE80211_TX_CTL_NO_CCK_RATE: This frame will be sent at non CCK rate. + * This flag is actually used for management frame especially for P2P + * frames not being sent at CCK rate in 2GHz band. * * Note: If you have to add new flags to the enumeration, then don't * forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary. @@ -393,6 +396,7 @@ enum mac80211_tx_control_flags { IEEE80211_TX_CTL_STBC = BIT(23) | BIT(24), IEEE80211_TX_CTL_TX_OFFCHAN = BIT(25), IEEE80211_TX_INTFL_TKIP_MIC_FAILURE = BIT(26), + IEEE80211_TX_CTL_NO_CCK_RATE = BIT(27), }; #define IEEE80211_TX_CTL_STBC_SHIFT 23 diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 9cba0104e291..56c35041ba97 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1897,6 +1897,9 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, flags |= IEEE80211_TX_CTL_TX_OFFCHAN; } + if (no_cck) + flags |= IEEE80211_TX_CTL_NO_CCK_RATE; + if (is_offchan && !offchan) return -EBUSY; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 21186e280ceb..4822d69930e2 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1324,7 +1324,7 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, - u32 ratemask, bool directed); + u32 ratemask, bool directed, bool no_cck); void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata, const size_t supp_rates_len, diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 1a59fb6630d4..cc80d320c922 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1239,7 +1239,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) } else { ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID); ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid[1], NULL, 0, - (u32) -1, true); + (u32) -1, true, false); } ifmgd->probe_send_count++; diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 3d5a2cb835c4..f61244c0e0a2 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -233,6 +233,27 @@ static void rc_send_low_broadcast(s8 *idx, u32 basic_rates, /* could not find a basic rate; use original selection */ } +static inline s8 +rate_lowest_non_cck_index(struct ieee80211_supported_band *sband, + struct ieee80211_sta *sta) +{ + int i; + + for (i = 0; i < sband->n_bitrates; i++) { + struct ieee80211_rate *srate = &sband->bitrates[i]; + if ((srate->bitrate == 10) || (srate->bitrate == 20) || + (srate->bitrate == 55) || (srate->bitrate == 110)) + continue; + + if (rate_supported(sta, sband->band, i)) + return i; + } + + /* No matching rate found */ + return 0; +} + + bool rate_control_send_low(struct ieee80211_sta *sta, void *priv_sta, struct ieee80211_tx_rate_control *txrc) @@ -242,7 +263,13 @@ bool rate_control_send_low(struct ieee80211_sta *sta, int mcast_rate; if (!sta || !priv_sta || rc_no_data_or_no_ack(txrc)) { - info->control.rates[0].idx = rate_lowest_index(txrc->sband, sta); + if ((sband->band != IEEE80211_BAND_2GHZ) || + !(info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)) + info->control.rates[0].idx = + rate_lowest_index(txrc->sband, sta); + else + info->control.rates[0].idx = + rate_lowest_non_cck_index(txrc->sband, sta); info->control.rates[0].count = (info->flags & IEEE80211_TX_CTL_NO_ACK) ? 1 : txrc->hw->max_rate_tries; diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 6f09eca01112..830e60f65779 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -660,7 +660,8 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, local->scan_req->ssids[i].ssid, local->scan_req->ssids[i].ssid_len, local->scan_req->ie, local->scan_req->ie_len, - local->scan_req->rates[band], false); + local->scan_req->rates[band], false, + local->scan_req->no_cck); /* * After sending probe requests, wait for probe responses diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 4b1466d5b6a1..ead345db7127 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -899,14 +899,18 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, - u32 ratemask, bool directed) + u32 ratemask, bool directed, bool no_cck) { struct sk_buff *skb; skb = ieee80211_build_probe_req(sdata, dst, ratemask, ssid, ssid_len, ie, ie_len, directed); - if (skb) + if (skb) { + if (no_cck) + IEEE80211_SKB_CB(skb)->flags |= + IEEE80211_TX_CTL_NO_CCK_RATE; ieee80211_tx_skb(sdata, skb); + } } u32 ieee80211_sta_get_rates(struct ieee80211_local *local, diff --git a/net/mac80211/work.c b/net/mac80211/work.c index bac34394c05e..af374fab1a12 100644 --- a/net/mac80211/work.c +++ b/net/mac80211/work.c @@ -458,7 +458,7 @@ ieee80211_direct_probe(struct ieee80211_work *wk) */ ieee80211_send_probe_req(sdata, NULL, wk->probe_auth.ssid, wk->probe_auth.ssid_len, NULL, 0, - (u32) -1, true); + (u32) -1, true, false); wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; run_again(local, wk->timeout); -- cgit v1.2.3 From f70f01c2ebbe31fbd8a96be3b45c5620dac45b96 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 25 Sep 2011 20:06:53 +0300 Subject: cfg80211/mac80211: add netdev param to set_txq_params() tx params are currently configured per hw, although they should be configured per interface. Signed-off-by: Eliad Peller Signed-off-by: John W. Linville --- include/net/cfg80211.h | 2 +- net/mac80211/cfg.c | 1 + net/wireless/nl80211.c | 6 ++++++ 3 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index c1dd56b7cce5..31d823a3092b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1486,7 +1486,7 @@ struct cfg80211_ops { int (*change_bss)(struct wiphy *wiphy, struct net_device *dev, struct bss_parameters *params); - int (*set_txq_params)(struct wiphy *wiphy, + int (*set_txq_params)(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_txq_params *params); int (*set_channel)(struct wiphy *wiphy, struct net_device *dev, diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 56c35041ba97..726fb8819b43 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1271,6 +1271,7 @@ static int ieee80211_change_bss(struct wiphy *wiphy, } static int ieee80211_set_txq_params(struct wiphy *wiphy, + struct net_device *dev, struct ieee80211_txq_params *params) { struct ieee80211_local *local = wiphy_priv(wiphy); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 48c1bf1a142d..74d16192fbf0 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1236,6 +1236,11 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) goto bad_res; } + if (!netdev) { + result = -EINVAL; + goto bad_res; + } + nla_for_each_nested(nl_txq_params, info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS], rem_txq_params) { @@ -1248,6 +1253,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) goto bad_res; result = rdev->ops->set_txq_params(&rdev->wiphy, + netdev, &txq_params); if (result) goto bad_res; -- cgit v1.2.3