Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next
Johan Hedberg <johan.hedberg@gmail.com> says: "Here's another bluetooth-next pull request for 3.19. We've got: - Various fixes, cleanups and improvements to ieee802154/mac802154 - Support for a Broadcom BCM20702A1 variant - Lots of lockdep fixes - Fixed handling of LE CoC errors that should trigger SMP" Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
commit
f48ecb19bc
|
@ -4699,6 +4699,7 @@ F: net/mac802154/
|
|||
F: drivers/net/ieee802154/
|
||||
F: include/linux/nl802154.h
|
||||
F: include/linux/ieee802154.h
|
||||
F: include/net/nl802154.h
|
||||
F: include/net/mac802154.h
|
||||
F: include/net/af_ieee802154.h
|
||||
F: include/net/cfg802154.h
|
||||
|
|
|
@ -106,6 +106,8 @@ static const struct usb_device_id btusb_table[] = {
|
|||
{ USB_DEVICE(0x0b05, 0x17b5) },
|
||||
{ USB_DEVICE(0x0b05, 0x17cb) },
|
||||
{ USB_DEVICE(0x413c, 0x8197) },
|
||||
{ USB_DEVICE(0x13d3, 0x3404),
|
||||
.driver_info = BTUSB_BCM_PATCHRAM },
|
||||
|
||||
/* Foxconn - Hon Hai */
|
||||
{ USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01) },
|
||||
|
|
|
@ -74,7 +74,7 @@ static int ath_wakeup_ar3k(struct tty_struct *tty)
|
|||
|
||||
status = tty->driver->ops->tiocmget(tty);
|
||||
|
||||
/* Disable Automatic RTSCTS */
|
||||
/* Enable Automatic RTSCTS */
|
||||
ktermios.c_cflag |= CRTSCTS;
|
||||
status = tty_set_termios(tty, &ktermios);
|
||||
|
||||
|
|
|
@ -46,10 +46,6 @@ struct at86rf2xx_chip_data {
|
|||
u16 t_off_to_tx_on;
|
||||
u16 t_frame;
|
||||
u16 t_p_ack;
|
||||
/* short interframe spacing time */
|
||||
u16 t_sifs;
|
||||
/* long interframe spacing time */
|
||||
u16 t_lifs;
|
||||
/* completion timeout for tx in msecs */
|
||||
u16 t_tx_timeout;
|
||||
int rssi_base_val;
|
||||
|
@ -719,19 +715,10 @@ at86rf230_tx_complete(void *context)
|
|||
|
||||
enable_irq(lp->spi->irq);
|
||||
|
||||
if (lp->max_frame_retries <= 0) {
|
||||
/* Interfame spacing time, which is phy depend.
|
||||
* TODO
|
||||
* Move this handling in MAC 802.15.4 layer.
|
||||
* This is currently a workaround to avoid fragmenation issues.
|
||||
*/
|
||||
if (skb->len > 18)
|
||||
udelay(lp->data->t_lifs);
|
||||
else
|
||||
udelay(lp->data->t_sifs);
|
||||
}
|
||||
|
||||
ieee802154_xmit_complete(lp->hw, skb);
|
||||
if (lp->max_frame_retries <= 0)
|
||||
ieee802154_xmit_complete(lp->hw, skb, true);
|
||||
else
|
||||
ieee802154_xmit_complete(lp->hw, skb, false);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1038,6 +1025,36 @@ at86rf212_set_channel(struct at86rf230_local *lp, u8 page, u8 channel)
|
|||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* This sets the symbol_duration according frequency on the 212.
|
||||
* TODO move this handling while set channel and page in cfg802154.
|
||||
* We can do that, this timings are according 802.15.4 standard.
|
||||
* If we do that in cfg802154, this is a more generic calculation.
|
||||
*
|
||||
* This should also protected from ifs_timer. Means cancel timer and
|
||||
* init with a new value. For now, this is okay.
|
||||
*/
|
||||
if (channel == 0) {
|
||||
if (page == 0) {
|
||||
/* SUB:0 and BPSK:0 -> BPSK-20 */
|
||||
lp->hw->phy->symbol_duration = 50;
|
||||
} else {
|
||||
/* SUB:1 and BPSK:0 -> BPSK-40 */
|
||||
lp->hw->phy->symbol_duration = 25;
|
||||
}
|
||||
} else {
|
||||
if (page == 0)
|
||||
/* SUB:0 and BPSK:1 -> OQPSK-100/200/400 */
|
||||
lp->hw->phy->symbol_duration = 40;
|
||||
else
|
||||
/* SUB:1 and BPSK:1 -> OQPSK-250/500/1000 */
|
||||
lp->hw->phy->symbol_duration = 16;
|
||||
}
|
||||
|
||||
lp->hw->phy->lifs_period = IEEE802154_LIFS_PERIOD *
|
||||
lp->hw->phy->symbol_duration;
|
||||
lp->hw->phy->sifs_period = IEEE802154_SIFS_PERIOD *
|
||||
lp->hw->phy->symbol_duration;
|
||||
|
||||
return at86rf230_write_subreg(lp, SR_CHANNEL, channel);
|
||||
}
|
||||
|
||||
|
@ -1047,23 +1064,11 @@ at86rf230_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
|
|||
struct at86rf230_local *lp = hw->priv;
|
||||
int rc;
|
||||
|
||||
if (page > 31 ||
|
||||
!(lp->hw->phy->channels_supported[page] & BIT(channel))) {
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = lp->data->set_channel(lp, page, channel);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* Wait for PLL */
|
||||
usleep_range(lp->data->t_channel_switch,
|
||||
lp->data->t_channel_switch + 10);
|
||||
hw->phy->current_channel = channel;
|
||||
hw->phy->current_page = page;
|
||||
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -1179,9 +1184,6 @@ at86rf230_set_csma_params(struct ieee802154_hw *hw, u8 min_be, u8 max_be,
|
|||
struct at86rf230_local *lp = hw->priv;
|
||||
int rc;
|
||||
|
||||
if (min_be > max_be || max_be > 8 || retries > 5)
|
||||
return -EINVAL;
|
||||
|
||||
rc = at86rf230_write_subreg(lp, SR_MIN_BE, min_be);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
@ -1199,9 +1201,6 @@ at86rf230_set_frame_retries(struct ieee802154_hw *hw, s8 retries)
|
|||
struct at86rf230_local *lp = hw->priv;
|
||||
int rc = 0;
|
||||
|
||||
if (retries < -1 || retries > 15)
|
||||
return -EINVAL;
|
||||
|
||||
lp->tx_aret = retries >= 0;
|
||||
lp->max_frame_retries = retries;
|
||||
|
||||
|
@ -1263,8 +1262,6 @@ static struct at86rf2xx_chip_data at86rf233_data = {
|
|||
.t_off_to_tx_on = 80,
|
||||
.t_frame = 4096,
|
||||
.t_p_ack = 545,
|
||||
.t_sifs = 192,
|
||||
.t_lifs = 640,
|
||||
.t_tx_timeout = 2000,
|
||||
.rssi_base_val = -91,
|
||||
.set_channel = at86rf23x_set_channel,
|
||||
|
@ -1279,8 +1276,6 @@ static struct at86rf2xx_chip_data at86rf231_data = {
|
|||
.t_off_to_tx_on = 110,
|
||||
.t_frame = 4096,
|
||||
.t_p_ack = 545,
|
||||
.t_sifs = 192,
|
||||
.t_lifs = 640,
|
||||
.t_tx_timeout = 2000,
|
||||
.rssi_base_val = -91,
|
||||
.set_channel = at86rf23x_set_channel,
|
||||
|
@ -1295,8 +1290,6 @@ static struct at86rf2xx_chip_data at86rf212_data = {
|
|||
.t_off_to_tx_on = 200,
|
||||
.t_frame = 4096,
|
||||
.t_p_ack = 545,
|
||||
.t_sifs = 192,
|
||||
.t_lifs = 640,
|
||||
.t_tx_timeout = 2000,
|
||||
.rssi_base_val = -100,
|
||||
.set_channel = at86rf212_set_channel,
|
||||
|
@ -1432,6 +1425,7 @@ at86rf230_detect_device(struct at86rf230_local *lp)
|
|||
lp->data = &at86rf231_data;
|
||||
lp->hw->phy->channels_supported[0] = 0x7FFF800;
|
||||
lp->hw->phy->current_channel = 11;
|
||||
lp->hw->phy->symbol_duration = 16;
|
||||
break;
|
||||
case 7:
|
||||
chip = "at86rf212";
|
||||
|
@ -1441,6 +1435,7 @@ at86rf230_detect_device(struct at86rf230_local *lp)
|
|||
lp->hw->phy->channels_supported[0] = 0x00007FF;
|
||||
lp->hw->phy->channels_supported[2] = 0x00007FF;
|
||||
lp->hw->phy->current_channel = 5;
|
||||
lp->hw->phy->symbol_duration = 25;
|
||||
} else {
|
||||
rc = -ENOTSUPP;
|
||||
}
|
||||
|
@ -1450,6 +1445,7 @@ at86rf230_detect_device(struct at86rf230_local *lp)
|
|||
lp->data = &at86rf233_data;
|
||||
lp->hw->phy->channels_supported[0] = 0x7FFF800;
|
||||
lp->hw->phy->current_channel = 13;
|
||||
lp->hw->phy->symbol_duration = 16;
|
||||
break;
|
||||
default:
|
||||
chip = "unkown";
|
||||
|
|
|
@ -58,9 +58,6 @@ fakelb_hw_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
|
|||
{
|
||||
pr_debug("set channel to %d\n", channel);
|
||||
|
||||
hw->phy->current_page = page;
|
||||
hw->phy->current_channel = channel;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,8 +30,18 @@
|
|||
#define IEEE802154_MTU 127
|
||||
#define IEEE802154_MIN_PSDU_LEN 5
|
||||
|
||||
#define IEEE802154_PAN_ID_BROADCAST 0xffff
|
||||
#define IEEE802154_ADDR_SHORT_BROADCAST 0xffff
|
||||
#define IEEE802154_ADDR_SHORT_UNSPEC 0xfffe
|
||||
|
||||
#define IEEE802154_EXTENDED_ADDR_LEN 8
|
||||
|
||||
#define IEEE802154_LIFS_PERIOD 40
|
||||
#define IEEE802154_SIFS_PERIOD 12
|
||||
|
||||
#define IEEE802154_MAX_CHANNEL 26
|
||||
#define IEEE802154_MAX_PAGE 31
|
||||
|
||||
#define IEEE802154_FC_TYPE_BEACON 0x0 /* Frame is beacon */
|
||||
#define IEEE802154_FC_TYPE_DATA 0x1 /* Frame is data */
|
||||
#define IEEE802154_FC_TYPE_ACK 0x2 /* Frame is acknowledgment */
|
||||
|
|
|
@ -639,7 +639,7 @@ struct hci_cp_user_passkey_reply {
|
|||
struct hci_cp_remote_oob_data_reply {
|
||||
bdaddr_t bdaddr;
|
||||
__u8 hash[16];
|
||||
__u8 randomizer[16];
|
||||
__u8 rand[16];
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_REMOTE_OOB_DATA_NEG_REPLY 0x0433
|
||||
|
@ -731,9 +731,9 @@ struct hci_rp_set_csb {
|
|||
struct hci_cp_remote_oob_ext_data_reply {
|
||||
bdaddr_t bdaddr;
|
||||
__u8 hash192[16];
|
||||
__u8 randomizer192[16];
|
||||
__u8 rand192[16];
|
||||
__u8 hash256[16];
|
||||
__u8 randomizer256[16];
|
||||
__u8 rand256[16];
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_SNIFF_MODE 0x0803
|
||||
|
@ -940,7 +940,7 @@ struct hci_cp_write_ssp_mode {
|
|||
struct hci_rp_read_local_oob_data {
|
||||
__u8 status;
|
||||
__u8 hash[16];
|
||||
__u8 randomizer[16];
|
||||
__u8 rand[16];
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_READ_INQ_RSP_TX_POWER 0x0c58
|
||||
|
@ -1024,9 +1024,9 @@ struct hci_cp_write_sc_support {
|
|||
struct hci_rp_read_local_oob_ext_data {
|
||||
__u8 status;
|
||||
__u8 hash192[16];
|
||||
__u8 randomizer192[16];
|
||||
__u8 rand192[16];
|
||||
__u8 hash256[16];
|
||||
__u8 randomizer256[16];
|
||||
__u8 rand256[16];
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_READ_LOCAL_VERSION 0x1001
|
||||
|
|
|
@ -108,6 +108,7 @@ struct smp_csrk {
|
|||
|
||||
struct smp_ltk {
|
||||
struct list_head list;
|
||||
struct rcu_head rcu;
|
||||
bdaddr_t bdaddr;
|
||||
u8 bdaddr_type;
|
||||
u8 authenticated;
|
||||
|
@ -120,6 +121,7 @@ struct smp_ltk {
|
|||
|
||||
struct smp_irk {
|
||||
struct list_head list;
|
||||
struct rcu_head rcu;
|
||||
bdaddr_t rpa;
|
||||
bdaddr_t bdaddr;
|
||||
u8 addr_type;
|
||||
|
@ -138,9 +140,9 @@ struct oob_data {
|
|||
struct list_head list;
|
||||
bdaddr_t bdaddr;
|
||||
u8 hash192[16];
|
||||
u8 randomizer192[16];
|
||||
u8 rand192[16];
|
||||
u8 hash256[16];
|
||||
u8 randomizer256[16];
|
||||
u8 rand256[16];
|
||||
};
|
||||
|
||||
#define HCI_MAX_SHORT_NAME_LENGTH 10
|
||||
|
@ -941,10 +943,10 @@ void hci_remote_oob_data_clear(struct hci_dev *hdev);
|
|||
struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev,
|
||||
bdaddr_t *bdaddr);
|
||||
int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
u8 *hash, u8 *randomizer);
|
||||
u8 *hash, u8 *rand);
|
||||
int hci_add_remote_oob_ext_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
u8 *hash192, u8 *randomizer192,
|
||||
u8 *hash256, u8 *randomizer256);
|
||||
u8 *hash192, u8 *rand192,
|
||||
u8 *hash256, u8 *rand256);
|
||||
int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr);
|
||||
|
||||
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
|
||||
|
@ -1372,8 +1374,8 @@ void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
|
|||
u8 status);
|
||||
void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
|
||||
void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
|
||||
u8 *randomizer192, u8 *hash256,
|
||||
u8 *randomizer256, u8 status);
|
||||
u8 *rand192, u8 *hash256, u8 *rand256,
|
||||
u8 status);
|
||||
void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
|
||||
u8 addr_type, u8 *dev_class, s8 rssi, u32 flags,
|
||||
u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len);
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#define __L2CAP_H
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/atomic.h>
|
||||
|
||||
/* L2CAP defaults */
|
||||
#define L2CAP_DEFAULT_MTU 672
|
||||
|
@ -481,6 +482,7 @@ struct l2cap_chan {
|
|||
struct hci_conn *hs_hcon;
|
||||
struct hci_chan *hs_hchan;
|
||||
struct kref kref;
|
||||
atomic_t nesting;
|
||||
|
||||
__u8 state;
|
||||
|
||||
|
@ -713,6 +715,17 @@ enum {
|
|||
FLAG_HOLD_HCI_CONN,
|
||||
};
|
||||
|
||||
/* Lock nesting levels for L2CAP channels. We need these because lockdep
|
||||
* otherwise considers all channels equal and will e.g. complain about a
|
||||
* connection oriented channel triggering SMP procedures or a listening
|
||||
* channel creating and locking a child channel.
|
||||
*/
|
||||
enum {
|
||||
L2CAP_NESTING_SMP,
|
||||
L2CAP_NESTING_NORMAL,
|
||||
L2CAP_NESTING_PARENT,
|
||||
};
|
||||
|
||||
enum {
|
||||
L2CAP_TX_STATE_XMIT,
|
||||
L2CAP_TX_STATE_WAIT_F,
|
||||
|
@ -778,7 +791,7 @@ void l2cap_chan_put(struct l2cap_chan *c);
|
|||
|
||||
static inline void l2cap_chan_lock(struct l2cap_chan *chan)
|
||||
{
|
||||
mutex_lock(&chan->lock);
|
||||
mutex_lock_nested(&chan->lock, atomic_read(&chan->nesting));
|
||||
}
|
||||
|
||||
static inline void l2cap_chan_unlock(struct l2cap_chan *chan)
|
||||
|
|
|
@ -299,28 +299,28 @@ struct mgmt_cp_user_passkey_neg_reply {
|
|||
#define MGMT_READ_LOCAL_OOB_DATA_SIZE 0
|
||||
struct mgmt_rp_read_local_oob_data {
|
||||
__u8 hash[16];
|
||||
__u8 randomizer[16];
|
||||
__u8 rand[16];
|
||||
} __packed;
|
||||
struct mgmt_rp_read_local_oob_ext_data {
|
||||
__u8 hash192[16];
|
||||
__u8 randomizer192[16];
|
||||
__u8 rand192[16];
|
||||
__u8 hash256[16];
|
||||
__u8 randomizer256[16];
|
||||
__u8 rand256[16];
|
||||
} __packed;
|
||||
|
||||
#define MGMT_OP_ADD_REMOTE_OOB_DATA 0x0021
|
||||
struct mgmt_cp_add_remote_oob_data {
|
||||
struct mgmt_addr_info addr;
|
||||
__u8 hash[16];
|
||||
__u8 randomizer[16];
|
||||
__u8 rand[16];
|
||||
} __packed;
|
||||
#define MGMT_ADD_REMOTE_OOB_DATA_SIZE (MGMT_ADDR_INFO_SIZE + 32)
|
||||
struct mgmt_cp_add_remote_oob_ext_data {
|
||||
struct mgmt_addr_info addr;
|
||||
__u8 hash192[16];
|
||||
__u8 randomizer192[16];
|
||||
__u8 rand192[16];
|
||||
__u8 hash256[16];
|
||||
__u8 randomizer256[16];
|
||||
__u8 rand256[16];
|
||||
} __packed;
|
||||
#define MGMT_ADD_REMOTE_OOB_EXT_DATA_SIZE (MGMT_ADDR_INFO_SIZE + 64)
|
||||
|
||||
|
|
|
@ -17,17 +17,12 @@
|
|||
#ifndef __NET_CFG802154_H
|
||||
#define __NET_CFG802154_H
|
||||
|
||||
#include <linux/ieee802154.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/bug.h>
|
||||
|
||||
/* According to the IEEE 802.15.4 stadard the upper most significant bits of
|
||||
* the 32-bit channel bitmaps shall be used as an integer value to specify 32
|
||||
* possible channel pages. The lower 27 bits of the channel bit map shall be
|
||||
* used as a bit mask to specify channel numbers within a channel page.
|
||||
*/
|
||||
#define WPAN_NUM_CHANNELS 27
|
||||
#define WPAN_NUM_PAGES 32
|
||||
#include <net/nl802154.h>
|
||||
|
||||
struct wpan_phy;
|
||||
|
||||
|
@ -35,13 +30,43 @@ struct cfg802154_ops {
|
|||
struct net_device * (*add_virtual_intf_deprecated)(struct wpan_phy *wpan_phy,
|
||||
const char *name,
|
||||
int type);
|
||||
void (*del_virtual_intf_deprecated)(struct wpan_phy *wpan_phy,
|
||||
struct net_device *dev);
|
||||
void (*del_virtual_intf_deprecated)(struct wpan_phy *wpan_phy,
|
||||
struct net_device *dev);
|
||||
int (*add_virtual_intf)(struct wpan_phy *wpan_phy,
|
||||
const char *name,
|
||||
enum nl802154_iftype type,
|
||||
__le64 extended_addr);
|
||||
int (*del_virtual_intf)(struct wpan_phy *wpan_phy,
|
||||
struct wpan_dev *wpan_dev);
|
||||
int (*set_channel)(struct wpan_phy *wpan_phy, u8 page, u8 channel);
|
||||
int (*set_pan_id)(struct wpan_phy *wpan_phy,
|
||||
struct wpan_dev *wpan_dev, __le16 pan_id);
|
||||
int (*set_short_addr)(struct wpan_phy *wpan_phy,
|
||||
struct wpan_dev *wpan_dev, __le16 short_addr);
|
||||
int (*set_backoff_exponent)(struct wpan_phy *wpan_phy,
|
||||
struct wpan_dev *wpan_dev, u8 min_be,
|
||||
u8 max_be);
|
||||
int (*set_max_csma_backoffs)(struct wpan_phy *wpan_phy,
|
||||
struct wpan_dev *wpan_dev,
|
||||
u8 max_csma_backoffs);
|
||||
int (*set_max_frame_retries)(struct wpan_phy *wpan_phy,
|
||||
struct wpan_dev *wpan_dev,
|
||||
s8 max_frame_retries);
|
||||
int (*set_lbt_mode)(struct wpan_phy *wpan_phy,
|
||||
struct wpan_dev *wpan_dev, bool mode);
|
||||
};
|
||||
|
||||
struct wpan_phy {
|
||||
struct mutex pib_lock;
|
||||
|
||||
/* If multiple wpan_phys are registered and you're handed e.g.
|
||||
* a regular netdev with assigned ieee802154_ptr, you won't
|
||||
* know whether it points to a wpan_phy your driver has registered
|
||||
* or not. Assign this to something global to your driver to
|
||||
* help determine whether you own this wpan_phy or not.
|
||||
*/
|
||||
const void *privid;
|
||||
|
||||
/*
|
||||
* This is a PIB according to 802.15.4-2011.
|
||||
* We do not provide timing-related variables, as they
|
||||
|
@ -49,19 +74,22 @@ struct wpan_phy {
|
|||
*/
|
||||
u8 current_channel;
|
||||
u8 current_page;
|
||||
u32 channels_supported[32];
|
||||
u32 channels_supported[IEEE802154_MAX_PAGE + 1];
|
||||
s8 transmit_power;
|
||||
u8 cca_mode;
|
||||
u8 min_be;
|
||||
u8 max_be;
|
||||
u8 csma_retries;
|
||||
s8 frame_retries;
|
||||
|
||||
__le64 perm_extended_addr;
|
||||
|
||||
bool lbt;
|
||||
s32 cca_ed_level;
|
||||
|
||||
/* PHY depended MAC PIB values */
|
||||
|
||||
/* 802.15.4 acronym: Tdsym in usec */
|
||||
u8 symbol_duration;
|
||||
/* lifs and sifs periods timing */
|
||||
u16 lifs_period;
|
||||
u16 sifs_period;
|
||||
|
||||
struct device dev;
|
||||
|
||||
char priv[0] __aligned(NETDEV_ALIGN);
|
||||
|
@ -69,12 +97,38 @@ struct wpan_phy {
|
|||
|
||||
struct wpan_dev {
|
||||
struct wpan_phy *wpan_phy;
|
||||
int iftype;
|
||||
|
||||
/* the remainder of this struct should be private to cfg802154 */
|
||||
struct list_head list;
|
||||
struct net_device *netdev;
|
||||
|
||||
u32 identifier;
|
||||
|
||||
/* MAC PIB */
|
||||
__le16 pan_id;
|
||||
__le16 short_addr;
|
||||
__le64 extended_addr;
|
||||
|
||||
/* MAC BSN field */
|
||||
u8 bsn;
|
||||
/* MAC DSN field */
|
||||
u8 dsn;
|
||||
|
||||
u8 min_be;
|
||||
u8 max_be;
|
||||
u8 csma_retries;
|
||||
s8 frame_retries;
|
||||
|
||||
bool lbt;
|
||||
|
||||
bool promiscuous_mode;
|
||||
};
|
||||
|
||||
#define to_phy(_dev) container_of(_dev, struct wpan_phy, dev)
|
||||
|
||||
struct wpan_phy *
|
||||
wpan_phy_alloc(const struct cfg802154_ops *ops, size_t priv_size);
|
||||
wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size);
|
||||
static inline void wpan_phy_set_dev(struct wpan_phy *phy, struct device *dev)
|
||||
{
|
||||
phy->dev.parent = dev;
|
||||
|
|
|
@ -260,6 +260,7 @@ void ieee802154_rx_irqsafe(struct ieee802154_hw *hw, struct sk_buff *skb,
|
|||
|
||||
void ieee802154_wake_queue(struct ieee802154_hw *hw);
|
||||
void ieee802154_stop_queue(struct ieee802154_hw *hw);
|
||||
void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb);
|
||||
void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb,
|
||||
bool ifs_handling);
|
||||
|
||||
#endif /* NET_MAC802154_H */
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
#ifndef __NL802154_H
|
||||
#define __NL802154_H
|
||||
/*
|
||||
* 802.15.4 netlink interface public header
|
||||
*
|
||||
* Copyright 2014 Alexander Aring <aar@pengutronix.de>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#define NL802154_GENL_NAME "nl802154"
|
||||
|
||||
enum nl802154_commands {
|
||||
/* don't change the order or add anything between, this is ABI! */
|
||||
/* currently we don't shipping this file via uapi, ignore the above one */
|
||||
NL802154_CMD_UNSPEC,
|
||||
|
||||
NL802154_CMD_GET_WPAN_PHY, /* can dump */
|
||||
NL802154_CMD_SET_WPAN_PHY,
|
||||
NL802154_CMD_NEW_WPAN_PHY,
|
||||
NL802154_CMD_DEL_WPAN_PHY,
|
||||
|
||||
NL802154_CMD_GET_INTERFACE, /* can dump */
|
||||
NL802154_CMD_SET_INTERFACE,
|
||||
NL802154_CMD_NEW_INTERFACE,
|
||||
NL802154_CMD_DEL_INTERFACE,
|
||||
|
||||
NL802154_CMD_SET_CHANNEL,
|
||||
|
||||
NL802154_CMD_SET_PAN_ID,
|
||||
NL802154_CMD_SET_SHORT_ADDR,
|
||||
|
||||
NL802154_CMD_SET_TX_POWER,
|
||||
NL802154_CMD_SET_CCA_MODE,
|
||||
NL802154_CMD_SET_CCA_ED_LEVEL,
|
||||
|
||||
NL802154_CMD_SET_MAX_FRAME_RETRIES,
|
||||
|
||||
NL802154_CMD_SET_BACKOFF_EXPONENT,
|
||||
NL802154_CMD_SET_MAX_CSMA_BACKOFFS,
|
||||
|
||||
NL802154_CMD_SET_LBT_MODE,
|
||||
|
||||
/* add new commands above here */
|
||||
|
||||
/* used to define NL802154_CMD_MAX below */
|
||||
__NL802154_CMD_AFTER_LAST,
|
||||
NL802154_CMD_MAX = __NL802154_CMD_AFTER_LAST - 1
|
||||
};
|
||||
|
||||
enum nl802154_attrs {
|
||||
/* don't change the order or add anything between, this is ABI! */
|
||||
/* currently we don't shipping this file via uapi, ignore the above one */
|
||||
NL802154_ATTR_UNSPEC,
|
||||
|
||||
NL802154_ATTR_WPAN_PHY,
|
||||
NL802154_ATTR_WPAN_PHY_NAME,
|
||||
|
||||
NL802154_ATTR_IFINDEX,
|
||||
NL802154_ATTR_IFNAME,
|
||||
NL802154_ATTR_IFTYPE,
|
||||
|
||||
NL802154_ATTR_WPAN_DEV,
|
||||
|
||||
NL802154_ATTR_PAGE,
|
||||
NL802154_ATTR_CHANNEL,
|
||||
|
||||
NL802154_ATTR_PAN_ID,
|
||||
NL802154_ATTR_SHORT_ADDR,
|
||||
|
||||
NL802154_ATTR_TX_POWER,
|
||||
|
||||
NL802154_ATTR_CCA_MODE,
|
||||
NL802154_ATTR_CCA_MODE3_AND,
|
||||
NL802154_ATTR_CCA_ED_LEVEL,
|
||||
|
||||
NL802154_ATTR_MAX_FRAME_RETRIES,
|
||||
|
||||
NL802154_ATTR_MAX_BE,
|
||||
NL802154_ATTR_MIN_BE,
|
||||
NL802154_ATTR_MAX_CSMA_BACKOFFS,
|
||||
|
||||
NL802154_ATTR_LBT_MODE,
|
||||
|
||||
NL802154_ATTR_GENERATION,
|
||||
|
||||
NL802154_ATTR_CHANNELS_SUPPORTED,
|
||||
NL802154_ATTR_SUPPORTED_CHANNEL,
|
||||
|
||||
NL802154_ATTR_EXTENDED_ADDR,
|
||||
|
||||
/* add attributes here, update the policy in nl802154.c */
|
||||
|
||||
__NL802154_ATTR_AFTER_LAST,
|
||||
NL802154_ATTR_MAX = __NL802154_ATTR_AFTER_LAST - 1
|
||||
};
|
||||
|
||||
enum nl802154_iftype {
|
||||
/* for backwards compatibility TODO */
|
||||
NL802154_IFTYPE_UNSPEC = -1,
|
||||
|
||||
NL802154_IFTYPE_NODE,
|
||||
NL802154_IFTYPE_MONITOR,
|
||||
NL802154_IFTYPE_COORD,
|
||||
|
||||
/* keep last */
|
||||
NUM_NL802154_IFTYPES,
|
||||
NL802154_IFTYPE_MAX = NUM_NL802154_IFTYPES - 1
|
||||
};
|
||||
|
||||
#endif /* __NL802154_H */
|
|
@ -87,13 +87,6 @@ struct lowpan_dev {
|
|||
struct delayed_work notify_peers;
|
||||
};
|
||||
|
||||
static inline void peer_free(struct rcu_head *head)
|
||||
{
|
||||
struct lowpan_peer *e = container_of(head, struct lowpan_peer, rcu);
|
||||
|
||||
kfree(e);
|
||||
}
|
||||
|
||||
static inline struct lowpan_dev *lowpan_dev(const struct net_device *netdev)
|
||||
{
|
||||
return netdev_priv(netdev);
|
||||
|
@ -108,7 +101,7 @@ static inline void peer_add(struct lowpan_dev *dev, struct lowpan_peer *peer)
|
|||
static inline bool peer_del(struct lowpan_dev *dev, struct lowpan_peer *peer)
|
||||
{
|
||||
list_del_rcu(&peer->list);
|
||||
call_rcu(&peer->rcu, peer_free);
|
||||
kfree_rcu(peer, rcu);
|
||||
|
||||
module_put(THIS_MODULE);
|
||||
|
||||
|
@ -614,17 +607,13 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
|
|||
int err = 0;
|
||||
bdaddr_t addr;
|
||||
u8 addr_type;
|
||||
struct sk_buff *tmpskb;
|
||||
|
||||
/* We must take a copy of the skb before we modify/replace the ipv6
|
||||
* header as the header could be used elsewhere
|
||||
*/
|
||||
tmpskb = skb_unshare(skb, GFP_ATOMIC);
|
||||
if (!tmpskb) {
|
||||
kfree_skb(skb);
|
||||
skb = skb_unshare(skb, GFP_ATOMIC);
|
||||
if (!skb)
|
||||
return NET_XMIT_DROP;
|
||||
}
|
||||
skb = tmpskb;
|
||||
|
||||
/* Return values from setup_header()
|
||||
* <0 - error, packet is dropped
|
||||
|
@ -1141,6 +1130,8 @@ static struct l2cap_chan *bt_6lowpan_listen(void)
|
|||
pchan->state = BT_LISTEN;
|
||||
pchan->src_type = BDADDR_LE_PUBLIC;
|
||||
|
||||
atomic_set(&pchan->nesting, L2CAP_NESTING_PARENT);
|
||||
|
||||
BT_DBG("psm 0x%04x chan %p src type %d", psm_6lowpan, pchan,
|
||||
pchan->src_type);
|
||||
|
||||
|
@ -1223,7 +1214,7 @@ static void disconnect_all_peers(void)
|
|||
l2cap_chan_close(peer->chan, ENOENT);
|
||||
|
||||
list_del_rcu(&peer->list);
|
||||
call_rcu(&peer->rcu, peer_free);
|
||||
kfree_rcu(peer, rcu);
|
||||
|
||||
module_put(THIS_MODULE);
|
||||
}
|
||||
|
|
|
@ -134,6 +134,7 @@ struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
|
|||
static int hmac_sha256(u8 *key, u8 ksize, char *plaintext, u8 psize, u8 *output)
|
||||
{
|
||||
struct crypto_shash *tfm;
|
||||
struct shash_desc *shash;
|
||||
int ret;
|
||||
|
||||
if (!ksize)
|
||||
|
@ -148,18 +149,24 @@ static int hmac_sha256(u8 *key, u8 ksize, char *plaintext, u8 psize, u8 *output)
|
|||
ret = crypto_shash_setkey(tfm, key, ksize);
|
||||
if (ret) {
|
||||
BT_DBG("crypto_ahash_setkey failed: err %d", ret);
|
||||
} else {
|
||||
char desc[sizeof(struct shash_desc) +
|
||||
crypto_shash_descsize(tfm)] CRYPTO_MINALIGN_ATTR;
|
||||
struct shash_desc *shash = (struct shash_desc *)desc;
|
||||
|
||||
shash->tfm = tfm;
|
||||
shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
||||
|
||||
ret = crypto_shash_digest(shash, plaintext, psize,
|
||||
output);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm),
|
||||
GFP_KERNEL);
|
||||
if (!shash) {
|
||||
ret = -ENOMEM;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
shash->tfm = tfm;
|
||||
shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
||||
|
||||
ret = crypto_shash_digest(shash, plaintext, psize, output);
|
||||
|
||||
kfree(shash);
|
||||
|
||||
failed:
|
||||
crypto_free_shash(tfm);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -748,16 +748,15 @@ static const struct file_operations white_list_fops = {
|
|||
static int identity_resolving_keys_show(struct seq_file *f, void *ptr)
|
||||
{
|
||||
struct hci_dev *hdev = f->private;
|
||||
struct list_head *p, *n;
|
||||
struct smp_irk *irk;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
list_for_each_safe(p, n, &hdev->identity_resolving_keys) {
|
||||
struct smp_irk *irk = list_entry(p, struct smp_irk, list);
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
|
||||
seq_printf(f, "%pMR (type %u) %*phN %pMR\n",
|
||||
&irk->bdaddr, irk->addr_type,
|
||||
16, irk->val, &irk->rpa);
|
||||
}
|
||||
hci_dev_unlock(hdev);
|
||||
rcu_read_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -778,17 +777,15 @@ static const struct file_operations identity_resolving_keys_fops = {
|
|||
static int long_term_keys_show(struct seq_file *f, void *ptr)
|
||||
{
|
||||
struct hci_dev *hdev = f->private;
|
||||
struct list_head *p, *n;
|
||||
struct smp_ltk *ltk;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
list_for_each_safe(p, n, &hdev->long_term_keys) {
|
||||
struct smp_ltk *ltk = list_entry(p, struct smp_ltk, list);
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(ltk, &hdev->long_term_keys, list)
|
||||
seq_printf(f, "%pMR (type %u) %u 0x%02x %u %.4x %.16llx %*phN\n",
|
||||
<k->bdaddr, ltk->bdaddr_type, ltk->authenticated,
|
||||
ltk->type, ltk->enc_size, __le16_to_cpu(ltk->ediv),
|
||||
__le64_to_cpu(ltk->rand), 16, ltk->val);
|
||||
}
|
||||
hci_dev_unlock(hdev);
|
||||
rcu_read_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2564,6 +2561,11 @@ static int hci_dev_do_close(struct hci_dev *hdev)
|
|||
if (test_bit(HCI_MGMT, &hdev->dev_flags))
|
||||
cancel_delayed_work_sync(&hdev->rpa_expired);
|
||||
|
||||
/* Avoid potential lockdep warnings from the *_flush() calls by
|
||||
* ensuring the workqueue is empty up front.
|
||||
*/
|
||||
drain_workqueue(hdev->workqueue);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_inquiry_cache_flush(hdev);
|
||||
hci_pend_le_actions_clear(hdev);
|
||||
|
@ -2687,6 +2689,11 @@ int hci_dev_reset(__u16 dev)
|
|||
skb_queue_purge(&hdev->rx_q);
|
||||
skb_queue_purge(&hdev->cmd_q);
|
||||
|
||||
/* Avoid potential lockdep warnings from the *_flush() calls by
|
||||
* ensuring the workqueue is empty up front.
|
||||
*/
|
||||
drain_workqueue(hdev->workqueue);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_inquiry_cache_flush(hdev);
|
||||
hci_conn_hash_flush(hdev);
|
||||
|
@ -3106,21 +3113,21 @@ void hci_link_keys_clear(struct hci_dev *hdev)
|
|||
|
||||
void hci_smp_ltks_clear(struct hci_dev *hdev)
|
||||
{
|
||||
struct smp_ltk *k, *tmp;
|
||||
struct smp_ltk *k;
|
||||
|
||||
list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) {
|
||||
list_del(&k->list);
|
||||
kfree(k);
|
||||
list_for_each_entry_rcu(k, &hdev->long_term_keys, list) {
|
||||
list_del_rcu(&k->list);
|
||||
kfree_rcu(k, rcu);
|
||||
}
|
||||
}
|
||||
|
||||
void hci_smp_irks_clear(struct hci_dev *hdev)
|
||||
{
|
||||
struct smp_irk *k, *tmp;
|
||||
struct smp_irk *k;
|
||||
|
||||
list_for_each_entry_safe(k, tmp, &hdev->identity_resolving_keys, list) {
|
||||
list_del(&k->list);
|
||||
kfree(k);
|
||||
list_for_each_entry_rcu(k, &hdev->identity_resolving_keys, list) {
|
||||
list_del_rcu(&k->list);
|
||||
kfree_rcu(k, rcu);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3184,15 +3191,18 @@ struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand,
|
|||
{
|
||||
struct smp_ltk *k;
|
||||
|
||||
list_for_each_entry(k, &hdev->long_term_keys, list) {
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(k, &hdev->long_term_keys, list) {
|
||||
if (k->ediv != ediv || k->rand != rand)
|
||||
continue;
|
||||
|
||||
if (ltk_role(k->type) != role)
|
||||
continue;
|
||||
|
||||
rcu_read_unlock();
|
||||
return k;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
@ -3202,11 +3212,16 @@ struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
|||
{
|
||||
struct smp_ltk *k;
|
||||
|
||||
list_for_each_entry(k, &hdev->long_term_keys, list)
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(k, &hdev->long_term_keys, list) {
|
||||
if (addr_type == k->bdaddr_type &&
|
||||
bacmp(bdaddr, &k->bdaddr) == 0 &&
|
||||
ltk_role(k->type) == role)
|
||||
ltk_role(k->type) == role) {
|
||||
rcu_read_unlock();
|
||||
return k;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
@ -3215,18 +3230,23 @@ struct smp_irk *hci_find_irk_by_rpa(struct hci_dev *hdev, bdaddr_t *rpa)
|
|||
{
|
||||
struct smp_irk *irk;
|
||||
|
||||
list_for_each_entry(irk, &hdev->identity_resolving_keys, list) {
|
||||
if (!bacmp(&irk->rpa, rpa))
|
||||
return irk;
|
||||
}
|
||||
|
||||
list_for_each_entry(irk, &hdev->identity_resolving_keys, list) {
|
||||
if (smp_irk_matches(hdev, irk->val, rpa)) {
|
||||
bacpy(&irk->rpa, rpa);
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
|
||||
if (!bacmp(&irk->rpa, rpa)) {
|
||||
rcu_read_unlock();
|
||||
return irk;
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
|
||||
if (smp_irk_matches(hdev, irk->val, rpa)) {
|
||||
bacpy(&irk->rpa, rpa);
|
||||
rcu_read_unlock();
|
||||
return irk;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -3239,11 +3259,15 @@ struct smp_irk *hci_find_irk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
|||
if (addr_type == ADDR_LE_DEV_RANDOM && (bdaddr->b[5] & 0xc0) != 0xc0)
|
||||
return NULL;
|
||||
|
||||
list_for_each_entry(irk, &hdev->identity_resolving_keys, list) {
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
|
||||
if (addr_type == irk->addr_type &&
|
||||
bacmp(bdaddr, &irk->bdaddr) == 0)
|
||||
bacmp(bdaddr, &irk->bdaddr) == 0) {
|
||||
rcu_read_unlock();
|
||||
return irk;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
@ -3309,7 +3333,7 @@ struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
|||
key = kzalloc(sizeof(*key), GFP_KERNEL);
|
||||
if (!key)
|
||||
return NULL;
|
||||
list_add(&key->list, &hdev->long_term_keys);
|
||||
list_add_rcu(&key->list, &hdev->long_term_keys);
|
||||
}
|
||||
|
||||
bacpy(&key->bdaddr, bdaddr);
|
||||
|
@ -3338,7 +3362,7 @@ struct smp_irk *hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
|||
bacpy(&irk->bdaddr, bdaddr);
|
||||
irk->addr_type = addr_type;
|
||||
|
||||
list_add(&irk->list, &hdev->identity_resolving_keys);
|
||||
list_add_rcu(&irk->list, &hdev->identity_resolving_keys);
|
||||
}
|
||||
|
||||
memcpy(irk->val, val, 16);
|
||||
|
@ -3365,17 +3389,17 @@ int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
|||
|
||||
int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type)
|
||||
{
|
||||
struct smp_ltk *k, *tmp;
|
||||
struct smp_ltk *k;
|
||||
int removed = 0;
|
||||
|
||||
list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) {
|
||||
list_for_each_entry_rcu(k, &hdev->long_term_keys, list) {
|
||||
if (bacmp(bdaddr, &k->bdaddr) || k->bdaddr_type != bdaddr_type)
|
||||
continue;
|
||||
|
||||
BT_DBG("%s removing %pMR", hdev->name, bdaddr);
|
||||
|
||||
list_del(&k->list);
|
||||
kfree(k);
|
||||
list_del_rcu(&k->list);
|
||||
kfree_rcu(k, rcu);
|
||||
removed++;
|
||||
}
|
||||
|
||||
|
@ -3384,16 +3408,16 @@ int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type)
|
|||
|
||||
void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type)
|
||||
{
|
||||
struct smp_irk *k, *tmp;
|
||||
struct smp_irk *k;
|
||||
|
||||
list_for_each_entry_safe(k, tmp, &hdev->identity_resolving_keys, list) {
|
||||
list_for_each_entry_rcu(k, &hdev->identity_resolving_keys, list) {
|
||||
if (bacmp(bdaddr, &k->bdaddr) || k->addr_type != addr_type)
|
||||
continue;
|
||||
|
||||
BT_DBG("%s removing %pMR", hdev->name, bdaddr);
|
||||
|
||||
list_del(&k->list);
|
||||
kfree(k);
|
||||
list_del_rcu(&k->list);
|
||||
kfree_rcu(k, rcu);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3455,7 +3479,7 @@ void hci_remote_oob_data_clear(struct hci_dev *hdev)
|
|||
}
|
||||
|
||||
int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
u8 *hash, u8 *randomizer)
|
||||
u8 *hash, u8 *rand)
|
||||
{
|
||||
struct oob_data *data;
|
||||
|
||||
|
@ -3470,10 +3494,10 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
|||
}
|
||||
|
||||
memcpy(data->hash192, hash, sizeof(data->hash192));
|
||||
memcpy(data->randomizer192, randomizer, sizeof(data->randomizer192));
|
||||
memcpy(data->rand192, rand, sizeof(data->rand192));
|
||||
|
||||
memset(data->hash256, 0, sizeof(data->hash256));
|
||||
memset(data->randomizer256, 0, sizeof(data->randomizer256));
|
||||
memset(data->rand256, 0, sizeof(data->rand256));
|
||||
|
||||
BT_DBG("%s for %pMR", hdev->name, bdaddr);
|
||||
|
||||
|
@ -3481,8 +3505,8 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
|||
}
|
||||
|
||||
int hci_add_remote_oob_ext_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
u8 *hash192, u8 *randomizer192,
|
||||
u8 *hash256, u8 *randomizer256)
|
||||
u8 *hash192, u8 *rand192,
|
||||
u8 *hash256, u8 *rand256)
|
||||
{
|
||||
struct oob_data *data;
|
||||
|
||||
|
@ -3497,10 +3521,10 @@ int hci_add_remote_oob_ext_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
|||
}
|
||||
|
||||
memcpy(data->hash192, hash192, sizeof(data->hash192));
|
||||
memcpy(data->randomizer192, randomizer192, sizeof(data->randomizer192));
|
||||
memcpy(data->rand192, rand192, sizeof(data->rand192));
|
||||
|
||||
memcpy(data->hash256, hash256, sizeof(data->hash256));
|
||||
memcpy(data->randomizer256, randomizer256, sizeof(data->randomizer256));
|
||||
memcpy(data->rand256, rand256, sizeof(data->rand256));
|
||||
|
||||
BT_DBG("%s for %pMR", hdev->name, bdaddr);
|
||||
|
||||
|
|
|
@ -994,8 +994,8 @@ static void hci_cc_read_local_oob_data(struct hci_dev *hdev,
|
|||
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
mgmt_read_local_oob_data_complete(hdev, rp->hash, rp->randomizer,
|
||||
NULL, NULL, rp->status);
|
||||
mgmt_read_local_oob_data_complete(hdev, rp->hash, rp->rand, NULL, NULL,
|
||||
rp->status);
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
|
@ -1007,8 +1007,8 @@ static void hci_cc_read_local_oob_ext_data(struct hci_dev *hdev,
|
|||
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
mgmt_read_local_oob_data_complete(hdev, rp->hash192, rp->randomizer192,
|
||||
rp->hash256, rp->randomizer256,
|
||||
mgmt_read_local_oob_data_complete(hdev, rp->hash192, rp->rand192,
|
||||
rp->hash256, rp->rand256,
|
||||
rp->status);
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
@ -1581,7 +1581,14 @@ static void hci_check_pending_name(struct hci_dev *hdev, struct hci_conn *conn,
|
|||
struct discovery_state *discov = &hdev->discovery;
|
||||
struct inquiry_entry *e;
|
||||
|
||||
if (conn && !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
|
||||
/* Update the mgmt connected state if necessary. Be careful with
|
||||
* conn objects that exist but are not (yet) connected however.
|
||||
* Only those in BT_CONFIG or BT_CONNECTED states can be
|
||||
* considered connected.
|
||||
*/
|
||||
if (conn &&
|
||||
(conn->state == BT_CONFIG || conn->state == BT_CONNECTED) &&
|
||||
!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
|
||||
mgmt_device_connected(hdev, conn, 0, name, name_len);
|
||||
|
||||
if (discov->state == DISCOVERY_STOPPED)
|
||||
|
@ -3989,11 +3996,9 @@ static void hci_remote_oob_data_request_evt(struct hci_dev *hdev,
|
|||
|
||||
bacpy(&cp.bdaddr, &ev->bdaddr);
|
||||
memcpy(cp.hash192, data->hash192, sizeof(cp.hash192));
|
||||
memcpy(cp.randomizer192, data->randomizer192,
|
||||
sizeof(cp.randomizer192));
|
||||
memcpy(cp.rand192, data->rand192, sizeof(cp.rand192));
|
||||
memcpy(cp.hash256, data->hash256, sizeof(cp.hash256));
|
||||
memcpy(cp.randomizer256, data->randomizer256,
|
||||
sizeof(cp.randomizer256));
|
||||
memcpy(cp.rand256, data->rand256, sizeof(cp.rand256));
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_EXT_DATA_REPLY,
|
||||
sizeof(cp), &cp);
|
||||
|
@ -4002,8 +4007,7 @@ static void hci_remote_oob_data_request_evt(struct hci_dev *hdev,
|
|||
|
||||
bacpy(&cp.bdaddr, &ev->bdaddr);
|
||||
memcpy(cp.hash, data->hash192, sizeof(cp.hash));
|
||||
memcpy(cp.randomizer, data->randomizer192,
|
||||
sizeof(cp.randomizer));
|
||||
memcpy(cp.rand, data->rand192, sizeof(cp.rand));
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_REPLY,
|
||||
sizeof(cp), &cp);
|
||||
|
@ -4571,8 +4575,8 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
*/
|
||||
if (ltk->type == SMP_STK) {
|
||||
set_bit(HCI_CONN_STK_ENCRYPT, &conn->flags);
|
||||
list_del(<k->list);
|
||||
kfree(ltk);
|
||||
list_del_rcu(<k->list);
|
||||
kfree_rcu(ltk, rcu);
|
||||
} else {
|
||||
clear_bit(HCI_CONN_STK_ENCRYPT, &conn->flags);
|
||||
}
|
||||
|
|
|
@ -736,14 +736,10 @@ static int hidp_setup_hid(struct hidp_session *session,
|
|||
struct hid_device *hid;
|
||||
int err;
|
||||
|
||||
session->rd_data = kzalloc(req->rd_size, GFP_KERNEL);
|
||||
if (!session->rd_data)
|
||||
return -ENOMEM;
|
||||
session->rd_data = memdup_user(req->rd_data, req->rd_size);
|
||||
if (IS_ERR(session->rd_data))
|
||||
return PTR_ERR(session->rd_data);
|
||||
|
||||
if (copy_from_user(session->rd_data, req->rd_data, req->rd_size)) {
|
||||
err = -EFAULT;
|
||||
goto fault;
|
||||
}
|
||||
session->rd_size = req->rd_size;
|
||||
|
||||
hid = hid_allocate_device();
|
||||
|
|
|
@ -424,6 +424,9 @@ struct l2cap_chan *l2cap_chan_create(void)
|
|||
|
||||
mutex_init(&chan->lock);
|
||||
|
||||
/* Set default lock nesting level */
|
||||
atomic_set(&chan->nesting, L2CAP_NESTING_NORMAL);
|
||||
|
||||
write_lock(&chan_list_lock);
|
||||
list_add(&chan->global_l, &chan_list);
|
||||
write_unlock(&chan_list_lock);
|
||||
|
@ -567,7 +570,8 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
|
|||
|
||||
__clear_chan_timer(chan);
|
||||
|
||||
BT_DBG("chan %p, conn %p, err %d", chan, conn, err);
|
||||
BT_DBG("chan %p, conn %p, err %d, state %s", chan, conn, err,
|
||||
state_to_string(chan->state));
|
||||
|
||||
chan->ops->teardown(chan, err);
|
||||
|
||||
|
@ -5215,9 +5219,10 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
|
|||
u8 *data)
|
||||
{
|
||||
struct l2cap_le_conn_rsp *rsp = (struct l2cap_le_conn_rsp *) data;
|
||||
struct hci_conn *hcon = conn->hcon;
|
||||
u16 dcid, mtu, mps, credits, result;
|
||||
struct l2cap_chan *chan;
|
||||
int err;
|
||||
int err, sec_level;
|
||||
|
||||
if (cmd_len < sizeof(*rsp))
|
||||
return -EPROTO;
|
||||
|
@ -5256,6 +5261,26 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
|
|||
l2cap_chan_ready(chan);
|
||||
break;
|
||||
|
||||
case L2CAP_CR_AUTHENTICATION:
|
||||
case L2CAP_CR_ENCRYPTION:
|
||||
/* If we already have MITM protection we can't do
|
||||
* anything.
|
||||
*/
|
||||
if (hcon->sec_level > BT_SECURITY_MEDIUM) {
|
||||
l2cap_chan_del(chan, ECONNREFUSED);
|
||||
break;
|
||||
}
|
||||
|
||||
sec_level = hcon->sec_level + 1;
|
||||
if (chan->sec_level < sec_level)
|
||||
chan->sec_level = sec_level;
|
||||
|
||||
/* We'll need to send a new Connect Request */
|
||||
clear_bit(FLAG_LE_CONN_REQ_SENT, &chan->flags);
|
||||
|
||||
smp_conn_security(hcon, chan->sec_level);
|
||||
break;
|
||||
|
||||
default:
|
||||
l2cap_chan_del(chan, ECONNREFUSED);
|
||||
break;
|
||||
|
@ -5388,7 +5413,8 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
|
|||
mutex_lock(&conn->chan_lock);
|
||||
l2cap_chan_lock(pchan);
|
||||
|
||||
if (!smp_sufficient_security(conn->hcon, pchan->sec_level)) {
|
||||
if (!smp_sufficient_security(conn->hcon, pchan->sec_level,
|
||||
SMP_ALLOW_STK)) {
|
||||
result = L2CAP_CR_AUTHENTICATION;
|
||||
chan = NULL;
|
||||
goto response_unlock;
|
||||
|
@ -7329,7 +7355,8 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
|
|||
l2cap_start_connection(chan);
|
||||
else
|
||||
__set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
|
||||
} else if (chan->state == BT_CONNECT2) {
|
||||
} else if (chan->state == BT_CONNECT2 &&
|
||||
chan->mode != L2CAP_MODE_LE_FLOWCTL) {
|
||||
struct l2cap_conn_rsp rsp;
|
||||
__u16 res, stat;
|
||||
|
||||
|
|
|
@ -285,6 +285,12 @@ static int l2cap_sock_listen(struct socket *sock, int backlog)
|
|||
sk->sk_max_ack_backlog = backlog;
|
||||
sk->sk_ack_backlog = 0;
|
||||
|
||||
/* Listening channels need to use nested locking in order not to
|
||||
* cause lockdep warnings when the created child channels end up
|
||||
* being locked in the same thread as the parent channel.
|
||||
*/
|
||||
atomic_set(&chan->nesting, L2CAP_NESTING_PARENT);
|
||||
|
||||
chan->state = BT_LISTEN;
|
||||
sk->sk_state = BT_LISTEN;
|
||||
|
||||
|
@ -301,7 +307,7 @@ static int l2cap_sock_accept(struct socket *sock, struct socket *newsock,
|
|||
long timeo;
|
||||
int err = 0;
|
||||
|
||||
lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
|
||||
lock_sock_nested(sk, L2CAP_NESTING_PARENT);
|
||||
|
||||
timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
|
||||
|
||||
|
@ -333,7 +339,7 @@ static int l2cap_sock_accept(struct socket *sock, struct socket *newsock,
|
|||
|
||||
release_sock(sk);
|
||||
timeo = schedule_timeout(timeo);
|
||||
lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
|
||||
lock_sock_nested(sk, L2CAP_NESTING_PARENT);
|
||||
}
|
||||
__set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(sk_sleep(sk), &wait);
|
||||
|
@ -1096,6 +1102,8 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
|
|||
chan = l2cap_pi(sk)->chan;
|
||||
conn = chan->conn;
|
||||
|
||||
BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
|
||||
|
||||
if (conn)
|
||||
mutex_lock(&conn->chan_lock);
|
||||
|
||||
|
@ -1153,12 +1161,16 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
|
|||
{
|
||||
struct sock *sk;
|
||||
|
||||
BT_DBG("parent %p", parent);
|
||||
BT_DBG("parent %p state %s", parent,
|
||||
state_to_string(parent->sk_state));
|
||||
|
||||
/* Close not yet accepted channels */
|
||||
while ((sk = bt_accept_dequeue(parent, NULL))) {
|
||||
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
||||
|
||||
BT_DBG("child chan %p state %s", chan,
|
||||
state_to_string(chan->state));
|
||||
|
||||
l2cap_chan_lock(chan);
|
||||
__clear_chan_timer(chan);
|
||||
l2cap_chan_close(chan, ECONNRESET);
|
||||
|
@ -1246,7 +1258,16 @@ static void l2cap_sock_teardown_cb(struct l2cap_chan *chan, int err)
|
|||
struct sock *sk = chan->data;
|
||||
struct sock *parent;
|
||||
|
||||
lock_sock(sk);
|
||||
BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
|
||||
|
||||
/* This callback can be called both for server (BT_LISTEN)
|
||||
* sockets as well as "normal" ones. To avoid lockdep warnings
|
||||
* with child socket locking (through l2cap_sock_cleanup_listen)
|
||||
* we need separation into separate nesting levels. The simplest
|
||||
* way to accomplish this is to inherit the nesting level used
|
||||
* for the channel.
|
||||
*/
|
||||
lock_sock_nested(sk, atomic_read(&chan->nesting));
|
||||
|
||||
parent = bt_sk(sk)->parent;
|
||||
|
||||
|
|
|
@ -3589,8 +3589,16 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
|
|||
struct mgmt_cp_add_remote_oob_data *cp = data;
|
||||
u8 status;
|
||||
|
||||
if (cp->addr.type != BDADDR_BREDR) {
|
||||
err = cmd_complete(sk, hdev->id,
|
||||
MGMT_OP_ADD_REMOTE_OOB_DATA,
|
||||
MGMT_STATUS_INVALID_PARAMS,
|
||||
&cp->addr, sizeof(cp->addr));
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr,
|
||||
cp->hash, cp->randomizer);
|
||||
cp->hash, cp->rand);
|
||||
if (err < 0)
|
||||
status = MGMT_STATUS_FAILED;
|
||||
else
|
||||
|
@ -3602,11 +3610,17 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
|
|||
struct mgmt_cp_add_remote_oob_ext_data *cp = data;
|
||||
u8 status;
|
||||
|
||||
if (cp->addr.type != BDADDR_BREDR) {
|
||||
err = cmd_complete(sk, hdev->id,
|
||||
MGMT_OP_ADD_REMOTE_OOB_DATA,
|
||||
MGMT_STATUS_INVALID_PARAMS,
|
||||
&cp->addr, sizeof(cp->addr));
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
err = hci_add_remote_oob_ext_data(hdev, &cp->addr.bdaddr,
|
||||
cp->hash192,
|
||||
cp->randomizer192,
|
||||
cp->hash256,
|
||||
cp->randomizer256);
|
||||
cp->hash192, cp->rand192,
|
||||
cp->hash256, cp->rand256);
|
||||
if (err < 0)
|
||||
status = MGMT_STATUS_FAILED;
|
||||
else
|
||||
|
@ -3620,6 +3634,7 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
|
|||
MGMT_STATUS_INVALID_PARAMS);
|
||||
}
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
return err;
|
||||
}
|
||||
|
@ -3633,14 +3648,26 @@ static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
|
|||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
if (cp->addr.type != BDADDR_BREDR)
|
||||
return cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
|
||||
MGMT_STATUS_INVALID_PARAMS,
|
||||
&cp->addr, sizeof(cp->addr));
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!bacmp(&cp->addr.bdaddr, BDADDR_ANY)) {
|
||||
hci_remote_oob_data_clear(hdev);
|
||||
status = MGMT_STATUS_SUCCESS;
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr);
|
||||
if (err < 0)
|
||||
status = MGMT_STATUS_INVALID_PARAMS;
|
||||
else
|
||||
status = MGMT_STATUS_SUCCESS;
|
||||
|
||||
done:
|
||||
err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
|
||||
status, &cp->addr, sizeof(cp->addr));
|
||||
|
||||
|
@ -6742,8 +6769,8 @@ void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
|
|||
}
|
||||
|
||||
void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
|
||||
u8 *randomizer192, u8 *hash256,
|
||||
u8 *randomizer256, u8 status)
|
||||
u8 *rand192, u8 *hash256, u8 *rand256,
|
||||
u8 status)
|
||||
{
|
||||
struct pending_cmd *cmd;
|
||||
|
||||
|
@ -6758,16 +6785,14 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
|
|||
mgmt_status(status));
|
||||
} else {
|
||||
if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags) &&
|
||||
hash256 && randomizer256) {
|
||||
hash256 && rand256) {
|
||||
struct mgmt_rp_read_local_oob_ext_data rp;
|
||||
|
||||
memcpy(rp.hash192, hash192, sizeof(rp.hash192));
|
||||
memcpy(rp.randomizer192, randomizer192,
|
||||
sizeof(rp.randomizer192));
|
||||
memcpy(rp.rand192, rand192, sizeof(rp.rand192));
|
||||
|
||||
memcpy(rp.hash256, hash256, sizeof(rp.hash256));
|
||||
memcpy(rp.randomizer256, randomizer256,
|
||||
sizeof(rp.randomizer256));
|
||||
memcpy(rp.rand256, rand256, sizeof(rp.rand256));
|
||||
|
||||
cmd_complete(cmd->sk, hdev->id,
|
||||
MGMT_OP_READ_LOCAL_OOB_DATA, 0,
|
||||
|
@ -6776,8 +6801,7 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
|
|||
struct mgmt_rp_read_local_oob_data rp;
|
||||
|
||||
memcpy(rp.hash, hash192, sizeof(rp.hash));
|
||||
memcpy(rp.randomizer, randomizer192,
|
||||
sizeof(rp.randomizer));
|
||||
memcpy(rp.rand, rand192, sizeof(rp.rand));
|
||||
|
||||
cmd_complete(cmd->sk, hdev->id,
|
||||
MGMT_OP_READ_LOCAL_OOB_DATA, 0,
|
||||
|
|
|
@ -383,18 +383,18 @@ static void smp_chan_destroy(struct l2cap_conn *conn)
|
|||
/* If pairing failed clean up any keys we might have */
|
||||
if (!complete) {
|
||||
if (smp->ltk) {
|
||||
list_del(&smp->ltk->list);
|
||||
kfree(smp->ltk);
|
||||
list_del_rcu(&smp->ltk->list);
|
||||
kfree_rcu(smp->ltk, rcu);
|
||||
}
|
||||
|
||||
if (smp->slave_ltk) {
|
||||
list_del(&smp->slave_ltk->list);
|
||||
kfree(smp->slave_ltk);
|
||||
list_del_rcu(&smp->slave_ltk->list);
|
||||
kfree_rcu(smp->slave_ltk, rcu);
|
||||
}
|
||||
|
||||
if (smp->remote_irk) {
|
||||
list_del(&smp->remote_irk->list);
|
||||
kfree(smp->remote_irk);
|
||||
list_del_rcu(&smp->remote_irk->list);
|
||||
kfree_rcu(smp->remote_irk, rcu);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -514,8 +514,6 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
|
|||
set_bit(SMP_FLAG_TK_VALID, &smp->flags);
|
||||
}
|
||||
|
||||
hci_dev_lock(hcon->hdev);
|
||||
|
||||
if (method == REQ_PASSKEY)
|
||||
ret = mgmt_user_passkey_request(hcon->hdev, &hcon->dst,
|
||||
hcon->type, hcon->dst_type);
|
||||
|
@ -528,8 +526,6 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
|
|||
hcon->type, hcon->dst_type,
|
||||
passkey, 0);
|
||||
|
||||
hci_dev_unlock(hcon->hdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -659,8 +655,8 @@ static void smp_notify_keys(struct l2cap_conn *conn)
|
|||
* just remove it.
|
||||
*/
|
||||
if (!bacmp(&smp->remote_irk->rpa, BDADDR_ANY)) {
|
||||
list_del(&smp->remote_irk->list);
|
||||
kfree(smp->remote_irk);
|
||||
list_del_rcu(&smp->remote_irk->list);
|
||||
kfree_rcu(smp->remote_irk, rcu);
|
||||
smp->remote_irk = NULL;
|
||||
}
|
||||
}
|
||||
|
@ -1126,18 +1122,20 @@ static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level)
|
||||
bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level,
|
||||
enum smp_key_pref key_pref)
|
||||
{
|
||||
if (sec_level == BT_SECURITY_LOW)
|
||||
return true;
|
||||
|
||||
/* If we're encrypted with an STK always claim insufficient
|
||||
* security. This way we allow the connection to be re-encrypted
|
||||
* with an LTK, even if the LTK provides the same level of
|
||||
* security. Only exception is if we don't have an LTK (e.g.
|
||||
* because of key distribution bits).
|
||||
/* If we're encrypted with an STK but the caller prefers using
|
||||
* LTK claim insufficient security. This way we allow the
|
||||
* connection to be re-encrypted with an LTK, even if the LTK
|
||||
* provides the same level of security. Only exception is if we
|
||||
* don't have an LTK (e.g. because of key distribution bits).
|
||||
*/
|
||||
if (test_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags) &&
|
||||
if (key_pref == SMP_USE_LTK &&
|
||||
test_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags) &&
|
||||
hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type,
|
||||
hcon->role))
|
||||
return false;
|
||||
|
@ -1171,7 +1169,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
else
|
||||
sec_level = authreq_to_seclevel(auth);
|
||||
|
||||
if (smp_sufficient_security(hcon, sec_level))
|
||||
if (smp_sufficient_security(hcon, sec_level, SMP_USE_LTK))
|
||||
return 0;
|
||||
|
||||
if (sec_level > hcon->pending_sec_level)
|
||||
|
@ -1221,7 +1219,7 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
|
|||
if (!test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags))
|
||||
return 1;
|
||||
|
||||
if (smp_sufficient_security(hcon, sec_level))
|
||||
if (smp_sufficient_security(hcon, sec_level, SMP_USE_LTK))
|
||||
return 1;
|
||||
|
||||
if (sec_level > hcon->pending_sec_level)
|
||||
|
@ -1323,7 +1321,6 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
|
||||
skb_pull(skb, sizeof(*rp));
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
authenticated = (hcon->sec_level == BT_SECURITY_HIGH);
|
||||
ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, SMP_LTK,
|
||||
authenticated, smp->tk, smp->enc_key_size,
|
||||
|
@ -1331,7 +1328,6 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
smp->ltk = ltk;
|
||||
if (!(smp->remote_key_dist & KEY_DIST_MASK))
|
||||
smp_distribute_keys(smp);
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1378,8 +1374,6 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn,
|
|||
|
||||
skb_pull(skb, sizeof(*info));
|
||||
|
||||
hci_dev_lock(hcon->hdev);
|
||||
|
||||
/* Strictly speaking the Core Specification (4.1) allows sending
|
||||
* an empty address which would force us to rely on just the IRK
|
||||
* as "identity information". However, since such
|
||||
|
@ -1407,8 +1401,6 @@ distribute:
|
|||
if (!(smp->remote_key_dist & KEY_DIST_MASK))
|
||||
smp_distribute_keys(smp);
|
||||
|
||||
hci_dev_unlock(hcon->hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1417,7 +1409,6 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
struct smp_cmd_sign_info *rp = (void *) skb->data;
|
||||
struct l2cap_chan *chan = conn->smp;
|
||||
struct smp_chan *smp = chan->data;
|
||||
struct hci_dev *hdev = conn->hcon->hdev;
|
||||
struct smp_csrk *csrk;
|
||||
|
||||
BT_DBG("conn %p", conn);
|
||||
|
@ -1430,7 +1421,6 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
|
||||
skb_pull(skb, sizeof(*rp));
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
csrk = kzalloc(sizeof(*csrk), GFP_KERNEL);
|
||||
if (csrk) {
|
||||
csrk->master = 0x01;
|
||||
|
@ -1438,7 +1428,6 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
}
|
||||
smp->csrk = csrk;
|
||||
smp_distribute_keys(smp);
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1662,6 +1651,13 @@ static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan)
|
|||
chan->omtu = pchan->omtu;
|
||||
chan->mode = pchan->mode;
|
||||
|
||||
/* Other L2CAP channels may request SMP routines in order to
|
||||
* change the security level. This means that the SMP channel
|
||||
* lock must be considered in its own category to avoid lockdep
|
||||
* warnings.
|
||||
*/
|
||||
atomic_set(&chan->nesting, L2CAP_NESTING_SMP);
|
||||
|
||||
BT_DBG("created chan %p", chan);
|
||||
|
||||
return chan;
|
||||
|
@ -1693,7 +1689,7 @@ int smp_register(struct hci_dev *hdev)
|
|||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
|
||||
tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, 0);
|
||||
if (IS_ERR(tfm_aes)) {
|
||||
int err = PTR_ERR(tfm_aes);
|
||||
BT_ERR("Unable to create crypto context");
|
||||
|
@ -1719,6 +1715,9 @@ int smp_register(struct hci_dev *hdev)
|
|||
chan->imtu = L2CAP_DEFAULT_MTU;
|
||||
chan->ops = &smp_root_chan_ops;
|
||||
|
||||
/* Set correct nesting level for a parent/listening channel */
|
||||
atomic_set(&chan->nesting, L2CAP_NESTING_PARENT);
|
||||
|
||||
hdev->smp_data = chan;
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -133,8 +133,15 @@ static inline u8 smp_ltk_sec_level(struct smp_ltk *key)
|
|||
return BT_SECURITY_MEDIUM;
|
||||
}
|
||||
|
||||
/* Key preferences for smp_sufficient security */
|
||||
enum smp_key_pref {
|
||||
SMP_ALLOW_STK,
|
||||
SMP_USE_LTK,
|
||||
};
|
||||
|
||||
/* SMP Commands */
|
||||
bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level);
|
||||
bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level,
|
||||
enum smp_key_pref key_pref);
|
||||
int smp_conn_security(struct hci_conn *hcon, __u8 sec_level);
|
||||
int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey);
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ obj-$(CONFIG_IEEE802154_6LOWPAN) += ieee802154_6lowpan.o
|
|||
|
||||
ieee802154_6lowpan-y := 6lowpan_rtnl.o reassembly.o
|
||||
ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o core.o \
|
||||
header_ops.o sysfs.o
|
||||
header_ops.o sysfs.o nl802154.o
|
||||
af_802154-y := af_ieee802154.o raw.o dgram.o
|
||||
|
||||
ccflags-y += -D__CHECK_ENDIAN__
|
||||
|
|
|
@ -18,11 +18,17 @@
|
|||
#include <linux/device.h>
|
||||
|
||||
#include <net/cfg802154.h>
|
||||
#include <net/rtnetlink.h>
|
||||
|
||||
#include "ieee802154.h"
|
||||
#include "nl802154.h"
|
||||
#include "sysfs.h"
|
||||
#include "core.h"
|
||||
|
||||
/* RCU-protected (and RTNL for writers) */
|
||||
LIST_HEAD(cfg802154_rdev_list);
|
||||
int cfg802154_rdev_list_generation;
|
||||
|
||||
static int wpan_phy_match(struct device *dev, const void *data)
|
||||
{
|
||||
return !strcmp(dev_name(dev), (const char *)data);
|
||||
|
@ -69,8 +75,25 @@ int wpan_phy_for_each(int (*fn)(struct wpan_phy *phy, void *data),
|
|||
}
|
||||
EXPORT_SYMBOL(wpan_phy_for_each);
|
||||
|
||||
struct cfg802154_registered_device *
|
||||
cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx)
|
||||
{
|
||||
struct cfg802154_registered_device *result = NULL, *rdev;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
|
||||
if (rdev->wpan_phy_idx == wpan_phy_idx) {
|
||||
result = rdev;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct wpan_phy *
|
||||
wpan_phy_alloc(const struct cfg802154_ops *ops, size_t priv_size)
|
||||
wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size)
|
||||
{
|
||||
static atomic_t wpan_phy_counter = ATOMIC_INIT(0);
|
||||
struct cfg802154_registered_device *rdev;
|
||||
|
@ -97,25 +120,71 @@ wpan_phy_alloc(const struct cfg802154_ops *ops, size_t priv_size)
|
|||
|
||||
mutex_init(&rdev->wpan_phy.pib_lock);
|
||||
|
||||
INIT_LIST_HEAD(&rdev->wpan_dev_list);
|
||||
device_initialize(&rdev->wpan_phy.dev);
|
||||
dev_set_name(&rdev->wpan_phy.dev, "wpan-phy%d", rdev->wpan_phy_idx);
|
||||
|
||||
rdev->wpan_phy.dev.class = &wpan_phy_class;
|
||||
rdev->wpan_phy.dev.platform_data = rdev;
|
||||
|
||||
init_waitqueue_head(&rdev->dev_wait);
|
||||
|
||||
return &rdev->wpan_phy;
|
||||
}
|
||||
EXPORT_SYMBOL(wpan_phy_alloc);
|
||||
EXPORT_SYMBOL(wpan_phy_new);
|
||||
|
||||
int wpan_phy_register(struct wpan_phy *phy)
|
||||
{
|
||||
return device_add(&phy->dev);
|
||||
struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy);
|
||||
int ret;
|
||||
|
||||
rtnl_lock();
|
||||
ret = device_add(&phy->dev);
|
||||
if (ret) {
|
||||
rtnl_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
list_add_rcu(&rdev->list, &cfg802154_rdev_list);
|
||||
cfg802154_rdev_list_generation++;
|
||||
|
||||
/* TODO phy registered lock */
|
||||
rtnl_unlock();
|
||||
|
||||
/* TODO nl802154 phy notify */
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(wpan_phy_register);
|
||||
|
||||
void wpan_phy_unregister(struct wpan_phy *phy)
|
||||
{
|
||||
struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy);
|
||||
|
||||
wait_event(rdev->dev_wait, ({
|
||||
int __count;
|
||||
rtnl_lock();
|
||||
__count = rdev->opencount;
|
||||
rtnl_unlock();
|
||||
__count == 0; }));
|
||||
|
||||
rtnl_lock();
|
||||
/* TODO nl802154 phy notify */
|
||||
/* TODO phy registered lock */
|
||||
|
||||
WARN_ON(!list_empty(&rdev->wpan_dev_list));
|
||||
|
||||
/* First remove the hardware from everywhere, this makes
|
||||
* it impossible to find from userspace.
|
||||
*/
|
||||
list_del_rcu(&rdev->list);
|
||||
synchronize_rcu();
|
||||
|
||||
cfg802154_rdev_list_generation++;
|
||||
|
||||
device_del(&phy->dev);
|
||||
|
||||
rtnl_unlock();
|
||||
}
|
||||
EXPORT_SYMBOL(wpan_phy_unregister);
|
||||
|
||||
|
@ -130,6 +199,79 @@ void cfg802154_dev_free(struct cfg802154_registered_device *rdev)
|
|||
kfree(rdev);
|
||||
}
|
||||
|
||||
static void
|
||||
cfg802154_update_iface_num(struct cfg802154_registered_device *rdev,
|
||||
int iftype, int num)
|
||||
{
|
||||
ASSERT_RTNL();
|
||||
|
||||
rdev->num_running_ifaces += num;
|
||||
}
|
||||
|
||||
static int cfg802154_netdev_notifier_call(struct notifier_block *nb,
|
||||
unsigned long state, void *ptr)
|
||||
{
|
||||
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
||||
struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
|
||||
struct cfg802154_registered_device *rdev;
|
||||
|
||||
if (!wpan_dev)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
|
||||
|
||||
/* TODO WARN_ON unspec type */
|
||||
|
||||
switch (state) {
|
||||
/* TODO NETDEV_DEVTYPE */
|
||||
case NETDEV_REGISTER:
|
||||
wpan_dev->identifier = ++rdev->wpan_dev_id;
|
||||
list_add_rcu(&wpan_dev->list, &rdev->wpan_dev_list);
|
||||
rdev->devlist_generation++;
|
||||
|
||||
wpan_dev->netdev = dev;
|
||||
break;
|
||||
case NETDEV_DOWN:
|
||||
cfg802154_update_iface_num(rdev, wpan_dev->iftype, -1);
|
||||
|
||||
rdev->opencount--;
|
||||
wake_up(&rdev->dev_wait);
|
||||
break;
|
||||
case NETDEV_UP:
|
||||
cfg802154_update_iface_num(rdev, wpan_dev->iftype, 1);
|
||||
|
||||
rdev->opencount++;
|
||||
break;
|
||||
case NETDEV_UNREGISTER:
|
||||
/* It is possible to get NETDEV_UNREGISTER
|
||||
* multiple times. To detect that, check
|
||||
* that the interface is still on the list
|
||||
* of registered interfaces, and only then
|
||||
* remove and clean it up.
|
||||
*/
|
||||
if (!list_empty(&wpan_dev->list)) {
|
||||
list_del_rcu(&wpan_dev->list);
|
||||
rdev->devlist_generation++;
|
||||
}
|
||||
/* synchronize (so that we won't find this netdev
|
||||
* from other code any more) and then clear the list
|
||||
* head so that the above code can safely check for
|
||||
* !list_empty() to avoid double-cleanup.
|
||||
*/
|
||||
synchronize_rcu();
|
||||
INIT_LIST_HEAD(&wpan_dev->list);
|
||||
break;
|
||||
default:
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block cfg802154_netdev_notifier = {
|
||||
.notifier_call = cfg802154_netdev_notifier_call,
|
||||
};
|
||||
|
||||
static int __init wpan_phy_class_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
@ -138,11 +280,25 @@ static int __init wpan_phy_class_init(void)
|
|||
if (rc)
|
||||
goto err;
|
||||
|
||||
rc = ieee802154_nl_init();
|
||||
rc = register_netdevice_notifier(&cfg802154_netdev_notifier);
|
||||
if (rc)
|
||||
goto err_nl;
|
||||
|
||||
rc = ieee802154_nl_init();
|
||||
if (rc)
|
||||
goto err_notifier;
|
||||
|
||||
rc = nl802154_init();
|
||||
if (rc)
|
||||
goto err_ieee802154_nl;
|
||||
|
||||
return 0;
|
||||
|
||||
err_ieee802154_nl:
|
||||
ieee802154_nl_exit();
|
||||
|
||||
err_notifier:
|
||||
unregister_netdevice_notifier(&cfg802154_netdev_notifier);
|
||||
err_nl:
|
||||
wpan_phy_sysfs_exit();
|
||||
err:
|
||||
|
@ -152,7 +308,9 @@ subsys_initcall(wpan_phy_class_init);
|
|||
|
||||
static void __exit wpan_phy_class_exit(void)
|
||||
{
|
||||
nl802154_exit();
|
||||
ieee802154_nl_exit();
|
||||
unregister_netdevice_notifier(&cfg802154_netdev_notifier);
|
||||
wpan_phy_sysfs_exit();
|
||||
}
|
||||
module_exit(wpan_phy_class_exit);
|
||||
|
|
|
@ -5,10 +5,22 @@
|
|||
|
||||
struct cfg802154_registered_device {
|
||||
const struct cfg802154_ops *ops;
|
||||
struct list_head list;
|
||||
|
||||
/* wpan_phy index, internal only */
|
||||
int wpan_phy_idx;
|
||||
|
||||
/* also protected by devlist_mtx */
|
||||
int opencount;
|
||||
wait_queue_head_t dev_wait;
|
||||
|
||||
/* protected by RTNL only */
|
||||
int num_running_ifaces;
|
||||
|
||||
/* associated wpan interfaces, protected by rtnl or RCU */
|
||||
struct list_head wpan_dev_list;
|
||||
int devlist_generation, wpan_dev_id;
|
||||
|
||||
/* must be last because of the way we do wpan_phy_priv(),
|
||||
* and it should at least be aligned to NETDEV_ALIGN
|
||||
*/
|
||||
|
@ -23,7 +35,12 @@ wpan_phy_to_rdev(struct wpan_phy *wpan_phy)
|
|||
wpan_phy);
|
||||
}
|
||||
|
||||
extern struct list_head cfg802154_rdev_list;
|
||||
extern int cfg802154_rdev_list_generation;
|
||||
|
||||
/* free object */
|
||||
void cfg802154_dev_free(struct cfg802154_registered_device *rdev);
|
||||
struct cfg802154_registered_device *
|
||||
cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx);
|
||||
|
||||
#endif /* __IEEE802154_CORE_H */
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#define IEEE_802154_LOCAL_H
|
||||
|
||||
int __init ieee802154_nl_init(void);
|
||||
void __exit ieee802154_nl_exit(void);
|
||||
void ieee802154_nl_exit(void);
|
||||
|
||||
#define IEEE802154_OP(_cmd, _func) \
|
||||
{ \
|
||||
|
|
|
@ -155,7 +155,7 @@ int __init ieee802154_nl_init(void)
|
|||
ieee802154_mcgrps);
|
||||
}
|
||||
|
||||
void __exit ieee802154_nl_exit(void)
|
||||
void ieee802154_nl_exit(void)
|
||||
{
|
||||
genl_unregister_family(&nl802154_family);
|
||||
}
|
||||
|
|
|
@ -113,7 +113,9 @@ static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid,
|
|||
if (ops->get_mac_params) {
|
||||
struct ieee802154_mac_params params;
|
||||
|
||||
rtnl_lock();
|
||||
ops->get_mac_params(dev, ¶ms);
|
||||
rtnl_unlock();
|
||||
|
||||
if (nla_put_s8(msg, IEEE802154_ATTR_TXPOWER,
|
||||
params.transmit_power) ||
|
||||
|
@ -164,7 +166,10 @@ static struct net_device *ieee802154_nl_get_dev(struct genl_info *info)
|
|||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
if (dev->type != ARPHRD_IEEE802154) {
|
||||
/* Check on mtu is currently a hacked solution because lowpan
|
||||
* and wpan have the same ARPHRD type.
|
||||
*/
|
||||
if (dev->type != ARPHRD_IEEE802154 || dev->mtu != IEEE802154_MTU) {
|
||||
dev_put(dev);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -348,8 +353,10 @@ int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
rtnl_lock();
|
||||
ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page,
|
||||
bcn_ord, sf_ord, pan_coord, blx, coord_realign);
|
||||
rtnl_unlock();
|
||||
|
||||
/* FIXME: add validation for unused parameters to be sane
|
||||
* for SoftMAC
|
||||
|
@ -444,7 +451,11 @@ int ieee802154_dump_iface(struct sk_buff *skb, struct netlink_callback *cb)
|
|||
|
||||
idx = 0;
|
||||
for_each_netdev(net, dev) {
|
||||
if (idx < s_idx || (dev->type != ARPHRD_IEEE802154))
|
||||
/* Check on mtu is currently a hacked solution because lowpan
|
||||
* and wpan have the same ARPHRD type.
|
||||
*/
|
||||
if (idx < s_idx || dev->type != ARPHRD_IEEE802154 ||
|
||||
dev->mtu != IEEE802154_MTU)
|
||||
goto cont;
|
||||
|
||||
if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).portid,
|
||||
|
@ -497,6 +508,7 @@ int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info)
|
|||
phy = dev->ieee802154_ptr->wpan_phy;
|
||||
get_device(&phy->dev);
|
||||
|
||||
rtnl_lock();
|
||||
ops->get_mac_params(dev, ¶ms);
|
||||
|
||||
if (info->attrs[IEEE802154_ATTR_TXPOWER])
|
||||
|
@ -524,6 +536,7 @@ int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info)
|
|||
params.frame_retries = nla_get_s8(info->attrs[IEEE802154_ATTR_FRAME_RETRIES]);
|
||||
|
||||
rc = ops->set_mac_params(dev, ¶ms);
|
||||
rtnl_unlock();
|
||||
|
||||
wpan_phy_put(phy);
|
||||
dev_put(dev);
|
||||
|
@ -776,7 +789,11 @@ ieee802154_llsec_dump_table(struct sk_buff *skb, struct netlink_callback *cb,
|
|||
int rc;
|
||||
|
||||
for_each_netdev(net, dev) {
|
||||
if (idx < first_dev || dev->type != ARPHRD_IEEE802154)
|
||||
/* Check on mtu is currently a hacked solution because lowpan
|
||||
* and wpan have the same ARPHRD type.
|
||||
*/
|
||||
if (idx < first_dev || dev->type != ARPHRD_IEEE802154 ||
|
||||
dev->mtu != IEEE802154_MTU)
|
||||
goto skip;
|
||||
|
||||
data.ops = ieee802154_mlme_ops(dev);
|
||||
|
|
|
@ -0,0 +1,957 @@
|
|||
/* 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.
|
||||
*
|
||||
* Authors:
|
||||
* Alexander Aring <aar@pengutronix.de>
|
||||
*
|
||||
* Based on: net/wireless/nl80211.c
|
||||
*/
|
||||
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
#include <net/cfg802154.h>
|
||||
#include <net/genetlink.h>
|
||||
#include <net/mac802154.h>
|
||||
#include <net/netlink.h>
|
||||
#include <net/nl802154.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
#include "nl802154.h"
|
||||
#include "rdev-ops.h"
|
||||
#include "core.h"
|
||||
|
||||
static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
|
||||
static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
|
||||
/* the netlink family */
|
||||
static struct genl_family nl802154_fam = {
|
||||
.id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
|
||||
.name = NL802154_GENL_NAME, /* have users key off the name instead */
|
||||
.hdrsize = 0, /* no private header */
|
||||
.version = 1, /* no particular meaning now */
|
||||
.maxattr = NL802154_ATTR_MAX,
|
||||
.netnsok = true,
|
||||
.pre_doit = nl802154_pre_doit,
|
||||
.post_doit = nl802154_post_doit,
|
||||
};
|
||||
|
||||
/* multicast groups */
|
||||
enum nl802154_multicast_groups {
|
||||
NL802154_MCGRP_CONFIG,
|
||||
};
|
||||
|
||||
static const struct genl_multicast_group nl802154_mcgrps[] = {
|
||||
[NL802154_MCGRP_CONFIG] = { .name = "config", },
|
||||
};
|
||||
|
||||
/* returns ERR_PTR values */
|
||||
static struct wpan_dev *
|
||||
__cfg802154_wpan_dev_from_attrs(struct net *netns, struct nlattr **attrs)
|
||||
{
|
||||
struct cfg802154_registered_device *rdev;
|
||||
struct wpan_dev *result = NULL;
|
||||
bool have_ifidx = attrs[NL802154_ATTR_IFINDEX];
|
||||
bool have_wpan_dev_id = attrs[NL802154_ATTR_WPAN_DEV];
|
||||
u64 wpan_dev_id;
|
||||
int wpan_phy_idx = -1;
|
||||
int ifidx = -1;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
if (!have_ifidx && !have_wpan_dev_id)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (have_ifidx)
|
||||
ifidx = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]);
|
||||
if (have_wpan_dev_id) {
|
||||
wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]);
|
||||
wpan_phy_idx = wpan_dev_id >> 32;
|
||||
}
|
||||
|
||||
list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
|
||||
struct wpan_dev *wpan_dev;
|
||||
|
||||
/* TODO netns compare */
|
||||
|
||||
if (have_wpan_dev_id && rdev->wpan_phy_idx != wpan_phy_idx)
|
||||
continue;
|
||||
|
||||
list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
|
||||
if (have_ifidx && wpan_dev->netdev &&
|
||||
wpan_dev->netdev->ifindex == ifidx) {
|
||||
result = wpan_dev;
|
||||
break;
|
||||
}
|
||||
if (have_wpan_dev_id &&
|
||||
wpan_dev->identifier == (u32)wpan_dev_id) {
|
||||
result = wpan_dev;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result)
|
||||
break;
|
||||
}
|
||||
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
static struct cfg802154_registered_device *
|
||||
__cfg802154_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
|
||||
{
|
||||
struct cfg802154_registered_device *rdev = NULL, *tmp;
|
||||
struct net_device *netdev;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
if (!attrs[NL802154_ATTR_WPAN_PHY] &&
|
||||
!attrs[NL802154_ATTR_IFINDEX] &&
|
||||
!attrs[NL802154_ATTR_WPAN_DEV])
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (attrs[NL802154_ATTR_WPAN_PHY])
|
||||
rdev = cfg802154_rdev_by_wpan_phy_idx(
|
||||
nla_get_u32(attrs[NL802154_ATTR_WPAN_PHY]));
|
||||
|
||||
if (attrs[NL802154_ATTR_WPAN_DEV]) {
|
||||
u64 wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]);
|
||||
struct wpan_dev *wpan_dev;
|
||||
bool found = false;
|
||||
|
||||
tmp = cfg802154_rdev_by_wpan_phy_idx(wpan_dev_id >> 32);
|
||||
if (tmp) {
|
||||
/* make sure wpan_dev exists */
|
||||
list_for_each_entry(wpan_dev, &tmp->wpan_dev_list, list) {
|
||||
if (wpan_dev->identifier != (u32)wpan_dev_id)
|
||||
continue;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
tmp = NULL;
|
||||
|
||||
if (rdev && tmp != rdev)
|
||||
return ERR_PTR(-EINVAL);
|
||||
rdev = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
if (attrs[NL802154_ATTR_IFINDEX]) {
|
||||
int ifindex = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]);
|
||||
|
||||
netdev = __dev_get_by_index(netns, ifindex);
|
||||
if (netdev) {
|
||||
if (netdev->ieee802154_ptr)
|
||||
tmp = wpan_phy_to_rdev(
|
||||
netdev->ieee802154_ptr->wpan_phy);
|
||||
else
|
||||
tmp = NULL;
|
||||
|
||||
/* not wireless device -- return error */
|
||||
if (!tmp)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
/* mismatch -- return error */
|
||||
if (rdev && tmp != rdev)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
rdev = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rdev)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
/* TODO netns compare */
|
||||
|
||||
return rdev;
|
||||
}
|
||||
|
||||
/* This function returns a pointer to the driver
|
||||
* that the genl_info item that is passed refers to.
|
||||
*
|
||||
* The result of this can be a PTR_ERR and hence must
|
||||
* be checked with IS_ERR() for errors.
|
||||
*/
|
||||
static struct cfg802154_registered_device *
|
||||
cfg802154_get_dev_from_info(struct net *netns, struct genl_info *info)
|
||||
{
|
||||
return __cfg802154_rdev_from_attrs(netns, info->attrs);
|
||||
}
|
||||
|
||||
/* policy for the attributes */
|
||||
static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = {
|
||||
[NL802154_ATTR_WPAN_PHY] = { .type = NLA_U32 },
|
||||
[NL802154_ATTR_WPAN_PHY_NAME] = { .type = NLA_NUL_STRING,
|
||||
.len = 20-1 },
|
||||
|
||||
[NL802154_ATTR_IFINDEX] = { .type = NLA_U32 },
|
||||
[NL802154_ATTR_IFTYPE] = { .type = NLA_U32 },
|
||||
[NL802154_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
|
||||
|
||||
[NL802154_ATTR_WPAN_DEV] = { .type = NLA_U64 },
|
||||
|
||||
[NL802154_ATTR_PAGE] = { .type = NLA_U8, },
|
||||
[NL802154_ATTR_CHANNEL] = { .type = NLA_U8, },
|
||||
|
||||
[NL802154_ATTR_TX_POWER] = { .type = NLA_S8, },
|
||||
|
||||
[NL802154_ATTR_CCA_MODE] = { .type = NLA_U8, },
|
||||
|
||||
[NL802154_ATTR_SUPPORTED_CHANNEL] = { .type = NLA_U32, },
|
||||
|
||||
[NL802154_ATTR_PAN_ID] = { .type = NLA_U16, },
|
||||
[NL802154_ATTR_EXTENDED_ADDR] = { .type = NLA_U64 },
|
||||
[NL802154_ATTR_SHORT_ADDR] = { .type = NLA_U16, },
|
||||
|
||||
[NL802154_ATTR_MIN_BE] = { .type = NLA_U8, },
|
||||
[NL802154_ATTR_MAX_BE] = { .type = NLA_U8, },
|
||||
[NL802154_ATTR_MAX_CSMA_BACKOFFS] = { .type = NLA_U8, },
|
||||
|
||||
[NL802154_ATTR_MAX_FRAME_RETRIES] = { .type = NLA_S8, },
|
||||
|
||||
[NL802154_ATTR_LBT_MODE] = { .type = NLA_U8, },
|
||||
};
|
||||
|
||||
/* message building helper */
|
||||
static inline void *nl802154hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
|
||||
int flags, u8 cmd)
|
||||
{
|
||||
/* since there is no private header just add the generic one */
|
||||
return genlmsg_put(skb, portid, seq, &nl802154_fam, flags, cmd);
|
||||
}
|
||||
|
||||
static int
|
||||
nl802154_send_wpan_phy_channels(struct cfg802154_registered_device *rdev,
|
||||
struct sk_buff *msg)
|
||||
{
|
||||
struct nlattr *nl_page;
|
||||
unsigned long page;
|
||||
|
||||
nl_page = nla_nest_start(msg, NL802154_ATTR_CHANNELS_SUPPORTED);
|
||||
if (!nl_page)
|
||||
return -ENOBUFS;
|
||||
|
||||
for (page = 0; page <= IEEE802154_MAX_PAGE; page++) {
|
||||
if (nla_put_u32(msg, NL802154_ATTR_SUPPORTED_CHANNEL,
|
||||
rdev->wpan_phy.channels_supported[page]))
|
||||
return -ENOBUFS;
|
||||
}
|
||||
nla_nest_end(msg, nl_page);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nl802154_send_wpan_phy(struct cfg802154_registered_device *rdev,
|
||||
enum nl802154_commands cmd,
|
||||
struct sk_buff *msg, u32 portid, u32 seq,
|
||||
int flags)
|
||||
{
|
||||
void *hdr;
|
||||
|
||||
hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
|
||||
if (!hdr)
|
||||
return -ENOBUFS;
|
||||
|
||||
if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) ||
|
||||
nla_put_string(msg, NL802154_ATTR_WPAN_PHY_NAME,
|
||||
wpan_phy_name(&rdev->wpan_phy)) ||
|
||||
nla_put_u32(msg, NL802154_ATTR_GENERATION,
|
||||
cfg802154_rdev_list_generation))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (cmd != NL802154_CMD_NEW_WPAN_PHY)
|
||||
goto finish;
|
||||
|
||||
/* DUMP PHY PIB */
|
||||
|
||||
/* current channel settings */
|
||||
if (nla_put_u8(msg, NL802154_ATTR_PAGE,
|
||||
rdev->wpan_phy.current_page) ||
|
||||
nla_put_u8(msg, NL802154_ATTR_CHANNEL,
|
||||
rdev->wpan_phy.current_channel))
|
||||
goto nla_put_failure;
|
||||
|
||||
/* supported channels array */
|
||||
if (nl802154_send_wpan_phy_channels(rdev, msg))
|
||||
goto nla_put_failure;
|
||||
|
||||
/* cca mode */
|
||||
if (nla_put_u8(msg, NL802154_ATTR_CCA_MODE,
|
||||
rdev->wpan_phy.cca_mode))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (nla_put_s8(msg, NL802154_ATTR_TX_POWER,
|
||||
rdev->wpan_phy.transmit_power))
|
||||
goto nla_put_failure;
|
||||
|
||||
finish:
|
||||
return genlmsg_end(msg, hdr);
|
||||
|
||||
nla_put_failure:
|
||||
genlmsg_cancel(msg, hdr);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
struct nl802154_dump_wpan_phy_state {
|
||||
s64 filter_wpan_phy;
|
||||
long start;
|
||||
|
||||
};
|
||||
|
||||
static int nl802154_dump_wpan_phy_parse(struct sk_buff *skb,
|
||||
struct netlink_callback *cb,
|
||||
struct nl802154_dump_wpan_phy_state *state)
|
||||
{
|
||||
struct nlattr **tb = nl802154_fam.attrbuf;
|
||||
int ret = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl802154_fam.hdrsize,
|
||||
tb, nl802154_fam.maxattr, nl802154_policy);
|
||||
|
||||
/* TODO check if we can handle error here,
|
||||
* we have no backward compatibility
|
||||
*/
|
||||
if (ret)
|
||||
return 0;
|
||||
|
||||
if (tb[NL802154_ATTR_WPAN_PHY])
|
||||
state->filter_wpan_phy = nla_get_u32(tb[NL802154_ATTR_WPAN_PHY]);
|
||||
if (tb[NL802154_ATTR_WPAN_DEV])
|
||||
state->filter_wpan_phy = nla_get_u64(tb[NL802154_ATTR_WPAN_DEV]) >> 32;
|
||||
if (tb[NL802154_ATTR_IFINDEX]) {
|
||||
struct net_device *netdev;
|
||||
struct cfg802154_registered_device *rdev;
|
||||
int ifidx = nla_get_u32(tb[NL802154_ATTR_IFINDEX]);
|
||||
|
||||
/* TODO netns */
|
||||
netdev = __dev_get_by_index(&init_net, ifidx);
|
||||
if (!netdev)
|
||||
return -ENODEV;
|
||||
if (netdev->ieee802154_ptr) {
|
||||
rdev = wpan_phy_to_rdev(
|
||||
netdev->ieee802154_ptr->wpan_phy);
|
||||
state->filter_wpan_phy = rdev->wpan_phy_idx;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nl802154_dump_wpan_phy(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
{
|
||||
int idx = 0, ret;
|
||||
struct nl802154_dump_wpan_phy_state *state = (void *)cb->args[0];
|
||||
struct cfg802154_registered_device *rdev;
|
||||
|
||||
rtnl_lock();
|
||||
if (!state) {
|
||||
state = kzalloc(sizeof(*state), GFP_KERNEL);
|
||||
if (!state) {
|
||||
rtnl_unlock();
|
||||
return -ENOMEM;
|
||||
}
|
||||
state->filter_wpan_phy = -1;
|
||||
ret = nl802154_dump_wpan_phy_parse(skb, cb, state);
|
||||
if (ret) {
|
||||
kfree(state);
|
||||
rtnl_unlock();
|
||||
return ret;
|
||||
}
|
||||
cb->args[0] = (long)state;
|
||||
}
|
||||
|
||||
list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
|
||||
/* TODO net ns compare */
|
||||
if (++idx <= state->start)
|
||||
continue;
|
||||
if (state->filter_wpan_phy != -1 &&
|
||||
state->filter_wpan_phy != rdev->wpan_phy_idx)
|
||||
continue;
|
||||
/* attempt to fit multiple wpan_phy data chunks into the skb */
|
||||
ret = nl802154_send_wpan_phy(rdev,
|
||||
NL802154_CMD_NEW_WPAN_PHY,
|
||||
skb,
|
||||
NETLINK_CB(cb->skb).portid,
|
||||
cb->nlh->nlmsg_seq, NLM_F_MULTI);
|
||||
if (ret < 0) {
|
||||
if ((ret == -ENOBUFS || ret == -EMSGSIZE) &&
|
||||
!skb->len && cb->min_dump_alloc < 4096) {
|
||||
cb->min_dump_alloc = 4096;
|
||||
rtnl_unlock();
|
||||
return 1;
|
||||
}
|
||||
idx--;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
rtnl_unlock();
|
||||
|
||||
state->start = idx;
|
||||
|
||||
return skb->len;
|
||||
}
|
||||
|
||||
static int nl802154_dump_wpan_phy_done(struct netlink_callback *cb)
|
||||
{
|
||||
kfree((void *)cb->args[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nl802154_get_wpan_phy(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct sk_buff *msg;
|
||||
struct cfg802154_registered_device *rdev = info->user_ptr[0];
|
||||
|
||||
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
if (nl802154_send_wpan_phy(rdev, NL802154_CMD_NEW_WPAN_PHY, msg,
|
||||
info->snd_portid, info->snd_seq, 0) < 0) {
|
||||
nlmsg_free(msg);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
return genlmsg_reply(msg, info);
|
||||
}
|
||||
|
||||
static inline u64 wpan_dev_id(struct wpan_dev *wpan_dev)
|
||||
{
|
||||
return (u64)wpan_dev->identifier |
|
||||
((u64)wpan_phy_to_rdev(wpan_dev->wpan_phy)->wpan_phy_idx << 32);
|
||||
}
|
||||
|
||||
static int
|
||||
nl802154_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
|
||||
struct cfg802154_registered_device *rdev,
|
||||
struct wpan_dev *wpan_dev)
|
||||
{
|
||||
struct net_device *dev = wpan_dev->netdev;
|
||||
void *hdr;
|
||||
|
||||
hdr = nl802154hdr_put(msg, portid, seq, flags,
|
||||
NL802154_CMD_NEW_INTERFACE);
|
||||
if (!hdr)
|
||||
return -1;
|
||||
|
||||
if (dev &&
|
||||
(nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex) ||
|
||||
nla_put_string(msg, NL802154_ATTR_IFNAME, dev->name)))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) ||
|
||||
nla_put_u32(msg, NL802154_ATTR_IFTYPE, wpan_dev->iftype) ||
|
||||
nla_put_u64(msg, NL802154_ATTR_WPAN_DEV, wpan_dev_id(wpan_dev)) ||
|
||||
nla_put_u32(msg, NL802154_ATTR_GENERATION,
|
||||
rdev->devlist_generation ^
|
||||
(cfg802154_rdev_list_generation << 2)))
|
||||
goto nla_put_failure;
|
||||
|
||||
/* address settings */
|
||||
if (nla_put_le64(msg, NL802154_ATTR_EXTENDED_ADDR,
|
||||
wpan_dev->extended_addr) ||
|
||||
nla_put_le16(msg, NL802154_ATTR_SHORT_ADDR,
|
||||
wpan_dev->short_addr) ||
|
||||
nla_put_le16(msg, NL802154_ATTR_PAN_ID, wpan_dev->pan_id))
|
||||
goto nla_put_failure;
|
||||
|
||||
/* ARET handling */
|
||||
if (nla_put_s8(msg, NL802154_ATTR_MAX_FRAME_RETRIES,
|
||||
wpan_dev->frame_retries) ||
|
||||
nla_put_u8(msg, NL802154_ATTR_MAX_BE, wpan_dev->max_be) ||
|
||||
nla_put_u8(msg, NL802154_ATTR_MAX_CSMA_BACKOFFS,
|
||||
wpan_dev->csma_retries) ||
|
||||
nla_put_u8(msg, NL802154_ATTR_MIN_BE, wpan_dev->min_be))
|
||||
goto nla_put_failure;
|
||||
|
||||
/* listen before transmit */
|
||||
if (nla_put_u8(msg, NL802154_ATTR_LBT_MODE, wpan_dev->lbt))
|
||||
goto nla_put_failure;
|
||||
|
||||
return genlmsg_end(msg, hdr);
|
||||
|
||||
nla_put_failure:
|
||||
genlmsg_cancel(msg, hdr);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
static int
|
||||
nl802154_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
{
|
||||
int wp_idx = 0;
|
||||
int if_idx = 0;
|
||||
int wp_start = cb->args[0];
|
||||
int if_start = cb->args[1];
|
||||
struct cfg802154_registered_device *rdev;
|
||||
struct wpan_dev *wpan_dev;
|
||||
|
||||
rtnl_lock();
|
||||
list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
|
||||
/* TODO netns compare */
|
||||
if (wp_idx < wp_start) {
|
||||
wp_idx++;
|
||||
continue;
|
||||
}
|
||||
if_idx = 0;
|
||||
|
||||
list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
|
||||
if (if_idx < if_start) {
|
||||
if_idx++;
|
||||
continue;
|
||||
}
|
||||
if (nl802154_send_iface(skb, NETLINK_CB(cb->skb).portid,
|
||||
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
||||
rdev, wpan_dev) < 0) {
|
||||
goto out;
|
||||
}
|
||||
if_idx++;
|
||||
}
|
||||
|
||||
wp_idx++;
|
||||
}
|
||||
out:
|
||||
rtnl_unlock();
|
||||
|
||||
cb->args[0] = wp_idx;
|
||||
cb->args[1] = if_idx;
|
||||
|
||||
return skb->len;
|
||||
}
|
||||
|
||||
static int nl802154_get_interface(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct sk_buff *msg;
|
||||
struct cfg802154_registered_device *rdev = info->user_ptr[0];
|
||||
struct wpan_dev *wdev = info->user_ptr[1];
|
||||
|
||||
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
if (nl802154_send_iface(msg, info->snd_portid, info->snd_seq, 0,
|
||||
rdev, wdev) < 0) {
|
||||
nlmsg_free(msg);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
return genlmsg_reply(msg, info);
|
||||
}
|
||||
|
||||
static int nl802154_new_interface(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg802154_registered_device *rdev = info->user_ptr[0];
|
||||
enum nl802154_iftype type = NL802154_IFTYPE_UNSPEC;
|
||||
__le64 extended_addr = cpu_to_le64(0x0000000000000000ULL);
|
||||
|
||||
/* TODO avoid failing a new interface
|
||||
* creation due to pending removal?
|
||||
*/
|
||||
|
||||
if (!info->attrs[NL802154_ATTR_IFNAME])
|
||||
return -EINVAL;
|
||||
|
||||
if (info->attrs[NL802154_ATTR_IFTYPE]) {
|
||||
type = nla_get_u32(info->attrs[NL802154_ATTR_IFTYPE]);
|
||||
if (type > NL802154_IFTYPE_MAX)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* TODO add nla_get_le64 to netlink */
|
||||
if (info->attrs[NL802154_ATTR_EXTENDED_ADDR])
|
||||
extended_addr = (__force __le64)nla_get_u64(
|
||||
info->attrs[NL802154_ATTR_EXTENDED_ADDR]);
|
||||
|
||||
if (!rdev->ops->add_virtual_intf)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return rdev_add_virtual_intf(rdev,
|
||||
nla_data(info->attrs[NL802154_ATTR_IFNAME]),
|
||||
type, extended_addr);
|
||||
}
|
||||
|
||||
static int nl802154_del_interface(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg802154_registered_device *rdev = info->user_ptr[0];
|
||||
struct wpan_dev *wpan_dev = info->user_ptr[1];
|
||||
|
||||
if (!rdev->ops->del_virtual_intf)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* If we remove a wpan device without a netdev then clear
|
||||
* user_ptr[1] so that nl802154_post_doit won't dereference it
|
||||
* to check if it needs to do dev_put(). Otherwise it crashes
|
||||
* since the wpan_dev has been freed, unlike with a netdev where
|
||||
* we need the dev_put() for the netdev to really be freed.
|
||||
*/
|
||||
if (!wpan_dev->netdev)
|
||||
info->user_ptr[1] = NULL;
|
||||
|
||||
return rdev_del_virtual_intf(rdev, wpan_dev);
|
||||
}
|
||||
|
||||
static int nl802154_set_channel(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg802154_registered_device *rdev = info->user_ptr[0];
|
||||
u8 channel, page;
|
||||
|
||||
if (!info->attrs[NL802154_ATTR_PAGE] ||
|
||||
!info->attrs[NL802154_ATTR_CHANNEL])
|
||||
return -EINVAL;
|
||||
|
||||
page = nla_get_u8(info->attrs[NL802154_ATTR_PAGE]);
|
||||
channel = nla_get_u8(info->attrs[NL802154_ATTR_CHANNEL]);
|
||||
|
||||
/* check 802.15.4 constraints */
|
||||
if (page > IEEE802154_MAX_PAGE || channel > IEEE802154_MAX_CHANNEL)
|
||||
return -EINVAL;
|
||||
|
||||
return rdev_set_channel(rdev, page, channel);
|
||||
}
|
||||
|
||||
static int nl802154_set_pan_id(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg802154_registered_device *rdev = info->user_ptr[0];
|
||||
struct net_device *dev = info->user_ptr[1];
|
||||
struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
|
||||
__le16 pan_id;
|
||||
|
||||
/* conflict here while tx/rx calls */
|
||||
if (netif_running(dev))
|
||||
return -EBUSY;
|
||||
|
||||
/* don't change address fields on monitor */
|
||||
if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
|
||||
return -EINVAL;
|
||||
|
||||
if (!info->attrs[NL802154_ATTR_PAN_ID])
|
||||
return -EINVAL;
|
||||
|
||||
pan_id = nla_get_le16(info->attrs[NL802154_ATTR_PAN_ID]);
|
||||
|
||||
return rdev_set_pan_id(rdev, wpan_dev, pan_id);
|
||||
}
|
||||
|
||||
static int nl802154_set_short_addr(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg802154_registered_device *rdev = info->user_ptr[0];
|
||||
struct net_device *dev = info->user_ptr[1];
|
||||
struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
|
||||
__le16 short_addr;
|
||||
|
||||
/* conflict here while tx/rx calls */
|
||||
if (netif_running(dev))
|
||||
return -EBUSY;
|
||||
|
||||
/* don't change address fields on monitor */
|
||||
if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
|
||||
return -EINVAL;
|
||||
|
||||
if (!info->attrs[NL802154_ATTR_SHORT_ADDR])
|
||||
return -EINVAL;
|
||||
|
||||
short_addr = nla_get_le16(info->attrs[NL802154_ATTR_SHORT_ADDR]);
|
||||
|
||||
return rdev_set_short_addr(rdev, wpan_dev, short_addr);
|
||||
}
|
||||
|
||||
static int
|
||||
nl802154_set_backoff_exponent(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg802154_registered_device *rdev = info->user_ptr[0];
|
||||
struct net_device *dev = info->user_ptr[1];
|
||||
struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
|
||||
u8 min_be, max_be;
|
||||
|
||||
/* should be set on netif open inside phy settings */
|
||||
if (netif_running(dev))
|
||||
return -EBUSY;
|
||||
|
||||
if (!info->attrs[NL802154_ATTR_MIN_BE] ||
|
||||
!info->attrs[NL802154_ATTR_MAX_BE])
|
||||
return -EINVAL;
|
||||
|
||||
min_be = nla_get_u8(info->attrs[NL802154_ATTR_MIN_BE]);
|
||||
max_be = nla_get_u8(info->attrs[NL802154_ATTR_MAX_BE]);
|
||||
|
||||
/* check 802.15.4 constraints */
|
||||
if (max_be < 3 || max_be > 8 || min_be > max_be)
|
||||
return -EINVAL;
|
||||
|
||||
return rdev_set_backoff_exponent(rdev, wpan_dev, min_be, max_be);
|
||||
}
|
||||
|
||||
static int
|
||||
nl802154_set_max_csma_backoffs(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg802154_registered_device *rdev = info->user_ptr[0];
|
||||
struct net_device *dev = info->user_ptr[1];
|
||||
struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
|
||||
u8 max_csma_backoffs;
|
||||
|
||||
/* conflict here while other running iface settings */
|
||||
if (netif_running(dev))
|
||||
return -EBUSY;
|
||||
|
||||
if (!info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS])
|
||||
return -EINVAL;
|
||||
|
||||
max_csma_backoffs = nla_get_u8(
|
||||
info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS]);
|
||||
|
||||
/* check 802.15.4 constraints */
|
||||
if (max_csma_backoffs > 5)
|
||||
return -EINVAL;
|
||||
|
||||
return rdev_set_max_csma_backoffs(rdev, wpan_dev, max_csma_backoffs);
|
||||
}
|
||||
|
||||
static int
|
||||
nl802154_set_max_frame_retries(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg802154_registered_device *rdev = info->user_ptr[0];
|
||||
struct net_device *dev = info->user_ptr[1];
|
||||
struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
|
||||
s8 max_frame_retries;
|
||||
|
||||
if (netif_running(dev))
|
||||
return -EBUSY;
|
||||
|
||||
if (!info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES])
|
||||
return -EINVAL;
|
||||
|
||||
max_frame_retries = nla_get_s8(
|
||||
info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES]);
|
||||
|
||||
/* check 802.15.4 constraints */
|
||||
if (max_frame_retries < -1 || max_frame_retries > 7)
|
||||
return -EINVAL;
|
||||
|
||||
return rdev_set_max_frame_retries(rdev, wpan_dev, max_frame_retries);
|
||||
}
|
||||
|
||||
static int nl802154_set_lbt_mode(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg802154_registered_device *rdev = info->user_ptr[0];
|
||||
struct net_device *dev = info->user_ptr[1];
|
||||
struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
|
||||
bool mode;
|
||||
|
||||
if (netif_running(dev))
|
||||
return -EBUSY;
|
||||
|
||||
if (!info->attrs[NL802154_ATTR_LBT_MODE])
|
||||
return -EINVAL;
|
||||
|
||||
mode = !!nla_get_u8(info->attrs[NL802154_ATTR_LBT_MODE]);
|
||||
return rdev_set_lbt_mode(rdev, wpan_dev, mode);
|
||||
}
|
||||
|
||||
#define NL802154_FLAG_NEED_WPAN_PHY 0x01
|
||||
#define NL802154_FLAG_NEED_NETDEV 0x02
|
||||
#define NL802154_FLAG_NEED_RTNL 0x04
|
||||
#define NL802154_FLAG_CHECK_NETDEV_UP 0x08
|
||||
#define NL802154_FLAG_NEED_NETDEV_UP (NL802154_FLAG_NEED_NETDEV |\
|
||||
NL802154_FLAG_CHECK_NETDEV_UP)
|
||||
#define NL802154_FLAG_NEED_WPAN_DEV 0x10
|
||||
#define NL802154_FLAG_NEED_WPAN_DEV_UP (NL802154_FLAG_NEED_WPAN_DEV |\
|
||||
NL802154_FLAG_CHECK_NETDEV_UP)
|
||||
|
||||
static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct cfg802154_registered_device *rdev;
|
||||
struct wpan_dev *wpan_dev;
|
||||
struct net_device *dev;
|
||||
bool rtnl = ops->internal_flags & NL802154_FLAG_NEED_RTNL;
|
||||
|
||||
if (rtnl)
|
||||
rtnl_lock();
|
||||
|
||||
if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_PHY) {
|
||||
rdev = cfg802154_get_dev_from_info(genl_info_net(info), info);
|
||||
if (IS_ERR(rdev)) {
|
||||
if (rtnl)
|
||||
rtnl_unlock();
|
||||
return PTR_ERR(rdev);
|
||||
}
|
||||
info->user_ptr[0] = rdev;
|
||||
} else if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV ||
|
||||
ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
|
||||
ASSERT_RTNL();
|
||||
wpan_dev = __cfg802154_wpan_dev_from_attrs(genl_info_net(info),
|
||||
info->attrs);
|
||||
if (IS_ERR(wpan_dev)) {
|
||||
if (rtnl)
|
||||
rtnl_unlock();
|
||||
return PTR_ERR(wpan_dev);
|
||||
}
|
||||
|
||||
dev = wpan_dev->netdev;
|
||||
rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
|
||||
|
||||
if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV) {
|
||||
if (!dev) {
|
||||
if (rtnl)
|
||||
rtnl_unlock();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
info->user_ptr[1] = dev;
|
||||
} else {
|
||||
info->user_ptr[1] = wpan_dev;
|
||||
}
|
||||
|
||||
if (dev) {
|
||||
if (ops->internal_flags & NL802154_FLAG_CHECK_NETDEV_UP &&
|
||||
!netif_running(dev)) {
|
||||
if (rtnl)
|
||||
rtnl_unlock();
|
||||
return -ENETDOWN;
|
||||
}
|
||||
|
||||
dev_hold(dev);
|
||||
}
|
||||
|
||||
info->user_ptr[0] = rdev;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
if (info->user_ptr[1]) {
|
||||
if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
|
||||
struct wpan_dev *wpan_dev = info->user_ptr[1];
|
||||
|
||||
if (wpan_dev->netdev)
|
||||
dev_put(wpan_dev->netdev);
|
||||
} else {
|
||||
dev_put(info->user_ptr[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (ops->internal_flags & NL802154_FLAG_NEED_RTNL)
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
static const struct genl_ops nl802154_ops[] = {
|
||||
{
|
||||
.cmd = NL802154_CMD_GET_WPAN_PHY,
|
||||
.doit = nl802154_get_wpan_phy,
|
||||
.dumpit = nl802154_dump_wpan_phy,
|
||||
.done = nl802154_dump_wpan_phy_done,
|
||||
.policy = nl802154_policy,
|
||||
/* can be retrieved by unprivileged users */
|
||||
.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
|
||||
NL802154_FLAG_NEED_RTNL,
|
||||
},
|
||||
{
|
||||
.cmd = NL802154_CMD_GET_INTERFACE,
|
||||
.doit = nl802154_get_interface,
|
||||
.dumpit = nl802154_dump_interface,
|
||||
.policy = nl802154_policy,
|
||||
/* can be retrieved by unprivileged users */
|
||||
.internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
|
||||
NL802154_FLAG_NEED_RTNL,
|
||||
},
|
||||
{
|
||||
.cmd = NL802154_CMD_NEW_INTERFACE,
|
||||
.doit = nl802154_new_interface,
|
||||
.policy = nl802154_policy,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
|
||||
NL802154_FLAG_NEED_RTNL,
|
||||
},
|
||||
{
|
||||
.cmd = NL802154_CMD_DEL_INTERFACE,
|
||||
.doit = nl802154_del_interface,
|
||||
.policy = nl802154_policy,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
|
||||
NL802154_FLAG_NEED_RTNL,
|
||||
},
|
||||
{
|
||||
.cmd = NL802154_CMD_SET_CHANNEL,
|
||||
.doit = nl802154_set_channel,
|
||||
.policy = nl802154_policy,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
|
||||
NL802154_FLAG_NEED_RTNL,
|
||||
},
|
||||
{
|
||||
.cmd = NL802154_CMD_SET_PAN_ID,
|
||||
.doit = nl802154_set_pan_id,
|
||||
.policy = nl802154_policy,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = NL802154_FLAG_NEED_NETDEV |
|
||||
NL802154_FLAG_NEED_RTNL,
|
||||
},
|
||||
{
|
||||
.cmd = NL802154_CMD_SET_SHORT_ADDR,
|
||||
.doit = nl802154_set_short_addr,
|
||||
.policy = nl802154_policy,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = NL802154_FLAG_NEED_NETDEV |
|
||||
NL802154_FLAG_NEED_RTNL,
|
||||
},
|
||||
{
|
||||
.cmd = NL802154_CMD_SET_BACKOFF_EXPONENT,
|
||||
.doit = nl802154_set_backoff_exponent,
|
||||
.policy = nl802154_policy,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = NL802154_FLAG_NEED_NETDEV |
|
||||
NL802154_FLAG_NEED_RTNL,
|
||||
},
|
||||
{
|
||||
.cmd = NL802154_CMD_SET_MAX_CSMA_BACKOFFS,
|
||||
.doit = nl802154_set_max_csma_backoffs,
|
||||
.policy = nl802154_policy,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = NL802154_FLAG_NEED_NETDEV |
|
||||
NL802154_FLAG_NEED_RTNL,
|
||||
},
|
||||
{
|
||||
.cmd = NL802154_CMD_SET_MAX_FRAME_RETRIES,
|
||||
.doit = nl802154_set_max_frame_retries,
|
||||
.policy = nl802154_policy,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = NL802154_FLAG_NEED_NETDEV |
|
||||
NL802154_FLAG_NEED_RTNL,
|
||||
},
|
||||
{
|
||||
.cmd = NL802154_CMD_SET_LBT_MODE,
|
||||
.doit = nl802154_set_lbt_mode,
|
||||
.policy = nl802154_policy,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = NL802154_FLAG_NEED_NETDEV |
|
||||
NL802154_FLAG_NEED_RTNL,
|
||||
},
|
||||
};
|
||||
|
||||
/* initialisation/exit functions */
|
||||
int nl802154_init(void)
|
||||
{
|
||||
return genl_register_family_with_ops_groups(&nl802154_fam, nl802154_ops,
|
||||
nl802154_mcgrps);
|
||||
}
|
||||
|
||||
void nl802154_exit(void)
|
||||
{
|
||||
genl_unregister_family(&nl802154_fam);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef __IEEE802154_NL802154_H
|
||||
#define __IEEE802154_NL802154_H
|
||||
|
||||
int nl802154_init(void);
|
||||
void nl802154_exit(void);
|
||||
|
||||
#endif /* __IEEE802154_NL802154_H */
|
|
@ -20,4 +20,70 @@ rdev_del_virtual_intf_deprecated(struct cfg802154_registered_device *rdev,
|
|||
rdev->ops->del_virtual_intf_deprecated(&rdev->wpan_phy, dev);
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_add_virtual_intf(struct cfg802154_registered_device *rdev, char *name,
|
||||
enum nl802154_iftype type, __le64 extended_addr)
|
||||
{
|
||||
return rdev->ops->add_virtual_intf(&rdev->wpan_phy, name, type,
|
||||
extended_addr);
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_del_virtual_intf(struct cfg802154_registered_device *rdev,
|
||||
struct wpan_dev *wpan_dev)
|
||||
{
|
||||
return rdev->ops->del_virtual_intf(&rdev->wpan_phy, wpan_dev);
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_set_channel(struct cfg802154_registered_device *rdev, u8 page, u8 channel)
|
||||
{
|
||||
return rdev->ops->set_channel(&rdev->wpan_phy, page, channel);
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_set_pan_id(struct cfg802154_registered_device *rdev,
|
||||
struct wpan_dev *wpan_dev, __le16 pan_id)
|
||||
{
|
||||
return rdev->ops->set_pan_id(&rdev->wpan_phy, wpan_dev, pan_id);
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_set_short_addr(struct cfg802154_registered_device *rdev,
|
||||
struct wpan_dev *wpan_dev, __le16 short_addr)
|
||||
{
|
||||
return rdev->ops->set_short_addr(&rdev->wpan_phy, wpan_dev, short_addr);
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_set_backoff_exponent(struct cfg802154_registered_device *rdev,
|
||||
struct wpan_dev *wpan_dev, u8 min_be, u8 max_be)
|
||||
{
|
||||
return rdev->ops->set_backoff_exponent(&rdev->wpan_phy, wpan_dev,
|
||||
min_be, max_be);
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_set_max_csma_backoffs(struct cfg802154_registered_device *rdev,
|
||||
struct wpan_dev *wpan_dev, u8 max_csma_backoffs)
|
||||
{
|
||||
return rdev->ops->set_max_csma_backoffs(&rdev->wpan_phy, wpan_dev,
|
||||
max_csma_backoffs);
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_set_max_frame_retries(struct cfg802154_registered_device *rdev,
|
||||
struct wpan_dev *wpan_dev, s8 max_frame_retries)
|
||||
{
|
||||
return rdev->ops->set_max_frame_retries(&rdev->wpan_phy, wpan_dev,
|
||||
max_frame_retries);
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_set_lbt_mode(struct cfg802154_registered_device *rdev,
|
||||
struct wpan_dev *wpan_dev, bool mode)
|
||||
{
|
||||
return rdev->ops->set_lbt_mode(&rdev->wpan_phy, wpan_dev, mode);
|
||||
}
|
||||
|
||||
#endif /* __CFG802154_RDEV_OPS */
|
||||
|
|
|
@ -27,6 +27,27 @@ dev_to_rdev(struct device *dev)
|
|||
wpan_phy.dev);
|
||||
}
|
||||
|
||||
#define SHOW_FMT(name, fmt, member) \
|
||||
static ssize_t name ## _show(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member); \
|
||||
} \
|
||||
static DEVICE_ATTR_RO(name)
|
||||
|
||||
SHOW_FMT(index, "%d", wpan_phy_idx);
|
||||
|
||||
static ssize_t name_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct wpan_phy *wpan_phy = &dev_to_rdev(dev)->wpan_phy;
|
||||
|
||||
return sprintf(buf, "%s\n", dev_name(&wpan_phy->dev));
|
||||
}
|
||||
static DEVICE_ATTR_RO(name);
|
||||
|
||||
#define MASTER_SHOW_COMPLEX(name, format_string, args...) \
|
||||
static ssize_t name ## _show(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf) \
|
||||
|
@ -78,6 +99,9 @@ static void wpan_phy_release(struct device *dev)
|
|||
}
|
||||
|
||||
static struct attribute *pmib_attrs[] = {
|
||||
&dev_attr_index.attr,
|
||||
&dev_attr_name.attr,
|
||||
/* below will be removed soon */
|
||||
&dev_attr_current_channel.attr,
|
||||
&dev_attr_current_page.attr,
|
||||
&dev_attr_channels_supported.attr,
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <net/cfg802154.h>
|
||||
|
||||
#include "ieee802154_i.h"
|
||||
#include "driver-ops.h"
|
||||
#include "cfg.h"
|
||||
|
||||
static struct net_device *
|
||||
|
@ -27,7 +28,8 @@ ieee802154_add_iface_deprecated(struct wpan_phy *wpan_phy,
|
|||
struct net_device *dev;
|
||||
|
||||
rtnl_lock();
|
||||
dev = ieee802154_if_add(local, name, NULL, type);
|
||||
dev = ieee802154_if_add(local, name, type,
|
||||
cpu_to_le64(0x0000000000000000ULL));
|
||||
rtnl_unlock();
|
||||
|
||||
return dev;
|
||||
|
@ -41,7 +43,168 @@ static void ieee802154_del_iface_deprecated(struct wpan_phy *wpan_phy,
|
|||
ieee802154_if_remove(sdata);
|
||||
}
|
||||
|
||||
static int
|
||||
ieee802154_add_iface(struct wpan_phy *phy, const char *name,
|
||||
enum nl802154_iftype type, __le64 extended_addr)
|
||||
{
|
||||
struct ieee802154_local *local = wpan_phy_priv(phy);
|
||||
struct net_device *err;
|
||||
|
||||
err = ieee802154_if_add(local, name, type, extended_addr);
|
||||
if (IS_ERR(err))
|
||||
return PTR_ERR(err);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ieee802154_del_iface(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev)
|
||||
{
|
||||
ieee802154_if_remove(IEEE802154_WPAN_DEV_TO_SUB_IF(wpan_dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ieee802154_set_channel(struct wpan_phy *wpan_phy, u8 page, u8 channel)
|
||||
{
|
||||
struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
|
||||
int ret;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
/* check if phy support this setting */
|
||||
if (!(wpan_phy->channels_supported[page] & BIT(channel)))
|
||||
return -EINVAL;
|
||||
|
||||
ret = drv_set_channel(local, page, channel);
|
||||
if (!ret) {
|
||||
wpan_phy->current_page = page;
|
||||
wpan_phy->current_channel = channel;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
ieee802154_set_pan_id(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
|
||||
__le16 pan_id)
|
||||
{
|
||||
ASSERT_RTNL();
|
||||
|
||||
/* TODO
|
||||
* I am not sure about to check here on broadcast pan_id.
|
||||
* Broadcast is a valid setting, comment from 802.15.4:
|
||||
* If this value is 0xffff, the device is not associated.
|
||||
*
|
||||
* This could useful to simple deassociate an device.
|
||||
*/
|
||||
if (pan_id == cpu_to_le16(IEEE802154_PAN_ID_BROADCAST))
|
||||
return -EINVAL;
|
||||
|
||||
wpan_dev->pan_id = pan_id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ieee802154_set_backoff_exponent(struct wpan_phy *wpan_phy,
|
||||
struct wpan_dev *wpan_dev,
|
||||
u8 min_be, u8 max_be)
|
||||
{
|
||||
struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
if (!(local->hw.flags & IEEE802154_HW_CSMA_PARAMS))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
wpan_dev->min_be = min_be;
|
||||
wpan_dev->max_be = max_be;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ieee802154_set_short_addr(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
|
||||
__le16 short_addr)
|
||||
{
|
||||
ASSERT_RTNL();
|
||||
|
||||
/* TODO
|
||||
* I am not sure about to check here on broadcast short_addr.
|
||||
* Broadcast is a valid setting, comment from 802.15.4:
|
||||
* A value of 0xfffe indicates that the device has
|
||||
* associated but has not been allocated an address. A
|
||||
* value of 0xffff indicates that the device does not
|
||||
* have a short address.
|
||||
*
|
||||
* I think we should allow to set these settings but
|
||||
* don't allow to allow socket communication with it.
|
||||
*/
|
||||
if (short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC) ||
|
||||
short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_BROADCAST))
|
||||
return -EINVAL;
|
||||
|
||||
wpan_dev->short_addr = short_addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ieee802154_set_max_csma_backoffs(struct wpan_phy *wpan_phy,
|
||||
struct wpan_dev *wpan_dev,
|
||||
u8 max_csma_backoffs)
|
||||
{
|
||||
struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
if (!(local->hw.flags & IEEE802154_HW_CSMA_PARAMS))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
wpan_dev->csma_retries = max_csma_backoffs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ieee802154_set_max_frame_retries(struct wpan_phy *wpan_phy,
|
||||
struct wpan_dev *wpan_dev,
|
||||
s8 max_frame_retries)
|
||||
{
|
||||
struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
if (!(local->hw.flags & IEEE802154_HW_FRAME_RETRIES))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
wpan_dev->frame_retries = max_frame_retries;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ieee802154_set_lbt_mode(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
|
||||
bool mode)
|
||||
{
|
||||
struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
if (!(local->hw.flags & IEEE802154_HW_LBT))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
wpan_dev->lbt = mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct cfg802154_ops mac802154_config_ops = {
|
||||
.add_virtual_intf_deprecated = ieee802154_add_iface_deprecated,
|
||||
.del_virtual_intf_deprecated = ieee802154_del_iface_deprecated,
|
||||
.add_virtual_intf = ieee802154_add_iface,
|
||||
.del_virtual_intf = ieee802154_del_iface,
|
||||
.set_channel = ieee802154_set_channel,
|
||||
.set_pan_id = ieee802154_set_pan_id,
|
||||
.set_short_addr = ieee802154_set_short_addr,
|
||||
.set_backoff_exponent = ieee802154_set_backoff_exponent,
|
||||
.set_max_csma_backoffs = ieee802154_set_max_csma_backoffs,
|
||||
.set_max_frame_retries = ieee802154_set_max_frame_retries,
|
||||
.set_lbt_mode = ieee802154_set_lbt_mode,
|
||||
};
|
||||
|
|
|
@ -50,16 +50,15 @@ static inline void drv_stop(struct ieee802154_local *local)
|
|||
local->started = false;
|
||||
}
|
||||
|
||||
static inline int drv_set_channel(struct ieee802154_local *local,
|
||||
const u8 page, const u8 channel)
|
||||
static inline int
|
||||
drv_set_channel(struct ieee802154_local *local, u8 page, u8 channel)
|
||||
{
|
||||
might_sleep();
|
||||
|
||||
return local->ops->set_channel(&local->hw, page, channel);
|
||||
}
|
||||
|
||||
static inline int drv_set_tx_power(struct ieee802154_local *local,
|
||||
const s8 dbm)
|
||||
static inline int drv_set_tx_power(struct ieee802154_local *local, s8 dbm)
|
||||
{
|
||||
might_sleep();
|
||||
|
||||
|
@ -71,8 +70,7 @@ static inline int drv_set_tx_power(struct ieee802154_local *local,
|
|||
return local->ops->set_txpower(&local->hw, dbm);
|
||||
}
|
||||
|
||||
static inline int drv_set_cca_mode(struct ieee802154_local *local,
|
||||
const u8 cca_mode)
|
||||
static inline int drv_set_cca_mode(struct ieee802154_local *local, u8 cca_mode)
|
||||
{
|
||||
might_sleep();
|
||||
|
||||
|
@ -84,8 +82,7 @@ static inline int drv_set_cca_mode(struct ieee802154_local *local,
|
|||
return local->ops->set_cca_mode(&local->hw, cca_mode);
|
||||
}
|
||||
|
||||
static inline int drv_set_lbt_mode(struct ieee802154_local *local,
|
||||
const bool mode)
|
||||
static inline int drv_set_lbt_mode(struct ieee802154_local *local, bool mode)
|
||||
{
|
||||
might_sleep();
|
||||
|
||||
|
@ -97,8 +94,8 @@ static inline int drv_set_lbt_mode(struct ieee802154_local *local,
|
|||
return local->ops->set_lbt(&local->hw, mode);
|
||||
}
|
||||
|
||||
static inline int drv_set_cca_ed_level(struct ieee802154_local *local,
|
||||
const s32 ed_level)
|
||||
static inline int
|
||||
drv_set_cca_ed_level(struct ieee802154_local *local, s32 ed_level)
|
||||
{
|
||||
might_sleep();
|
||||
|
||||
|
@ -110,8 +107,7 @@ static inline int drv_set_cca_ed_level(struct ieee802154_local *local,
|
|||
return local->ops->set_cca_ed_level(&local->hw, ed_level);
|
||||
}
|
||||
|
||||
static inline int drv_set_pan_id(struct ieee802154_local *local,
|
||||
const __le16 pan_id)
|
||||
static inline int drv_set_pan_id(struct ieee802154_local *local, __le16 pan_id)
|
||||
{
|
||||
struct ieee802154_hw_addr_filt filt;
|
||||
|
||||
|
@ -128,8 +124,8 @@ static inline int drv_set_pan_id(struct ieee802154_local *local,
|
|||
IEEE802154_AFILT_PANID_CHANGED);
|
||||
}
|
||||
|
||||
static inline int drv_set_extended_addr(struct ieee802154_local *local,
|
||||
const __le64 extended_addr)
|
||||
static inline int
|
||||
drv_set_extended_addr(struct ieee802154_local *local, __le64 extended_addr)
|
||||
{
|
||||
struct ieee802154_hw_addr_filt filt;
|
||||
|
||||
|
@ -146,8 +142,8 @@ static inline int drv_set_extended_addr(struct ieee802154_local *local,
|
|||
IEEE802154_AFILT_IEEEADDR_CHANGED);
|
||||
}
|
||||
|
||||
static inline int drv_set_short_addr(struct ieee802154_local *local,
|
||||
const __le16 short_addr)
|
||||
static inline int
|
||||
drv_set_short_addr(struct ieee802154_local *local, __le16 short_addr)
|
||||
{
|
||||
struct ieee802154_hw_addr_filt filt;
|
||||
|
||||
|
@ -164,8 +160,8 @@ static inline int drv_set_short_addr(struct ieee802154_local *local,
|
|||
IEEE802154_AFILT_SADDR_CHANGED);
|
||||
}
|
||||
|
||||
static inline int drv_set_pan_coord(struct ieee802154_local *local,
|
||||
const bool is_coord)
|
||||
static inline int
|
||||
drv_set_pan_coord(struct ieee802154_local *local, bool is_coord)
|
||||
{
|
||||
struct ieee802154_hw_addr_filt filt;
|
||||
|
||||
|
@ -182,9 +178,9 @@ static inline int drv_set_pan_coord(struct ieee802154_local *local,
|
|||
IEEE802154_AFILT_PANC_CHANGED);
|
||||
}
|
||||
|
||||
static inline int drv_set_csma_params(struct ieee802154_local *local,
|
||||
u8 min_be, u8 max_be,
|
||||
u8 max_csma_backoffs)
|
||||
static inline int
|
||||
drv_set_csma_params(struct ieee802154_local *local, u8 min_be, u8 max_be,
|
||||
u8 max_csma_backoffs)
|
||||
{
|
||||
might_sleep();
|
||||
|
||||
|
@ -197,8 +193,8 @@ static inline int drv_set_csma_params(struct ieee802154_local *local,
|
|||
max_csma_backoffs);
|
||||
}
|
||||
|
||||
static inline int drv_set_max_frame_retries(struct ieee802154_local *local,
|
||||
s8 max_frame_retries)
|
||||
static inline int
|
||||
drv_set_max_frame_retries(struct ieee802154_local *local, s8 max_frame_retries)
|
||||
{
|
||||
might_sleep();
|
||||
|
||||
|
@ -210,8 +206,8 @@ static inline int drv_set_max_frame_retries(struct ieee802154_local *local,
|
|||
return local->ops->set_frame_retries(&local->hw, max_frame_retries);
|
||||
}
|
||||
|
||||
static inline int drv_set_promiscuous_mode(struct ieee802154_local *local,
|
||||
const bool on)
|
||||
static inline int
|
||||
drv_set_promiscuous_mode(struct ieee802154_local *local, bool on)
|
||||
{
|
||||
might_sleep();
|
||||
|
||||
|
|
|
@ -20,8 +20,10 @@
|
|||
#define __IEEE802154_I_H
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <net/cfg802154.h>
|
||||
#include <net/mac802154.h>
|
||||
#include <net/nl802154.h>
|
||||
#include <net/ieee802154_netdev.h>
|
||||
|
||||
#include "llsec.h"
|
||||
|
@ -51,6 +53,8 @@ struct ieee802154_local {
|
|||
*/
|
||||
struct workqueue_struct *workqueue;
|
||||
|
||||
struct hrtimer ifs_timer;
|
||||
|
||||
bool started;
|
||||
|
||||
struct tasklet_struct tasklet;
|
||||
|
@ -84,18 +88,6 @@ struct ieee802154_sub_if_data {
|
|||
|
||||
spinlock_t mib_lock;
|
||||
|
||||
__le16 pan_id;
|
||||
__le16 short_addr;
|
||||
__le64 extended_addr;
|
||||
bool promiscuous_mode;
|
||||
|
||||
struct ieee802154_mac_params mac_params;
|
||||
|
||||
/* MAC BSN field */
|
||||
u8 bsn;
|
||||
/* MAC DSN field */
|
||||
u8 dsn;
|
||||
|
||||
/* protects sec from concurrent access by netlink. access by
|
||||
* encrypt/decrypt/header_create safe without additional protection.
|
||||
*/
|
||||
|
@ -108,6 +100,9 @@ struct ieee802154_sub_if_data {
|
|||
|
||||
#define MAC802154_CHAN_NONE 0xff /* No channel is assigned */
|
||||
|
||||
/* utility functions/constants */
|
||||
extern const void *const mac802154_wpan_phy_privid; /* for wpan_phy privid */
|
||||
|
||||
static inline struct ieee802154_local *
|
||||
hw_to_local(struct ieee802154_hw *hw)
|
||||
{
|
||||
|
@ -120,22 +115,25 @@ IEEE802154_DEV_TO_SUB_IF(const struct net_device *dev)
|
|||
return netdev_priv(dev);
|
||||
}
|
||||
|
||||
static inline struct ieee802154_sub_if_data *
|
||||
IEEE802154_WPAN_DEV_TO_SUB_IF(struct wpan_dev *wpan_dev)
|
||||
{
|
||||
return container_of(wpan_dev, struct ieee802154_sub_if_data, wpan_dev);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ieee802154_sdata_running(struct ieee802154_sub_if_data *sdata)
|
||||
{
|
||||
return test_bit(SDATA_STATE_RUNNING, &sdata->state);
|
||||
}
|
||||
|
||||
extern struct ieee802154_reduced_mlme_ops mac802154_mlme_reduced;
|
||||
extern struct ieee802154_mlme_ops mac802154_mlme_wpan;
|
||||
|
||||
void mac802154_monitor_setup(struct net_device *dev);
|
||||
netdev_tx_t
|
||||
ieee802154_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev);
|
||||
|
||||
void mac802154_wpan_setup(struct net_device *dev);
|
||||
netdev_tx_t
|
||||
ieee802154_subif_start_xmit(struct sk_buff *skb, struct net_device *dev);
|
||||
enum hrtimer_restart ieee802154_xmit_ifs_timer(struct hrtimer *timer);
|
||||
|
||||
/* MIB callbacks */
|
||||
void mac802154_dev_set_short_addr(struct net_device *dev, __le16 val);
|
||||
|
@ -178,11 +176,13 @@ void mac802154_get_table(struct net_device *dev,
|
|||
struct ieee802154_llsec_table **t);
|
||||
void mac802154_unlock_table(struct net_device *dev);
|
||||
|
||||
struct net_device *
|
||||
mac802154_add_iface(struct wpan_phy *phy, const char *name, int type);
|
||||
/* interface handling */
|
||||
int ieee802154_iface_init(void);
|
||||
void ieee802154_iface_exit(void);
|
||||
void ieee802154_if_remove(struct ieee802154_sub_if_data *sdata);
|
||||
struct net_device *
|
||||
ieee802154_if_add(struct ieee802154_local *local, const char *name,
|
||||
struct wpan_dev **new_wpan_dev, int type);
|
||||
enum nl802154_iftype type, __le64 extended_addr);
|
||||
void ieee802154_remove_interfaces(struct ieee802154_local *local);
|
||||
|
||||
#endif /* __IEEE802154_I_H */
|
||||
|
|
|
@ -22,8 +22,7 @@
|
|||
#include <linux/if_arp.h>
|
||||
#include <linux/ieee802154.h>
|
||||
|
||||
#include <net/rtnetlink.h>
|
||||
#include <linux/nl802154.h>
|
||||
#include <net/nl802154.h>
|
||||
#include <net/mac802154.h>
|
||||
#include <net/ieee802154_netdev.h>
|
||||
#include <net/cfg802154.h>
|
||||
|
@ -35,16 +34,17 @@ static int mac802154_wpan_update_llsec(struct net_device *dev)
|
|||
{
|
||||
struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
|
||||
struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
|
||||
struct wpan_dev *wpan_dev = &sdata->wpan_dev;
|
||||
int rc = 0;
|
||||
|
||||
if (ops->llsec) {
|
||||
struct ieee802154_llsec_params params;
|
||||
int changed = 0;
|
||||
|
||||
params.pan_id = sdata->pan_id;
|
||||
params.pan_id = wpan_dev->pan_id;
|
||||
changed |= IEEE802154_LLSEC_PARAM_PAN_ID;
|
||||
|
||||
params.hwaddr = sdata->extended_addr;
|
||||
params.hwaddr = wpan_dev->extended_addr;
|
||||
changed |= IEEE802154_LLSEC_PARAM_HWADDR;
|
||||
|
||||
rc = ops->llsec->set_params(dev, ¶ms, changed);
|
||||
|
@ -57,10 +57,13 @@ static int
|
|||
mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
||||
{
|
||||
struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
|
||||
struct wpan_dev *wpan_dev = &sdata->wpan_dev;
|
||||
struct sockaddr_ieee802154 *sa =
|
||||
(struct sockaddr_ieee802154 *)&ifr->ifr_addr;
|
||||
int err = -ENOIOCTLCMD;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
spin_lock_bh(&sdata->mib_lock);
|
||||
|
||||
switch (cmd) {
|
||||
|
@ -68,8 +71,8 @@ mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
|||
{
|
||||
u16 pan_id, short_addr;
|
||||
|
||||
pan_id = le16_to_cpu(sdata->pan_id);
|
||||
short_addr = le16_to_cpu(sdata->short_addr);
|
||||
pan_id = le16_to_cpu(wpan_dev->pan_id);
|
||||
short_addr = le16_to_cpu(wpan_dev->short_addr);
|
||||
if (pan_id == IEEE802154_PANID_BROADCAST ||
|
||||
short_addr == IEEE802154_ADDR_BROADCAST) {
|
||||
err = -EADDRNOTAVAIL;
|
||||
|
@ -85,6 +88,11 @@ mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
|||
break;
|
||||
}
|
||||
case SIOCSIFADDR:
|
||||
if (netif_running(dev)) {
|
||||
spin_unlock_bh(&sdata->mib_lock);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
dev_warn(&dev->dev,
|
||||
"Using DEBUGing ioctl SIOCSIFADDR isn't recommended!\n");
|
||||
if (sa->family != AF_IEEE802154 ||
|
||||
|
@ -96,8 +104,8 @@ mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
|||
break;
|
||||
}
|
||||
|
||||
sdata->pan_id = cpu_to_le16(sa->addr.pan_id);
|
||||
sdata->short_addr = cpu_to_le16(sa->addr.short_addr);
|
||||
wpan_dev->pan_id = cpu_to_le16(sa->addr.pan_id);
|
||||
wpan_dev->short_addr = cpu_to_le16(sa->addr.short_addr);
|
||||
|
||||
err = mac802154_wpan_update_llsec(dev);
|
||||
break;
|
||||
|
@ -121,7 +129,7 @@ static int mac802154_wpan_mac_addr(struct net_device *dev, void *p)
|
|||
return -EINVAL;
|
||||
|
||||
memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
|
||||
sdata->extended_addr = extended_addr;
|
||||
sdata->wpan_dev.extended_addr = extended_addr;
|
||||
|
||||
return mac802154_wpan_update_llsec(dev);
|
||||
}
|
||||
|
@ -135,7 +143,7 @@ static int mac802154_slave_open(struct net_device *dev)
|
|||
|
||||
ASSERT_RTNL();
|
||||
|
||||
if (sdata->vif.type == IEEE802154_DEV_WPAN) {
|
||||
if (sdata->vif.type == NL802154_IFTYPE_NODE) {
|
||||
mutex_lock(&sdata->local->iflist_mtx);
|
||||
list_for_each_entry(subif, &sdata->local->interfaces, list) {
|
||||
if (subif != sdata &&
|
||||
|
@ -172,6 +180,7 @@ static int mac802154_wpan_open(struct net_device *dev)
|
|||
int rc;
|
||||
struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
|
||||
struct ieee802154_local *local = sdata->local;
|
||||
struct wpan_dev *wpan_dev = &sdata->wpan_dev;
|
||||
struct wpan_phy *phy = sdata->local->phy;
|
||||
|
||||
rc = mac802154_slave_open(dev);
|
||||
|
@ -181,42 +190,42 @@ static int mac802154_wpan_open(struct net_device *dev)
|
|||
mutex_lock(&phy->pib_lock);
|
||||
|
||||
if (local->hw.flags & IEEE802154_HW_PROMISCUOUS) {
|
||||
rc = drv_set_promiscuous_mode(local, sdata->promiscuous_mode);
|
||||
rc = drv_set_promiscuous_mode(local,
|
||||
wpan_dev->promiscuous_mode);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (local->hw.flags & IEEE802154_HW_AFILT) {
|
||||
rc = drv_set_pan_id(local, sdata->pan_id);
|
||||
rc = drv_set_pan_id(local, wpan_dev->pan_id);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
|
||||
rc = drv_set_extended_addr(local, sdata->extended_addr);
|
||||
rc = drv_set_extended_addr(local, wpan_dev->extended_addr);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
|
||||
rc = drv_set_short_addr(local, sdata->short_addr);
|
||||
rc = drv_set_short_addr(local, wpan_dev->short_addr);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (local->hw.flags & IEEE802154_HW_LBT) {
|
||||
rc = drv_set_lbt_mode(local, sdata->mac_params.lbt);
|
||||
rc = drv_set_lbt_mode(local, wpan_dev->lbt);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (local->hw.flags & IEEE802154_HW_CSMA_PARAMS) {
|
||||
rc = drv_set_csma_params(local, sdata->mac_params.min_be,
|
||||
sdata->mac_params.max_be,
|
||||
sdata->mac_params.csma_retries);
|
||||
rc = drv_set_csma_params(local, wpan_dev->min_be,
|
||||
wpan_dev->max_be,
|
||||
wpan_dev->csma_retries);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (local->hw.flags & IEEE802154_HW_FRAME_RETRIES) {
|
||||
rc = drv_set_max_frame_retries(local,
|
||||
sdata->mac_params.frame_retries);
|
||||
rc = drv_set_max_frame_retries(local, wpan_dev->frame_retries);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
}
|
||||
|
@ -236,6 +245,8 @@ static int mac802154_slave_close(struct net_device *dev)
|
|||
|
||||
ASSERT_RTNL();
|
||||
|
||||
hrtimer_cancel(&local->ifs_timer);
|
||||
|
||||
netif_stop_queue(dev);
|
||||
local->open_count--;
|
||||
|
||||
|
@ -288,6 +299,7 @@ static int mac802154_header_create(struct sk_buff *skb,
|
|||
{
|
||||
struct ieee802154_hdr hdr;
|
||||
struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
|
||||
struct wpan_dev *wpan_dev = &sdata->wpan_dev;
|
||||
struct ieee802154_mac_cb *cb = mac_cb(skb);
|
||||
int hlen;
|
||||
|
||||
|
@ -306,17 +318,17 @@ static int mac802154_header_create(struct sk_buff *skb,
|
|||
if (!saddr) {
|
||||
spin_lock_bh(&sdata->mib_lock);
|
||||
|
||||
if (sdata->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST) ||
|
||||
sdata->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) ||
|
||||
sdata->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) {
|
||||
if (wpan_dev->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST) ||
|
||||
wpan_dev->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) ||
|
||||
wpan_dev->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) {
|
||||
hdr.source.mode = IEEE802154_ADDR_LONG;
|
||||
hdr.source.extended_addr = sdata->extended_addr;
|
||||
hdr.source.extended_addr = wpan_dev->extended_addr;
|
||||
} else {
|
||||
hdr.source.mode = IEEE802154_ADDR_SHORT;
|
||||
hdr.source.short_addr = sdata->short_addr;
|
||||
hdr.source.short_addr = wpan_dev->short_addr;
|
||||
}
|
||||
|
||||
hdr.source.pan_id = sdata->pan_id;
|
||||
hdr.source.pan_id = wpan_dev->pan_id;
|
||||
|
||||
spin_unlock_bh(&sdata->mib_lock);
|
||||
} else {
|
||||
|
@ -394,42 +406,48 @@ static void ieee802154_if_setup(struct net_device *dev)
|
|||
}
|
||||
|
||||
static int
|
||||
ieee802154_setup_sdata(struct ieee802154_sub_if_data *sdata, int type)
|
||||
ieee802154_setup_sdata(struct ieee802154_sub_if_data *sdata,
|
||||
enum nl802154_iftype type)
|
||||
{
|
||||
struct wpan_dev *wpan_dev = &sdata->wpan_dev;
|
||||
|
||||
/* set some type-dependent values */
|
||||
sdata->vif.type = type;
|
||||
sdata->wpan_dev.iftype = type;
|
||||
|
||||
get_random_bytes(&sdata->bsn, 1);
|
||||
get_random_bytes(&sdata->dsn, 1);
|
||||
get_random_bytes(&wpan_dev->bsn, 1);
|
||||
get_random_bytes(&wpan_dev->dsn, 1);
|
||||
|
||||
/* defaults per 802.15.4-2011 */
|
||||
sdata->mac_params.min_be = 3;
|
||||
sdata->mac_params.max_be = 5;
|
||||
sdata->mac_params.csma_retries = 4;
|
||||
wpan_dev->min_be = 3;
|
||||
wpan_dev->max_be = 5;
|
||||
wpan_dev->csma_retries = 4;
|
||||
/* for compatibility, actual default is 3 */
|
||||
sdata->mac_params.frame_retries = -1;
|
||||
wpan_dev->frame_retries = -1;
|
||||
|
||||
ieee802154_be64_to_le64(&sdata->extended_addr, sdata->dev->dev_addr);
|
||||
sdata->pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST);
|
||||
sdata->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
|
||||
wpan_dev->pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST);
|
||||
wpan_dev->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
|
||||
|
||||
switch (type) {
|
||||
case IEEE802154_DEV_WPAN:
|
||||
case NL802154_IFTYPE_NODE:
|
||||
ieee802154_be64_to_le64(&wpan_dev->extended_addr,
|
||||
sdata->dev->dev_addr);
|
||||
|
||||
sdata->dev->header_ops = &mac802154_header_ops;
|
||||
sdata->dev->destructor = mac802154_wpan_free;
|
||||
sdata->dev->netdev_ops = &mac802154_wpan_ops;
|
||||
sdata->dev->ml_priv = &mac802154_mlme_wpan;
|
||||
sdata->promiscuous_mode = false;
|
||||
wpan_dev->promiscuous_mode = false;
|
||||
|
||||
spin_lock_init(&sdata->mib_lock);
|
||||
mutex_init(&sdata->sec_mtx);
|
||||
|
||||
mac802154_llsec_init(&sdata->sec);
|
||||
break;
|
||||
case IEEE802154_DEV_MONITOR:
|
||||
case NL802154_IFTYPE_MONITOR:
|
||||
sdata->dev->destructor = free_netdev;
|
||||
sdata->dev->netdev_ops = &mac802154_monitor_ops;
|
||||
sdata->promiscuous_mode = true;
|
||||
wpan_dev->promiscuous_mode = true;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
|
@ -440,7 +458,7 @@ ieee802154_setup_sdata(struct ieee802154_sub_if_data *sdata, int type)
|
|||
|
||||
struct net_device *
|
||||
ieee802154_if_add(struct ieee802154_local *local, const char *name,
|
||||
struct wpan_dev **new_wpan_dev, int type)
|
||||
enum nl802154_iftype type, __le64 extended_addr)
|
||||
{
|
||||
struct net_device *ndev = NULL;
|
||||
struct ieee802154_sub_if_data *sdata = NULL;
|
||||
|
@ -459,11 +477,18 @@ ieee802154_if_add(struct ieee802154_local *local, const char *name,
|
|||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
ieee802154_le64_to_be64(ndev->perm_addr,
|
||||
&local->hw.phy->perm_extended_addr);
|
||||
switch (type) {
|
||||
case IEEE802154_DEV_WPAN:
|
||||
case NL802154_IFTYPE_NODE:
|
||||
ndev->type = ARPHRD_IEEE802154;
|
||||
if (ieee802154_is_valid_extended_addr(extended_addr))
|
||||
ieee802154_le64_to_be64(ndev->dev_addr, &extended_addr);
|
||||
else
|
||||
memcpy(ndev->dev_addr, ndev->perm_addr,
|
||||
IEEE802154_EXTENDED_ADDR_LEN);
|
||||
break;
|
||||
case IEEE802154_DEV_MONITOR:
|
||||
case NL802154_IFTYPE_MONITOR:
|
||||
ndev->type = ARPHRD_IEEE802154_MONITOR;
|
||||
break;
|
||||
default:
|
||||
|
@ -471,9 +496,6 @@ ieee802154_if_add(struct ieee802154_local *local, const char *name,
|
|||
goto err;
|
||||
}
|
||||
|
||||
ieee802154_le64_to_be64(ndev->perm_addr,
|
||||
&local->hw.phy->perm_extended_addr);
|
||||
memcpy(ndev->dev_addr, ndev->perm_addr, IEEE802154_EXTENDED_ADDR_LEN);
|
||||
/* TODO check this */
|
||||
SET_NETDEV_DEV(ndev, &local->phy->dev);
|
||||
sdata = netdev_priv(ndev);
|
||||
|
@ -498,9 +520,6 @@ ieee802154_if_add(struct ieee802154_local *local, const char *name,
|
|||
list_add_tail_rcu(&sdata->list, &local->interfaces);
|
||||
mutex_unlock(&local->iflist_mtx);
|
||||
|
||||
if (new_wpan_dev)
|
||||
*new_wpan_dev = &sdata->wpan_dev;
|
||||
|
||||
return ndev;
|
||||
|
||||
err:
|
||||
|
@ -519,3 +538,51 @@ void ieee802154_if_remove(struct ieee802154_sub_if_data *sdata)
|
|||
synchronize_rcu();
|
||||
unregister_netdevice(sdata->dev);
|
||||
}
|
||||
|
||||
void ieee802154_remove_interfaces(struct ieee802154_local *local)
|
||||
{
|
||||
struct ieee802154_sub_if_data *sdata, *tmp;
|
||||
|
||||
mutex_lock(&local->iflist_mtx);
|
||||
list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) {
|
||||
list_del(&sdata->list);
|
||||
|
||||
unregister_netdevice(sdata->dev);
|
||||
}
|
||||
mutex_unlock(&local->iflist_mtx);
|
||||
}
|
||||
|
||||
static int netdev_notify(struct notifier_block *nb,
|
||||
unsigned long state, void *ptr)
|
||||
{
|
||||
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
||||
struct ieee802154_sub_if_data *sdata;
|
||||
|
||||
if (state != NETDEV_CHANGENAME)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
if (!dev->ieee802154_ptr || !dev->ieee802154_ptr->wpan_phy)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
if (dev->ieee802154_ptr->wpan_phy->privid != mac802154_wpan_phy_privid)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
sdata = IEEE802154_DEV_TO_SUB_IF(dev);
|
||||
memcpy(sdata->name, dev->name, IFNAMSIZ);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block mac802154_netdev_notifier = {
|
||||
.notifier_call = netdev_notify,
|
||||
};
|
||||
|
||||
int ieee802154_iface_init(void)
|
||||
{
|
||||
return register_netdevice_notifier(&mac802154_netdev_notifier);
|
||||
}
|
||||
|
||||
void ieee802154_iface_exit(void)
|
||||
{
|
||||
unregister_netdevice_notifier(&mac802154_netdev_notifier);
|
||||
}
|
||||
|
|
|
@ -39,6 +39,8 @@ static int mac802154_mlme_start_req(struct net_device *dev,
|
|||
struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
|
||||
int rc = 0;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
BUG_ON(addr->mode != IEEE802154_ADDR_SHORT);
|
||||
|
||||
mac802154_dev_set_pan_id(dev, addr->pan_id);
|
||||
|
@ -72,11 +74,22 @@ static int mac802154_set_mac_params(struct net_device *dev,
|
|||
{
|
||||
struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
|
||||
struct ieee802154_local *local = sdata->local;
|
||||
struct wpan_dev *wpan_dev = &sdata->wpan_dev;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&sdata->local->iflist_mtx);
|
||||
sdata->mac_params = *params;
|
||||
mutex_unlock(&sdata->local->iflist_mtx);
|
||||
ASSERT_RTNL();
|
||||
|
||||
/* PHY */
|
||||
wpan_dev->wpan_phy->transmit_power = params->transmit_power;
|
||||
wpan_dev->wpan_phy->cca_mode = params->cca_mode;
|
||||
wpan_dev->wpan_phy->cca_ed_level = params->cca_ed_level;
|
||||
|
||||
/* MAC */
|
||||
wpan_dev->min_be = params->min_be;
|
||||
wpan_dev->max_be = params->max_be;
|
||||
wpan_dev->csma_retries = params->csma_retries;
|
||||
wpan_dev->frame_retries = params->frame_retries;
|
||||
wpan_dev->lbt = params->lbt;
|
||||
|
||||
if (local->hw.flags & IEEE802154_HW_TXPOWER) {
|
||||
ret = drv_set_tx_power(local, params->transmit_power);
|
||||
|
@ -103,10 +116,21 @@ static void mac802154_get_mac_params(struct net_device *dev,
|
|||
struct ieee802154_mac_params *params)
|
||||
{
|
||||
struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
|
||||
struct wpan_dev *wpan_dev = &sdata->wpan_dev;
|
||||
|
||||
mutex_lock(&sdata->local->iflist_mtx);
|
||||
*params = sdata->mac_params;
|
||||
mutex_unlock(&sdata->local->iflist_mtx);
|
||||
ASSERT_RTNL();
|
||||
|
||||
/* PHY */
|
||||
params->transmit_power = wpan_dev->wpan_phy->transmit_power;
|
||||
params->cca_mode = wpan_dev->wpan_phy->cca_mode;
|
||||
params->cca_ed_level = wpan_dev->wpan_phy->cca_ed_level;
|
||||
|
||||
/* MAC */
|
||||
params->min_be = wpan_dev->min_be;
|
||||
params->max_be = wpan_dev->max_be;
|
||||
params->csma_retries = wpan_dev->csma_retries;
|
||||
params->frame_retries = wpan_dev->frame_retries;
|
||||
params->lbt = wpan_dev->lbt;
|
||||
}
|
||||
|
||||
static struct ieee802154_llsec_ops mac802154_llsec_ops = {
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
* Written by:
|
||||
* Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
|
||||
*
|
||||
* Based on the code from 'linux-zigbee.sourceforge.net' project.
|
||||
*
|
||||
* 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.
|
||||
|
@ -21,7 +19,7 @@
|
|||
#include <linux/netdevice.h>
|
||||
|
||||
#include <net/netlink.h>
|
||||
#include <linux/nl802154.h>
|
||||
#include <net/nl802154.h>
|
||||
#include <net/mac802154.h>
|
||||
#include <net/ieee802154_netdev.h>
|
||||
#include <net/route.h>
|
||||
|
@ -86,12 +84,14 @@ ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops)
|
|||
|
||||
priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len;
|
||||
|
||||
phy = wpan_phy_alloc(&mac802154_config_ops, priv_size);
|
||||
phy = wpan_phy_new(&mac802154_config_ops, priv_size);
|
||||
if (!phy) {
|
||||
pr_err("failure to allocate master IEEE802.15.4 device\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
phy->privid = mac802154_wpan_phy_privid;
|
||||
|
||||
local = wpan_phy_priv(phy);
|
||||
local->phy = phy;
|
||||
local->hw.phy = local->phy;
|
||||
|
@ -123,6 +123,18 @@ void ieee802154_free_hw(struct ieee802154_hw *hw)
|
|||
}
|
||||
EXPORT_SYMBOL(ieee802154_free_hw);
|
||||
|
||||
static void ieee802154_setup_wpan_phy_pib(struct wpan_phy *wpan_phy)
|
||||
{
|
||||
/* TODO warn on empty symbol_duration
|
||||
* Should be done when all drivers sets this value.
|
||||
*/
|
||||
|
||||
wpan_phy->lifs_period = IEEE802154_LIFS_PERIOD *
|
||||
wpan_phy->symbol_duration;
|
||||
wpan_phy->sifs_period = IEEE802154_SIFS_PERIOD *
|
||||
wpan_phy->symbol_duration;
|
||||
}
|
||||
|
||||
int ieee802154_register_hw(struct ieee802154_hw *hw)
|
||||
{
|
||||
struct ieee802154_local *local = hw_to_local(hw);
|
||||
|
@ -136,15 +148,21 @@ int ieee802154_register_hw(struct ieee802154_hw *hw)
|
|||
goto out;
|
||||
}
|
||||
|
||||
hrtimer_init(&local->ifs_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
local->ifs_timer.function = ieee802154_xmit_ifs_timer;
|
||||
|
||||
wpan_phy_set_dev(local->phy, local->hw.parent);
|
||||
|
||||
ieee802154_setup_wpan_phy_pib(local->phy);
|
||||
|
||||
rc = wpan_phy_register(local->phy);
|
||||
if (rc < 0)
|
||||
goto out_wq;
|
||||
|
||||
rtnl_lock();
|
||||
|
||||
dev = ieee802154_if_add(local, "wpan%d", NULL, IEEE802154_DEV_WPAN);
|
||||
dev = ieee802154_if_add(local, "wpan%d", NL802154_IFTYPE_NODE,
|
||||
cpu_to_le64(0x0000000000000000ULL));
|
||||
if (IS_ERR(dev)) {
|
||||
rtnl_unlock();
|
||||
rc = PTR_ERR(dev);
|
||||
|
@ -165,7 +183,6 @@ EXPORT_SYMBOL(ieee802154_register_hw);
|
|||
void ieee802154_unregister_hw(struct ieee802154_hw *hw)
|
||||
{
|
||||
struct ieee802154_local *local = hw_to_local(hw);
|
||||
struct ieee802154_sub_if_data *sdata, *next;
|
||||
|
||||
tasklet_kill(&local->tasklet);
|
||||
flush_workqueue(local->workqueue);
|
||||
|
@ -173,13 +190,7 @@ void ieee802154_unregister_hw(struct ieee802154_hw *hw)
|
|||
|
||||
rtnl_lock();
|
||||
|
||||
list_for_each_entry_safe(sdata, next, &local->interfaces, list) {
|
||||
mutex_lock(&sdata->local->iflist_mtx);
|
||||
list_del(&sdata->list);
|
||||
mutex_unlock(&sdata->local->iflist_mtx);
|
||||
|
||||
unregister_netdevice(sdata->dev);
|
||||
}
|
||||
ieee802154_remove_interfaces(local);
|
||||
|
||||
rtnl_unlock();
|
||||
|
||||
|
@ -187,5 +198,20 @@ void ieee802154_unregister_hw(struct ieee802154_hw *hw)
|
|||
}
|
||||
EXPORT_SYMBOL(ieee802154_unregister_hw);
|
||||
|
||||
MODULE_DESCRIPTION("IEEE 802.15.4 implementation");
|
||||
static int __init ieee802154_init(void)
|
||||
{
|
||||
return ieee802154_iface_init();
|
||||
}
|
||||
|
||||
static void __exit ieee802154_exit(void)
|
||||
{
|
||||
ieee802154_iface_exit();
|
||||
|
||||
rcu_barrier();
|
||||
}
|
||||
|
||||
subsys_initcall(ieee802154_init);
|
||||
module_exit(ieee802154_exit);
|
||||
|
||||
MODULE_DESCRIPTION("IEEE 802.15.4 subsystem");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -33,7 +33,7 @@ void mac802154_dev_set_short_addr(struct net_device *dev, __le16 val)
|
|||
BUG_ON(dev->type != ARPHRD_IEEE802154);
|
||||
|
||||
spin_lock_bh(&sdata->mib_lock);
|
||||
sdata->short_addr = val;
|
||||
sdata->wpan_dev.short_addr = val;
|
||||
spin_unlock_bh(&sdata->mib_lock);
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ __le16 mac802154_dev_get_short_addr(const struct net_device *dev)
|
|||
BUG_ON(dev->type != ARPHRD_IEEE802154);
|
||||
|
||||
spin_lock_bh(&sdata->mib_lock);
|
||||
ret = sdata->short_addr;
|
||||
ret = sdata->wpan_dev.short_addr;
|
||||
spin_unlock_bh(&sdata->mib_lock);
|
||||
|
||||
return ret;
|
||||
|
@ -59,7 +59,7 @@ __le16 mac802154_dev_get_pan_id(const struct net_device *dev)
|
|||
BUG_ON(dev->type != ARPHRD_IEEE802154);
|
||||
|
||||
spin_lock_bh(&sdata->mib_lock);
|
||||
ret = sdata->pan_id;
|
||||
ret = sdata->wpan_dev.pan_id;
|
||||
spin_unlock_bh(&sdata->mib_lock);
|
||||
|
||||
return ret;
|
||||
|
@ -72,7 +72,7 @@ void mac802154_dev_set_pan_id(struct net_device *dev, __le16 val)
|
|||
BUG_ON(dev->type != ARPHRD_IEEE802154);
|
||||
|
||||
spin_lock_bh(&sdata->mib_lock);
|
||||
sdata->pan_id = val;
|
||||
sdata->wpan_dev.pan_id = val;
|
||||
spin_unlock_bh(&sdata->mib_lock);
|
||||
}
|
||||
|
||||
|
@ -82,7 +82,7 @@ u8 mac802154_dev_get_dsn(const struct net_device *dev)
|
|||
|
||||
BUG_ON(dev->type != ARPHRD_IEEE802154);
|
||||
|
||||
return sdata->dsn++;
|
||||
return sdata->wpan_dev.dsn++;
|
||||
}
|
||||
|
||||
void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan)
|
||||
|
|
|
@ -25,8 +25,7 @@
|
|||
|
||||
#include <net/mac802154.h>
|
||||
#include <net/ieee802154_netdev.h>
|
||||
#include <net/rtnetlink.h>
|
||||
#include <linux/nl802154.h>
|
||||
#include <net/nl802154.h>
|
||||
|
||||
#include "ieee802154_i.h"
|
||||
|
||||
|
@ -42,6 +41,7 @@ static int
|
|||
ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata,
|
||||
struct sk_buff *skb, const struct ieee802154_hdr *hdr)
|
||||
{
|
||||
struct wpan_dev *wpan_dev = &sdata->wpan_dev;
|
||||
__le16 span, sshort;
|
||||
int rc;
|
||||
|
||||
|
@ -49,8 +49,8 @@ ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata,
|
|||
|
||||
spin_lock_bh(&sdata->mib_lock);
|
||||
|
||||
span = sdata->pan_id;
|
||||
sshort = sdata->short_addr;
|
||||
span = wpan_dev->pan_id;
|
||||
sshort = wpan_dev->short_addr;
|
||||
|
||||
switch (mac_cb(skb)->dest.mode) {
|
||||
case IEEE802154_ADDR_NONE:
|
||||
|
@ -65,7 +65,7 @@ ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata,
|
|||
if (mac_cb(skb)->dest.pan_id != span &&
|
||||
mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST))
|
||||
skb->pkt_type = PACKET_OTHERHOST;
|
||||
else if (mac_cb(skb)->dest.extended_addr == sdata->extended_addr)
|
||||
else if (mac_cb(skb)->dest.extended_addr == wpan_dev->extended_addr)
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
else
|
||||
skb->pkt_type = PACKET_OTHERHOST;
|
||||
|
@ -208,7 +208,7 @@ __ieee802154_rx_handle_packet(struct ieee802154_local *local,
|
|||
}
|
||||
|
||||
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
|
||||
if (sdata->vif.type != IEEE802154_DEV_WPAN ||
|
||||
if (sdata->vif.type != NL802154_IFTYPE_NODE ||
|
||||
!netif_running(sdata->dev))
|
||||
continue;
|
||||
|
||||
|
@ -233,7 +233,7 @@ ieee802154_monitors_rx(struct ieee802154_local *local, struct sk_buff *skb)
|
|||
skb->protocol = htons(ETH_P_IEEE802154);
|
||||
|
||||
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
|
||||
if (sdata->vif.type != IEEE802154_DEV_MONITOR)
|
||||
if (sdata->vif.type != NL802154_IFTYPE_MONITOR)
|
||||
continue;
|
||||
|
||||
if (!ieee802154_sdata_running(sdata))
|
||||
|
|
|
@ -60,7 +60,7 @@ static void ieee802154_xmit_worker(struct work_struct *work)
|
|||
if (res)
|
||||
goto err_tx;
|
||||
|
||||
ieee802154_xmit_complete(&local->hw, skb);
|
||||
ieee802154_xmit_complete(&local->hw, skb, false);
|
||||
|
||||
dev->stats.tx_packets++;
|
||||
dev->stats.tx_bytes += skb->len;
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
|
||||
#include "ieee802154_i.h"
|
||||
|
||||
/* privid for wpan_phys to determine whether they belong to us or not */
|
||||
const void *const mac802154_wpan_phy_privid = &mac802154_wpan_phy_privid;
|
||||
|
||||
void ieee802154_wake_queue(struct ieee802154_hw *hw)
|
||||
{
|
||||
struct ieee802154_local *local = hw_to_local(hw);
|
||||
|
@ -47,9 +50,35 @@ void ieee802154_stop_queue(struct ieee802154_hw *hw)
|
|||
}
|
||||
EXPORT_SYMBOL(ieee802154_stop_queue);
|
||||
|
||||
void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb)
|
||||
enum hrtimer_restart ieee802154_xmit_ifs_timer(struct hrtimer *timer)
|
||||
{
|
||||
ieee802154_wake_queue(hw);
|
||||
consume_skb(skb);
|
||||
struct ieee802154_local *local =
|
||||
container_of(timer, struct ieee802154_local, ifs_timer);
|
||||
|
||||
ieee802154_wake_queue(&local->hw);
|
||||
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb,
|
||||
bool ifs_handling)
|
||||
{
|
||||
if (ifs_handling) {
|
||||
struct ieee802154_local *local = hw_to_local(hw);
|
||||
|
||||
if (skb->len > 18)
|
||||
hrtimer_start(&local->ifs_timer,
|
||||
ktime_set(0, hw->phy->lifs_period * NSEC_PER_USEC),
|
||||
HRTIMER_MODE_REL);
|
||||
else
|
||||
hrtimer_start(&local->ifs_timer,
|
||||
ktime_set(0, hw->phy->sifs_period * NSEC_PER_USEC),
|
||||
HRTIMER_MODE_REL);
|
||||
|
||||
consume_skb(skb);
|
||||
} else {
|
||||
ieee802154_wake_queue(hw);
|
||||
consume_skb(skb);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(ieee802154_xmit_complete);
|
||||
|
|
Loading…
Reference in New Issue