2009-05-26 11:22:08

by Dmitry Baryshkov

[permalink] [raw]
Subject: [RFC][WIP] IEEE 802.15.4 implementation for Linux

Hi,

As a part of research activities in the Embedded Systems - Open Platform Group
from Siemens Corporate Technology we are working on adding support for
the IEEE 802.15.4 Wireless Personal Area Networks to the Linux. Our current
implementation is neither certified nor even feature complete. However
we'd like to present current state of our patchset to the Linux developers
community to gain comments, fixes, ideas, etc. This is not yet a pull request,
but more like an RFC.

The project page is available at http://apps.sourceforge.net/trac/linux-zigbee
with source code of kernel part available from git at
http://zigbee-linux.git.sourceforge.net, mirrored for convenience at
git://git.kernel.org/pub/scm/linux/kernel/git/lumag/lowpan.git

The source code for userspace utils is available from git at
http://linux-zigbee.git.sourceforge.net/

Several comments about our implementation:
* As with 802.11 there are two types of devices: the smart ones that implement
most parts of the protocol by themselves and more or less dumb ones which
simply send and receive what they are told. Currently we do only support the
second type of devices (SoftMAC)
* The implementation is split between code driving radio, (master, mwpanX
interface: mdev.c), code processing frames according the IEEE 802.15.4
rules (slave wpanX devices) and finally sockets (af_ieee802154.c, dgram.c,
raw.c).
* We do present two example drivers using our stack. One is purely virtual
one either looping the packets back or connecting several virtual interfaces
(the one at fakelb.c), and the driver for the Freescale MC13192 evaluation
boards (13192-SARD, 13192-EVK) using our custom firmware (currently only
available at request, we are working on publishing it). The driver for the
Atmel at86rf230/at86rf231 chips will follow in several weeks.

The following changes since commit 59a3759d0fe8d969888c741bb33f4946e4d3750d:
Linus Torvalds (1):
Linux 2.6.30-rc7

are available in the git repository at:

git://git.kernel.org/pub/scm/linux/kernel/git/lumag/lowpan.git for-review-v0

Dmitry Eremin-Solenikov (5):
Add constants for the ieee 802.15.4/ZigBee stack
net: add IEEE 802.15.4 partial implementation
ieee802154: add socket address family code
ieee802154: add virtual loopback driver
ieee802154: add serial dongle driver

drivers/Makefile | 1 +
drivers/ieee802154/Kconfig | 26 +
drivers/ieee802154/Makefile | 4 +
drivers/ieee802154/fakelb.c | 335 +++++++++++
drivers/ieee802154/serial.c | 1001 ++++++++++++++++++++++++++++++++
drivers/net/Kconfig | 2 +
include/linux/if.h | 2 +
include/linux/if_arp.h | 2 +
include/linux/if_ether.h | 2 +
include/linux/socket.h | 6 +-
include/linux/tty.h | 3 +-
include/net/ieee802154/af_ieee802154.h | 65 ++
include/net/ieee802154/beacon.h | 53 ++
include/net/ieee802154/beacon_hash.h | 40 ++
include/net/ieee802154/const.h | 696 ++++++++++++++++++++++
include/net/ieee802154/crc.h | 38 ++
include/net/ieee802154/dev.h | 107 ++++
include/net/ieee802154/mac_def.h | 96 +++
include/net/ieee802154/netdev.h | 74 +++
include/net/ieee802154/nl.h | 166 ++++++
include/net/ieee802154/phy.h | 117 ++++
net/Kconfig | 1 +
net/Makefile | 1 +
net/core/dev.c | 6 +-
net/core/sock.c | 3 +
net/ieee802154/Kconfig | 12 +
net/ieee802154/Makefile | 6 +
net/ieee802154/af_ieee802154.c | 312 ++++++++++
net/ieee802154/beacon.c | 251 ++++++++
net/ieee802154/beacon_hash.c | 105 ++++
net/ieee802154/crc.c | 73 +++
net/ieee802154/dev.c | 935 +++++++++++++++++++++++++++++
net/ieee802154/dgram.c | 372 ++++++++++++
net/ieee802154/mac_cmd.c | 226 +++++++
net/ieee802154/main.c | 175 ++++++
net/ieee802154/mdev.c | 189 ++++++
net/ieee802154/netlink.c | 637 ++++++++++++++++++++
net/ieee802154/raw.c | 249 ++++++++
net/ieee802154/scan.c | 211 +++++++
net/ieee802154/start.c | 46 ++
40 files changed, 6642 insertions(+), 4 deletions(-)
create mode 100644 drivers/ieee802154/Kconfig
create mode 100644 drivers/ieee802154/Makefile
create mode 100644 drivers/ieee802154/fakelb.c
create mode 100644 drivers/ieee802154/serial.c
create mode 100644 include/net/ieee802154/af_ieee802154.h
create mode 100644 include/net/ieee802154/beacon.h
create mode 100644 include/net/ieee802154/beacon_hash.h
create mode 100644 include/net/ieee802154/const.h
create mode 100644 include/net/ieee802154/crc.h
create mode 100644 include/net/ieee802154/dev.h
create mode 100644 include/net/ieee802154/mac_def.h
create mode 100644 include/net/ieee802154/netdev.h
create mode 100644 include/net/ieee802154/nl.h
create mode 100644 include/net/ieee802154/phy.h
create mode 100644 net/ieee802154/Kconfig
create mode 100644 net/ieee802154/Makefile
create mode 100644 net/ieee802154/af_ieee802154.c
create mode 100644 net/ieee802154/beacon.c
create mode 100644 net/ieee802154/beacon_hash.c
create mode 100644 net/ieee802154/crc.c
create mode 100644 net/ieee802154/dev.c
create mode 100644 net/ieee802154/dgram.c
create mode 100644 net/ieee802154/mac_cmd.c
create mode 100644 net/ieee802154/main.c
create mode 100644 net/ieee802154/mdev.c
create mode 100644 net/ieee802154/netlink.c
create mode 100644 net/ieee802154/raw.c
create mode 100644 net/ieee802154/scan.c
create mode 100644 net/ieee802154/start.c

--
With best wishes
Dmitry



2009-05-26 11:23:30

by Dmitry Baryshkov

[permalink] [raw]
Subject: [PATCH 2/5] net: add IEEE 802.15.4 partial implementation

Add support for communication over IEEE 802.15.4 networks. This implementation
is neither certified nor complete, but aims to that goal. This commit contains
only parts of MAC implementation, socket code and drivers supporting this stack
will follow.

Initial implementation was done by Maxim Gorbachyov, Maxim Osipov and Pavel
Smolensky as a research project at Siemens AG. Later the stack was heavily
reworked to better suit the linux networking model, and is now maitained
as an open project partially sponsored by Siemens.

Signed-off-by: Dmitry Eremin-Solenikov <[email protected]>
Signed-off-by: Sergey Lapin <[email protected]>
---
include/net/ieee802154/af_ieee802154.h | 65 +++
include/net/ieee802154/beacon.h | 53 ++
include/net/ieee802154/beacon_hash.h | 40 ++
include/net/ieee802154/const.h | 696 ++++++++++++++++++++++++
include/net/ieee802154/crc.h | 38 ++
include/net/ieee802154/dev.h | 107 ++++
include/net/ieee802154/mac_def.h | 96 ++++
include/net/ieee802154/netdev.h | 74 +++
include/net/ieee802154/nl.h | 166 ++++++
include/net/ieee802154/phy.h | 117 ++++
net/Kconfig | 1 +
net/Makefile | 1 +
net/ieee802154/Kconfig | 12 +
net/ieee802154/Makefile | 5 +
net/ieee802154/beacon.c | 251 +++++++++
net/ieee802154/beacon_hash.c | 105 ++++
net/ieee802154/crc.c | 73 +++
net/ieee802154/dev.c | 935 ++++++++++++++++++++++++++++++++
net/ieee802154/mac_cmd.c | 226 ++++++++
net/ieee802154/main.c | 175 ++++++
net/ieee802154/mdev.c | 189 +++++++
net/ieee802154/netlink.c | 637 ++++++++++++++++++++++
net/ieee802154/scan.c | 211 +++++++
net/ieee802154/start.c | 46 ++
24 files changed, 4319 insertions(+), 0 deletions(-)
create mode 100644 include/net/ieee802154/af_ieee802154.h
create mode 100644 include/net/ieee802154/beacon.h
create mode 100644 include/net/ieee802154/beacon_hash.h
create mode 100644 include/net/ieee802154/const.h
create mode 100644 include/net/ieee802154/crc.h
create mode 100644 include/net/ieee802154/dev.h
create mode 100644 include/net/ieee802154/mac_def.h
create mode 100644 include/net/ieee802154/netdev.h
create mode 100644 include/net/ieee802154/nl.h
create mode 100644 include/net/ieee802154/phy.h
create mode 100644 net/ieee802154/Kconfig
create mode 100644 net/ieee802154/Makefile
create mode 100644 net/ieee802154/beacon.c
create mode 100644 net/ieee802154/beacon_hash.c
create mode 100644 net/ieee802154/crc.c
create mode 100644 net/ieee802154/dev.c
create mode 100644 net/ieee802154/mac_cmd.c
create mode 100644 net/ieee802154/main.c
create mode 100644 net/ieee802154/mdev.c
create mode 100644 net/ieee802154/netlink.c
create mode 100644 net/ieee802154/scan.c
create mode 100644 net/ieee802154/start.c

diff --git a/include/net/ieee802154/af_ieee802154.h b/include/net/ieee802154/af_ieee802154.h
new file mode 100644
index 0000000..6eb7f51
--- /dev/null
+++ b/include/net/ieee802154/af_ieee802154.h
@@ -0,0 +1,65 @@
+/*
+ * IEEE 802.15.4 inteface for userspace
+ *
+ * Copyright 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ * Sergey Lapin <[email protected]>
+ * Dmitry Eremin-Solenikov <[email protected]>
+ */
+
+#ifndef _AF_IEEE802154_H
+#define _AF_IEEE802154_H
+
+#include <linux/socket.h> /* for sa_family_t */
+
+enum {
+ IEEE802154_ADDR_NONE = 0x0,
+ /* RESERVED = 0x01, */
+ IEEE802154_ADDR_SHORT = 0x2, /* 16-bit address + PANid */
+ IEEE802154_ADDR_LONG = 0x3, /* 64-bit address + PANid */
+};
+
+/* address length, octets */
+#define IEEE802154_ADDR_LEN 8
+
+struct ieee802154_addr {
+ int addr_type;
+ u16 pan_id;
+ union {
+ u8 hwaddr[IEEE802154_ADDR_LEN];
+ u16 short_addr;
+ };
+};
+
+struct sockaddr_ieee802154 {
+ sa_family_t family; /* AF_IEEE802154 */
+ struct ieee802154_addr addr;
+};
+
+/* master device */
+#define IEEE802154_SIOC_ADD_SLAVE (SIOCDEVPRIVATE + 0)
+
+#ifdef __KERNEL__
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+extern struct proto ieee802154_raw_prot;
+extern struct proto ieee802154_dgram_prot;
+void ieee802154_raw_deliver(struct net_device *dev, struct sk_buff *skb);
+int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb);
+#endif
+
+#endif
diff --git a/include/net/ieee802154/beacon.h b/include/net/ieee802154/beacon.h
new file mode 100644
index 0000000..baca263
--- /dev/null
+++ b/include/net/ieee802154/beacon.h
@@ -0,0 +1,53 @@
+/*
+ * beacon.h
+ *
+ * Copyright (C) 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ * Pavel Smolenskiy <[email protected]>
+ * Maxim Gorbachyov <[email protected]>
+ */
+
+#ifndef IEEE802154_BEACON_H
+#define IEEE802154_BEACON_H
+
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include "af_ieee802154.h"
+
+/* Per spec; optimizations are needed */
+struct ieee802154_pandsc {
+ struct list_head list;
+ struct ieee802154_addr addr; /* Contains panid */
+ int channel;
+ u16 sf;
+ bool gts_permit;
+ u8 lqi;
+ u32 timestamp; /* FIXME */
+ bool security;
+ u8 mac_sec;
+ bool sec_fail;
+};
+
+int parse_beacon_frame(struct sk_buff *skb, u8 * buf,
+ int *flags, struct list_head *al);
+
+int ieee802154_send_beacon(struct net_device *dev, struct ieee802154_addr *saddr,
+ u16 pan_id, const u8 *buf, int len,
+ int flags, struct list_head *al);
+
+#endif /* IEEE802154_BEACON_H */
+
diff --git a/include/net/ieee802154/beacon_hash.h b/include/net/ieee802154/beacon_hash.h
new file mode 100644
index 0000000..db8457c
--- /dev/null
+++ b/include/net/ieee802154/beacon_hash.h
@@ -0,0 +1,40 @@
+/*
+ * MAC beacon hash storage
+ *
+ * Copyright 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ * Sergey Lapin <[email protected]>
+ * Dmitry Eremin-Solenikov <[email protected]>
+ */
+
+#ifndef IEEE802154_BEACON_HASH_H
+#define IEEE802154_BEACON_HASH_H
+
+#define IEEE802154_BEACON_HTABLE_SIZE 256
+
+struct beacon_node {
+ struct hlist_node list;
+ struct ieee802154_addr coord_addr;
+ u16 pan_addr;
+};
+struct beacon_node *ieee802154_beacon_find_pan(struct ieee802154_addr *coord_addr,
+ u16 pan_addr);
+void ieee802154_beacon_hash_add(struct ieee802154_addr *coord_addr);
+void ieee802154_beacon_hash_del(struct ieee802154_addr *coord_addr);
+void ieee802154_beacon_hash_dump(void);
+#endif
+
diff --git a/include/net/ieee802154/const.h b/include/net/ieee802154/const.h
new file mode 100644
index 0000000..d6e0a2e
--- /dev/null
+++ b/include/net/ieee802154/const.h
@@ -0,0 +1,696 @@
+/*
+ * ieee802154_const.h
+ *
+ * Description: IEEE 802.15.4 Constants and return codes.
+ *
+ * Copyright (C) 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ * Pavel Smolenskiy <[email protected]>
+ * Maxim Gorbachyov <[email protected]>
+ */
+
+#ifndef IEEE802154_CONST_H
+#define IEEE802154_CONST_H
+
+#define IEEE802154_ACK_LEN 3 /* Size of acknowledge frame */
+
+/* Time related constants, in microseconds.
+ *
+ * The 1SYM_TIME values are shown how much time is needed to transmit one
+ * symbol across media.
+ * The callculation is following:
+ * For a 2450 MHZ radio freq rate is 62,5 ksym/sec. A byte (8 bit) transfered
+ * by low 4 bits in first symbol, high 4 bits in next symbol. So, to transmit
+ * 1 byte in 2450Mhz freq 2 symbols are needed. Therefore we have 31,25 kbyte/sec
+ * rate. 1 symbol transfered in 16*10^(-6) sec, or 16 microseconds.
+ * For a 868Mhz and 915Mhz, 1 symbol is equal to 1 byte. So, we have 20kbyte/sec
+ * and 40 kbyte/sec respectively. And 5*10^(-5) sec and 2,5*10(-5) sec,
+ * or 50 and 25 microseconds respectively for 868Mhz and 915Mhz freq.
+ */
+#define IEEE802154_2450MHZ_1SYM_TIME 16
+#define IEEE802154_868MHZ_1SYM_TIME 50
+#define IEEE802154_915MHZ_1SYM_TIME 25
+
+/******************************************************************************/
+/* MAC constants */
+/******************************************************************************/
+/** The number of symbols forming a superframe slot when the superframe order
+ * is equal to 0.
+ */
+#define IEEE802154_BASE_SD 60
+
+/** The number of slots contained in any superframe. */
+#define IEEE802154_NUM_SFS 16
+
+/** The number of symbols forming a superframe when the superframe order is
+ * equal to 0.
+ */
+#define IEEE802154_BASE_SFD (IEEE802154_BASE_SD*IEEE802154_NUM_SFS)
+
+/** The 64 bit (IEEE) address assigned to the device. */
+#define IEEE802154_EXT_ADDR 0
+
+/** The maximum value of the backoff exponent in the CSMA-CA algorithm. */
+#define IEEE802154_MAXBE 5
+
+/** The maximum number of octets added by the MAC sublayer to the payload of
+ * its beacon frame.
+ */
+#define IEEE802154_MAX_BOVERHEAD 75
+
+/** The maximum size, in octets, of a beacon payload. */
+#define IEEE802154_MAX_BPAYLOAD (IEEE802154_MAX_PHY_PACKET_SIZE - IEEE802154_MAX_BOVERHEAD)
+
+/** The number of superframes in which a GTS descriptor exists in the beacon
+ * frame of a PAN coordinator.
+ */
+#define IEEE802154_GTS_DESC_PERS_TIME 4
+
+/** The maximum number of octets added by the MAC sublayer to its payload
+ * without security. If security is required on a frame, its secure processing
+ * may inflate the frame length so that it is greater than this value. In
+ * this case, an error is generated through the appropriate .confirm or
+ * MLME-COMM-STATUS.indication primitives.
+ */
+#define IEEE802154_MAX_FRAME_OVERHEAD 25
+
+/** The maximum number of CAP symbols in a beaconenabled PAN, or symbols in a
+ * nonbeacon-enabled PAN, to wait for a frame intended as a response to a
+ * data request frame.
+ */
+#define IEEE802154_MAX_FRAME_RESP_TIME 1220
+
+/** The maximum number of retries allowed after a transmission failure. */
+#define IEEE802154_MAX_FRAME_RETRIES 3
+
+/** The number of consecutive lost beacons that will cause the MAC sublayer of
+ * a receiving device to declare a loss of synchronization.
+ */
+#define IEEE802154_MAX_LOST_BEACONS 4
+
+/** The maximum number of octets that can be transmitted in the MAC frame
+ * payload field.
+ */
+#define IEEE802154_MAX_FRAME_SIZE (IEEE802154_MAX_PHY_PACKET_SIZE - IEEE802154_MAX_FRAME_OVERHEAD)
+
+/** The maximum size of an MPDU, in octets, that can be followed by a short
+ * interframe spacing (SIFS) period.
+ */
+#define IEEE802154_MAX_SIFS_FRAME_SIZE 18
+
+/** The minimum number of symbols forming the CAP. This ensures that MAC
+ * commands can still be transferred to devices when GTSs are being used.
+ * An exception to this minimum shall be allowed for the accommodation of the
+ * temporary increase in the beacon frame length needed to perform GTS
+ * maintenance (see 802.15.4-2003.pdf, item 7.2.2.1.3). */
+#define IEEE802154_MIN_CAP_LEN 440
+
+/** The minimum number of symbols forming a long interframe spacing (LIFS) period. */
+#define IEEE802154_MIN_LIFS_PERIOD 40
+
+/** The minimum number of symbols forming a SIFS period. */
+#define IEEE802154_MIN_SIFS_PERIOD 12
+
+/** The maximum number of symbols a device shall wait for a response command to
+ * be available following a request command.
+ */
+#define IEEE802154_RESPONSE_WAIT_TIME (32*IEEE802154_BASE_SFD)
+
+/** The number of symbols forming the basic time period used by the CSMA-CA
+ * algorithm.
+ */
+#define IEEE802154_UNIT_BACKOFF_PERIOD 20
+
+/******************************************************************************/
+/* PAN Information Base (PIB), MAC attribute identifiers */
+/* See IEEE802.15.4-2003 draft, Table 71 */
+/******************************************************************************/
+/** The maximum number of symbols to wait for an acknowledgment frame to arrive
+ * following a transmitted data frame. This value is dependent on the currently
+ * selected logical channel. For 0 ≤ phyCurrentChannel ≤ 10, this value is equal
+ * to 120. For 11 ≤ phyCurrentChannel ≤ 26, this value is equal to 54.
+ */
+#define IEEE802154_ACK_WAIT_DURATION 0x40
+
+/** Indication of whether a coordinator is currently allowing association.
+ * A value of TRUE indicates that association is permitted.
+ */
+#define IEEE802154_ASSOCIATION_PERMIT 0x41
+
+/** Indication of whether a device automatically sends a data request command
+ * if its address is listed in the beacon frame. A value of TRUE indicates that
+ * the data request command is automatically sent.
+ */
+#define IEEE802154_AUTO_REQUEST 0x42
+
+/** Indication of whether battery life extension, by reduction of coordinator
+ * receiver operation time during the CAP, is enabled. A value of TRUE indicates
+ * that it is enabled.
+ */
+#define IEEE802154_BAT_LIFE_EXT 0x43
+
+/** The number of backoff periods during which the receiver is enabled following
+ * a beacon in battery life extension mode. This value is dependent on the
+ * currently selected logical channel. For 0 ≤ phyCurrentChannel ≤ 10, this
+ * value is equal to 8. For 11 ≤ phyCurrentChannel ≤ 26, this value is equal
+ * to 6.
+ */
+#define IEEE802154_BAT_LIFE_EXT_PERIOD 0x44
+
+/** The contents of the beacon payload.*/
+#define IEEE802154_BEACON_PAYLOAD 0x45
+
+/** The length, in octets, of the beacon payload.*/
+#define IEEE802154_BEACON_PAYLOAD_LEN 0x46
+
+/** Specification of how often the coordinator transmits a beacon. The
+ * macBeaconOrder, BO, and the beacon interval, BI, are related as follows:
+ * for 0 ≤ BO ≤ 14, BI = aBaseSuperframeDuration * 2BO symbols. If BO = 15,
+ * the coordinator will not transmit a beacon.
+ */
+#define IEEE802154_BEACON_ORDER 0x47
+
+/** The time that the device transmitted its last beacon frame, in symbol
+ * periods. The measurement shall be taken at the same symbol boundary within
+ * every transmitted beacon frame, the location of which is implementation
+ * specific. The precision of this value shall be a minimum of 20 bits, with the
+ * lowest four bits being the least significant.
+ */
+#define IEEE802154_BEACON_TX_TIME 0x48
+
+/** The sequence number added to the transmitted beacon frame. */
+#define IEEE802154_BSN 0x49
+
+/** The 64 bit address of the coordinator with which the device is associated. */
+#define IEEE802154_COORD_EXTENDED_ADDRESS 0x4a
+
+/** The 16 bit short address assigned to the coordinator with which the device
+ * is associated. A value of 0xfffe indicates that the coordinator is only
+ * using its 64 bit extended address. A value of 0xffff indicates that this
+ * value is unknown.
+ */
+#define IEEE802154_COORD_SHORT_ADDRESS 0x4b
+
+/** The sequence number added to the transmitted data or MAC command frame. */
+#define IEEE802154_DSN 0x4c
+
+/** TRUE if the PAN coordinator is to accept GTS requests. FALSE otherwise. */
+#define IEEE802154_GTS_PERMIT 0x4d
+
+/** The maximum number of backoffs the CSMA-CA algorithm will attempt before
+ * declaring a channel access failure.
+ */
+#define IEEE802154_MAX_CSMA_BACKOFF 0x4e
+
+/** The minimum value of the backoff exponent in the CSMA-CA algorithm. Note
+ * that if this value is set to 0, collision avoidance is disabled during the
+ * first iteration of the algorithm. Also note that for the slotted version of
+ * the CSMACA algorithm with the battery life extension enabled, the minimum
+ * value of the backoff exponent will be the lesser of 2 and the value of
+ * macMinBE.
+ */
+#define IEEE802154_MIN_BE 0x4f
+
+/** The 16 bit identifier of the PAN on which the device is operating. If this
+ * value is 0 x ffff, the device is not associated.
+ */
+#define IEEE802154_PANID 0x50
+
+/** This indicates whether the MAC sublayer is in a promiscuous (receive all)
+ * mode. A value of TRUE indicates that the MAC sublayer accepts all frames
+ * received from the PHY.
+ */
+#define IEEE802154_PROMISCOUS_MODE 0x51
+
+/** This indicates whether the MAC sublayer is to enable its receiver during
+ * idle periods.
+ */
+#define IEEE802154_RXON_WHEN_IDLE 0x52
+
+/** The 16 bit address that the device uses to communicate in the PAN. If the
+ * device is a PAN coordinator, this value shall be chosen before a PAN is
+ * started. Otherwise, the address is allocated by a coordinator during
+ * association. 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.
+ */
+#define IEEE802154_SHORT_ADDRESS 0x53
+
+/** This specifies the length of the active portion of the superframe, including
+ * the beacon frame. The macSuperframeOrder, SO, and the superframe duration,
+ * SD, are related as follows: for 0 ≤ SO ≤ BO ≤ 14, SD = aBaseSuperframeDuration * 2SO
+ * symbols. If SO = 15, the superframe will not be active following the beacon.
+ */
+#define IEEE802154_SUPERFRAME_ORDER 0x54
+
+/** The maximum time (in superframe periods) that a transaction is stored by a
+ * coordinator and indicated in its beacon.
+ */
+#define IEEE802154_TRANSACTION_PERSISTENSE_TIME 0x55
+
+/******************************************************************************/
+/* PAN Information Base (PIB), MAC attribute ranges */
+/* See IEEE802.15.4-2003 draft, Table 71 */
+/******************************************************************************/
+/**
+ * The maximum number of symbols to wait for an acknowledgment frame to arrive
+ * following a transmitted data frame. This value is dependent on the currently
+ * selected logical channel. For 0 ≤ phyCurrentChannel ≤ 10, this value is equal
+ * to 120. For 11 ≤ phyCurrentChannel ≤ 26, this value is equal to 54.
+ */
+#define IEEE802154_ACK_WAIT_DURATION_DEF 0x36
+#define IEEE802154_ACK_WAIT_DURATION_MIN 0x36
+#define IEEE802154_ACK_WAIT_DURATION_MAX 0x78
+
+/**
+ * Indication of whether a coordinator is currently allowing association. A
+ * value of TRUE indicates that association is permitted.
+ */
+#define IEEE802154_ASSOCIATION_PERMIT_DEF false
+
+/**
+ * Indication of whether a device automatically sends a data request command if
+ * its address is listed in the beacon frame. A value of TRUE indicates that the
+ * data request command is automatically sent.
+ */
+#define IEEE802154_AUTO_REQUEST_DEF true
+
+/**
+ * Indication of whether battery life extension, by reduction of coordinator
+ * receiver operation time during the CAP, is enabled. A value of TRUE indicates
+ * that it is enabled.
+ */
+#define IEEE802154_BAT_LIFE_EXT_DEF true
+
+/**
+ * The number of backoff periods during which the receiver is enabled following
+ * a beacon in battery life extension mode. This value is dependent on the
+ * currently selected logical channel. For 0 ≤ phyCurrentChannel ≤ 10, this
+ * value is equal to 8. For 11 ≤ phyCurrentChannel ≤ 26, this value is equal
+ * to 6.
+ */
+#define IEEE802154_BAT_LIFE_EXT_PERIOD_DEF 0x6
+#define IEEE802154_BAT_LIFE_EXT_PERIOD_MIN 0x6
+#define IEEE802154_BAT_LIFE_EXT_PERIOD_MAX 0x8
+
+/**
+ * The contents of the beacon payload.
+ */
+#define IEEE802154_BEACON_PAYLOAD_DEF NULL
+
+/**
+ * The length, in octets, of the beacon payload.
+ */
+#define IEEE802154_BEACON_PAYLOAD_LEN_DEF 0x0
+#define IEEE802154_BEACON_PAYLOAD_LEN_MIN 0x0
+#define IEEE802154_BEACON_PAYLOAD_LEN_MAX IEEE802154_BEACON_PAYLOAD_LEN
+
+/**
+ * Specification of how often the coordinator transmits a beacon. The
+ * macBeaconOrder, BO, and the beacon interval, BI, are related as follows:
+ * for 0 ≤ BO ≤ 14, BI = aBaseSuperframeDuration * 2BO symbols. If BO = 15, the
+ * coordinator will not transmit a beacon.
+ */
+#define IEEE802154_BEACON_ORDER_DEF 0xf
+#define IEEE802154_BEACON_ORDER_MIN 0x0
+#define IEEE802154_BEACON_ORDER_MAX 0xf
+
+/**
+ * The time that the device transmitted its last beacon frame, in symbol periods.
+ * The measurement shall be taken at the same symbol boundary within every
+ * transmitted beacon frame, the location of which is implementation specific.
+ * The precision of this value shall be a minimum of 20 bits, with the lowest
+ * four bits being the least significant.
+ */
+#define IEEE802154_BEACON_TX_TIME_DEF 0x0
+#define IEEE802154_BEACON_TX_TIME_MIN 0x0
+#define IEEE802154_BEACON_TX_TIME_MAX 0xffffffff
+
+/**
+ * The sequence number added to the transmitted beacon frame.
+ */
+#define IEEE802154_BSN_MIN 0x0
+#define IEEE802154_BSN_MAX 0xff
+
+/**
+ * The 16 bit short address assigned to the coordinator with which the device is
+ * associated. A value of 0xfffe indicates that the coordinator is only using
+ * its 64 bit extended address. A value of 0xffff indicates that this value is
+ * unknown.
+ */
+#define IEEE802154_COORD_SHORT_ADDRESS_DEF 0xffff
+#define IEEE802154_COORD_SHORT_ADDRESS_MIN 0x0
+#define IEEE802154_COORD_SHORT_ADDRESS_MAX 0xffff
+#define IEEE802154_COORD_SHORT_ADDRESS_64BIT 0xfffe
+
+#define IEEE802154_COORD_EXT_ADDRESS_DEF 0xffffffff
+
+/**
+ * The sequence number added to the transmitted data or MAC command frame.
+ */
+#define IEEE802154_DSN_MIN 0x0
+#define IEEE802154_DSN_MAX 0xff
+
+/**
+ * TRUE if the PAN coordinator is to accept GTS requests. FALSE otherwise.
+ */
+#define IEEE802154_GTS_PERMIT_DEF true
+
+/**
+ * The maximum number of backoffs the CSMA-CA algorithm will attempt before
+ * declaring a channel access failure.
+ */
+#define IEEE802154_MAX_CSMA_BACKOFF_DEF 0x4
+#define IEEE802154_MAX_CSMA_BACKOFF_MIN 0x0
+#define IEEE802154_MAX_CSMA_BACKOFF_MAX 0x5
+
+/**
+ * The minimum value of the backoff exponent in the CSMA-CA algorithm. Note that
+ * if this value is set to 0, collision avoidance is disabled during the first
+ * iteration of the algorithm. Also note that for the slotted version of the
+ * CSMA-CA algorithm with the battery life extension enabled, the minimum value
+ * of the backoff exponent will be the lesser of 2 and the value of macMinBE.
+ */
+#define IEEE802154_MIN_BE_DEF 0x3
+#define IEEE802154_MIN_BE_MIN 0x0
+#define IEEE802154_MIN_BE_MAX 0x3
+
+/**
+ * The 16 bit identifier of the PAN on which the device is operating. If this
+ * value is 0xffff, the device is not associated.
+ */
+#define IEEE802154_PANID_DEF 0xffff
+#define IEEE802154_PANID_MIN 0x0
+#define IEEE802154_PANID_MAX 0xffff
+
+/**
+ * This indicates whether the MAC sublayer is in a promiscuous (receive all)
+ * mode. A value of TRUE indicates that the MAC sublayer accepts all frames
+ * received from the PHY.
+ */
+#define IEEE802154_PROMISCOUS_MODE_DEF false
+
+/**
+ * This indicates whether the MAC sublayer is to enable its receiver during idle
+ * periods.
+ */
+#define IEEE802154_RXON_WHEN_IDLE_DEF false
+
+/**
+ * The 16 bit address that the device uses to communicate in the PAN. If the
+ * device is a PAN coordinator, this value shall be chosen before a PAN is
+ * started. Otherwise, the address is allocated by a coordinator during
+ * association. 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.
+ */
+#define IEEE802154_SHORT_ADDRESS_DEF 0xffff
+#define IEEE802154_SHORT_ADDRESS_MIN 0x0
+#define IEEE802154_SHORT_ADDRESS_MAX 0xffff
+
+/**
+ * This specifies the length of the active portion of the superframe, including
+ * the beacon frame. The macSuperframeOrder, SO, and the superframe duration,
+ * SD, are related as follows: for 0 ≤ SO ≤ BO ≤ 14,
+ * SD = aBaseSuperframeDuration * 2SO symbols. If SO = 15, the superframe will
+ * not be active following the beacon.
+ */
+#define IEEE802154_SUPERFRAME_ORDER_DEF 0xf
+#define IEEE802154_SUPERFRAME_ORDER_MIN 0x0
+#define IEEE802154_SUPERFRAME_ORDER_MAX 0xf
+
+/**
+ * The maximum time (in superframe periods) that a transaction is stored by a
+ * coordinator and indicated in its beacon.
+ */
+#define IEEE802154_TRANSACTION_PERSISTENSE_TIME_DEF 0x1f4
+#define IEEE802154_TRANSACTION_PERSISTENSE_TIME_MIN 0x0
+#define IEEE802154_TRANSACTION_PERSISTENSE_TIME_MAX 0xffff
+
+/******************************************************************************/
+/* PAN Information Base (PIB), MAC security attribute identifiers */
+/* See IEEE802.15.4-2003 draft, Table 71 */
+/******************************************************************************/
+/** A set of ACL entries, each containing address information, security suite
+ * information, and security material to be used to protect frames between the
+ * MAC sublayer and the specified device.
+ */
+#define IEEE802154_ACL_ENTRY_DESCRIPTOR_SET 0x70
+
+/** The number of entries in the ACL descriptor set. */
+#define IEEE802154_ACL_ENTRY_DESCRIPTOR_SET_SIZE 0x71
+
+/** Indication of whether the device is able to transmit secure frames to or
+ * accept secure frames from devices that are not explicitly listed in the ACL.
+ * It is also used to communicate with multiple devices at once. A value of
+ * TRUE indicates that such transmissions are permitted.
+ */
+#define IEEE802154_DEFAULT_SECURITY 0x72
+
+/** The number of octets contained in ACLSecurityMaterial. */
+#define IEEE802154_DEFAULT_SECURITY_MLEN 0x73
+
+/** The specific security material to be used to protect frames between the MAC
+ * sublayer and devices not in the ACL (see 802.15.4-2003.pdf, item 7.6.1.8).
+ */
+#define IEEE802154_DEFAULT_SECURITY_MATERIAL 0x74
+
+/** The unique identifier of the security suite to be used to protect
+ * communications between the MAC and devices not in the ACL as specified in
+ * 802.15.4-2003.pdf, Table 75.
+ */
+#define IEEE802154_DEFAULT_SECURITY_SUITE 0x75
+
+/** The identifier of the security use as specified in 802.15.4-2003.pdf, item
+ * 7.5.8.
+ * 0 x 00 = Unsecured mode.
+ * 0 x 01 = ACL mode.
+ * 0 x 02 = Secured mode.
+ */
+#define IEEE802154_SECURITY_MODE 0x76
+
+/******************************************************************************/
+/* PAN Information Base (PIB), MAC security attribute ranges */
+/* See IEEE802.15.4-2003 draft, Table 72 */
+/******************************************************************************/
+/** A set of ACL entries, each containing address information, security suite
+ * information, and security material to be used to protect frames between the
+ * MAC sublayer and the specified device.
+ */
+#define IEEE802154_ACL_ENTRY_DESCRIPTOR_SET_DEF NULL
+
+/** The number of entries in the ACL descriptor set. */
+#define IEEE802154_ACL_ENTRY_DESCRIPTOR_SET_SIZE_DEF 0x0
+#define IEEE802154_ACL_ENTRY_DESCRIPTOR_SET_SIZE_MIN 0x0
+#define IEEE802154_ACL_ENTRY_DESCRIPTOR_SET_SIZE_MAX 0xff
+
+/** Indication of whether the device is able to transmit secure frames to or
+ * accept secure frames from devices that are not explicitly listed in the ACL.
+ * It is also used to communicate with multiple devices at once. A value of
+ * TRUE indicates that such transmissions are permitted.
+ */
+#define IEEE802154_DEFAULT_SECURITY_DEF false
+
+/** The number of octets contained in ACLSecurityMaterial. */
+#define IEEE802154_DEFAULT_SECURITY_MLEN_DEF 0x15
+#define IEEE802154_DEFAULT_SECURITY_MLEN_MIN 0x0
+#define IEEE802154_DEFAULT_SECURITY_MLEN_MAX 0x1a
+
+/** The specific security material to be used to protect frames between the MAC
+ * sublayer and devices not in the ACL (see 802.15.4-2003.pdf, item 7.6.1.8).
+ */
+/*#warning "FIXME: IEEE802154_DEFAULT_SECURITY_MATERIAL_MAX value"*/
+#define IEEE802154_DEFAULT_SECURITY_MATERIAL_DEF NULL
+#define IEEE802154_DEFAULT_SECURITY_MATERIAL_MIN NULL
+#define IEEE802154_DEFAULT_SECURITY_MATERIAL_MAX 0x20
+
+/** The unique identifier of the security suite to be used to protect
+ * communications between the MAC and devices not in the ACL as specified in
+ * 802.15.4-2003.pdf, Table 75.
+ */
+#define IEEE802154_DEFAULT_SECURITY_SUITE_DEF 0x0
+#define IEEE802154_DEFAULT_SECURITY_SUITE_MIN 0x0
+#define IEEE802154_DEFAULT_SECURITY_SUITE_MAX 0x7
+
+/** The identifier of the security use as specified in 802.15.4-2003.pdf, item
+ * 7.5.8.
+ * 0 x 00 = Unsecured mode.
+ * 0 x 01 = ACL mode.
+ * 0 x 02 = Secured mode.
+ */
+#define IEEE802154_SECURITY_MODE_DEF 0x0
+#define IEEE802154_SECURITY_MODE_MIN 0x0
+#define IEEE802154_SECURITY_MODE_MAX 0x2
+
+
+/****************/
+/* Result codes */
+/****************/
+/**
+ * \brief PHY return codes description
+ *
+ * The return values of PHY operations
+ */
+enum ieee802154_rcodes {
+ /**< The CCA attempt has detected a busy channel */
+ IEEE802154_BUSY = 0x0,
+ /** The transceiver is asked to change its state while receiving */
+ IEEE802154_BUSY_RX = 0x1,
+ /** The transceiver is asked to change its state while transmitting */
+ IEEE802154_BUSY_TX = 0x2,
+ /** The transceiver is to be switched off */
+ IEEE802154_FORCE_TRX_OFF = 0x3,
+ /** The CCA attempt has detected an idle channel */
+ IEEE802154_IDLE = 0x4,
+ /**
+ * A SET/GET request was issued with a parameter in the primitive that
+ * is out of the valid range
+ */
+ IEEE802154_PHY_INVALID_PARAMETER = 0x5,
+ /**
+ * The transceiver is in or is to be configured into the receiver
+ * enabled state
+ */
+ IEEE802154_RX_ON = 0x6,
+ /**
+ * A SET/GET, an ED operation, or a transceiver state change was
+ * successful
+ */
+ IEEE802154_PHY_SUCCESS = 0x7,
+ /**
+ * The transceiver is in or is to be configured into the transceiver
+ * disabled state
+ */
+ IEEE802154_TRX_OFF = 0x8,
+ /**
+ * The transceiver is in or is to be configured into the transmitter
+ * enabled state
+ */
+ IEEE802154_TX_ON = 0x9,
+ /**
+ * A SET/GET request was issued with the identifier of an attribute that
+ * is not supported
+ */
+ IEEE802154_UNSUPPORTED_ATTRIBUTE = 0xa,
+};
+
+/**
+ * \brief MAC return codes description
+ *
+ * The return values of MAC operations
+ */
+enum ieee802154_mac_rcodes {
+ /**
+ * The requested operation was completed successfully. For a transmission
+ * request, this value indicates a successful transmission.
+ */
+ IEEE802154_SUCCESS = 0x0,
+ /**< The disassociation reason code: coordinator kick off device from pan */
+ IEEE802154_KICK_DEV = 0x1,
+ /**< The disassociation reason code: device wishes to leave the pan */
+ IEEE802154_LEAVE_DEV = 0x2,
+ /**< The beacon was lost following a synchronization request. */
+ IEEE802154_BEACON_LOSS = 0xe0,
+ /**
+ * A transmission could not take place due to activity on the
+ * channel, i.e., the CSMA-CA mechanism has failed.
+ */
+ IEEE802154_CHNL_ACCESS_FAIL = 0xe1,
+ /**< The GTS request has been denied by the PAN coordinator. */
+ IEEE802154_DENINED = 0xe2,
+ /**< The attempt to disable the transceiver has failed. */
+ IEEE802154_DISABLE_TRX_FAIL = 0xe3,
+ /**
+ * The received frame induces a failed security check according to
+ * the security suite.
+ */
+ IEEE802154_FAILED_SECURITY_CHECK = 0xe4,
+ /**
+ * The frame resulting from secure processing has a length that is
+ * greater than aMACMaxFrameSize.
+ */
+ IEEE802154_FRAME_TOO_LONG = 0xe5,
+ /**
+ * The requested GTS transmission failed because the specified GTS
+ * either did not have a transmit GTS direction or was not defined.
+ */
+ IEEE802154_INVALID_GTS = 0xe6,
+ /**
+ * A request to purge an MSDU from the transaction queue was made using
+ * an MSDU handle that was not found in the transaction table.
+ */
+ IEEE802154_INVALID_HANDLE = 0xe7,
+ /**< A parameter in the primitive is out of the valid range.*/
+ IEEE802154_INVALID_PARAMETER = 0xe8,
+ /**< No acknowledgment was received after aMaxFrameRetries. */
+ IEEE802154_NO_ACK = 0xe9,
+ /**< A scan operation failed to find any network beacons.*/
+ IEEE802154_NO_BEACON = 0xea,
+ /**< No response data were available following a request. */
+ IEEE802154_NO_DATA = 0xeb,
+ /**< The operation failed because a short address was not allocated. */
+ IEEE802154_NO_SHORT_ADDRESS = 0xec,
+ /**
+ * A receiver enable request was unsuccessful because it could not be
+ * completed within the CAP.
+ */
+ IEEE802154_OUT_OF_CAP = 0xed,
+ /**
+ * A PAN identifier conflict has been detected and communicated to the
+ * PAN coordinator.
+ */
+ IEEE802154_PANID_CONFLICT = 0xee,
+ /**< A coordinator realignment command has been received. */
+ IEEE802154_REALIGMENT = 0xef,
+ /**< The transaction has expired and its information discarded. */
+ IEEE802154_TRANSACTION_EXPIRED = 0xf0,
+ /**< There is no capacity to store the transaction. */
+ IEEE802154_TRANSACTION_OVERFLOW = 0xf1,
+ /**
+ * The transceiver was in the transmitter enabled state when the
+ * receiver was requested to be enabled.
+ */
+ IEEE802154_TX_ACTIVE = 0xf2,
+ /**< The appropriate key is not available in the ACL. */
+ IEEE802154_UNAVAILABLE_KEY = 0xf3,
+ /**
+ * A SET/GET request was issued with the identifier of a PIB attribute
+ * that is not supported.
+ */
+ IEEE802154_UNSUPPORTED_ATTR = 0xf4,
+ /*
+ * A request to perform a scan operation failed because the MLME was
+ * in the process of performing a previously initiated scan operation.
+ */
+ IEEE802154_SCAN_IN_PROGRESS = 0xfc,
+};
+
+/**
+ * other errors
+ */
+#define IEEE802154_ERROR 0xff
+
+#define ZB_ED_MAX 0xff
+#define ZB_ED_MIN 0x0
+/* #define ZB_ED_EDGE 0x7f */
+/* I've got 0xBE on idle channel; let threshold be a little higher */
+#define ZB_ED_EDGE 0xc8
+
+/* In an ideal world this should be 1 */
+#define IEEE802154_SLOW_SERIAL_FIXUP 75
+
+#endif /* IEEE802154_CONST_H */
diff --git a/include/net/ieee802154/crc.h b/include/net/ieee802154/crc.h
new file mode 100644
index 0000000..5b37218
--- /dev/null
+++ b/include/net/ieee802154/crc.h
@@ -0,0 +1,38 @@
+/*
+ * based on crc-itu-t.c.
+ * Basically it's CRC-ITU-T but with inverted bit numbering
+ *
+ * Copyright 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ * Dmitry Eremin-Solenikov <[email protected]>
+ */
+
+extern const u16 ieee802154_crc_table[256];
+
+static inline u16 ieee802154_crc_byte(u16 crc, const u8 data)
+{
+ return (crc >> 8) ^ ieee802154_crc_table[(crc ^ data) & 0xff];
+}
+
+static inline u16 ieee802154_crc(u16 crc, const u8 *buffer, size_t len)
+{
+ while (len--)
+ crc = ieee802154_crc_byte(crc, *buffer++);
+ return crc;
+}
+
+
diff --git a/include/net/ieee802154/dev.h b/include/net/ieee802154/dev.h
new file mode 100644
index 0000000..1d28187
--- /dev/null
+++ b/include/net/ieee802154/dev.h
@@ -0,0 +1,107 @@
+/*
+ * IEEE802.15.4-2003 specification
+ *
+ * Copyright (C) 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ */
+#ifndef IEEE802154_DEV_H
+#define IEEE802154_DEV_H
+
+#include <linux/skbuff.h>
+#include <net/ieee802154/phy.h>
+#include <net/ieee802154/const.h>
+
+struct ieee802154_pib {
+ int type;
+ u32 val;
+};
+
+#define IEEE802154_PIB_CURCHAN 0 /* Current channel, u8 6.1.2 */
+#define IEEE802154_PIB_CHANSUPP 1 /* Channel mask, u32 6.1.2 */
+#define IEEE802154_PIB_TRPWR 2 /* Transmit power, u8 6.4.2 */
+#define IEEE802154_PIB_CCAMODE 3 /* CCA mode, u8 6.7.9 */
+
+struct ieee802154_dev {
+ const char *name;
+ int extra_tx_headroom; /* headroom to reserve for tx skb */
+ void *priv; /* driver-specific data */
+ u32 channel_mask;
+ u8 current_channel;
+ u32 flags; /* Flags for device to set */
+ struct device *parent;
+};
+
+/* Checksum is in hardware and is omitted from packet */
+#define IEEE802154_OPS_OMIT_CKSUM (1 << 0)
+
+
+struct ieee802154_ops {
+ struct module *owner;
+ phy_status_t (*tx)(struct ieee802154_dev *dev, struct sk_buff *skb);
+ phy_status_t (*cca)(struct ieee802154_dev *dev);
+ phy_status_t (*ed)(struct ieee802154_dev *dev, u8 *level);
+ phy_status_t (*set_trx_state)(struct ieee802154_dev *dev, phy_status_t state);
+ phy_status_t (*set_channel)(struct ieee802154_dev *dev, int channel);
+ /* FIXME: PIB get/set ??? */
+};
+
+#ifdef __KERNEL__
+#define IEEE802154_MAC_CMD_SCAN 0
+
+struct ieee802154_priv {
+ struct ieee802154_dev hw;
+ struct ieee802154_ops *ops;
+ struct net_device *master;
+ struct list_head slaves;
+ spinlock_t slaves_lock;
+ /* This one is used for scanning and other
+ * jobs not to be interfered with serial driver */
+ struct workqueue_struct *dev_workqueue;
+ /* MAC BSN field */
+ u8 bsn;
+ /* MAC BSN field */
+ u8 dsn;
+};
+
+#define ieee802154_to_priv(_hw) container_of(_hw, struct ieee802154_priv, hw)
+
+#endif
+
+struct ieee802154_dev *ieee802154_alloc_device(void);
+int ieee802154_register_device(struct ieee802154_dev *dev, struct ieee802154_ops *ops);
+void ieee802154_unregister_device(struct ieee802154_dev *dev);
+void ieee802154_free_device(struct ieee802154_dev *dev);
+
+int ieee802154_add_slave(struct ieee802154_dev *hw, const u8 *addr);
+/* void ieee802154_del_slave(struct ieee802154_dev *hw, struct net_device *slave); */
+void ieee802154_drop_slaves(struct ieee802154_dev *hw);
+
+void ieee802154_rx(struct ieee802154_dev *dev, struct sk_buff *skb, u8 lqi);
+void ieee802154_rx_irqsafe(struct ieee802154_dev *dev, struct sk_buff *skb, u8 lqi);
+
+int ieee802154_pib_set(struct ieee802154_dev *hw, struct ieee802154_pib *pib);
+int ieee802154_pib_get(struct ieee802154_dev *hw, struct ieee802154_pib *pib);
+
+int ieee802154_slave_register_notifier(struct net_device *dev, struct notifier_block *nb);
+int ieee802154_slave_unregister_notifier(struct net_device *dev, struct notifier_block *nb);
+int ieee802154_slave_event(struct net_device *dev, int event, void *data);
+
+#define IEEE802154_NOTIFIER_BEACON 0x0
+
+void ieee802154_set_pan_id(struct net_device *dev, u16 panid);
+#endif
+
diff --git a/include/net/ieee802154/mac_def.h b/include/net/ieee802154/mac_def.h
new file mode 100644
index 0000000..42597f7
--- /dev/null
+++ b/include/net/ieee802154/mac_def.h
@@ -0,0 +1,96 @@
+/*
+ * IEEE802.15.4-2003 specification
+ *
+ * Copyright (C) 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ * Pavel Smolenskiy <[email protected]>
+ * Maxim Gorbachyov <[email protected]>
+ * Maxim Osipov <[email protected]>
+ * Dmitry Eremin-Solenikov <[email protected]>
+ */
+
+#ifndef IEEE802154_MAC_DEF_H
+#define IEEE802154_MAC_DEF_H
+
+#define IEEE802154_PANID_BROADCAST 0xffff
+#define IEEE802154_ADDR_BROADCAST 0xffff
+#define IEEE802154_ADDR_UNDEF 0xfffe
+
+#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 */
+#define IEEE802154_FC_TYPE_MAC_CMD 0x3 /* Frame is MAC command */
+
+#define IEEE802154_FC_TYPE_SHIFT 0
+#define IEEE802154_FC_TYPE_MASK ((1 << 3) - 1)
+#define IEEE802154_FC_TYPE(x) ((x & IEEE802154_FC_TYPE_MASK) >> IEEE802154_FC_TYPE_SHIFT)
+#define IEEE802154_FC_SET_TYPE(v, x) do {v = (((v) & ~IEEE802154_FC_TYPE_MASK) | \
+ (((x) << IEEE802154_FC_TYPE_SHIFT) \
+ & IEEE802154_FC_TYPE_MASK)); } while (0)
+
+#define IEEE802154_FC_SECEN (1 << 3)
+#define IEEE802154_FC_FRPEND (1 << 4)
+#define IEEE802154_FC_ACK_REQ (1 << 5)
+#define IEEE802154_FC_INTRA_PAN (1 << 6)
+
+#define IEEE802154_FC_SAMODE_SHIFT 14
+#define IEEE802154_FC_SAMODE_MASK (3 << IEEE802154_FC_SAMODE_SHIFT)
+#define IEEE802154_FC_DAMODE_SHIFT 10
+#define IEEE802154_FC_DAMODE_MASK (3 << IEEE802154_FC_DAMODE_SHIFT)
+
+#define IEEE802154_FC_SAMODE(x) \
+ (((x) & IEEE802154_FC_SAMODE_MASK) >> IEEE802154_FC_SAMODE_SHIFT)
+
+#define IEEE802154_FC_DAMODE(x) \
+ (((x) & IEEE802154_FC_DAMODE_MASK) >> IEEE802154_FC_DAMODE_SHIFT)
+
+
+/* MAC's Command Frames Identifiers */
+#define IEEE802154_CMD_ASSOCIATION_REQ 0x01
+#define IEEE802154_CMD_ASSOCIATION_RESP 0x02
+#define IEEE802154_CMD_DISASSOCIATION_NOTIFY 0x03
+#define IEEE802154_CMD_DATA_REQ 0x04
+#define IEEE802154_CMD_PANID_CONFLICT_NOTIFY 0x05
+#define IEEE802154_CMD_ORPHAN_NOTIFY 0x06
+#define IEEE802154_CMD_BEACON_REQ 0x07
+#define IEEE802154_CMD_COORD_REALIGN_NOTIFY 0x08
+#define IEEE802154_CMD_GTS_REQ 0x09
+
+#ifdef __KERNEL__
+int ieee802154_process_cmd(struct net_device *dev, struct sk_buff *skb);
+
+int ieee802154_send_cmd(struct net_device *dev,
+ struct ieee802154_addr *addr, struct ieee802154_addr *saddr,
+ const u8 *buf, int len);
+
+int ieee802154_send_beacon_req(struct net_device *dev);
+int ieee802154_mlme_scan_req(struct net_device *dev, u8 type, u32 channels, u8 duration);
+
+int ieee802154_mlme_start_req(struct net_device *dev, u16 panid, u8 channel,
+ u8 bcn_ord, u8 sf_ord, u8 pan_coord, u8 blx,
+ u8 coord_realign, u8 sec);
+
+#define IEEE802154_MAC_SCAN_ED 0
+#define IEEE802154_MAC_SCAN_ACTIVE 1
+#define IEEE802154_MAC_SCAN_PASSIVE 2
+#define IEEE802154_MAC_SCAN_ORPHAN 3
+
+#endif
+
+#endif
+
+
diff --git a/include/net/ieee802154/netdev.h b/include/net/ieee802154/netdev.h
new file mode 100644
index 0000000..80a22bd
--- /dev/null
+++ b/include/net/ieee802154/netdev.h
@@ -0,0 +1,74 @@
+/*
+ * IEEE802154.4 net device
+ *
+ * Copyright 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef IEEE802154_NETDEV_H
+#define IEEE802154_NETDEV_H
+#include <linux/netdevice.h>
+#include <net/ieee802154/dev.h>
+#include <net/ieee802154/phy.h>
+#include <net/ieee802154/af_ieee802154.h>
+
+int ieee802154_register_netdev_master(struct ieee802154_priv *hw);
+void ieee802154_unregister_netdev_master(struct ieee802154_priv *hw);
+
+/* FIXME: this header should be probably separated, as it contains both driver-specific and stack specific things */
+void ieee802154_subif_rx(struct ieee802154_dev *hw, struct sk_buff *skb);
+struct ieee802154_priv *ieee802154_slave_get_hw(struct net_device *dev);
+
+struct ieee802154_addr;
+struct net_device *ieee802154_get_dev(struct net *net, struct ieee802154_addr *sa);
+
+/* FIXME: should be dropped in favour of MIB getting */
+u16 ieee802154_dev_get_pan_id(struct net_device *dev);
+u16 ieee802154_dev_get_short_addr(struct net_device *dev);
+void ieee802154_dev_set_pan_id(struct net_device *dev, u16 val);
+void ieee802154_dev_set_short_addr(struct net_device *dev, u16 val);
+void ieee802154_dev_set_channel(struct net_device *dev, u8 chan);
+
+struct ieee802154_phy_cb {
+ u8 lqi;
+ u8 chan;
+};
+
+#define PHY_CB(skb) ((struct ieee802154_phy_cb *)(skb)->cb)
+
+
+struct ieee802154_mac_cb {
+ struct ieee802154_phy_cb phy;
+ struct ieee802154_addr sa;
+ struct ieee802154_addr da;
+ u8 flags;
+ u8 seq;
+};
+#define MAC_CB(skb) ((struct ieee802154_mac_cb *)(skb)->cb)
+
+#define MAC_CB_FLAG_TYPEMASK ((1 << 3) - 1)
+
+#define MAC_CB_FLAG_ACKREQ (1 << 3)
+#define MAC_CB_FLAG_SECEN (1 << 4)
+#define MAC_CB_FLAG_INTRAPAN (1 << 5)
+
+#define MAC_CB_IS_ACKREQ(skb) (MAC_CB(skb)->flags & MAC_CB_FLAG_ACKREQ)
+#define MAC_CB_IS_SECEN(skb) (MAC_CB(skb)->flags & MAC_CB_FLAG_SECEN)
+#define MAC_CB_IS_INTRAPAN(skb) (MAC_CB(skb)->flags & MAC_CB_FLAG_INTRAPAN)
+#define MAC_CB_TYPE(skb) (MAC_CB(skb)->flags & MAC_CB_FLAG_TYPEMASK)
+
+#endif
+
diff --git a/include/net/ieee802154/nl.h b/include/net/ieee802154/nl.h
new file mode 100644
index 0000000..311a1da
--- /dev/null
+++ b/include/net/ieee802154/nl.h
@@ -0,0 +1,166 @@
+/*
+ * ieee802154_nl.h
+ *
+ * Copyright (C) 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef IEEE802154_NL_H
+#define IEEE802154_NL_H
+
+#define IEEE802154_NL_NAME "802.15.4 MAC"
+#define IEEE802154_MCAST_COORD_NAME "coordinator"
+#define IEEE802154_MCAST_BEACON_NAME "beacon"
+
+enum {
+ __IEEE802154_ATTR_INVALID,
+
+ IEEE802154_ATTR_DEV_NAME,
+ IEEE802154_ATTR_DEV_INDEX,
+
+ IEEE802154_ATTR_STATUS,
+
+ IEEE802154_ATTR_SHORT_ADDR,
+ IEEE802154_ATTR_HW_ADDR,
+ IEEE802154_ATTR_PAN_ID,
+
+ IEEE802154_ATTR_CHANNEL,
+
+ IEEE802154_ATTR_COORD_SHORT_ADDR,
+ IEEE802154_ATTR_COORD_HW_ADDR,
+ IEEE802154_ATTR_COORD_PAN_ID,
+
+ IEEE802154_ATTR_SRC_SHORT_ADDR,
+ IEEE802154_ATTR_SRC_HW_ADDR,
+ IEEE802154_ATTR_SRC_PAN_ID,
+
+ IEEE802154_ATTR_DEST_SHORT_ADDR,
+ IEEE802154_ATTR_DEST_HW_ADDR,
+ IEEE802154_ATTR_DEST_PAN_ID,
+
+ IEEE802154_ATTR_CAPABILITY, /* FIXME: this is association */
+ IEEE802154_ATTR_REASON,
+ IEEE802154_ATTR_SCAN_TYPE,
+ IEEE802154_ATTR_CHANNELS,
+ IEEE802154_ATTR_DURATION,
+ IEEE802154_ATTR_ED_LIST,
+ IEEE802154_ATTR_BCN_ORD,
+ IEEE802154_ATTR_SF_ORD,
+ IEEE802154_ATTR_PAN_COORD,
+ IEEE802154_ATTR_BAT_EXT,
+ IEEE802154_ATTR_COORD_REALIGN,
+ IEEE802154_ATTR_SEC,
+
+ __IEEE802154_ATTR_MAX,
+};
+
+#define IEEE802154_ATTR_MAX (__IEEE802154_ATTR_MAX - 1)
+#define NLA_HW_ADDR NLA_U64
+#define NLA_GET_HW_ADDR(attr, addr) do { u64 _temp = nla_get_u64(attr); memcpy(addr, &_temp, 8); } while (0)
+#define NLA_PUT_HW_ADDR(msg, attr, addr) do { u64 _temp; memcpy(&_temp, addr, 8); NLA_PUT_U64(msg, attr, _temp); } while (0)
+
+#ifdef IEEE802154_NL_WANT_POLICY
+static struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = {
+ [IEEE802154_ATTR_DEV_NAME] = { .type = NLA_STRING, },
+ [IEEE802154_ATTR_DEV_INDEX] = { .type = NLA_U32, },
+
+ [IEEE802154_ATTR_STATUS] = { .type = NLA_U8, },
+ [IEEE802154_ATTR_SHORT_ADDR] = { .type = NLA_U16, },
+ [IEEE802154_ATTR_HW_ADDR] = { .type = NLA_HW_ADDR, },
+ [IEEE802154_ATTR_PAN_ID] = { .type = NLA_U16, },
+ [IEEE802154_ATTR_CHANNEL] = { .type = NLA_U8, },
+ [IEEE802154_ATTR_COORD_SHORT_ADDR] = { .type = NLA_U16, },
+ [IEEE802154_ATTR_COORD_HW_ADDR] = { .type = NLA_HW_ADDR, },
+ [IEEE802154_ATTR_COORD_PAN_ID] = { .type = NLA_U16, },
+ [IEEE802154_ATTR_SRC_SHORT_ADDR] = { .type = NLA_U16, },
+ [IEEE802154_ATTR_SRC_HW_ADDR] = { .type = NLA_HW_ADDR, },
+ [IEEE802154_ATTR_SRC_PAN_ID] = { .type = NLA_U16, },
+ [IEEE802154_ATTR_DEST_SHORT_ADDR] = { .type = NLA_U16, },
+ [IEEE802154_ATTR_DEST_HW_ADDR] = { .type = NLA_HW_ADDR, },
+ [IEEE802154_ATTR_DEST_PAN_ID] = { .type = NLA_U16, },
+
+ [IEEE802154_ATTR_CAPABILITY] = { .type = NLA_U8, },
+ [IEEE802154_ATTR_REASON] = { .type = NLA_U8, },
+ [IEEE802154_ATTR_SCAN_TYPE] = { .type = NLA_U8, },
+ [IEEE802154_ATTR_CHANNELS] = { .type = NLA_U32, },
+ [IEEE802154_ATTR_DURATION] = { .type = NLA_U8, },
+#ifdef __KERNEL__
+ [IEEE802154_ATTR_ED_LIST] = { .len = 27 },
+#else
+ [IEEE802154_ATTR_ED_LIST] = { .minlen = 27, .maxlen = 27 },
+#endif
+};
+#endif
+
+/* commands */
+/* REQ should be responded with CONF
+ * and INDIC with RESP
+ */
+enum {
+ __IEEE802154_COMMAND_INVALID,
+
+ IEEE802154_ASSOCIATE_REQ,
+ IEEE802154_ASSOCIATE_CONF,
+ IEEE802154_DISASSOCIATE_REQ,
+ IEEE802154_DISASSOCIATE_CONF,
+ IEEE802154_GET_REQ,
+ IEEE802154_GET_CONF,
+/* IEEE802154_GTS_REQ, */
+/* IEEE802154_GTS_CONF, */
+ IEEE802154_RESET_REQ,
+ IEEE802154_RESET_CONF,
+/* IEEE802154_RX_ENABLE_REQ, */
+/* IEEE802154_RX_ENABLE_CONF, */
+ IEEE802154_SCAN_REQ,
+ IEEE802154_SCAN_CONF,
+ IEEE802154_SET_REQ,
+ IEEE802154_SET_CONF,
+ IEEE802154_START_REQ,
+ IEEE802154_START_CONF,
+ IEEE802154_SYNC_REQ,
+ IEEE802154_POLL_REQ,
+ IEEE802154_POLL_CONF,
+
+ IEEE802154_ASSOCIATE_INDIC,
+ IEEE802154_ASSOCIATE_RESP,
+ IEEE802154_DISASSOCIATE_INDIC,
+ IEEE802154_BEACON_NOTIFY_INDIC,
+/* IEEE802154_GTS_INDIC, */
+ IEEE802154_ORPHAN_INDIC,
+ IEEE802154_ORPHAN_RESP,
+ IEEE802154_COMM_STATUS_INDIC,
+ IEEE802154_SYNC_LOSS_INDIC,
+
+ __IEEE802154_CMD_MAX,
+};
+
+#define IEEE802154_CMD_MAX (__IEEE802154_CMD_MAX - 1)
+
+
+#ifdef __KERNEL__
+int ieee802154_nl_init(void);
+void ieee802154_nl_exit(void);
+
+int ieee802154_nl_assoc_indic(struct net_device *dev, struct ieee802154_addr *addr, u8 cap);
+int ieee802154_nl_assoc_confirm(struct net_device *dev, u16 short_addr, u8 status);
+int ieee802154_nl_disassoc_indic(struct net_device *dev, struct ieee802154_addr *addr, u8 reason);
+int ieee802154_nl_disassoc_confirm(struct net_device *dev, u8 status);
+int ieee802154_nl_scan_confirm(struct net_device *dev, u8 status, u8 scan_type, u32 unscanned,
+ u8 *edl/*, struct list_head *pan_desc_list */);
+int ieee802154_nl_beacon_indic(struct net_device *dev, u16 panid, u16 coord_addr); /* TODO */
+#endif
+
+#endif
diff --git a/include/net/ieee802154/phy.h b/include/net/ieee802154/phy.h
new file mode 100644
index 0000000..ca154fa
--- /dev/null
+++ b/include/net/ieee802154/phy.h
@@ -0,0 +1,117 @@
+/*
+ * IEEE802.15.4-2003 specification
+ * Physical interface.
+ *
+ * Copyright (C) 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ * Pavel Smolenskiy <[email protected]>
+ * Maxim Gorbachyov <[email protected]>
+ * Maxim Osipov <[email protected]>
+ */
+
+#ifndef IEEE802154_PHY_H
+#define IEEE802154_PHY_H
+
+/******************************************************************************/
+/* PHY constants */
+/******************************************************************************/
+/** The maximum PSDU size (in octets) the PHY shall be able to receive,
+ * 127 octets
+ */
+#define IEEE802154_MAX_PHY_PACKET_SIZE 127
+
+/** RX-to-TX or TX-to-RX maximum turnaround time in symbol periods */
+#define IEEE802154_TURNAROUND_TIME 12
+
+/******************************************************************************/
+/* PAN Information Base (PIB), PHY attribute identifiers */
+/* See IEEE802.15.4-2003 draft, Table 19 */
+/******************************************************************************/
+/**
+ * The RF channel to use for all following transmissions and receptions
+ * (Integer, 0-26)
+ */
+#define IEEE802154_PHY_CURRENT_CHANNEL 0x00
+
+/**
+ * The 5 most significant bits (MSBs) (b27,... ,b31) of phyChannelsSupported
+ * shall be reserved and set to 0, and the 27 LSBs (b0,b1, ... b26) shall
+ * indicate the status (1=available, 0=unavailable) for each of the 27 valid
+ * channels (bk shall indicate the status of channel k) (Bitmap)
+ */
+#define IEEE802154_PHY_CHANNELS_SUPPORTED 0x01
+
+/**
+ * The 2 MSBs represent the tolerance on the transmit power:
+ *
+ * 00 = +- 1 dB
+ * 01 = +- 3 dB
+ * 02 = +- 6 dB
+ *
+ * The 6 LSBs represent a signed integer in twos-complement format,
+ * corresponding to the nominal transmit power of the device in decibels
+ * relative to 1 mW. The lowest value of phyTransmitPower shall be interpreted
+ * as less than or equal -32 dBm (Bitmap, 0x00-0xBF)
+ */
+#define IEEE802154_PHY_TRANSMIT_POWER 0x02
+
+/** The CCA mode (Integer, 1-3) */
+#define IEEE802154_PHY_CCA_MODE 0x03
+
+#define IEEE802154_CCA_ED 0x1
+#define IEEE802154_CCA_CS 0x2
+#define IEEE802154_CCA_CSED 0x3
+
+/******************************************************************************/
+/* PAN Information Base (PIB), attribute ranges */
+/* See IEEE802.15.4-2003 draft, Table 19 */
+/******************************************************************************/
+#define IEEE802154_PHY_CURRENT_CHANNEL_MIN 0x0
+#define IEEE802154_PHY_CURRENT_CHANNEL_MAX 0x1a
+
+#define IEEE802154_PHY_CHANNELS_SUPPORTED_MIN 0x0
+#define IEEE802154_PHY_CHANNELS_SUPPORTED_MAX 0x7FFFFFF
+
+#define IEEE802154_PHY_TRANSMIT_POWER_MIN 0x0
+#define IEEE802154_PHY_TRANSMIT_POWER_MAX 0xbf
+
+#define IEEE802154_PHY_CCA_MODE_MIN 0x1
+#define IEEE802154_PHY_CCA_MODE_MAX 0x3
+
+#ifdef __KERNEL__
+
+typedef enum {
+ PHY_BUSY = 0, /* cca */
+ PHY_BUSY_RX, /* state */
+ PHY_BUSY_TX, /* state */
+ PHY_FORCE_TRX_OFF,
+ PHY_IDLE, /* cca */
+ PHY_INVALID_PARAMETER, /* pib get/set */
+ PHY_RX_ON, /* state */
+ PHY_SUCCESS, /* ed */
+ PHY_TRX_OFF, /* cca, ed, state */
+ PHY_TX_ON, /* cca, ed, state */
+ PHY_UNSUPPORTED_ATTRIBUTE, /* pib get/set */
+ PHY_READ_ONLY, /* pib get/set */
+
+ PHY_INVAL = -1, /* all */
+ PHY_ERROR = -2, /* all */
+} phy_status_t;
+
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/net/Kconfig b/net/Kconfig
index c19f549..7051b97 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -179,6 +179,7 @@ source "net/lapb/Kconfig"
source "net/econet/Kconfig"
source "net/wanrouter/Kconfig"
source "net/phonet/Kconfig"
+source "net/ieee802154/Kconfig"
source "net/sched/Kconfig"
source "net/dcb/Kconfig"

diff --git a/net/Makefile b/net/Makefile
index 9e00a55..ba324ae 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -60,6 +60,7 @@ obj-$(CONFIG_NET_9P) += 9p/
ifneq ($(CONFIG_DCB),)
obj-y += dcb/
endif
+obj-y += ieee802154/

ifeq ($(CONFIG_NET),y)
obj-$(CONFIG_SYSCTL) += sysctl_net.o
diff --git a/net/ieee802154/Kconfig b/net/ieee802154/Kconfig
new file mode 100644
index 0000000..f540cd1
--- /dev/null
+++ b/net/ieee802154/Kconfig
@@ -0,0 +1,12 @@
+config IEEE802154
+ tristate "IEEE Std 802.15.4 Low-Rate Wireless Personal Area Networks support (EXPERIMENTAL)"
+ depends on EXPERIMENTAL
+ ---help---
+ IEEE Std 802.15.4 defines a low data rate, low power and low complexity
+ short range wireless personal area networks. It was designed to
+ organise networks of sensors, switches, etc automation devices. Maximum
+ allowed data rate is 250 kb/s and typical personal operating space
+ around 10m.
+
+ Say Y here to compile LR-WPAN support into the kernel or say M to
+ compile it as modules.
diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile
new file mode 100644
index 0000000..d964365
--- /dev/null
+++ b/net/ieee802154/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_IEEE802154) += ieee802154.o
+ieee802154-objs := main.o mdev.o dev.o netlink.o mac_cmd.o scan.o \
+ crc.o beacon.o beacon_hash.o start.o
+
+EXTRA_CFLAGS += -Wall -DDEBUG
diff --git a/net/ieee802154/beacon.c b/net/ieee802154/beacon.c
new file mode 100644
index 0000000..72a5ac3
--- /dev/null
+++ b/net/ieee802154/beacon.c
@@ -0,0 +1,251 @@
+/*
+ * MAC beacon interface
+ *
+ * Copyright 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ * Sergey Lapin <[email protected]>
+ * Dmitry Eremin-Solenikov <[email protected]>
+ */
+
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/list.h>
+
+#include <net/ieee802154/af_ieee802154.h>
+#include <net/ieee802154/mac_def.h>
+#include <net/ieee802154/netdev.h>
+#include <net/ieee802154/beacon.h>
+#include <net/ieee802154/nl.h>
+
+/* Beacon frame format per specification is the followinf:
+ * Standard MAC frame header:
+ * FC (2) SEQ (1)
+ * Addressing (4-20)
+ * Beacon fields:
+ * <Superframe specification> (2)
+ * <GTS> (?)
+ * <Pending address> (?)
+ * <Beacon payload> (?)
+ * FCS (2)
+ *
+ * Superframe specification:
+ * bit Value
+ * 15 Association permit
+ * 14 PAN coordinator
+ * 13 Reserved
+ * 12 Battery life extension
+ * 8-11 Final CAP slot
+ * 4-7 Superframe order
+ * 0-3 Beacon order
+ *
+ * GTS:
+ * <GTS specification> (1)
+ * <GTS directions> (0-1)
+ * <GTS list> (?)
+ *
+ * Pending address:
+ * <Pending address specification> (1)
+ * <Pending address list (?)
+ *
+ * GTS specification:
+ * bit Value
+ * 7 GTS permit
+ * 3-6 Reserved
+ * 0-2 GTS descriptor count
+ *
+ * Pending address specification:
+ * bit Value
+ * 7 Reserved
+ * 4-6 Number of extended addresses pendinf
+ * 3 Reserved
+ * 0-2 Number of short addresses pending
+ * */
+
+#define IEEE802154_BEACON_SF_BO_BEACONLESS (15 << 0)
+#define IEEE802154_BEACON_SF_SO(x) ((x & 0xf) << 4)
+#define IEEE802154_BEACON_SF_SO_INACTIVE IEEE802154_BEACON_SF_SO(15)
+#define IEEE802154_BEACON_SF_PANCOORD (1 << 14)
+#define IEEE802154_BEACON_SF_CANASSOC (1 << 15)
+#define IEEE802154_BEACON_GTS_COUNT(x) (x << 0)
+#define IEEE802154_BEACON_GTS_PERMIT (1 << 7)
+#define IEEE802154_BEACON_PA_SHORT(x) ((x & 7) << 0)
+#define IEEE802154_BEACON_PA_LONG(x) ((x & 7) << 4)
+
+/* Flags parameter */
+#define IEEE802154_BEACON_FLAG_PANCOORD (1 << 0)
+#define IEEE802154_BEACON_FLAG_CANASSOC (1 << 1)
+#define IEEE802154_BEACON_FLAG_GTSPERMIT (1 << 2)
+
+struct ieee802154_address_list {
+ struct list_head list;
+ struct ieee802154_addr addr;
+};
+/*
+ * @dev device
+ * @addr destination address
+ * @saddr source address
+ * @buf beacon payload
+ * @len beacon payload size
+ * @pan_coord - if we're PAN coordinator while sending this frame
+ * @gts_permit - wheather we allow GTS requests
+ * @al address list to be provided in beacon
+ *
+ * TODO:
+ * For a beacon frame, the sequence number field shall specify a BSN.
+ * Each coordinator shall store its current
+ * BSN value in the MAC PIB attribute macBSN and initialize it to a random value.
+ * The algorithm for choosing a random number is out of the scope
+ * of this standard. The coordinator shall copy the value of the macBSN
+ * attribute into the sequence number field of a beacon frame,
+ * each time one is generated, and shall then increment macBSN by one.
+ *
+*/
+
+
+int ieee802154_send_beacon(struct net_device *dev, struct ieee802154_addr *saddr,
+ u16 pan_id, const u8 *buf, int len,
+ int flags, struct list_head *al)
+{
+ struct sk_buff *skb;
+ int err;
+ u16 sf;
+ u8 gts;
+ u8 pa_spec;
+ int addr16_cnt;
+ int addr64_cnt;
+ struct ieee802154_addr addr;
+ struct ieee802154_priv *hw = ieee802154_slave_get_hw(dev);
+
+ BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+ skb = alloc_skb(LL_ALLOCATED_SPACE(dev) + len, GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+
+ skb_reserve(skb, LL_RESERVED_SPACE(dev));
+
+ skb_reset_network_header(skb);
+
+ MAC_CB(skb)->flags = IEEE802154_FC_TYPE_BEACON;
+ MAC_CB(skb)->seq = hw->bsn;
+
+ addr.addr_type = IEEE802154_ADDR_NONE;
+ err = dev_hard_header(skb, dev, ETH_P_IEEE802154, &addr, saddr, len);
+ if (err < 0) {
+ kfree_skb(skb);
+ return err;
+ }
+ skb_reset_mac_header(skb);
+
+ /* Superframe */
+ sf = IEEE802154_BEACON_SF_BO_BEACONLESS;
+ sf |= IEEE802154_BEACON_SF_SO_INACTIVE;
+ if (flags & IEEE802154_BEACON_FLAG_PANCOORD)
+ sf |= IEEE802154_BEACON_SF_PANCOORD;
+
+ if (flags & IEEE802154_BEACON_FLAG_CANASSOC)
+ sf |= IEEE802154_BEACON_SF_CANASSOC;
+ memcpy(skb_put(skb, sizeof(sf)), &sf, sizeof(sf));
+
+ /* TODO GTS */
+ gts = 0;
+
+ if (flags & IEEE802154_BEACON_FLAG_GTSPERMIT)
+ gts |= IEEE802154_BEACON_GTS_PERMIT;
+ memcpy(skb_put(skb, sizeof(gts)), &gts, sizeof(gts));
+
+ /* FIXME pending address */
+ addr16_cnt = 0;
+ addr64_cnt = 0;
+#if 0
+ /* need more thinking about this */
+ list_for_each_entry(l, al->list, list) {
+ struct ieee802154_addr *s = container_of(l, struct list_head, list);
+ if (s->addr_type == IEEE802154_ADDR_LONG)
+ addr16_cnt++;
+
+ if (s->addr_type == IEEE802154_ADDR_SHORT)
+ addr64_cnt++;
+ }
+#endif
+ pa_spec = IEEE802154_BEACON_PA_LONG(addr64_cnt) | IEEE802154_BEACON_PA_SHORT(addr16_cnt);
+ memcpy(skb_put(skb, sizeof(pa_spec)), &pa_spec, sizeof(pa_spec));
+
+ memcpy(skb_put(skb, len), buf, len);
+
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_IEEE802154);
+ hw->bsn++; /* FIXME locking */
+
+ return dev_queue_xmit(skb);
+}
+
+/* at entry to this function we need skb->data to point to start
+ * of beacon field and MAC frame already parsed into MAC_CB */
+
+int parse_beacon_frame(struct sk_buff *skb, u8 *buf,
+ int *flags, struct list_head *al)
+{
+ int offt = 0;
+ u8 gts_spec;
+ u8 pa_spec;
+ struct ieee802154_priv *hw = ieee802154_slave_get_hw(skb->dev);
+ struct ieee802154_pandsc *pd;
+ u16 sf = skb->data[0] + (skb->data[1] << 8);
+
+ pd = kzalloc(sizeof(struct ieee802154_pandsc), GFP_KERNEL);
+
+ /* Filling-up pre-parsed values */
+ pd->lqi = MAC_CB(skb)->phy.lqi;
+ pd->sf = sf;
+ memcpy(&pd->addr, &MAC_CB(skb)->da, sizeof(struct ieee802154_addr));
+
+ /* Supplying our nitifiers with data */
+ ieee802154_slave_event(skb->dev, IEEE802154_NOTIFIER_BEACON, pd);
+ ieee802154_nl_beacon_indic(skb->dev, 0xeba1, 1); /* FIXME */
+ kfree(pd);
+
+ offt += 2;
+ gts_spec = skb->data[offt++];
+ /* FIXME !!! */
+ if ((gts_spec & 7) != 0) {
+ pr_debug("We still don't parse GTS part properly");
+ return -ENOTSUPP;
+ }
+ pa_spec = skb->data[offt++];
+ /* FIXME !!! */
+ if (pa_spec != 0) {
+ pr_debug("We still don't parse PA part properly");
+ return -ENOTSUPP;
+ }
+
+ *flags = 0;
+
+ if (sf & IEEE802154_BEACON_SF_PANCOORD)
+ *flags |= IEEE802154_BEACON_FLAG_PANCOORD;
+
+ if (sf & IEEE802154_BEACON_SF_CANASSOC)
+ *flags |= IEEE802154_BEACON_FLAG_CANASSOC;
+ BUG_ON(skb->len - offt < 0);
+ /* FIXME */
+ if (buf && (skb->len - offt > 0))
+ memcpy(buf, skb->data + offt, skb->len - offt);
+ return 0;
+}
+
diff --git a/net/ieee802154/beacon_hash.c b/net/ieee802154/beacon_hash.c
new file mode 100644
index 0000000..f5049c1
--- /dev/null
+++ b/net/ieee802154/beacon_hash.c
@@ -0,0 +1,105 @@
+/*
+ * MAC beacon hash storage
+ *
+ * Copyright 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ * Sergey Lapin <[email protected]>
+ * Dmitry Eremin-Solenikov <[email protected]>
+ */
+
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <net/sock.h>
+
+#include <net/ieee802154/af_ieee802154.h>
+#include <net/ieee802154/beacon_hash.h>
+
+static struct hlist_head beacon_hash[IEEE802154_BEACON_HTABLE_SIZE];
+static DEFINE_RWLOCK(beacon_hash_lock);
+
+static int beacon_hashfn(struct ieee802154_addr *coord_addr, u16 pan_addr)
+{
+ return pan_addr % IEEE802154_BEACON_HTABLE_SIZE;
+}
+
+static void __beacon_add_node(struct ieee802154_addr *coord_addr, u16 pan_addr)
+{
+ struct beacon_node *node = kzalloc(sizeof(struct beacon_node), GFP_KERNEL);
+ struct hlist_head *list = &beacon_hash[beacon_hashfn(coord_addr, pan_addr)];
+ memcpy(&node->coord_addr, coord_addr, sizeof(struct ieee802154_addr));
+ node->pan_addr = pan_addr;
+ INIT_HLIST_NODE(&node->list);
+ hlist_add_head(&node->list, list);
+}
+
+struct beacon_node *ieee802154_beacon_find_pan(struct ieee802154_addr *coord_addr,
+ u16 pan_addr)
+{
+ struct hlist_head *list;
+ struct hlist_node *tmp;
+ list = &beacon_hash[beacon_hashfn(coord_addr, pan_addr)];
+ if (hlist_empty(list))
+ return NULL;
+ hlist_for_each(tmp, list) {
+ struct beacon_node *entry = hlist_entry(tmp, struct beacon_node, list);
+ if (entry->pan_addr == pan_addr)
+ return entry;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(ieee802154_beacon_find_pan);
+
+void ieee802154_beacon_hash_add(struct ieee802154_addr *coord_addr)
+{
+ if (!ieee802154_beacon_find_pan(coord_addr, coord_addr->pan_id)) {
+ write_lock(&beacon_hash_lock);
+ __beacon_add_node(coord_addr, coord_addr->pan_id);
+ write_unlock(&beacon_hash_lock);
+ }
+}
+EXPORT_SYMBOL(ieee802154_beacon_hash_add);
+
+void ieee802154_beacon_hash_del(struct ieee802154_addr *coord_addr)
+{
+ struct beacon_node *entry = ieee802154_beacon_find_pan(coord_addr,
+ coord_addr->pan_id);
+ if (!entry)
+ return;
+ write_lock(&beacon_hash_lock);
+ hlist_del(&entry->list);
+ write_unlock(&beacon_hash_lock);
+ kfree(entry);
+}
+EXPORT_SYMBOL(ieee802154_beacon_hash_del);
+
+void ieee802154_beacon_hash_dump(void)
+{
+ int i;
+ struct hlist_node *tmp;
+ pr_debug("beacon hash dump begin\n");
+ read_lock(&beacon_hash_lock);
+ for (i = 0; i < IEEE802154_BEACON_HTABLE_SIZE; i++) {
+ struct beacon_node *entry;
+ hlist_for_each(tmp, &beacon_hash[i]) {
+ entry = hlist_entry(tmp, struct beacon_node, list);
+ pr_debug("PAN: %d\n", entry->pan_addr);
+ }
+ }
+ read_unlock(&beacon_hash_lock);
+ pr_debug("beacon hash dump end\n");
+}
+
diff --git a/net/ieee802154/crc.c b/net/ieee802154/crc.c
new file mode 100644
index 0000000..8e7c0d5
--- /dev/null
+++ b/net/ieee802154/crc.c
@@ -0,0 +1,73 @@
+/*
+ * based on crc-itu-t.c
+ *
+ * Copyright 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ * Dmitry Eremin-Solenikov <[email protected]>
+ */
+
+#include <linux/types.h>
+#include <net/ieee802154/crc.h>
+
+const u16 ieee802154_crc_table[256] = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78,
+};
+
+#if 0
+void generate()
+{
+ int i, j;
+ uint16_t crc = 1;
+
+ for (i = 0x80; i ; i >>= 1) {
+ crc = (crc >> 1) ^ ((crc & 1) ? 0x8408 : 0);
+ for (j = 0; j < 256; j += 2 * i)
+ ieee802154_crc_table[i + j] = crc ^ ieee802154_crc_table[j];
+ }
+}
+#endif
diff --git a/net/ieee802154/dev.c b/net/ieee802154/dev.c
new file mode 100644
index 0000000..ecf5c98
--- /dev/null
+++ b/net/ieee802154/dev.c
@@ -0,0 +1,935 @@
+/*
+ * ZigBee socket interface
+ *
+ * Copyright 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ * Sergey Lapin <[email protected]>
+ * Maxim Gorbachyov <[email protected]>
+ */
+
+#include <linux/net.h>
+#include <linux/capability.h>
+#include <linux/module.h>
+#include <linux/if_arp.h>
+#include <linux/termios.h> /* For TIOCOUTQ/INQ */
+#include <linux/notifier.h>
+#include <linux/random.h>
+#include <net/datalink.h>
+#include <net/psnap.h>
+#include <net/sock.h>
+#include <net/tcp_states.h>
+#include <net/route.h>
+#include <net/ieee802154/dev.h>
+#include <net/ieee802154/netdev.h>
+#include <net/ieee802154/crc.h>
+#include <net/ieee802154/af_ieee802154.h>
+#include <net/ieee802154/mac_def.h>
+#include <net/ieee802154/beacon.h>
+#include <net/ieee802154/beacon_hash.h>
+
+struct ieee802154_netdev_priv {
+ struct list_head list;
+ struct ieee802154_priv *hw;
+ struct net_device *dev;
+
+ __le16 pan_id;
+ __le16 short_addr;
+
+ u8 chan;
+
+ /* This one is used to provide notifications */
+ struct blocking_notifier_head events;
+};
+
+static int ieee802154_net_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ieee802154_netdev_priv *priv;
+ priv = netdev_priv(dev);
+
+ if (!(priv->hw->hw.flags & IEEE802154_OPS_OMIT_CKSUM)) {
+ u16 crc = ieee802154_crc(0, skb->data, skb->len);
+ u8 *data = skb_put(skb, 2);
+ data[0] = crc & 0xff;
+ data[1] = crc >> 8;
+ }
+
+ PHY_CB(skb)->chan = priv->chan;
+
+ skb->iif = dev->ifindex;
+ skb->dev = priv->hw->master;
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
+
+ dev->trans_start = jiffies;
+ dev_queue_xmit(skb);
+
+ return 0;
+}
+
+static int ieee802154_slave_open(struct net_device *dev)
+{
+ struct ieee802154_netdev_priv *priv;
+ priv = netdev_priv(dev);
+ netif_start_queue(dev);
+ return 0;
+}
+
+static int ieee802154_slave_close(struct net_device *dev)
+{
+ struct ieee802154_netdev_priv *priv;
+ netif_stop_queue(dev);
+ priv = netdev_priv(dev);
+ netif_stop_queue(dev);
+ return 0;
+}
+
+
+static int ieee802154_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct ieee802154_netdev_priv *priv = netdev_priv(dev);
+ struct sockaddr_ieee802154 *sa = (struct sockaddr_ieee802154 *)&ifr->ifr_addr;
+ switch (cmd) {
+ case SIOCGIFADDR:
+ if (priv->pan_id == IEEE802154_PANID_BROADCAST || priv->short_addr == IEEE802154_ADDR_BROADCAST)
+ return -EADDRNOTAVAIL;
+
+ sa->family = AF_IEEE802154;
+ sa->addr.addr_type = IEEE802154_ADDR_SHORT;
+ sa->addr.pan_id = priv->pan_id;
+ sa->addr.short_addr = priv->short_addr;
+ return 0;
+ case SIOCSIFADDR:
+ dev_warn(&dev->dev, "Using DEBUGing ioctl SIOCSIFADDR isn't recommened!\n");
+ if (sa->family != AF_IEEE802154 || sa->addr.addr_type != IEEE802154_ADDR_SHORT ||
+ sa->addr.pan_id == IEEE802154_PANID_BROADCAST || sa->addr.short_addr == IEEE802154_ADDR_BROADCAST || sa->addr.short_addr == IEEE802154_ADDR_UNDEF)
+ return -EINVAL;
+
+ priv->pan_id = sa->addr.pan_id;
+ priv->short_addr = sa->addr.short_addr;
+ return 0;
+ }
+ return -ENOIOCTLCMD;
+}
+
+static int ieee802154_slave_mac_addr(struct net_device *dev, void *p)
+{
+ struct sockaddr *addr = p;
+
+ if (netif_running(dev))
+ return -EBUSY;
+ /* FIXME: validate addr */
+ memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+ return 0;
+}
+
+static int ieee802154_header_create(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, const void *_daddr,
+ const void *_saddr, unsigned len)
+{
+ u8 head[24] = {};
+ int pos = 0;
+
+ u16 fc;
+ const struct ieee802154_addr *saddr = _saddr;
+ const struct ieee802154_addr *daddr = _daddr;
+ struct ieee802154_addr dev_addr;
+ struct ieee802154_netdev_priv *priv = netdev_priv(dev);
+
+ fc = MAC_CB_TYPE(skb);
+ if (MAC_CB_IS_ACKREQ(skb))
+ fc |= IEEE802154_FC_ACK_REQ;
+
+ pos = 2;
+
+ head[pos++] = MAC_CB(skb)->seq; /* DSN/BSN */
+
+ if (!daddr)
+ return -EINVAL;
+
+ if (!saddr) {
+ if (priv->short_addr == IEEE802154_ADDR_BROADCAST || priv->short_addr == IEEE802154_ADDR_UNDEF || priv->pan_id == IEEE802154_PANID_BROADCAST) {
+ dev_addr.addr_type = IEEE802154_ADDR_LONG;
+ memcpy(dev_addr.hwaddr, dev->dev_addr, IEEE802154_ADDR_LEN);
+ } else {
+ dev_addr.addr_type = IEEE802154_ADDR_SHORT;
+ dev_addr.short_addr = priv->short_addr;
+ }
+
+ dev_addr.pan_id = priv->pan_id;
+ saddr = &dev_addr;
+ }
+
+ if (daddr->addr_type != IEEE802154_ADDR_NONE) {
+ fc |= (daddr->addr_type << IEEE802154_FC_DAMODE_SHIFT);
+
+ head[pos++] = daddr->pan_id & 0xff;
+ head[pos++] = daddr->pan_id >> 8;
+
+ if (daddr->addr_type == IEEE802154_ADDR_SHORT) {
+ head[pos++] = daddr->short_addr & 0xff;
+ head[pos++] = daddr->short_addr >> 8;
+ } else {
+ memcpy(head + pos, daddr->hwaddr, IEEE802154_ADDR_LEN);
+ pos += IEEE802154_ADDR_LEN;
+ }
+ }
+
+ if (saddr->addr_type != IEEE802154_ADDR_NONE) {
+ fc |= (saddr->addr_type << IEEE802154_FC_SAMODE_SHIFT);
+
+ if ((saddr->pan_id == daddr->pan_id) && (saddr->pan_id != IEEE802154_PANID_BROADCAST))
+ fc |= IEEE802154_FC_INTRA_PAN; /* PANID compression/ intra PAN */
+ else {
+ head[pos++] = saddr->pan_id & 0xff;
+ head[pos++] = saddr->pan_id >> 8;
+ }
+
+ if (saddr->addr_type == IEEE802154_ADDR_SHORT) {
+ head[pos++] = saddr->short_addr & 0xff;
+ head[pos++] = saddr->short_addr >> 8;
+ } else {
+ memcpy(head + pos, saddr->hwaddr, IEEE802154_ADDR_LEN);
+ pos += IEEE802154_ADDR_LEN;
+ }
+ }
+
+ head[0] = fc;
+ head[1] = fc >> 8;
+
+ memcpy(skb_push(skb, pos), head, pos);
+
+ return pos;
+}
+
+static int ieee802154_header_parse(const struct sk_buff *skb, unsigned char *haddr)
+{
+ const u8 *hdr = skb_mac_header(skb), *tail = skb_tail_pointer(skb);
+ struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr;
+ u16 fc;
+ int da_type;
+
+ if (hdr + 3 > tail)
+ goto malformed;
+
+ fc = hdr[0] | (hdr[1] << 8);
+
+ hdr += 3;
+
+ da_type = IEEE802154_FC_DAMODE(fc);
+ addr->addr_type = IEEE802154_FC_SAMODE(fc);
+
+ switch (da_type) {
+ case IEEE802154_ADDR_NONE:
+ if (fc & IEEE802154_FC_INTRA_PAN)
+ goto malformed;
+ break;
+
+ case IEEE802154_ADDR_LONG:
+ if (hdr + 2 > tail)
+ goto malformed;
+ if (fc & IEEE802154_FC_INTRA_PAN) {
+ addr->pan_id = hdr[0] | (hdr[1] << 8);
+ hdr += 2;
+ }
+
+ if (hdr + IEEE802154_ADDR_LEN > tail)
+ goto malformed;
+ hdr += IEEE802154_ADDR_LEN;
+ break;
+
+ case IEEE802154_ADDR_SHORT:
+ if (hdr + 2 > tail)
+ goto malformed;
+ if (fc & IEEE802154_FC_INTRA_PAN) {
+ addr->pan_id = hdr[0] | (hdr[1] << 8);
+ hdr += 2;
+ }
+
+ if (hdr + 2 > tail)
+ goto malformed;
+ hdr += 2;
+ break;
+
+ default:
+ goto malformed;
+
+ }
+
+ switch (addr->addr_type) {
+ case IEEE802154_ADDR_NONE:
+ break;
+
+ case IEEE802154_ADDR_LONG:
+ if (hdr + 2 > tail)
+ goto malformed;
+ if (!(fc & IEEE802154_FC_INTRA_PAN)) {
+ addr->pan_id = hdr[0] | (hdr[1] << 8);
+ hdr += 2;
+ }
+
+ if (hdr + IEEE802154_ADDR_LEN > tail)
+ goto malformed;
+ memcpy(addr->hwaddr, hdr, IEEE802154_ADDR_LEN);
+ hdr += IEEE802154_ADDR_LEN;
+ break;
+
+ case IEEE802154_ADDR_SHORT:
+ if (hdr + 2 > tail)
+ goto malformed;
+ if (!(fc & IEEE802154_FC_INTRA_PAN)) {
+ addr->pan_id = hdr[0] | (hdr[1] << 8);
+ hdr += 2;
+ }
+
+ if (hdr + 2 > tail)
+ goto malformed;
+ addr->short_addr = hdr[0] | (hdr[1] << 8);
+ hdr += 2;
+ break;
+
+ default:
+ goto malformed;
+
+ }
+
+ return sizeof(struct ieee802154_addr);
+
+malformed:
+ pr_debug("malformed packet\n");
+ return 0;
+}
+
+static struct header_ops ieee802154_header_ops = {
+ .create = ieee802154_header_create,
+ .parse = ieee802154_header_parse,
+};
+
+static void ieee802154_netdev_setup(struct net_device *dev)
+{
+ dev->addr_len = IEEE802154_ADDR_LEN;
+ memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN);
+ dev->features = NETIF_F_NO_CSUM;
+ dev->hard_header_len = 2 + 1 + 20 + 14;
+ dev->header_ops = &ieee802154_header_ops;
+ dev->needed_tailroom = 2; /* FCS */
+ dev->mtu = 127;
+ dev->tx_queue_len = 10;
+ dev->type = ARPHRD_IEEE802154;
+ dev->flags = IFF_NOARP | IFF_BROADCAST;
+ dev->watchdog_timeo = 0;
+}
+
+static void ieee802154_init_seq(struct ieee802154_priv *priv)
+{
+ get_random_bytes(&priv->bsn, 1);
+ get_random_bytes(&priv->dsn, 1);
+}
+
+static const struct net_device_ops ieee802154_slave_ops = {
+ .ndo_open = ieee802154_slave_open,
+ .ndo_stop = ieee802154_slave_close,
+ .ndo_start_xmit = ieee802154_net_xmit,
+ .ndo_do_ioctl = ieee802154_slave_ioctl,
+ .ndo_set_mac_address = ieee802154_slave_mac_addr,
+};
+
+int ieee802154_add_slave(struct ieee802154_dev *hw, const u8 *addr)
+{
+ struct net_device *dev;
+ struct ieee802154_netdev_priv *priv;
+ struct ieee802154_priv *ipriv = ieee802154_to_priv(hw);
+ int err;
+
+ ASSERT_RTNL();
+
+ dev = alloc_netdev(sizeof(struct ieee802154_netdev_priv),
+ "wpan%d", ieee802154_netdev_setup);
+ if (!dev) {
+ printk(KERN_ERR "Failure to initialize IEEE802154 device\n");
+ return -ENOMEM;
+ }
+ priv = netdev_priv(dev);
+ priv->dev = dev;
+ priv->hw = ieee802154_to_priv(hw);
+ ieee802154_init_seq(priv->hw);
+ BLOCKING_INIT_NOTIFIER_HEAD(&priv->events);
+ memcpy(dev->dev_addr, addr, dev->addr_len);
+ memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
+ dev->priv_flags = IFF_SLAVE_INACTIVE;
+ dev->netdev_ops = &ieee802154_slave_ops;
+
+ priv->pan_id = IEEE802154_PANID_DEF;
+ priv->short_addr = IEEE802154_SHORT_ADDRESS_DEF;
+
+ dev_hold(priv->hw->master);
+
+ dev->needed_headroom = priv->hw->master->needed_headroom;
+
+ spin_lock(&ieee802154_to_priv(hw)->slaves_lock);
+ list_add_tail(&priv->list, &ieee802154_to_priv(hw)->slaves);
+ spin_unlock(&ieee802154_to_priv(hw)->slaves_lock);
+ /*
+ * If the name is a format string the caller wants us to do a
+ * name allocation.
+ */
+ if (strchr(dev->name, '%')) {
+ err = dev_alloc_name(dev, dev->name);
+ if (err < 0)
+ goto out;
+ }
+
+ SET_NETDEV_DEV(dev, &ipriv->master->dev);
+
+ err = register_netdevice(dev);
+ if (err < 0)
+ goto out;
+
+ return dev->ifindex;
+out:
+ return err;
+}
+EXPORT_SYMBOL(ieee802154_add_slave);
+
+static void __ieee802154_del_slave(struct ieee802154_netdev_priv *ndp)
+{
+ struct net_device *dev = ndp->dev;
+ dev_put(ndp->hw->master);
+ unregister_netdev(ndp->dev);
+
+ spin_lock(&ndp->hw->slaves_lock);
+ list_del(&ndp->list);
+ spin_unlock(&ndp->hw->slaves_lock);
+
+ free_netdev(dev);
+}
+
+void ieee802154_drop_slaves(struct ieee802154_dev *hw)
+{
+ struct ieee802154_priv *priv = ieee802154_to_priv(hw);
+ struct ieee802154_netdev_priv *ndp, *next;
+
+ spin_lock(&priv->slaves_lock);
+ list_for_each_entry_safe(ndp, next, &priv->slaves, list) {
+ spin_unlock(&priv->slaves_lock);
+ __ieee802154_del_slave(ndp);
+ spin_lock(&priv->slaves_lock);
+ }
+ spin_unlock(&priv->slaves_lock);
+}
+EXPORT_SYMBOL(ieee802154_drop_slaves);
+
+static int ieee802154_send_ack(struct sk_buff *skb)
+{
+ u16 fc = IEEE802154_FC_TYPE_ACK;
+ u8 *data;
+ struct sk_buff *ackskb;
+
+ BUG_ON(!skb || !skb->dev);
+ BUG_ON(!MAC_CB_IS_ACKREQ(skb));
+
+ ackskb = alloc_skb(LL_ALLOCATED_SPACE(skb->dev) + IEEE802154_ACK_LEN, GFP_ATOMIC);
+
+ skb_reserve(ackskb, LL_RESERVED_SPACE(skb->dev));
+
+ skb_reset_network_header(ackskb);
+
+ data = skb_push(ackskb, IEEE802154_ACK_LEN);
+ data[0] = fc & 0xff;
+ data[1] = (fc >> 8) & 0xff;
+ data[2] = MAC_CB(skb)->seq;
+
+ skb_reset_mac_header(ackskb);
+
+ ackskb->dev = skb->dev;
+ pr_debug("ACK frame to %s device\n", skb->dev->name);
+ ackskb->protocol = htons(ETH_P_IEEE802154);
+ /* FIXME */
+
+ return dev_queue_xmit(ackskb);
+}
+
+static int ieee802154_process_beacon(struct net_device *dev, struct sk_buff *skb)
+{
+ int flags;
+ int ret;
+ ret = parse_beacon_frame(skb, NULL, &flags, NULL);
+
+ /* Here we have cb->sa = coordinator address, and PAN address */
+
+ if (ret < 0) {
+ ret = NET_RX_DROP;
+ goto fail;
+ }
+ dev_dbg(&dev->dev, "got beacon from pan %d\n", MAC_CB(skb)->sa.pan_id);
+ ieee802154_beacon_hash_add(&MAC_CB(skb)->sa);
+ ieee802154_beacon_hash_dump();
+ ret = NET_RX_SUCCESS;
+fail:
+ kfree_skb(skb);
+ return ret;
+}
+
+static int ieee802154_process_ack(struct net_device *dev, struct sk_buff *skb)
+{
+ pr_debug("got ACK for SEQ=%d\n", MAC_CB(skb)->seq);
+
+ kfree_skb(skb);
+ return NET_RX_SUCCESS;
+}
+
+static int ieee802154_process_data(struct net_device *dev, struct sk_buff *skb)
+{
+ return netif_rx(skb);
+}
+
+static int ieee802154_subif_frame(struct ieee802154_netdev_priv *ndp, struct sk_buff *skb)
+{
+ pr_debug("%s Getting packet via slave interface %s\n",
+ __func__, ndp->dev->name);
+
+ switch (MAC_CB(skb)->da.addr_type) {
+ case IEEE802154_ADDR_NONE:
+ if (MAC_CB(skb)->sa.addr_type != IEEE802154_ADDR_NONE)
+ /* FIXME: check if we are PAN coordinator :) */
+ skb->pkt_type = PACKET_OTHERHOST;
+ else
+ /* ACK comes with both addresses empty */
+ skb->pkt_type = PACKET_HOST;
+ break;
+ case IEEE802154_ADDR_LONG:
+ if (MAC_CB(skb)->da.pan_id != ndp->pan_id && MAC_CB(skb)->da.pan_id != IEEE802154_PANID_BROADCAST)
+ skb->pkt_type = PACKET_OTHERHOST;
+ else if (!memcmp(MAC_CB(skb)->da.hwaddr, ndp->dev->dev_addr, IEEE802154_ADDR_LEN))
+ skb->pkt_type = PACKET_HOST;
+ else if (!memcmp(MAC_CB(skb)->da.hwaddr, ndp->dev->broadcast, IEEE802154_ADDR_LEN))
+ /* FIXME: is this correct? */
+ skb->pkt_type = PACKET_BROADCAST;
+ else
+ skb->pkt_type = PACKET_OTHERHOST;
+ break;
+ case IEEE802154_ADDR_SHORT:
+ if (MAC_CB(skb)->da.pan_id != ndp->pan_id && MAC_CB(skb)->da.pan_id != IEEE802154_PANID_BROADCAST)
+ skb->pkt_type = PACKET_OTHERHOST;
+ else if (MAC_CB(skb)->da.short_addr == ndp->short_addr)
+ skb->pkt_type = PACKET_HOST;
+ else if (MAC_CB(skb)->da.short_addr == IEEE802154_ADDR_BROADCAST)
+ skb->pkt_type = PACKET_BROADCAST;
+ else
+ skb->pkt_type = PACKET_OTHERHOST;
+ break;
+ }
+
+ skb->dev = ndp->dev;
+
+ if (MAC_CB_IS_ACKREQ(skb))
+ ieee802154_send_ack(skb);
+
+ switch (MAC_CB_TYPE(skb)) {
+ case IEEE802154_FC_TYPE_BEACON:
+ return ieee802154_process_beacon(ndp->dev, skb);
+ case IEEE802154_FC_TYPE_ACK:
+ return ieee802154_process_ack(ndp->dev, skb);
+ case IEEE802154_FC_TYPE_MAC_CMD:
+ return ieee802154_process_cmd(ndp->dev, skb);
+ case IEEE802154_FC_TYPE_DATA:
+ return ieee802154_process_data(ndp->dev, skb);
+ default:
+ pr_warning("ieee802154: Bad frame received (type = %d)\n", MAC_CB_TYPE(skb));
+ kfree_skb(skb);
+ return NET_RX_DROP;
+ }
+}
+
+static u8 fetch_skb_u8(struct sk_buff *skb)
+{
+ u8 ret;
+
+ BUG_ON(skb->len < 1);
+
+ ret = skb->data[0];
+ skb_pull(skb, 1);
+
+ return ret;
+}
+
+static u16 fetch_skb_u16(struct sk_buff *skb)
+{
+ u16 ret;
+
+ BUG_ON(skb->len < 2);
+
+ ret = skb->data[0] + (skb->data[1] * 256);
+ skb_pull(skb, 2);
+ return ret;
+}
+
+static void fetch_skb_u64(struct sk_buff *skb, void *data)
+{
+ BUG_ON(skb->len < IEEE802154_ADDR_LEN);
+
+ memcpy(data, skb->data, IEEE802154_ADDR_LEN);
+ skb_pull(skb, IEEE802154_ADDR_LEN);
+}
+
+#define IEEE802154_FETCH_U8(skb, var) \
+ do { \
+ if (skb->len < 1) \
+ goto exit_error; \
+ var = fetch_skb_u8(skb); \
+ } while (0)
+
+#define IEEE802154_FETCH_U16(skb, var) \
+ do { \
+ if (skb->len < 2) \
+ goto exit_error; \
+ var = fetch_skb_u16(skb); \
+ } while (0)
+
+#define IEEE802154_FETCH_U64(skb, var) \
+ do { \
+ if (skb->len < IEEE802154_ADDR_LEN) \
+ goto exit_error; \
+ fetch_skb_u64(skb, &var); \
+ } while (0)
+
+static int parse_frame_start(struct sk_buff *skb)
+{
+ u8 *head = skb->data;
+ u16 fc;
+
+ if (skb->len < 3) {
+ pr_debug("frame size %d bytes is too short\n", skb->len);
+ return -EINVAL;
+ }
+
+ IEEE802154_FETCH_U16(skb, fc);
+ IEEE802154_FETCH_U8(skb, MAC_CB(skb)->seq);
+
+ pr_debug("%s: %04x dsn%02x\n", __func__, fc, head[2]);
+
+ MAC_CB(skb)->flags = IEEE802154_FC_TYPE(fc);
+
+ if (fc & IEEE802154_FC_ACK_REQ) {
+ pr_debug("%s(): ACKNOWLEDGE required\n", __func__);
+ MAC_CB(skb)->flags |= MAC_CB_FLAG_ACKREQ;
+ }
+
+ if (fc & IEEE802154_FC_SECEN)
+ MAC_CB(skb)->flags |= MAC_CB_FLAG_SECEN;
+
+ if (fc & IEEE802154_FC_INTRA_PAN)
+ MAC_CB(skb)->flags |= MAC_CB_FLAG_INTRAPAN;
+
+ /* TODO */
+ if (MAC_CB_IS_SECEN(skb)) {
+ pr_info("security support is not implemented\n");
+ return -EINVAL;
+ }
+
+ MAC_CB(skb)->sa.addr_type = IEEE802154_FC_SAMODE(fc);
+ if (MAC_CB(skb)->sa.addr_type == IEEE802154_ADDR_NONE)
+ pr_debug("%s(): src addr_type is NONE\n", __func__);
+
+ MAC_CB(skb)->da.addr_type = IEEE802154_FC_DAMODE(fc);
+ if (MAC_CB(skb)->da.addr_type == IEEE802154_ADDR_NONE)
+ pr_debug("%s(): dst addr_type is NONE\n", __func__);
+
+ if (IEEE802154_FC_TYPE(fc) == IEEE802154_FC_TYPE_ACK) {
+ /* ACK can only have NONE-type addresses */
+ if (MAC_CB(skb)->sa.addr_type != IEEE802154_ADDR_NONE ||
+ MAC_CB(skb)->da.addr_type != IEEE802154_ADDR_NONE)
+ return -EINVAL;
+ }
+
+ if (MAC_CB(skb)->da.addr_type != IEEE802154_ADDR_NONE) {
+ IEEE802154_FETCH_U16(skb, MAC_CB(skb)->da.pan_id);
+
+ if (MAC_CB_IS_INTRAPAN(skb)) { /* ! panid compress */
+ pr_debug("%s(): src IEEE802154_FC_INTRA_PAN\n", __func__);
+ MAC_CB(skb)->sa.pan_id = MAC_CB(skb)->da.pan_id;
+ pr_debug("%s(): src PAN address %04x\n",
+ __func__, MAC_CB(skb)->sa.pan_id);
+ }
+
+ pr_debug("%s(): dst PAN address %04x\n",
+ __func__, MAC_CB(skb)->da.pan_id);
+
+ if (MAC_CB(skb)->da.addr_type == IEEE802154_ADDR_SHORT) {
+ IEEE802154_FETCH_U16(skb, MAC_CB(skb)->da.short_addr);
+ pr_debug("%s(): dst SHORT address %04x\n",
+ __func__, MAC_CB(skb)->da.short_addr);
+
+ } else {
+ IEEE802154_FETCH_U64(skb, MAC_CB(skb)->da.hwaddr);
+ pr_debug("%s(): dst hardware addr\n", __func__);
+ }
+ }
+
+ if (MAC_CB(skb)->sa.addr_type != IEEE802154_ADDR_NONE) {
+ pr_debug("%s(): got src non-NONE address\n", __func__);
+ if (!(MAC_CB_IS_INTRAPAN(skb))) { /* ! panid compress */
+ IEEE802154_FETCH_U16(skb, MAC_CB(skb)->sa.pan_id);
+ pr_debug("%s(): src IEEE802154_FC_INTRA_PAN\n", __func__);
+ }
+
+ if (MAC_CB(skb)->sa.addr_type == IEEE802154_ADDR_SHORT) {
+ IEEE802154_FETCH_U16(skb, MAC_CB(skb)->sa.short_addr);
+ pr_debug("%s(): src IEEE802154_ADDR_SHORT\n", __func__);
+ } else {
+ IEEE802154_FETCH_U64(skb, MAC_CB(skb)->sa.hwaddr);
+ pr_debug("%s(): src hardware addr\n", __func__);
+ }
+ }
+
+ return 0;
+
+exit_error:
+ return -EINVAL;
+}
+
+void ieee802154_subif_rx(struct ieee802154_dev *hw, struct sk_buff *skb)
+{
+ struct ieee802154_priv *priv = ieee802154_to_priv(hw);
+ struct ieee802154_netdev_priv *ndp, *prev = NULL;
+ int ret;
+
+ BUILD_BUG_ON(sizeof(struct ieee802154_mac_cb) > sizeof(skb->cb));
+ pr_debug("%s()\n", __func__);
+
+ ret = parse_frame_start(skb); /* 3 bytes pulled after this */
+ if (ret) {
+ pr_debug("%s(): Got invalid frame\n", __func__);
+ goto out;
+ }
+
+ if (!(priv->hw.flags & IEEE802154_OPS_OMIT_CKSUM)) {
+ if (skb->len < 2) {
+ pr_debug("%s(): Got invalid frame\n", __func__);
+ goto out;
+ }
+ /* FIXME: check CRC if necessary */
+ skb_trim(skb, skb->len - 2); /* CRC */
+ }
+
+ pr_debug("%s() frame %d\n", __func__, MAC_CB_TYPE(skb));
+
+ rcu_read_lock();
+
+ list_for_each_entry_rcu(ndp, &priv->slaves, list)
+ {
+ if (prev) {
+ struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (skb2)
+ ieee802154_subif_frame(prev, skb2);
+ }
+
+ prev = ndp;
+ }
+
+ if (prev)
+ ieee802154_subif_frame(prev, skb);
+ else
+ kfree_skb(skb);
+
+ rcu_read_unlock();
+ return;
+
+out:
+ kfree_skb(skb);
+ return;
+}
+
+struct net_device *ieee802154_get_dev(struct net *net, struct ieee802154_addr *addr)
+{
+ struct net_device *dev = NULL;
+
+ switch (addr->addr_type) {
+ case IEEE802154_ADDR_LONG:
+ rtnl_lock();
+ dev = dev_getbyhwaddr(net, ARPHRD_IEEE802154, addr->hwaddr);
+ if (dev)
+ dev_hold(dev);
+ rtnl_unlock();
+ break;
+ case IEEE802154_ADDR_SHORT:
+ if (addr->pan_id != 0xffff && addr->short_addr != IEEE802154_ADDR_UNDEF && addr->short_addr != 0xffff) {
+ struct net_device *tmp;
+
+ rtnl_lock();
+
+ for_each_netdev(net, tmp) {
+ if (tmp->type == ARPHRD_IEEE802154) {
+ struct ieee802154_netdev_priv *priv = netdev_priv(tmp);
+ if (priv->pan_id == addr->pan_id && priv->short_addr == addr->short_addr) {
+ dev = tmp;
+ dev_hold(dev);
+ break;
+ }
+ }
+ }
+
+ rtnl_unlock();
+ }
+ break;
+ default:
+ pr_warning("Unsupported ieee802154 address type: %d\n", addr->addr_type);
+ break;
+ }
+
+ return dev;
+}
+EXPORT_SYMBOL(ieee802154_get_dev);
+
+u16 ieee802154_dev_get_pan_id(struct net_device *dev)
+{
+ struct ieee802154_netdev_priv *priv = netdev_priv(dev);
+
+ BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+ return priv->pan_id;
+}
+EXPORT_SYMBOL(ieee802154_dev_get_pan_id);
+
+u16 ieee802154_dev_get_short_addr(struct net_device *dev)
+{
+ struct ieee802154_netdev_priv *priv = netdev_priv(dev);
+
+ BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+ return priv->short_addr;
+}
+EXPORT_SYMBOL(ieee802154_dev_get_short_addr);
+
+void ieee802154_dev_set_pan_id(struct net_device *dev, u16 val)
+{
+ struct ieee802154_netdev_priv *priv = netdev_priv(dev);
+
+ BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+ priv->pan_id = val;
+}
+void ieee802154_dev_set_short_addr(struct net_device *dev, u16 val)
+{
+ struct ieee802154_netdev_priv *priv = netdev_priv(dev);
+
+ BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+ priv->short_addr = val;
+}
+void ieee802154_dev_set_channel(struct net_device *dev, u8 val)
+{
+ struct ieee802154_netdev_priv *priv = netdev_priv(dev);
+
+ BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+ priv->chan = val;
+}
+
+/* FIXME: come with better solution */
+struct ieee802154_priv *ieee802154_slave_get_hw(struct net_device *dev)
+{
+ struct ieee802154_netdev_priv *priv;
+ priv = netdev_priv(dev);
+ return priv->hw;
+}
+EXPORT_SYMBOL(ieee802154_slave_get_hw);
+
+int ieee802154_pib_set(struct ieee802154_dev *hw, struct ieee802154_pib *pib)
+{
+ int ret;
+ struct ieee802154_priv *priv = ieee802154_to_priv(hw);
+ BUG_ON(!hw);
+ BUG_ON(!pib);
+ switch (pib->type) {
+ case IEEE802154_PIB_CURCHAN:
+ /* Our internal mask is inverted
+ * 0 = channel is available
+ * 1 = channel is unavailable
+ * this saves initialization */
+ if (hw->channel_mask & (1 << (pib->val - 1)))
+ return -EINVAL;
+ ret = priv->ops->set_channel(hw, pib->val);
+ if (ret == PHY_ERROR)
+ return -EINVAL; /* FIXME */
+ hw->current_channel = pib->val;
+ break;
+ case IEEE802154_PIB_CHANSUPP:
+ hw->channel_mask = ~(pib->val);
+ break;
+ case IEEE802154_PIB_TRPWR:
+ /* TODO */
+ break;
+ case IEEE802154_PIB_CCAMODE:
+ /* TODO */
+ break;
+ default:
+ pr_debug("Unknown PIB type value\n");
+ return -ENOTSUPP;
+ }
+ return 0;
+}
+
+int ieee802154_pib_get(struct ieee802154_dev *hw, struct ieee802154_pib *pib)
+{
+ BUG_ON(!hw);
+ BUG_ON(!pib);
+ switch (pib->type) {
+ case IEEE802154_PIB_CURCHAN:
+ pib->val = hw->current_channel;
+ break;
+ case IEEE802154_PIB_CHANSUPP:
+ pib->val = ~(hw->channel_mask);
+ break;
+ case IEEE802154_PIB_TRPWR:
+ pib->val = 0;
+ break;
+ case IEEE802154_PIB_CCAMODE:
+ pib->val = 0;
+ break;
+ default:
+ pr_debug("Unknown PIB type value\n");
+ return -ENOTSUPP;
+ }
+ return 0;
+}
+
+int ieee802154_slave_register_notifier(struct net_device *dev, struct notifier_block *nb)
+{
+ struct ieee802154_netdev_priv *priv = netdev_priv(dev);
+ return blocking_notifier_chain_register(&priv->events, nb);
+}
+EXPORT_SYMBOL(ieee802154_slave_register_notifier);
+int ieee802154_slave_unregister_notifier(struct net_device *dev, struct notifier_block *nb)
+{
+ struct ieee802154_netdev_priv *priv = netdev_priv(dev);
+ return blocking_notifier_chain_unregister(&priv->events, nb);
+}
+EXPORT_SYMBOL(ieee802154_slave_unregister_notifier);
+int ieee802154_slave_event(struct net_device *dev, int event, void *data)
+{
+ struct ieee802154_netdev_priv *priv = netdev_priv(dev);
+ return blocking_notifier_call_chain(&priv->events, event, data);
+}
+EXPORT_SYMBOL(ieee802154_slave_event);
+
+/* device should be locked before running */
+void ieee802154_set_pan_id(struct net_device *dev, u16 panid)
+{
+ struct ieee802154_netdev_priv *priv = netdev_priv(dev);
+ priv->pan_id = panid;
+}
+
diff --git a/net/ieee802154/mac_cmd.c b/net/ieee802154/mac_cmd.c
new file mode 100644
index 0000000..88dc196
--- /dev/null
+++ b/net/ieee802154/mac_cmd.c
@@ -0,0 +1,226 @@
+/*
+ * MAC commands interface
+ *
+ * Copyright 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ * Sergey Lapin <[email protected]>
+ * Dmitry Eremin-Solenikov <[email protected]>
+ */
+
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <net/ieee802154/af_ieee802154.h>
+#include <net/ieee802154/mac_def.h>
+#include <net/ieee802154/netdev.h>
+#include <net/ieee802154/nl.h>
+#include <net/ieee802154/beacon.h>
+
+static int ieee802154_cmd_beacon_req(struct sk_buff *skb)
+{
+ struct ieee802154_addr saddr; /* jeez */
+ int flags = 0;
+ if (skb->len != 1)
+ return -EINVAL;
+
+ if (skb->pkt_type != PACKET_HOST)
+ return 0;
+
+ /* Checking if we're really PAN coordinator
+ * before sending beacons */
+ if (!(skb->dev->priv_flags & IFF_IEEE802154_COORD))
+ return 0;
+
+ if (MAC_CB(skb)->sa.addr_type != IEEE802154_ADDR_NONE ||
+ MAC_CB(skb)->da.addr_type != IEEE802154_ADDR_SHORT ||
+ MAC_CB(skb)->da.pan_id != IEEE802154_PANID_BROADCAST ||
+ MAC_CB(skb)->da.short_addr != IEEE802154_ADDR_BROADCAST)
+ return -EINVAL;
+
+
+ /* 7 bytes of MHR and 1 byte of command frame identifier
+ * We have no information in this command to proceed with.
+ * we need to submit beacon as answer to this. */
+
+ return ieee802154_send_beacon(skb->dev, &saddr, ieee802154_dev_get_pan_id(skb->dev),
+ NULL, 0, flags, NULL);
+}
+
+static int ieee802154_cmd_assoc_req(struct sk_buff *skb)
+{
+ u8 cap;
+
+ if (skb->len != 2)
+ return -EINVAL;
+
+ if (skb->pkt_type != PACKET_HOST)
+ return 0;
+
+ if (MAC_CB(skb)->sa.addr_type != IEEE802154_ADDR_LONG ||
+ MAC_CB(skb)->sa.pan_id != IEEE802154_PANID_BROADCAST)
+ return -EINVAL;
+
+ /* FIXME: check that we allow incoming ASSOC requests by consulting MIB */
+
+ cap = skb->data[1];
+
+ return ieee802154_nl_assoc_indic(skb->dev, &MAC_CB(skb)->sa, cap);
+}
+
+static int ieee802154_cmd_assoc_resp(struct sk_buff *skb)
+{
+ u8 status;
+ u16 short_addr;
+
+ if (skb->len != 4)
+ return -EINVAL;
+
+ if (skb->pkt_type != PACKET_HOST)
+ return 0;
+
+ if (MAC_CB(skb)->sa.addr_type != IEEE802154_ADDR_LONG ||
+ MAC_CB(skb)->sa.addr_type != IEEE802154_ADDR_LONG ||
+ !(MAC_CB(skb)->flags & MAC_CB_FLAG_INTRAPAN))
+ return -EINVAL;
+
+ /* FIXME: check that we requested association ? */
+
+ status = skb->data[3];
+ short_addr = skb->data[1] | (skb->data[2] << 8);
+ pr_info("Received ASSOC-RESP status %x, addr %hx\n", status, short_addr);
+ if (status) {
+ ieee802154_dev_set_short_addr(skb->dev, IEEE802154_ADDR_BROADCAST);
+ ieee802154_dev_set_pan_id(skb->dev, IEEE802154_PANID_BROADCAST);
+ } else
+ ieee802154_dev_set_short_addr(skb->dev, short_addr);
+
+ return ieee802154_nl_assoc_confirm(skb->dev, short_addr, status);
+}
+
+static int ieee802154_cmd_disassoc_notify(struct sk_buff *skb)
+{
+ u8 reason;
+
+ if (skb->len != 2)
+ return -EINVAL;
+
+ if (skb->pkt_type != PACKET_HOST)
+ return 0;
+
+ if (MAC_CB(skb)->sa.addr_type != IEEE802154_ADDR_LONG ||
+ (MAC_CB(skb)->da.addr_type != IEEE802154_ADDR_LONG &&
+ MAC_CB(skb)->da.addr_type != IEEE802154_ADDR_SHORT) ||
+ MAC_CB(skb)->sa.pan_id != MAC_CB(skb)->da.pan_id)
+ return -EINVAL;
+
+ reason = skb->data[1];
+
+ /* FIXME: checks if this was our coordinator and the disassoc us */
+ /* FIXME: if we device, one should receive ->da and not ->sa */
+ /* FIXME: the status should also help */
+
+ return ieee802154_nl_disassoc_indic(skb->dev, &MAC_CB(skb)->sa, reason);
+}
+
+int ieee802154_process_cmd(struct net_device *dev, struct sk_buff *skb)
+{
+ u8 cmd;
+
+ if (skb->len < 1) {
+ pr_warning("Uncomplete command frame!\n");
+ goto drop;
+ }
+
+ cmd = *(skb->data);
+ pr_debug("Command %02x on device %s\n", cmd, dev->name);
+
+ switch (cmd) {
+ case IEEE802154_CMD_ASSOCIATION_REQ:
+ ieee802154_cmd_assoc_req(skb);
+ break;
+ case IEEE802154_CMD_ASSOCIATION_RESP:
+ ieee802154_cmd_assoc_resp(skb);
+ break;
+ case IEEE802154_CMD_DISASSOCIATION_NOTIFY:
+ ieee802154_cmd_disassoc_notify(skb);
+ break;
+ case IEEE802154_CMD_BEACON_REQ:
+ ieee802154_cmd_beacon_req(skb);
+ break;
+ default:
+ pr_debug("Frame type is not supported yet\n");
+ goto drop;
+ }
+
+
+ kfree_skb(skb);
+ return NET_RX_SUCCESS;
+
+drop:
+ kfree_skb(skb);
+ return NET_RX_DROP;
+}
+
+int ieee802154_send_beacon_req(struct net_device *dev)
+{
+ struct ieee802154_addr addr;
+ struct ieee802154_addr saddr;
+ u8 cmd = IEEE802154_CMD_BEACON_REQ;
+ addr.addr_type = IEEE802154_ADDR_SHORT;
+ addr.short_addr = IEEE802154_ADDR_BROADCAST;
+ addr.pan_id = IEEE802154_PANID_BROADCAST;
+ saddr.addr_type = IEEE802154_ADDR_NONE;
+ return ieee802154_send_cmd(dev, &addr, &saddr, &cmd, 1);
+}
+
+int ieee802154_send_cmd(struct net_device *dev,
+ struct ieee802154_addr *addr, struct ieee802154_addr *saddr,
+ const u8 *buf, int len)
+{
+ struct sk_buff *skb;
+ int err;
+ struct ieee802154_priv *hw = ieee802154_slave_get_hw(dev);
+
+ BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+ skb = alloc_skb(LL_ALLOCATED_SPACE(dev) + len, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ skb_reserve(skb, LL_RESERVED_SPACE(dev));
+
+ skb_reset_network_header(skb);
+
+ MAC_CB(skb)->flags = IEEE802154_FC_TYPE_MAC_CMD | MAC_CB_FLAG_ACKREQ;
+ MAC_CB(skb)->seq = hw->dsn;
+ err = dev_hard_header(skb, dev, ETH_P_IEEE802154, addr, saddr, len);
+ if (err < 0) {
+ kfree_skb(skb);
+ return err;
+ }
+
+ skb_reset_mac_header(skb);
+ memcpy(skb_put(skb, len), buf, len);
+
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_IEEE802154);
+ hw->dsn++;
+
+ return dev_queue_xmit(skb);
+}
+
diff --git a/net/ieee802154/main.c b/net/ieee802154/main.c
new file mode 100644
index 0000000..743c1ea
--- /dev/null
+++ b/net/ieee802154/main.c
@@ -0,0 +1,175 @@
+/*
+ * ieee802154_phy.c
+ *
+ * Description: IEEE 802.15.4 PHY layer
+ *
+ * Copyright (C) 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ * Pavel Smolenskiy <[email protected]>
+ * Maxim Gorbachyov <[email protected]>
+ * Dmitry Eremin-Solenikov <[email protected]>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+
+#include <net/ieee802154/dev.h>
+#include <net/ieee802154/netdev.h>
+#include <net/ieee802154/nl.h>
+
+struct ieee802154_dev *ieee802154_alloc_device(void)
+{
+ struct ieee802154_priv *priv = kzalloc(sizeof(struct ieee802154_priv), GFP_KERNEL);
+ INIT_LIST_HEAD(&priv->slaves);
+ spin_lock_init(&priv->slaves_lock);
+ return &priv->hw;
+}
+EXPORT_SYMBOL(ieee802154_alloc_device);
+
+void ieee802154_free_device(struct ieee802154_dev *hw)
+{
+ struct ieee802154_priv *priv = ieee802154_to_priv(hw);
+
+ BUG_ON(!list_empty(&priv->slaves));
+ BUG_ON(priv->master);
+
+ kfree(priv);
+}
+EXPORT_SYMBOL(ieee802154_free_device);
+
+int ieee802154_register_device(struct ieee802154_dev *dev, struct ieee802154_ops *ops)
+{
+ struct ieee802154_priv *priv = ieee802154_to_priv(dev);
+ int rc;
+
+ if (!try_module_get(ops->owner))
+ return -EFAULT;
+
+ BUG_ON(!dev || !dev->name);
+ BUG_ON(!ops || !ops->tx || !ops->cca || !ops->ed || !ops->set_trx_state);
+
+ priv->ops = ops;
+ rc = ieee802154_register_netdev_master(priv);
+ if (rc < 0)
+ goto out;
+ priv->dev_workqueue = create_singlethread_workqueue(priv->master->name);
+ if (!priv->dev_workqueue)
+ goto out_wq;
+
+ return 0;
+
+out_wq:
+ ieee802154_unregister_netdev_master(priv);
+out:
+ return rc;
+}
+EXPORT_SYMBOL(ieee802154_register_device);
+
+void ieee802154_unregister_device(struct ieee802154_dev *dev)
+{
+ struct ieee802154_priv *priv = ieee802154_to_priv(dev);
+
+ ieee802154_drop_slaves(dev);
+ ieee802154_unregister_netdev_master(priv);
+ flush_workqueue(priv->dev_workqueue);
+ destroy_workqueue(priv->dev_workqueue);
+ module_put(priv->ops->owner);
+}
+EXPORT_SYMBOL(ieee802154_unregister_device);
+
+static void __ieee802154_rx_prepare(struct ieee802154_dev *dev, struct sk_buff *skb, u8 lqi)
+{
+ struct ieee802154_priv *priv = ieee802154_to_priv(dev);
+
+ BUG_ON(!skb);
+
+ PHY_CB(skb)->lqi = lqi;
+
+ skb->dev = priv->master;
+
+ skb->iif = skb->dev->ifindex;
+
+ skb->protocol = htons(ETH_P_IEEE802154);
+
+ skb_reset_mac_header(skb);
+}
+
+void ieee802154_rx(struct ieee802154_dev *dev, struct sk_buff *skb, u8 lqi)
+{
+ struct sk_buff *skb2;
+
+ __ieee802154_rx_prepare(dev, skb, lqi);
+
+ skb2 = skb_clone(skb, GFP_KERNEL);
+ netif_rx(skb2);
+
+ ieee802154_subif_rx(dev, skb);
+}
+EXPORT_SYMBOL(ieee802154_rx);
+
+struct rx_work {
+ struct sk_buff *skb;
+ struct work_struct work;
+ struct ieee802154_dev *dev;
+};
+
+static void ieee802154_rx_worker(struct work_struct *work)
+{
+ struct rx_work *rw = container_of(work, struct rx_work, work);
+ struct sk_buff *skb = rw->skb;
+
+ struct sk_buff *skb2 = skb_clone(skb, GFP_KERNEL);
+ netif_rx(skb2);
+
+ ieee802154_subif_rx(rw->dev, skb);
+ kfree(rw);
+}
+
+void ieee802154_rx_irqsafe(struct ieee802154_dev *dev, struct sk_buff *skb, u8 lqi)
+{
+ struct ieee802154_priv *priv = ieee802154_to_priv(dev);
+ struct rx_work *work = kzalloc(sizeof(struct rx_work), GFP_ATOMIC);
+
+ if (!work)
+ return;
+
+ __ieee802154_rx_prepare(dev, skb, lqi);
+
+ INIT_WORK(&work->work, ieee802154_rx_worker);
+ work->skb = skb;
+ work->dev = dev;
+
+ queue_work(priv->dev_workqueue, &work->work);
+}
+EXPORT_SYMBOL(ieee802154_rx_irqsafe);
+
+static int __init ieee802154_init(void)
+{
+ return ieee802154_nl_init();
+}
+module_init(ieee802154_init);
+
+static void __exit ieee802154_exit(void)
+{
+ ieee802154_nl_exit();
+}
+module_exit(ieee802154_exit);
+
+MODULE_DESCRIPTION("IEEE 802.15.4 implementation");
+MODULE_LICENSE("GPL v2");
+
diff --git a/net/ieee802154/mdev.c b/net/ieee802154/mdev.c
new file mode 100644
index 0000000..c5cab08
--- /dev/null
+++ b/net/ieee802154/mdev.c
@@ -0,0 +1,189 @@
+/*
+ * Interface from IEEE802154.4 MAC layer to the userspace, net_device part.
+ *
+ * Copyright (C) 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+
+#include <net/ieee802154/phy.h>
+#include <net/ieee802154/netdev.h>
+#include <net/ieee802154/af_ieee802154.h>
+
+struct ieee802154_mnetdev_priv {
+ struct ieee802154_priv *hw;
+ struct net_device *dev;
+};
+
+struct xmit_work {
+ struct sk_buff *skb;
+ struct work_struct work;
+ struct ieee802154_mnetdev_priv *priv;
+};
+
+static void ieee802154_xmit_worker(struct work_struct *work)
+{
+ struct xmit_work *xw = container_of(work, struct xmit_work, work);
+ phy_status_t res;
+
+ if (xw->priv->hw->hw.current_channel != PHY_CB(xw->skb)->chan) {
+ res = xw->priv->hw->ops->set_channel(&xw->priv->hw->hw, PHY_CB(xw->skb)->chan);
+ if (res != PHY_SUCCESS) {
+ pr_debug("set_channel failed\n");
+ goto out;
+ }
+ }
+
+ res = xw->priv->hw->ops->cca(&xw->priv->hw->hw);
+ if (res != PHY_IDLE) {
+ pr_debug("CCA failed\n");
+ goto out;
+ }
+
+ res = xw->priv->hw->ops->set_trx_state(&xw->priv->hw->hw, PHY_TX_ON);
+ if (res != PHY_SUCCESS && res != PHY_TX_ON) {
+ pr_debug("set_trx_state returned %d\n", res);
+ goto out;
+ }
+
+ res = xw->priv->hw->ops->tx(&xw->priv->hw->hw, xw->skb);
+
+out:
+ /* FIXME: result processing and/or requeue!!! */
+ dev_kfree_skb(xw->skb);
+
+ xw->priv->hw->ops->set_trx_state(&xw->priv->hw->hw, PHY_RX_ON);
+ kfree(xw);
+}
+
+static int ieee802154_master_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ieee802154_mnetdev_priv *priv = netdev_priv(dev);
+ struct xmit_work *work;
+
+ if (skb_cow_head(skb, priv->hw->hw.extra_tx_headroom)) {
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ work = kzalloc(sizeof(struct xmit_work), GFP_ATOMIC);
+ if (!work)
+ return NETDEV_TX_BUSY;
+
+ INIT_WORK(&work->work, ieee802154_xmit_worker);
+ work->skb = skb;
+ work->priv = priv;
+
+ queue_work(priv->hw->dev_workqueue, &work->work);
+
+ return NETDEV_TX_OK;
+}
+
+static int ieee802154_master_open(struct net_device *dev)
+{
+ struct ieee802154_mnetdev_priv *priv;
+ phy_status_t status;
+ priv = netdev_priv(dev);
+ if (!priv) {
+ pr_debug("%s:%s: unable to get master private data\n",
+ __FILE__, __func__);
+ return -ENODEV;
+ }
+ status = priv->hw->ops->set_trx_state(&priv->hw->hw, PHY_RX_ON);
+ if (status != PHY_SUCCESS) {
+ pr_debug("set_trx_state returned %d\n", status);
+ return -EBUSY;
+ }
+
+ netif_start_queue(dev);
+ return 0;
+}
+
+static int ieee802154_master_close(struct net_device *dev)
+{
+ struct ieee802154_mnetdev_priv *priv;
+ netif_stop_queue(dev);
+ priv = netdev_priv(dev);
+
+ priv->hw->ops->set_trx_state(&priv->hw->hw, PHY_FORCE_TRX_OFF);
+ return 0;
+}
+static int ieee802154_master_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct ieee802154_mnetdev_priv *priv = netdev_priv(dev);
+ switch (cmd) {
+ case IEEE802154_SIOC_ADD_SLAVE:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ return ieee802154_add_slave(&priv->hw->hw, (u8 *) &ifr->ifr_hwaddr.sa_data);
+ }
+ return -ENOIOCTLCMD;
+}
+
+static void ieee802154_netdev_setup_master(struct net_device *dev)
+{
+ dev->addr_len = 0;
+ memset(dev->broadcast, 0xff, dev->addr_len);
+ dev->features = NETIF_F_NO_CSUM;
+ dev->hard_header_len = 0;
+ dev->mtu = 127;
+ dev->tx_queue_len = 0;
+ dev->type = ARPHRD_IEEE802154_PHY;
+ dev->flags = IFF_NOARP | IFF_BROADCAST;
+ dev->watchdog_timeo = 0;
+}
+
+static const struct net_device_ops ieee802154_master_ops = {
+ .ndo_open = ieee802154_master_open,
+ .ndo_stop = ieee802154_master_close,
+ .ndo_start_xmit = ieee802154_master_hard_start_xmit,
+ .ndo_do_ioctl = ieee802154_master_ioctl,
+};
+
+int ieee802154_register_netdev_master(struct ieee802154_priv *hw)
+{
+ struct net_device *dev;
+ struct ieee802154_mnetdev_priv *priv;
+
+ dev = alloc_netdev(sizeof(struct ieee802154_mnetdev_priv),
+ "mwpan%d", ieee802154_netdev_setup_master);
+ if (!dev) {
+ printk(KERN_ERR "Failure to initialize master IEEE802154 device\n");
+ return -ENOMEM;
+ }
+ priv = netdev_priv(dev);
+ priv->dev = dev;
+ priv->hw = hw;
+ hw->master = dev;
+ dev->netdev_ops = &ieee802154_master_ops;
+ dev->needed_headroom = hw->hw.extra_tx_headroom;
+ SET_NETDEV_DEV(dev, hw->hw.parent);
+ register_netdev(dev);
+ return 0;
+}
+
+void ieee802154_unregister_netdev_master(struct ieee802154_priv *hw)
+{
+ struct net_device *dev = hw->master;
+ BUG_ON(!hw->master);
+
+ unregister_netdev(hw->master);
+ hw->master = NULL;
+ free_netdev(dev);
+}
diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c
new file mode 100644
index 0000000..2acf0f1
--- /dev/null
+++ b/net/ieee802154/netlink.c
@@ -0,0 +1,637 @@
+/*
+ * Netlink intefcace for IEEE 802.15.4 stack
+ *
+ * Copyright 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ * Sergey Lapin <[email protected]>
+ * Dmitry Eremin-Solenikov <[email protected]>
+ */
+
+#include <linux/kernel.h>
+#include <linux/if_arp.h>
+#include <net/netlink.h>
+#include <net/genetlink.h>
+#include <linux/netdevice.h>
+#include <net/ieee802154/af_ieee802154.h>
+#define IEEE802154_NL_WANT_POLICY
+#include <net/ieee802154/nl.h>
+#include <net/ieee802154/mac_def.h>
+#include <net/ieee802154/netdev.h>
+
+static unsigned int ieee802154_seq_num;
+
+static struct genl_family ieee802154_coordinator_family = {
+ .id = GENL_ID_GENERATE,
+ .hdrsize = 0,
+ .name = IEEE802154_NL_NAME,
+ .version = 1,
+ .maxattr = IEEE802154_ATTR_MAX,
+};
+
+static struct genl_multicast_group ieee802154_coord_mcgrp = {
+ .name = IEEE802154_MCAST_COORD_NAME,
+};
+
+static struct genl_multicast_group ieee802154_beacon_mcgrp = {
+ .name = IEEE802154_MCAST_BEACON_NAME,
+};
+
+/* Requests to userspace */
+
+int ieee802154_nl_assoc_indic(struct net_device *dev, struct ieee802154_addr *addr, u8 cap)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ pr_debug("%s\n", __func__);
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+ if (!msg)
+ goto out_msg;
+
+ hdr = genlmsg_put(msg, 0, ieee802154_seq_num++, &ieee802154_coordinator_family, /* flags*/ 0, IEEE802154_ASSOCIATE_INDIC);
+ if (!hdr)
+ goto out_free;
+
+ NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name);
+ NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex);
+ NLA_PUT_HW_ADDR(msg, IEEE802154_ATTR_HW_ADDR, dev->dev_addr);
+
+ /* FIXME: check that we really received hw address */
+ NLA_PUT_HW_ADDR(msg, IEEE802154_ATTR_SRC_HW_ADDR, addr->hwaddr);
+
+ NLA_PUT_U8(msg, IEEE802154_ATTR_CAPABILITY, cap);
+
+ if (!genlmsg_end(msg, hdr))
+ goto out_free;
+
+ return genlmsg_multicast(msg, 0, ieee802154_coord_mcgrp.id, GFP_ATOMIC);
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+out_free:
+ nlmsg_free(msg);
+out_msg:
+ return -ENOBUFS;
+}
+
+int ieee802154_nl_beacon_indic(struct net_device *dev, u16 panid, u16 coord_addr) /* TODO */
+{
+ struct sk_buff *msg;
+ void *hdr;
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+ if (!msg)
+ goto out_msg;
+ hdr = genlmsg_put(msg, 0, ieee802154_seq_num++, &ieee802154_coordinator_family, /* flags*/ 0, IEEE802154_ASSOCIATE_CONF);
+ if (!hdr)
+ goto out_free;
+
+ NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name);
+ NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex);
+ NLA_PUT_HW_ADDR(msg, IEEE802154_ATTR_HW_ADDR, dev->dev_addr);
+ NLA_PUT_U16(msg, IEEE802154_ATTR_COORD_SHORT_ADDR, coord_addr);
+ NLA_PUT_U16(msg, IEEE802154_ATTR_COORD_PAN_ID, panid);
+
+ if (!genlmsg_end(msg, hdr))
+ goto out_free;
+
+ /* FIXME different multicast group needed */
+ return genlmsg_multicast(msg, 0, ieee802154_beacon_mcgrp.id, GFP_ATOMIC);
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+out_free:
+ nlmsg_free(msg);
+out_msg:
+ return -ENOBUFS;
+}
+
+int ieee802154_nl_assoc_confirm(struct net_device *dev, u16 short_addr, u8 status)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ pr_debug("%s\n", __func__);
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+ if (!msg)
+ goto out_msg;
+
+ hdr = genlmsg_put(msg, 0, ieee802154_seq_num++, &ieee802154_coordinator_family, /* flags*/ 0, IEEE802154_ASSOCIATE_CONF);
+ if (!hdr)
+ goto out_free;
+
+ NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name);
+ NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex);
+ NLA_PUT_HW_ADDR(msg, IEEE802154_ATTR_HW_ADDR, dev->dev_addr);
+
+ NLA_PUT_U16(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr);
+ NLA_PUT_U8(msg, IEEE802154_ATTR_STATUS, status);
+
+ if (!genlmsg_end(msg, hdr))
+ goto out_free;
+
+ return genlmsg_multicast(msg, 0, ieee802154_coord_mcgrp.id, GFP_ATOMIC);
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+out_free:
+ nlmsg_free(msg);
+out_msg:
+ return -ENOBUFS;
+}
+
+int ieee802154_nl_disassoc_indic(struct net_device *dev, struct ieee802154_addr *addr, u8 reason)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ pr_debug("%s\n", __func__);
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+ if (!msg)
+ goto out_msg;
+
+ hdr = genlmsg_put(msg, 0, ieee802154_seq_num++, &ieee802154_coordinator_family, /* flags*/ 0, IEEE802154_DISASSOCIATE_INDIC);
+ if (!hdr)
+ goto out_free;
+
+ NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name);
+ NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex);
+ NLA_PUT_HW_ADDR(msg, IEEE802154_ATTR_HW_ADDR, dev->dev_addr);
+
+ if (addr->addr_type == IEEE802154_ADDR_LONG)
+ NLA_PUT_HW_ADDR(msg, IEEE802154_ATTR_SRC_HW_ADDR, addr->hwaddr);
+ else
+ NLA_PUT_U16(msg, IEEE802154_ATTR_SRC_SHORT_ADDR, addr->short_addr);
+
+ NLA_PUT_U8(msg, IEEE802154_ATTR_REASON, reason);
+
+ if (!genlmsg_end(msg, hdr))
+ goto out_free;
+
+ return genlmsg_multicast(msg, 0, ieee802154_coord_mcgrp.id, GFP_ATOMIC);
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+out_free:
+ nlmsg_free(msg);
+out_msg:
+ return -ENOBUFS;
+}
+
+int ieee802154_nl_disassoc_confirm(struct net_device *dev, u8 status)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ pr_debug("%s\n", __func__);
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+ if (!msg)
+ goto out_msg;
+
+ hdr = genlmsg_put(msg, 0, ieee802154_seq_num++, &ieee802154_coordinator_family, /* flags*/ 0, IEEE802154_DISASSOCIATE_CONF);
+ if (!hdr)
+ goto out_free;
+
+ NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name);
+ NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex);
+ NLA_PUT_HW_ADDR(msg, IEEE802154_ATTR_HW_ADDR, dev->dev_addr);
+
+ NLA_PUT_U8(msg, IEEE802154_ATTR_STATUS, status);
+
+ if (!genlmsg_end(msg, hdr))
+ goto out_free;
+
+ return genlmsg_multicast(msg, 0, ieee802154_coord_mcgrp.id, GFP_ATOMIC);
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+out_free:
+ nlmsg_free(msg);
+out_msg:
+ return -ENOBUFS;
+}
+
+int ieee802154_nl_scan_confirm(struct net_device *dev, u8 status, u8 scan_type, u32 unscanned,
+ u8 *edl/* , struct list_head *pan_desc_list */)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ pr_debug("%s\n", __func__);
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+ if (!msg)
+ goto out_msg;
+
+ hdr = genlmsg_put(msg, 0, ieee802154_seq_num++, &ieee802154_coordinator_family, /* flags*/ 0, IEEE802154_SCAN_CONF);
+ if (!hdr)
+ goto out_free;
+
+ NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name);
+ NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex);
+ NLA_PUT_HW_ADDR(msg, IEEE802154_ATTR_HW_ADDR, dev->dev_addr);
+
+ NLA_PUT_U8(msg, IEEE802154_ATTR_STATUS, status);
+ NLA_PUT_U8(msg, IEEE802154_ATTR_SCAN_TYPE, scan_type);
+ NLA_PUT_U32(msg, IEEE802154_ATTR_CHANNELS, unscanned);
+
+ if (edl)
+ NLA_PUT(msg, IEEE802154_ATTR_ED_LIST, 27, edl);
+
+ if (!genlmsg_end(msg, hdr))
+ goto out_free;
+
+ return genlmsg_multicast(msg, 0, ieee802154_coord_mcgrp.id, GFP_ATOMIC);
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+out_free:
+ nlmsg_free(msg);
+out_msg:
+ return -ENOBUFS;
+}
+
+/* Requests from userspace */
+
+static int ieee802154_associate_req(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net_device *dev;
+ struct ieee802154_addr addr, saddr;
+ u8 buf[2];
+ int pos = 0;
+ int ret = -EINVAL;
+
+ if (!info->attrs[IEEE802154_ATTR_CHANNEL]
+ || !info->attrs[IEEE802154_ATTR_COORD_PAN_ID]
+ || (!info->attrs[IEEE802154_ATTR_COORD_HW_ADDR] && !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR])
+ || !info->attrs[IEEE802154_ATTR_CAPABILITY])
+ return -EINVAL;
+
+ if (info->attrs[IEEE802154_ATTR_DEV_NAME]) {
+ char name[IFNAMSIZ + 1];
+ nla_strlcpy(name, info->attrs[IEEE802154_ATTR_DEV_NAME], sizeof(name));
+ dev = dev_get_by_name(&init_net, name);
+ } else if (info->attrs[IEEE802154_ATTR_DEV_INDEX]) {
+ dev = dev_get_by_index(&init_net, nla_get_u32(info->attrs[IEEE802154_ATTR_DEV_INDEX]));
+ } else
+ return -ENODEV;
+
+ if (!dev)
+ return -ENODEV;
+ if (dev->type != ARPHRD_IEEE802154) {
+ dev_put(dev);
+ return -EINVAL;
+ }
+
+ if (info->attrs[IEEE802154_ATTR_COORD_HW_ADDR]) {
+ addr.addr_type = IEEE802154_ADDR_LONG;
+ NLA_GET_HW_ADDR(info->attrs[IEEE802154_ATTR_COORD_HW_ADDR], addr.hwaddr);
+ } else {
+ addr.addr_type = IEEE802154_ADDR_SHORT;
+ addr.short_addr = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]);
+ }
+ addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);
+
+ saddr.addr_type = IEEE802154_ADDR_LONG;
+ saddr.pan_id = IEEE802154_PANID_BROADCAST;
+ memcpy(saddr.hwaddr, dev->dev_addr, IEEE802154_ADDR_LEN);
+
+ /* FIXME: set PIB/MIB info */
+ ieee802154_dev_set_pan_id(dev, addr.pan_id);
+ ieee802154_dev_set_channel(dev, nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]));
+
+ buf[pos++] = IEEE802154_CMD_ASSOCIATION_REQ;
+ buf[pos++] = nla_get_u8(info->attrs[IEEE802154_ATTR_CAPABILITY]);
+ ret = ieee802154_send_cmd(dev, &addr, &saddr, buf, pos);
+
+ dev_put(dev);
+ return ret;
+}
+
+static int ieee802154_associate_resp(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net_device *dev;
+ struct ieee802154_addr addr, saddr;
+ u8 buf[4];
+ int pos = 0;
+ u16 short_addr;
+ int ret = -EINVAL;
+
+ if (!info->attrs[IEEE802154_ATTR_STATUS]
+ || !info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]
+ || !info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR])
+ return -EINVAL;
+
+ if (info->attrs[IEEE802154_ATTR_DEV_NAME]) {
+ char name[IFNAMSIZ + 1];
+ nla_strlcpy(name, info->attrs[IEEE802154_ATTR_DEV_NAME], sizeof(name));
+ dev = dev_get_by_name(&init_net, name);
+ } else if (info->attrs[IEEE802154_ATTR_DEV_INDEX]) {
+ dev = dev_get_by_index(&init_net, nla_get_u32(info->attrs[IEEE802154_ATTR_DEV_INDEX]));
+ } else
+ return -ENODEV;
+
+ if (!dev)
+ return -ENODEV;
+ if (dev->type != ARPHRD_IEEE802154) {
+ dev_put(dev);
+ return -EINVAL;
+ }
+
+ addr.addr_type = IEEE802154_ADDR_LONG;
+ NLA_GET_HW_ADDR(info->attrs[IEEE802154_ATTR_DEST_HW_ADDR], addr.hwaddr);
+ addr.pan_id = ieee802154_dev_get_pan_id(dev);
+
+ saddr.addr_type = IEEE802154_ADDR_LONG;
+ saddr.pan_id = addr.pan_id;
+ memcpy(saddr.hwaddr, dev->dev_addr, IEEE802154_ADDR_LEN);
+
+ short_addr = nla_get_u16(info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]);
+
+ buf[pos++] = IEEE802154_CMD_ASSOCIATION_RESP;
+ buf[pos++] = short_addr;
+ buf[pos++] = short_addr >> 8;
+ buf[pos++] = nla_get_u8(info->attrs[IEEE802154_ATTR_STATUS]);
+
+ ret = ieee802154_send_cmd(dev, &addr, &saddr, buf, pos);
+
+ dev_put(dev);
+ return ret;
+}
+
+static int ieee802154_disassociate_req(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net_device *dev;
+ struct ieee802154_addr addr, saddr;
+ u8 buf[2];
+ int pos = 0;
+ int ret = -EINVAL;
+
+ if ((!info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] && !info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR])
+ || !info->attrs[IEEE802154_ATTR_REASON])
+ return -EINVAL;
+
+ if (info->attrs[IEEE802154_ATTR_DEV_NAME]) {
+ char name[IFNAMSIZ + 1];
+ nla_strlcpy(name, info->attrs[IEEE802154_ATTR_DEV_NAME], sizeof(name));
+ dev = dev_get_by_name(&init_net, name);
+ } else if (info->attrs[IEEE802154_ATTR_DEV_INDEX]) {
+ dev = dev_get_by_index(&init_net, nla_get_u32(info->attrs[IEEE802154_ATTR_DEV_INDEX]));
+ } else
+ return -ENODEV;
+
+ if (!dev)
+ return -ENODEV;
+ if (dev->type != ARPHRD_IEEE802154) {
+ dev_put(dev);
+ return -EINVAL;
+ }
+
+ if (info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]) {
+ addr.addr_type = IEEE802154_ADDR_LONG;
+ NLA_GET_HW_ADDR(info->attrs[IEEE802154_ATTR_DEST_HW_ADDR], addr.hwaddr);
+ } else {
+ addr.addr_type = IEEE802154_ADDR_SHORT;
+ addr.short_addr = nla_get_u16(info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]);
+ }
+ addr.pan_id = ieee802154_dev_get_pan_id(dev);
+
+ saddr.addr_type = IEEE802154_ADDR_LONG;
+ saddr.pan_id = ieee802154_dev_get_pan_id(dev);
+ memcpy(saddr.hwaddr, dev->dev_addr, IEEE802154_ADDR_LEN);
+
+ buf[pos++] = IEEE802154_CMD_DISASSOCIATION_NOTIFY;
+ buf[pos++] = nla_get_u8(info->attrs[IEEE802154_ATTR_REASON]);
+ ret = ieee802154_send_cmd(dev, &addr, &saddr, buf, pos);
+
+ /* FIXME: this should be after the ack receved */
+ ieee802154_dev_set_pan_id(dev, 0xffff);
+ ieee802154_dev_set_short_addr(dev, 0xffff);
+ ieee802154_nl_disassoc_confirm(dev, 0x00);
+
+ dev_put(dev);
+ return ret;
+}
+
+/*
+ * PANid, channel, beacon_order = 15, superframe_order = 15,
+ * PAN_coordinator, battery_life_extension = 0,
+ * coord_realignment = 0, security_enable = 0
+*/
+static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net_device *dev;
+ u16 panid;
+ u8 channel = 0, bcn_ord = 15, sf_ord = 15;
+ int pan_coord, blx = 0, coord_realign = 0, sec = 0;
+ u16 short_addr;
+ int ret;
+
+ if (!info->attrs[IEEE802154_ATTR_COORD_PAN_ID]
+ || !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]
+/*
+ || !info->attrs[IEEE802154_ATTR_CHANNEL]
+ || !info->attrs[IEEE802154_ATTR_BCN_ORD]
+ || !info->attrs[IEEE802154_ATTR_SF_ORD]
+*/
+ || !info->attrs[IEEE802154_ATTR_PAN_COORD]
+/*
+ || !info->attrs[IEEE802154_ATTR_BAT_EXT]
+ || !info->attrs[IEEE802154_ATTR_COORD_REALIGN]
+ || !info->attrs[IEEE802154_ATTR_SEC] */)
+ return -EINVAL;
+ if (info->attrs[IEEE802154_ATTR_DEV_NAME]) {
+ char name[IFNAMSIZ + 1];
+ nla_strlcpy(name, info->attrs[IEEE802154_ATTR_DEV_NAME], sizeof(name));
+ dev = dev_get_by_name(&init_net, name);
+ } else if (info->attrs[IEEE802154_ATTR_DEV_INDEX]) {
+ dev = dev_get_by_index(&init_net, nla_get_u32(info->attrs[IEEE802154_ATTR_DEV_INDEX]));
+ } else
+ return -ENODEV;
+
+ if (!dev)
+ return -ENODEV;
+
+
+ if (dev->type != ARPHRD_IEEE802154) {
+ dev_put(dev);
+ return -EINVAL;
+ }
+ panid = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);
+#if 0
+ channel = nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]);
+ bcn_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_BCN_ORD]);
+ sf_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_SF_ORD]);
+#endif
+ pan_coord = nla_get_u8(info->attrs[IEEE802154_ATTR_PAN_COORD]);
+#if 0
+ blx = nla_get_u8(info->attrs[IEEE802154_ATTR_BAT_EXT]);
+ coord_realign = nla_get_u8(info->attrs[IEEE802154_ATTR_COORD_REALIGN]);
+ sec = nla_get_u8(info->attrs[IEEE802154_ATTR_COORD_SEC]);
+#endif
+ short_addr = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]);
+ ret = ieee802154_mlme_start_req(dev, panid, channel, bcn_ord, sf_ord,
+ pan_coord, blx, coord_realign, sec);
+ if (ret < 0)
+ goto out;
+ ieee802154_dev_set_short_addr(dev, short_addr);
+out:
+ dev_put(dev);
+ return ret;
+}
+
+static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net_device *dev;
+ int ret;
+ u8 type;
+ u32 channels;
+ u8 duration;
+
+ if (!info->attrs[IEEE802154_ATTR_SCAN_TYPE]
+ || !info->attrs[IEEE802154_ATTR_CHANNELS]
+ || !info->attrs[IEEE802154_ATTR_DURATION])
+ return -EINVAL;
+
+ if (info->attrs[IEEE802154_ATTR_DEV_NAME]) {
+ char name[IFNAMSIZ + 1];
+ nla_strlcpy(name, info->attrs[IEEE802154_ATTR_DEV_NAME], sizeof(name));
+ dev = dev_get_by_name(&init_net, name);
+ } else if (info->attrs[IEEE802154_ATTR_DEV_INDEX]) {
+ dev = dev_get_by_index(&init_net, nla_get_u32(info->attrs[IEEE802154_ATTR_DEV_INDEX]));
+ } else
+ return -ENODEV;
+
+ if (!dev)
+ return -ENODEV;
+ if (dev->type != ARPHRD_IEEE802154) {
+ dev_put(dev);
+ return -EINVAL;
+ }
+
+ type = nla_get_u8(info->attrs[IEEE802154_ATTR_SCAN_TYPE]);
+ channels = nla_get_u32(info->attrs[IEEE802154_ATTR_CHANNELS]);
+ duration = nla_get_u8(info->attrs[IEEE802154_ATTR_DURATION]);
+
+ ret = ieee802154_mlme_scan_req(dev, type, channels, duration);
+
+ dev_put(dev);
+ return ret;
+}
+
+#define IEEE802154_OP(_cmd, _func) \
+ { \
+ .cmd = _cmd, \
+ .policy = ieee802154_policy, \
+ .doit = _func, \
+ .dumpit = NULL, \
+ .flags = GENL_ADMIN_PERM, \
+ }
+
+static struct genl_ops ieee802154_coordinator_ops[] = {
+ IEEE802154_OP(IEEE802154_ASSOCIATE_REQ, ieee802154_associate_req),
+ IEEE802154_OP(IEEE802154_ASSOCIATE_RESP, ieee802154_associate_resp),
+ IEEE802154_OP(IEEE802154_DISASSOCIATE_REQ, ieee802154_disassociate_req),
+ IEEE802154_OP(IEEE802154_SCAN_REQ, ieee802154_scan_req),
+ IEEE802154_OP(IEEE802154_START_REQ, ieee802154_start_req),
+};
+
+#if 0
+static int ieee802154_coordinator_rcv(struct sk_buff *skb, struct genl_info *info)
+{
+ struct sk_buff *msg;
+ void *hdr;
+ char name[IFNAMSIZ + 1];
+ struct net_device *dev;
+
+
+ pr_debug("%s\n", __func__);
+
+ if (!info->attrs[IEEE802154_ATTR_DEV_NAME])
+ return -EINVAL;
+
+ nla_strlcpy(name, info->attrs[IEEE802154_ATTR_DEV_NAME], sizeof(name));
+
+ dev = dev_get_by_name(&init_net, name);
+ if (!dev)
+ goto out_dev;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+ if (!msg)
+ goto out_msg;
+
+ hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq, &ieee802154_coordinator_family, /* flags*/ 0, /* cmd */ 1);
+ if (!hdr)
+ goto out_free;
+
+ NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, name);
+ NLA_PUT_U64(msg, IEEE802154_ATTR_HW_ADDR, *(u64 *)&dev->dev_addr);
+
+ if (!genlmsg_end(msg, hdr))
+ goto out_free;
+
+ return genlmsg_unicast(msg, info->snd_pid);
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+out_free:
+ nlmsg_free(msg);
+out_msg:
+ dev_put(dev);
+out_dev:
+ return -ENOBUFS;
+}
+#endif
+
+int __init ieee802154_nl_init(void)
+{
+ int rc;
+ int i;
+
+ rc = genl_register_family(&ieee802154_coordinator_family);
+ if (rc)
+ goto fail;
+
+ rc = genl_register_mc_group(&ieee802154_coordinator_family, &ieee802154_coord_mcgrp);
+ if (rc)
+ goto fail;
+
+ rc = genl_register_mc_group(&ieee802154_coordinator_family, &ieee802154_beacon_mcgrp);
+ if (rc)
+ goto fail;
+
+
+ for (i = 0; i < ARRAY_SIZE(ieee802154_coordinator_ops); i++) {
+ rc = genl_register_ops(&ieee802154_coordinator_family, &ieee802154_coordinator_ops[i]);
+ if (rc)
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ genl_unregister_family(&ieee802154_coordinator_family);
+ return rc;
+}
+
+void __exit ieee802154_nl_exit(void)
+{
+ genl_unregister_family(&ieee802154_coordinator_family);
+}
diff --git a/net/ieee802154/scan.c b/net/ieee802154/scan.c
new file mode 100644
index 0000000..a9c319f
--- /dev/null
+++ b/net/ieee802154/scan.c
@@ -0,0 +1,211 @@
+/*
+ * scan.c
+ *
+ * Description: MAC scan helper functions.
+ *
+ * Copyright (C) 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ * Pavel Smolenskiy <[email protected]>
+ * Maxim Gorbachyov <[email protected]>
+ */
+#include <linux/net.h>
+#include <linux/module.h>
+
+#include <net/ieee802154/beacon.h>
+#include <net/ieee802154/dev.h>
+#include <net/ieee802154/netdev.h>
+#include <net/ieee802154/mac_def.h>
+#include <net/ieee802154/nl.h>
+/*
+ * ED scan is periodic issuing of ed device function
+ * on evry permitted channel, so it is virtually PHY-only scan */
+
+struct scan_work {
+ struct work_struct work;
+
+ int (*scan_ch)(struct scan_work *work, int channel, u8 duration);
+ struct net_device *dev;
+
+ u8 edl[27];
+
+ u8 type;
+ u32 channels;
+ u8 duration;
+};
+
+static int scan_ed(struct scan_work *work, int channel, u8 duration)
+{
+ int ret;
+ struct ieee802154_priv *hw = ieee802154_slave_get_hw(work->dev);
+ pr_debug("ed scan channel %d duration %d\n", channel, duration);
+ ret = hw->ops->ed(&hw->hw, &work->edl[channel]);
+ pr_debug("ed scan channel %d value %d\n", channel, work->edl[channel]);
+ return ret;
+}
+
+struct scan_data {
+ struct notifier_block nb;
+ struct list_head scan_head;
+};
+
+static int beacon_notifier(struct notifier_block *p,
+ unsigned long event, void *data)
+{
+ struct ieee802154_pandsc *pd = data;
+ struct scan_data *sd = container_of(p, struct scan_data, nb);
+ switch (event) {
+ case IEEE802154_NOTIFIER_BEACON:
+ /* TODO: add item to list here */
+ pr_debug("got a beacon frame addr_type %d pan_id %d\n",
+ pd->addr.addr_type, pd->addr.pan_id);
+ break;
+ }
+ return 0;
+}
+
+
+static int scan_passive(struct scan_work *work, int channel, u8 duration)
+{
+ unsigned long j;
+ struct scan_data *data = kzalloc(sizeof(struct scan_data), GFP_KERNEL);
+ pr_debug("passive scan channel %d duration %d\n", channel, duration);
+ data->nb.notifier_call = beacon_notifier;
+ ieee802154_slave_register_notifier(work->dev, &data->nb);
+ /* Hope 2 msecs will be enough for scan */
+ j = msecs_to_jiffies(2);
+ while (j > 0)
+ j = schedule_timeout(j);
+
+ ieee802154_slave_unregister_notifier(work->dev, &data->nb);
+ kfree(data);
+ return PHY_SUCCESS;
+}
+/* Active scan is periodic submission of beacon request
+ * and waiting for beacons which is useful for collecting LWPAN information */
+static int scan_active(struct scan_work *work, int channel, u8 duration)
+{
+ int ret;
+ pr_debug("active scan channel %d duration %d\n", channel, duration);
+ ret = ieee802154_send_beacon_req(work->dev);
+ if (ret < 0)
+ return PHY_ERROR;
+ return scan_passive(work, channel, duration);
+}
+static int scan_orphan(struct scan_work *work, int channel, u8 duration)
+{
+ pr_debug("orphan scan channel %d duration %d\n", channel, duration);
+ return 0;
+}
+
+static void scanner(struct work_struct *work)
+{
+ struct scan_work *sw = container_of(work, struct scan_work, work);
+ struct ieee802154_priv *hw = ieee802154_slave_get_hw(sw->dev);
+ int i;
+ phy_status_t ret;
+
+ for (i = 0; i < 27; i++) {
+ if (!(sw->channels & (1 << i)))
+ continue;
+
+ ret = hw->ops->set_channel(&hw->hw, i);
+ if (ret != PHY_SUCCESS)
+ goto exit_error;
+
+ ret = sw->scan_ch(sw, i, sw->duration);
+ if (ret != PHY_SUCCESS)
+ goto exit_error;
+
+ sw->channels &= ~(1 << i);
+ }
+
+ ieee802154_nl_scan_confirm(sw->dev, IEEE802154_SUCCESS, sw->type, sw->channels,
+ sw->edl/*, NULL */);
+
+ kfree(sw);
+
+ return;
+
+exit_error:
+ ieee802154_nl_scan_confirm(sw->dev, IEEE802154_INVALID_PARAMETER, sw->type, sw->channels,
+ NULL/*, NULL */);
+ kfree(sw);
+ return;
+}
+
+/**
+ * @brief MLME-SAP.Scan request
+ *
+ * Alloc ed_detect list for ED scan.
+ *
+ * @param mac current mac pointer
+ * @param type type of the scan to be performed
+ * @param channels 32-bit mask of requested to scan channels
+ * @param duration scan duration, see ieee802.15.4-2003.pdf, page 145.
+ * @return 0 if request is ok, errno otherwise.
+ */
+int ieee802154_mlme_scan_req(struct net_device *dev, u8 type, u32 channels, u8 duration)
+{
+ struct ieee802154_priv *hw = ieee802154_slave_get_hw(dev);
+ struct scan_work *work;
+
+ pr_debug("%s()\n", __func__);
+
+ if (duration > 14)
+ goto inval;
+ if (channels & hw->hw.channel_mask)
+ goto inval;
+
+ work = kzalloc(sizeof(struct scan_work), GFP_KERNEL);
+ if (!work)
+ goto inval;
+
+ work->dev = dev;
+ work->channels = channels;
+ work->duration = duration;
+ work->type = type;
+
+ switch (type) {
+ case IEEE802154_MAC_SCAN_ED:
+ work->scan_ch = scan_ed;
+ break;
+ case IEEE802154_MAC_SCAN_ACTIVE:
+ work->scan_ch = scan_active;
+ break;
+ case IEEE802154_MAC_SCAN_PASSIVE:
+ work->scan_ch = scan_passive;
+ break;
+ case IEEE802154_MAC_SCAN_ORPHAN:
+ work->scan_ch = scan_orphan;
+ break;
+ default:
+ pr_debug("%s(): invalid type %d\n", __func__, type);
+ goto inval;
+ }
+
+ INIT_WORK(&work->work, scanner);
+ queue_work(hw->dev_workqueue, &work->work);
+
+ return 0;
+
+inval:
+ ieee802154_nl_scan_confirm(dev, IEEE802154_INVALID_PARAMETER, type, channels,
+ NULL/*, NULL */);
+ return -EINVAL;
+}
+EXPORT_SYMBOL(ieee802154_mlme_scan_req);
+
diff --git a/net/ieee802154/start.c b/net/ieee802154/start.c
new file mode 100644
index 0000000..f628cd8
--- /dev/null
+++ b/net/ieee802154/start.c
@@ -0,0 +1,46 @@
+/*
+ * MLME START
+ *
+ * Copyright 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ * Sergey Lapin <[email protected]>
+ * Dmitry Eremin-Solenikov <[email protected]>
+ */
+
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <net/ieee802154/af_ieee802154.h>
+#include <net/ieee802154/mac_def.h>
+#include <net/ieee802154/netdev.h>
+#include <net/ieee802154/nl.h>
+
+int ieee802154_mlme_start_req(struct net_device *dev, u16 panid, u8 channel,
+ u8 bcn_ord, u8 sf_ord, u8 pan_coord, u8 blx,
+ u8 coord_realign, u8 sec)
+{
+ ieee802154_set_pan_id(dev, panid);
+ if (pan_coord)
+ dev->priv_flags |= IFF_IEEE802154_COORD;
+ else
+ dev->priv_flags &= ~IFF_IEEE802154_COORD;
+
+ return 0;
+}
+
--
1.6.2.4


2009-05-26 11:23:39

by Dmitry Baryshkov

[permalink] [raw]
Subject: [PATCH 5/5] ieee802154: add serial dongle driver

Add a tty ldisc supporting IEEE 802.15.4 over serial line. Currently
the only protocol implemented/device supported is our firmware for
Freescale 13192 evaluation boards.

Signed-off-by: Dmitry Eremin-Solenikov <[email protected]>
Signed-off-by: Sergey Lapin <[email protected]>
---
drivers/ieee802154/Kconfig | 3 +
drivers/ieee802154/Makefile | 1 +
drivers/ieee802154/serial.c | 1001 +++++++++++++++++++++++++++++++++++++++++++
include/linux/tty.h | 3 +-
4 files changed, 1007 insertions(+), 1 deletions(-)
create mode 100644 drivers/ieee802154/serial.c

diff --git a/drivers/ieee802154/Kconfig b/drivers/ieee802154/Kconfig
index 6f03394..fdc9792 100644
--- a/drivers/ieee802154/Kconfig
+++ b/drivers/ieee802154/Kconfig
@@ -19,5 +19,8 @@ config IEEE802154_FAKELB
This driver can also be built as a module. To do so say M here.
The module will be called 'fakelb'.

+config IEEE802154_SERIAL
+ tristate "Simple LR-WPAN UART driver"
+
endif

diff --git a/drivers/ieee802154/Makefile b/drivers/ieee802154/Makefile
index c3d1db4..a8ce95f 100644
--- a/drivers/ieee802154/Makefile
+++ b/drivers/ieee802154/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_IEEE802154_FAKELB) += fakelb.o
+obj-$(CONFIG_IEEE802154_SERIAL) += serial.o

EXTRA_CFLAGS += -DDEBUG -DCONFIG_FFD
diff --git a/drivers/ieee802154/serial.c b/drivers/ieee802154/serial.c
new file mode 100644
index 0000000..4a222ec
--- /dev/null
+++ b/drivers/ieee802154/serial.c
@@ -0,0 +1,1001 @@
+/*
+ * ZigBee TTY line discipline.
+ *
+ * Provides interface between ZigBee stack and IEEE 802.15.4 compatible
+ * firmware over serial line. Communication protocol is described below.
+ *
+ * Copyright (C) 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ * Maxim Gorbachyov <[email protected]>
+ * Maxim Osipov <[email protected]>
+ * Sergey Lapin <[email protected]>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/completion.h>
+#include <linux/tty.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/ieee802154/dev.h>
+
+
+/* NOTE: be sure to use here the same values as in the firmware */
+#define START_BYTE1 'z'
+#define START_BYTE2 'b'
+#define MAX_DATA_SIZE 127
+
+#define IDLE_MODE 0x00
+#define RX_MODE 0x02
+#define TX_MODE 0x03
+#define FORCE_TRX_OFF 0xF0
+
+#define STATUS_SUCCESS 0
+#define STATUS_RX_ON 1
+#define STATUS_TX_ON 2
+#define STATUS_TRX_OFF 3
+#define STATUS_IDLE 4
+#define STATUS_BUSY 5
+#define STATUS_BUSY_RX 6
+#define STATUS_BUSY_TX 7
+#define STATUS_ERR 8
+
+/* We re-use PPP ioctl for our purposes */
+#define PPPIOCGUNIT _IOR('t', 86, int) /* get ppp unit number */
+
+/*
+ * The following messages are used to control ZigBee firmware.
+ * All communication has request/response format,
+ * except of asynchronous incoming data stream (DATA_RECV_* messages).
+ */
+enum {
+ NO_ID = 0, /* means no pending id */
+
+ /* Driver to Firmware */
+ CMD_OPEN = 0x01, /* u8 id */
+ CMD_CLOSE = 0x02, /* u8 id */
+ CMD_SET_CHANNEL = 0x04, /* u8 id, u8 channel */
+ CMD_ED = 0x05, /* u8 id */
+ CMD_CCA = 0x06, /* u8 id */
+ CMD_SET_STATE = 0x07, /* u8 id, u8 flag */
+ DATA_XMIT_BLOCK = 0x09, /* u8 id, u8 len, u8 data[len] */
+ DATA_XMIT_STREAM = 0x0a, /* u8 id, u8 c */
+ RESP_RECV_BLOCK = 0x0b, /* u8 id, u8 status */
+ RESP_RECV_STREAM = 0x0c, /* u8 id, u8 status */
+
+ /* Firmware to Driver */
+ RESP_OPEN = 0x81, /* u8 id, u8 status */
+ RESP_CLOSE = 0x82, /* u8 id, u8 status */
+ RESP_SET_CHANNEL = 0x84, /* u8 id, u8 status */
+ RESP_ED = 0x85, /* u8 id, u8 status, u8 level */
+ RESP_CCA = 0x86, /* u8 id, u8 status */
+ RESP_SET_STATE = 0x87, /* u8 id, u8 status */
+ RESP_XMIT_BLOCK = 0x89, /* u8 id, u8 status */
+ RESP_XMIT_STREAM = 0x8a, /* u8 id, u8 status */
+ DATA_RECV_BLOCK = 0x8b, /* u8 id, u8 lq, u8 len, u8 data[len] */
+ DATA_RECV_STREAM = 0x8c /* u8 id, u8 c */
+};
+
+enum {
+ STATE_WAIT_START1,
+ STATE_WAIT_START2,
+ STATE_WAIT_COMMAND,
+ STATE_WAIT_PARAM1,
+ STATE_WAIT_PARAM2,
+ STATE_WAIT_DATA
+};
+
+struct zb_device {
+ /* Relative devices */
+ struct tty_struct *tty;
+ struct ieee802154_dev *dev;
+
+ /* locks the ldisc for the command */
+ struct mutex mutex;
+
+ /* command completition */
+ wait_queue_head_t wq;
+ phy_status_t status;
+ u8 ed;
+
+ /* Internal state */
+ struct completion open_done;
+ unsigned char opened;
+ u8 pending_id;
+ unsigned int pending_size;
+ u8 *pending_data;
+ /* FIXME: WE NEED LOCKING!!! */
+
+ /* Command (rx) processing */
+ int state;
+ unsigned char id;
+ unsigned char param1;
+ unsigned char param2;
+ unsigned char index;
+ unsigned char data[MAX_DATA_SIZE];
+};
+
+/*****************************************************************************
+ * ZigBee serial device protocol handling
+ *****************************************************************************/
+static int _open_dev(struct zb_device *zbdev);
+
+static int
+_send_pending_data(struct zb_device *zbdev)
+{
+ unsigned int j;
+ struct tty_struct *tty;
+
+ BUG_ON(!zbdev);
+ tty = zbdev->tty;
+ if (!tty)
+ return -ENODEV;
+
+ zbdev->status = PHY_INVAL;
+
+ /* Debug info */
+ printk(KERN_INFO "%lu %s, %d bytes:", jiffies, __func__, zbdev->pending_size);
+ for (j = 0; j < zbdev->pending_size; ++j)
+ printk(KERN_CONT " 0x%02X", zbdev->pending_data[j]);
+ printk(KERN_CONT "\n");
+
+ if (tty->driver->ops->write(tty, zbdev->pending_data, zbdev->pending_size) != zbdev->pending_size) {
+ printk(KERN_ERR "%s: device write failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+send_cmd(struct zb_device *zbdev, u8 id)
+{
+ u8 len = 0, buf[4]; /* 4 because of 2 start bytes, id and optional extra */
+
+ /* Check arguments */
+ BUG_ON(!zbdev);
+
+ if (!zbdev->opened) {
+ if (!_open_dev(zbdev))
+ return -EAGAIN;
+ }
+
+ pr_debug("%s(): id = %u\n", __func__, id);
+ if (zbdev->pending_size) {
+ printk(KERN_ERR "%s(): cmd is already pending, id = %u\n",
+ __func__, zbdev->pending_id);
+ BUG();
+ }
+
+ /* Prepare a message */
+ buf[len++] = START_BYTE1;
+ buf[len++] = START_BYTE2;
+ buf[len++] = id;
+
+ zbdev->pending_id = id;
+ zbdev->pending_size = len;
+ zbdev->pending_data = kzalloc(zbdev->pending_size, GFP_KERNEL);
+ if (!zbdev->pending_data) {
+ printk(KERN_ERR "%s(): unable to allocate memory\n", __func__);
+ zbdev->pending_id = 0;
+ zbdev->pending_size = 0;
+ return -ENOMEM;
+ }
+ memcpy(zbdev->pending_data, buf, len);
+
+ return _send_pending_data(zbdev);
+}
+
+static int
+send_cmd2(struct zb_device *zbdev, u8 id, u8 extra)
+{
+ u8 len = 0, buf[4]; /* 4 because of 2 start bytes, id and optional extra */
+
+ /* Check arguments */
+ BUG_ON(!zbdev);
+
+ if (!zbdev->opened) {
+ if (!_open_dev(zbdev))
+ return -EAGAIN;
+ }
+
+ pr_debug("%s(): id = %u\n", __func__, id);
+ if (zbdev->pending_size) {
+ printk(KERN_ERR "%s(): cmd is already pending, id = %u\n",
+ __func__, zbdev->pending_id);
+ BUG();
+ }
+
+ /* Prepare a message */
+ buf[len++] = START_BYTE1;
+ buf[len++] = START_BYTE2;
+ buf[len++] = id;
+ buf[len++] = extra;
+
+ zbdev->pending_id = id;
+ zbdev->pending_size = len;
+ zbdev->pending_data = kzalloc(zbdev->pending_size, GFP_KERNEL);
+ if (!zbdev->pending_data) {
+ printk(KERN_ERR "%s(): unable to allocate memory\n", __func__);
+ zbdev->pending_id = 0;
+ zbdev->pending_size = 0;
+ return -ENOMEM;
+ }
+ memcpy(zbdev->pending_data, buf, len);
+
+ return _send_pending_data(zbdev);
+}
+
+static int
+send_block(struct zb_device *zbdev, u8 len, u8 *data)
+{
+ u8 i = 0, buf[4]; /* 4 because of 2 start bytes, id and len */
+
+ /* Check arguments */
+ BUG_ON(!zbdev);
+
+ if (!zbdev->opened) {
+ if (!_open_dev(zbdev))
+ return -EAGAIN;
+ }
+
+ pr_debug("%s(): id = %u\n", __func__, DATA_XMIT_BLOCK);
+ if (zbdev->pending_size) {
+ printk(KERN_ERR "%s(): cmd is already pending, id = %u\n",
+ __func__, zbdev->pending_id);
+ BUG();
+ }
+
+ /* Prepare a message */
+ buf[i++] = START_BYTE1;
+ buf[i++] = START_BYTE2;
+ buf[i++] = DATA_XMIT_BLOCK;
+ buf[i++] = len;
+
+ zbdev->pending_id = DATA_XMIT_BLOCK;
+ zbdev->pending_size = i + len;
+ zbdev->pending_data = kzalloc(zbdev->pending_size, GFP_KERNEL);
+ if (!zbdev->pending_data) {
+ printk(KERN_ERR "%s(): unable to allocate memory\n", __func__);
+ zbdev->pending_id = 0;
+ zbdev->pending_size = 0;
+ return -ENOMEM;
+ }
+ memcpy(zbdev->pending_data, buf, i);
+ memcpy(zbdev->pending_data + i, data, len);
+
+ return _send_pending_data(zbdev);
+}
+
+static void
+cleanup(struct zb_device *zbdev)
+{
+ zbdev->state = STATE_WAIT_START1;
+ zbdev->id = 0;
+ zbdev->param1 = 0;
+ zbdev->param2 = 0;
+ zbdev->index = 0;
+}
+
+static int
+is_command(unsigned char c)
+{
+ switch (c) {
+ /* ids we can get here: */
+ case RESP_OPEN:
+ case RESP_CLOSE:
+ case RESP_SET_CHANNEL:
+ case RESP_ED:
+ case RESP_CCA:
+ case RESP_SET_STATE:
+ case RESP_XMIT_BLOCK:
+ case RESP_XMIT_STREAM:
+ case DATA_RECV_BLOCK:
+ case DATA_RECV_STREAM:
+ return 1;
+ }
+ return 0;
+}
+
+static int
+_match_pending_id(struct zb_device *zbdev)
+{
+ return ((CMD_OPEN == zbdev->pending_id && RESP_OPEN == zbdev->id)
+ || (CMD_CLOSE == zbdev->pending_id && RESP_CLOSE == zbdev->id)
+ || (CMD_SET_CHANNEL == zbdev->pending_id && RESP_SET_CHANNEL == zbdev->id)
+ || (CMD_ED == zbdev->pending_id && RESP_ED == zbdev->id)
+ || (CMD_CCA == zbdev->pending_id && RESP_CCA == zbdev->id)
+ || (CMD_SET_STATE == zbdev->pending_id && RESP_SET_STATE == zbdev->id)
+ || (DATA_XMIT_BLOCK == zbdev->pending_id && RESP_XMIT_BLOCK == zbdev->id)
+ || (DATA_XMIT_STREAM == zbdev->pending_id && RESP_XMIT_STREAM == zbdev->id)
+ || DATA_RECV_BLOCK == zbdev->id
+ || DATA_RECV_STREAM == zbdev->id);
+}
+
+static void serial_net_rx(struct zb_device *zbdev)
+{
+ /* zbdev->param1 is LQI
+ * zbdev->param2 is length of data
+ * zbdev->data is data itself
+ */
+ struct sk_buff *skb;
+ skb = alloc_skb(zbdev->param2, GFP_ATOMIC);
+ skb_put(skb, zbdev->param2);
+ skb_copy_to_linear_data(skb, zbdev->data, zbdev->param2);
+ ieee802154_rx_irqsafe(zbdev->dev, skb, zbdev->param1);
+}
+
+static void
+process_command(struct zb_device *zbdev)
+{
+ /* Command processing */
+ if (!_match_pending_id(zbdev))
+ return;
+
+ if (RESP_OPEN == zbdev->id && STATUS_SUCCESS == zbdev->param1) {
+ zbdev->opened = 1;
+ pr_debug("Opened device\n");
+ complete(&zbdev->open_done);
+ /* Input is not processed during output, so
+ * using completion is not possible during output.
+ * so we need to handle open as any other command
+ * and hope for best
+ */
+ return;
+ }
+
+ if (!zbdev->opened)
+ return;
+
+ zbdev->pending_id = 0;
+ kfree(zbdev->pending_data);
+ zbdev->pending_data = NULL;
+ zbdev->pending_size = 0;
+ if (zbdev->id != DATA_RECV_BLOCK)
+ switch (zbdev->param1) {
+ case STATUS_SUCCESS:
+ zbdev->status = PHY_SUCCESS;
+ break;
+ case STATUS_RX_ON:
+ zbdev->status = PHY_RX_ON;
+ break;
+ case STATUS_TX_ON:
+ zbdev->status = PHY_TX_ON;
+ break;
+ case STATUS_TRX_OFF:
+ zbdev->status = PHY_TRX_OFF;
+ break;
+ case STATUS_BUSY:
+ zbdev->status = PHY_BUSY;
+ break;
+ case STATUS_IDLE:
+ zbdev->status = PHY_IDLE;
+ break;
+ case STATUS_BUSY_RX:
+ zbdev->status = PHY_BUSY_RX;
+ break;
+ case STATUS_BUSY_TX:
+ zbdev->status = PHY_BUSY_TX;
+ break;
+ default:
+ printk(KERN_ERR "%s: bad status received from firmware: %u\n",
+ __func__, zbdev->param1);
+ zbdev->status = PHY_ERROR;
+ break;
+ }
+
+ switch (zbdev->id) {
+ case RESP_ED:
+ zbdev->ed = zbdev->param2;
+ break;
+ case DATA_RECV_BLOCK:
+ pr_debug("Received block, lqi %02x, len %02x\n", zbdev->param1, zbdev->param2);
+ /* zbdev->param1 is LQ, zbdev->param2 is length */
+ serial_net_rx(zbdev);
+ break;
+ case DATA_RECV_STREAM:
+ /* TODO: update firmware to use this */
+ break;
+ }
+
+ wake_up(&zbdev->wq);
+}
+
+static void
+process_char(struct zb_device *zbdev, unsigned char c)
+{
+ /* Data processing */
+ pr_debug("Char: %d (0x%02x)\n", c, c);
+ switch (zbdev->state) {
+ case STATE_WAIT_START1:
+ if (START_BYTE1 == c)
+ zbdev->state = STATE_WAIT_START2;
+ break;
+
+ case STATE_WAIT_START2:
+ if (START_BYTE2 == c)
+ zbdev->state = STATE_WAIT_COMMAND;
+ else
+ cleanup(zbdev);
+ break;
+
+ case STATE_WAIT_COMMAND:
+ if (is_command(c)) {
+ zbdev->id = c;
+ zbdev->state = STATE_WAIT_PARAM1;
+ } else {
+ cleanup(zbdev);
+ printk(KERN_ERR "%s, unexpected command id: %x\n", __func__, c);
+ }
+ break;
+
+ case STATE_WAIT_PARAM1:
+ zbdev->param1 = c;
+ if ((RESP_ED == zbdev->id) || (DATA_RECV_BLOCK == zbdev->id))
+ zbdev->state = STATE_WAIT_PARAM2;
+ else {
+ process_command(zbdev);
+ cleanup(zbdev);
+ }
+ break;
+
+ case STATE_WAIT_PARAM2:
+ zbdev->param2 = c;
+ if (RESP_ED == zbdev->id) {
+ process_command(zbdev);
+ cleanup(zbdev);
+ } else if (DATA_RECV_BLOCK == zbdev->id)
+ zbdev->state = STATE_WAIT_DATA;
+ else
+ cleanup(zbdev);
+ break;
+
+ case STATE_WAIT_DATA:
+ if (zbdev->index < sizeof(zbdev->data)) {
+ zbdev->data[zbdev->index] = c;
+ zbdev->index++;
+ /* Pending data is received, param2 is length for DATA_RECV_BLOCK */
+ if (zbdev->index == zbdev->param2) {
+ process_command(zbdev);
+ cleanup(zbdev);
+ }
+ } else {
+ printk(KERN_ERR "%s(): data size is greater "
+ "than buffer available\n", __func__);
+ cleanup(zbdev);
+ }
+ break;
+
+ default:
+ cleanup(zbdev);
+ }
+}
+
+/*****************************************************************************
+ * Device operations for IEEE 802.15.4 PHY side interface ZigBee stack
+ *****************************************************************************/
+
+static int _open_dev(struct zb_device *zbdev)
+{
+ int retries;
+ u8 len = 0, buf[4]; /* 4 because of 2 start bytes, id and optional extra */
+
+ /* Check arguments */
+ BUG_ON(!zbdev);
+ if (zbdev->opened)
+ return 1;
+
+ pr_debug("%s()\n", __func__);
+ if (zbdev->pending_size) {
+ printk(KERN_ERR "%s(): cmd is already pending, id = %u\n",
+ __func__, zbdev->pending_id);
+ BUG();
+ }
+
+ /* Prepare a message */
+ buf[len++] = START_BYTE1;
+ buf[len++] = START_BYTE2;
+ buf[len++] = CMD_OPEN;
+
+ zbdev->pending_id = CMD_OPEN;
+ zbdev->pending_size = len;
+ zbdev->pending_data = kzalloc(zbdev->pending_size, GFP_KERNEL);
+ if (!zbdev->pending_data) {
+ printk(KERN_ERR "%s(): unable to allocate memory\n", __func__);
+ zbdev->pending_id = 0;
+ zbdev->pending_size = 0;
+ return -ENOMEM;
+ }
+ memcpy(zbdev->pending_data, buf, len);
+
+ retries = 5;
+ while (!zbdev->opened && retries) {
+ if (_send_pending_data(zbdev) != 0)
+ return 0;
+
+ /* 3 second before retransmission */
+ wait_for_completion_interruptible_timeout(&zbdev->open_done, msecs_to_jiffies(1000));
+ --retries;
+ }
+
+ zbdev->pending_id = 0;
+ kfree(zbdev->pending_data);
+ zbdev->pending_data = NULL;
+ zbdev->pending_size = 0;
+
+ if (zbdev->opened) {
+ printk(KERN_INFO "Opened connection to device\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Valid channels: 1-16 */
+static phy_status_t
+ieee802154_serial_set_channel(struct ieee802154_dev *dev, int channel)
+{
+ struct zb_device *zbdev;
+ phy_status_t ret;
+
+ pr_debug("%s\n", __func__);
+
+ zbdev = dev->priv;
+ if (NULL == zbdev) {
+ printk(KERN_ERR "%s: wrong phy\n", __func__);
+ return PHY_INVAL;
+ }
+
+ if (mutex_lock_interruptible(&zbdev->mutex))
+ return PHY_ERROR;
+ /* Our channels are actually from 11 to 26
+ * We have IEEE802.15.4 channel no from 0 to 26.
+ * channels 0-10 are not valid for us */
+ BUG_ON(channel < 11 || channel > 26);
+ /* ... but our crappy firmware numbers channels from 1 to 16
+ * which is a mystery. We suould enforce that using PIB API
+ * but additional checking here won't kill, and gcc will
+ * optimize this stuff anyway. */
+ BUG_ON((channel - 10) < 1 && (channel - 10) > 16);
+
+ if (send_cmd2(zbdev, CMD_SET_CHANNEL, channel - 10) != 0) {
+ ret = PHY_ERROR;
+ goto out;
+ }
+
+ if (wait_event_interruptible_timeout(zbdev->wq, zbdev->status != PHY_INVAL, msecs_to_jiffies(1000)) > 0)
+ ret = zbdev->status;
+ else
+ ret = PHY_ERROR;
+
+ if (ret == PHY_SUCCESS)
+ zbdev->dev->current_channel = channel;
+out:
+ mutex_unlock(&zbdev->mutex);
+ pr_debug("%s end\n", __func__);
+ return ret;
+}
+
+static phy_status_t
+ieee802154_serial_ed(struct ieee802154_dev *dev, u8 *level)
+{
+ struct zb_device *zbdev;
+ phy_status_t ret;
+
+ pr_debug("%s\n", __func__);
+
+ zbdev = dev->priv;
+ if (NULL == zbdev) {
+ printk(KERN_ERR "%s: wrong phy\n", __func__);
+ return PHY_INVAL;
+ }
+
+ if (mutex_lock_interruptible(&zbdev->mutex))
+ return PHY_ERROR;
+
+#if 0
+ if (send_cmd(zbdev, CMD_ED) != 0) {
+ ret = PHY_ERROR;
+ goto out;
+ }
+
+ if (wait_event_interruptible_timeout(zbdev->wq, zbdev->status != PHY_INVAL, msecs_to_jiffies(1000)) > 0) {
+ *level = zbdev->ed;
+ ret = zbdev->status;
+ } else
+ ret = PHY_ERROR;
+out:
+#else
+ /* Lets suppose we have energy on all channels
+ * till we fix something regarding hardware or driver */
+ *level = 0xbe;
+ ret = PHY_SUCCESS;
+#endif
+ mutex_unlock(&zbdev->mutex);
+ pr_debug("%s end\n", __func__);
+ return ret;
+}
+
+static phy_status_t
+ieee802154_serial_cca(struct ieee802154_dev *dev)
+{
+ struct zb_device *zbdev;
+ phy_status_t ret;
+
+ pr_debug("%s\n", __func__);
+
+ zbdev = dev->priv;
+ if (NULL == zbdev) {
+ printk(KERN_ERR "%s: wrong phy\n", __func__);
+ return PHY_INVAL;
+ }
+
+ if (mutex_lock_interruptible(&zbdev->mutex))
+ return PHY_ERROR;
+
+ if (send_cmd(zbdev, CMD_CCA) != 0) {
+ ret = PHY_ERROR;
+ goto out;
+ }
+
+ if (wait_event_interruptible_timeout(zbdev->wq, zbdev->status != PHY_INVAL, msecs_to_jiffies(1000)) > 0)
+ ret = zbdev->status;
+ else
+ ret = PHY_ERROR;
+out:
+ mutex_unlock(&zbdev->mutex);
+ pr_debug("%s end\n", __func__);
+ return ret;
+}
+
+static phy_status_t
+ieee802154_serial_set_state(struct ieee802154_dev *dev, phy_status_t state)
+{
+ struct zb_device *zbdev;
+ unsigned char flag;
+ phy_status_t ret;
+
+ pr_debug("%s %d\n", __func__, state);
+
+ zbdev = dev->priv;
+ if (NULL == zbdev) {
+ printk(KERN_ERR "%s: wrong phy\n", __func__);
+ return PHY_INVAL;
+ }
+
+ if (mutex_lock_interruptible(&zbdev->mutex))
+ return PHY_ERROR;
+
+ switch (state) {
+ case PHY_RX_ON:
+ flag = RX_MODE;
+ break;
+ case PHY_TX_ON:
+ flag = TX_MODE;
+ break;
+ case PHY_TRX_OFF:
+ flag = IDLE_MODE;
+ break;
+ case PHY_FORCE_TRX_OFF:
+ flag = FORCE_TRX_OFF;
+ break;
+ default:
+ ret = PHY_INVAL;
+ goto out;
+ }
+
+ if (send_cmd2(zbdev, CMD_SET_STATE, flag) != 0) {
+ ret = PHY_ERROR;
+ goto out;
+ }
+
+ if (wait_event_interruptible_timeout(zbdev->wq, zbdev->status != PHY_INVAL, msecs_to_jiffies(1000)) > 0)
+ ret = zbdev->status;
+ else
+ ret = PHY_ERROR;
+out:
+ mutex_unlock(&zbdev->mutex);
+ pr_debug("%s end\n", __func__);
+ return ret;
+}
+
+static phy_status_t
+ieee802154_serial_xmit(struct ieee802154_dev *dev, struct sk_buff *skb)
+{
+ struct zb_device *zbdev;
+ phy_status_t ret;
+
+ pr_debug("%s\n", __func__);
+
+ zbdev = dev->priv;
+ if (NULL == zbdev) {
+ printk(KERN_ERR "%s: wrong phy\n", __func__);
+ return PHY_INVAL;
+ }
+
+ if (mutex_lock_interruptible(&zbdev->mutex))
+ return PHY_ERROR;
+
+ if (send_block(zbdev, skb->len, skb->data) != 0) {
+ ret = PHY_ERROR;
+ goto out;
+ }
+
+ if (wait_event_interruptible_timeout(zbdev->wq, zbdev->status != PHY_INVAL, msecs_to_jiffies(1000)) > 0)
+ ret = zbdev->status;
+ else
+ ret = PHY_ERROR;
+out:
+
+ mutex_unlock(&zbdev->mutex);
+ pr_debug("%s end\n", __func__);
+ return ret;
+}
+
+/*****************************************************************************
+ * Line discipline interface for IEEE 802.15.4 serial device
+ *****************************************************************************/
+
+static struct ieee802154_ops serial_ops = {
+ .owner = THIS_MODULE,
+ .tx = ieee802154_serial_xmit,
+ .ed = ieee802154_serial_ed,
+ .cca = ieee802154_serial_cca,
+ .set_trx_state = ieee802154_serial_set_state,
+ .set_channel = ieee802154_serial_set_channel,
+};
+
+static int dev_minor_match(struct device *dev, void *data)
+{
+ int *minor = data;
+ return MINOR(dev->devt) == *minor;
+}
+
+/*
+ * Called when a tty is put into ZB line discipline. Called in process context.
+ * Returns 0 on success.
+ */
+static int
+ieee802154_tty_open(struct tty_struct *tty)
+{
+ struct zb_device *zbdev = tty->disc_data;
+ int err;
+ int minor;
+
+ pr_debug("Openning ldisc\n");
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (zbdev)
+ return -EBUSY;
+
+ /* Allocate device structure */
+ zbdev = kzalloc(sizeof(struct zb_device), GFP_KERNEL);
+ if (NULL == zbdev) {
+ printk(KERN_ERR "%s: can't allocate zb_device structure.\n", __func__);
+ return -ENOMEM;
+ }
+ mutex_init(&zbdev->mutex);
+ init_completion(&zbdev->open_done);
+ init_waitqueue_head(&zbdev->wq);
+
+ zbdev->dev = ieee802154_alloc_device();
+ if (!zbdev->dev) {
+ err = -ENOMEM;
+ goto out_free_zb;
+ }
+
+ zbdev->dev->name = "serialdev";
+ zbdev->dev->priv = zbdev;
+ zbdev->dev->extra_tx_headroom = 0;
+ zbdev->dev->channel_mask = 0x7ff;
+ zbdev->dev->current_channel = 11; /* it's 1st channel of 2.4 Ghz space */
+ zbdev->dev->flags = IEEE802154_OPS_OMIT_CKSUM;
+
+ minor = tty->index + tty->driver->minor_start;
+ zbdev->dev->parent = class_find_device(tty_class, NULL, &minor, dev_minor_match);
+
+ zbdev->tty = tty;
+ cleanup(zbdev);
+
+ tty->disc_data = zbdev;
+ tty->receive_room = MAX_DATA_SIZE;
+ tty->low_latency = 1;
+
+ /* FIXME: why is this needed. Note don't use ldisc_ref here as the
+ open path is before the ldisc is referencable */
+
+ if (tty->ldisc.ops->flush_buffer)
+ tty->ldisc.ops->flush_buffer(tty);
+ tty_driver_flush_buffer(tty);
+
+ err = ieee802154_register_device(zbdev->dev, &serial_ops);
+ /* we put it only after it has a chance to be get by network core */
+ if (zbdev->dev->parent)
+ put_device(zbdev->dev->parent);
+ if (err) {
+ printk(KERN_ERR "%s: device register failed\n", __func__);
+ goto out_free;
+ }
+
+ return 0;
+
+ ieee802154_unregister_device(zbdev->dev);
+
+out_free:
+ tty->disc_data = NULL;
+
+ ieee802154_free_device(zbdev->dev);
+out_free_zb:
+ kfree(zbdev);
+
+ return err;
+}
+
+/*
+ * Called when the tty is put into another line discipline or it hangs up. We
+ * have to wait for any cpu currently executing in any of the other zb_tty_*
+ * routines to finish before we can call zb_tty_close and free the
+ * zb_serial_dev struct. This routine must be called from process context, not
+ * interrupt or softirq context.
+ */
+static void
+ieee802154_tty_close(struct tty_struct *tty)
+{
+ struct zb_device *zbdev;
+
+ zbdev = tty->disc_data;
+ if (NULL == zbdev) {
+ printk(KERN_WARNING "%s: match is not found\n", __func__);
+ return;
+ }
+
+ tty->disc_data = NULL;
+ zbdev->tty = NULL;
+
+ ieee802154_unregister_device(zbdev->dev);
+
+ tty_ldisc_flush(tty);
+ tty_driver_flush_buffer(tty);
+
+ ieee802154_free_device(zbdev->dev);
+ kfree(zbdev);
+}
+
+/*
+ * Called on tty hangup in process context.
+ */
+static int
+ieee802154_tty_hangup(struct tty_struct *tty)
+{
+ ieee802154_tty_close(tty);
+ return 0;
+}
+
+/*
+ * Called in process context only. May be re-entered by multiple ioctl calling threads.
+ */
+static int
+ieee802154_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct zb_device *zbdev;
+ struct ifreq ifr;
+ struct ieee802154_priv *priv;
+ int err;
+ void __user *argp = (void __user *) arg;
+
+ pr_debug("cmd = 0x%x\n", cmd);
+ memset(&ifr, 0, sizeof(ifr));
+
+ zbdev = tty->disc_data;
+ if (NULL == zbdev) {
+ pr_debug("match is not found\n");
+ return -EINVAL;
+ }
+
+
+ switch (cmd) {
+ case PPPIOCGUNIT:
+ /* TODO: some error checking */
+ priv = ieee802154_to_priv(zbdev->dev);
+ BUG_ON(!priv->master);
+ err = -EFAULT;
+ if (copy_to_user(argp, priv->master->name, strlen(priv->master->name)))
+ break;
+ err = 0;
+ break;
+ default:
+ pr_debug("Unknown ioctl cmd: %u\n", cmd);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+/*
+ * This can now be called from hard interrupt level as well
+ * as soft interrupt level or mainline.
+ */
+static void
+ieee802154_tty_receive(struct tty_struct *tty, const unsigned char *buf, char *cflags, int count)
+{
+ struct zb_device *zbdev;
+ int i;
+
+ /* Debug info */
+ printk(KERN_INFO "%lu %s, received %d bytes:", jiffies, __func__, count);
+ for (i = 0; i < count; ++i)
+ printk(KERN_CONT " 0x%02X", buf[i]);
+ printk(KERN_CONT "\n");
+
+ /* Actual processing */
+ zbdev = tty->disc_data;
+ if (NULL == zbdev) {
+ printk(KERN_ERR "%s(): record for tty is not found\n", __func__);
+ return;
+ }
+ for (i = 0; i < count; ++i)
+ process_char(zbdev, buf[i]);
+#if 0
+ if (tty->driver->flush_chars)
+ tty->driver->flush_chars(tty);
+#endif
+ tty_unthrottle(tty);
+}
+
+/*
+ * Line discipline device structure
+ */
+static struct tty_ldisc_ops ieee802154_ldisc = {
+ .owner = THIS_MODULE,
+ .magic = TTY_LDISC_MAGIC,
+ .name = "ieee802154-ldisc",
+ .open = ieee802154_tty_open,
+ .close = ieee802154_tty_close,
+ .hangup = ieee802154_tty_hangup,
+ .receive_buf = ieee802154_tty_receive,
+ .ioctl = ieee802154_tty_ioctl,
+};
+
+/*****************************************************************************
+ * Module service routinues
+ *****************************************************************************/
+
+static int __init ieee802154_serial_init(void)
+{
+ printk(KERN_INFO "Initializing ZigBee TTY interface\n");
+
+ if (tty_register_ldisc(N_IEEE802154, &ieee802154_ldisc) != 0) {
+ printk(KERN_ERR "%s: line discipline register failed\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void __exit ieee802154_serial_cleanup(void)
+{
+ if (tty_unregister_ldisc(N_IEEE802154) != 0)
+ printk(KERN_CRIT "failed to unregister ZigBee line discipline.\n");
+}
+
+module_init(ieee802154_serial_init);
+module_exit(ieee802154_serial_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_LDISC(N_IEEE802154);
+
diff --git a/include/linux/tty.h b/include/linux/tty.h
index fc39db9..f533beb 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -23,7 +23,7 @@
*/
#define NR_UNIX98_PTY_DEFAULT 4096 /* Default maximum for Unix98 ptys */
#define NR_UNIX98_PTY_MAX (1 << MINORBITS) /* Absolute limit */
-#define NR_LDISCS 19
+#define NR_LDISCS 20

/* line disciplines */
#define N_TTY 0
@@ -46,6 +46,7 @@
#define N_GIGASET_M101 16 /* Siemens Gigaset M101 serial DECT adapter */
#define N_SLCAN 17 /* Serial / USB serial CAN Adaptors */
#define N_PPS 18 /* Pulse per Second */
+#define N_IEEE802154 19 /* Serial / USB serial IEEE802154.4 devices */

/*
* This character is the same as _POSIX_VDISABLE: it cannot be used as
--
1.6.2.4


2009-05-27 12:46:49

by Dmitry Baryshkov

[permalink] [raw]
Subject: Re: [RFC][WIP] IEEE 802.15.4 implementation for Linux

2009/5/27 Florian Fainelli <[email protected]>:
> Hi Dmitry,
>
> Le Tuesday 26 May 2009 13:21:57 Dmitry Eremin-Solenikov, vous avez ?crit?:
>> The project page is available at
>> http://apps.sourceforge.net/trac/linux-zigbee with source code of kernel
>> part available from git at
>> http://zigbee-linux.git.sourceforge.net, mirrored for convenience at
>> git://git.kernel.org/pub/scm/linux/kernel/git/lumag/lowpan.git
>>
>> The source code for userspace utils is available from git at
>> http://linux-zigbee.git.sourceforge.net/
>
> From the project page I understand that you plan on providing a ZigBee stack,
> based on this 802.15.4 stack ? Do you plan on submitting the ZigBee stack
> later on ?

The ZigBee stack currently is in design stage of development. When it will be
more or less "in shape" we will present it to the community.

> Maybe you should mention that this implementation is royalty-free, but that
> commercial products using it should both respect the GPL license and pay a
> ZigBee royalty as per the ZigBee Alliance . That is just for clarification
> since right now it simply does 802.15.4 which is not subject to this royalty
> thing.

We will look into this, thank you for putting our attention on this topic.

>
>>
>> Several comments about our implementation:
>> * As with 802.11 there are two types of devices: the smart ones that
>> implement most parts of the protocol by themselves and more or less dumb
>> ones which simply send and receive what they are told. Currently we do only
>> support the second type of devices (SoftMAC)
>> * The implementation is split between code driving radio, (master, mwpanX
>> ? interface: mdev.c), code processing frames according the IEEE 802.15.4
>> ? rules (slave wpanX devices) and finally sockets (af_ieee802154.c,
>> dgram.c, raw.c).
>
> This sounds good. Do you also plan on integrating some meshing algorithm on
> top of 802.15.4 ? If so,

Hmmm. IIUC the 802.15.4 doesn't have any knowledge about meshing. It's the
part of higher level protocol. ZigBee part will of course contain some
kind of routing/
meshing algos. If you'd like to implement mesing over raw 802.15.4,
it's up to your
protocol. We plan to provide all hooks necessary for doing this.

>> * We do present two example drivers using our stack. One is purely virtual
>> ? one either looping the packets back or connecting several virtual
>> interfaces (the one at fakelb.c), and the driver for the Freescale MC13192
>> evaluation boards (13192-SARD, 13192-EVK) using our custom firmware
>> (currently only available at request, we are working on publishing it). The
>> driver for the Atmel at86rf230/at86rf231 chips will follow in several
>> weeks.
>
> Is the virtual interface just like mac80211_hwsim for 802.11 ?h

More or less.We do not implement the global 'monitor' interface, only separate
radios. We have a page in the wiki that describes usage of this
virtual device driver:
http://apps.sourceforge.net/trac/linux-zigbee/wiki/GettingStarted

--
With best wishes
Dmitry

2009-05-26 11:23:32

by Dmitry Baryshkov

[permalink] [raw]
Subject: [PATCH 3/5] ieee802154: add socket address family code

Add support for BSD sockets over the IEEE 802.15.4 networks.
One can either send RAW datagrams or use SOCK_DGRAM to encapsulate data
inside normal IEEE 802.15.4 packets.

Signed-off-by: Dmitry Eremin-Solenikov <[email protected]>
Signed-off-by: Sergey Lapin <[email protected]>
---
net/ieee802154/Makefile | 3 +-
net/ieee802154/af_ieee802154.c | 312 +++++++++++++++++++++++++++++++++
net/ieee802154/dgram.c | 372 ++++++++++++++++++++++++++++++++++++++++
net/ieee802154/raw.c | 249 +++++++++++++++++++++++++++
4 files changed, 935 insertions(+), 1 deletions(-)
create mode 100644 net/ieee802154/af_ieee802154.c
create mode 100644 net/ieee802154/dgram.c
create mode 100644 net/ieee802154/raw.c

diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile
index d964365..aace5b1 100644
--- a/net/ieee802154/Makefile
+++ b/net/ieee802154/Makefile
@@ -1,5 +1,6 @@
-obj-$(CONFIG_IEEE802154) += ieee802154.o
+obj-$(CONFIG_IEEE802154) += ieee802154.o af_802154.o
ieee802154-objs := main.o mdev.o dev.o netlink.o mac_cmd.o scan.o \
crc.o beacon.o beacon_hash.o start.o
+af_802154-objs := af_ieee802154.o raw.o dgram.o

EXTRA_CFLAGS += -Wall -DDEBUG
diff --git a/net/ieee802154/af_ieee802154.c b/net/ieee802154/af_ieee802154.c
new file mode 100644
index 0000000..58e0a07
--- /dev/null
+++ b/net/ieee802154/af_ieee802154.c
@@ -0,0 +1,312 @@
+/*
+ * IEEE802154.4 socket interface
+ *
+ * Copyright 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ * Sergey Lapin <[email protected]>
+ * Maxim Gorbachyov <[email protected]>
+ */
+
+#include <linux/net.h>
+#include <linux/capability.h>
+#include <linux/module.h>
+#include <linux/if_arp.h>
+#include <linux/if.h>
+#include <linux/termios.h> /* For TIOCOUTQ/INQ */
+#include <linux/list.h>
+#include <net/datalink.h>
+#include <net/psnap.h>
+#include <net/sock.h>
+#include <net/tcp_states.h>
+#include <net/route.h>
+
+#include <net/ieee802154/af_ieee802154.h>
+#include <net/ieee802154/netdev.h>
+#include <net/ieee802154/phy.h>
+
+#define DBG_DUMP(data, len) { \
+ int i; \
+ pr_debug("file %s: function: %s: data: len %d:\n", __FILE__, __func__, len); \
+ for (i = 0; i < len; i++) {\
+ pr_debug("%02x: %02x\n", i, (data)[i]); \
+ } \
+}
+
+static int ieee802154_sock_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ if (sk) {
+ sock->sk = NULL;
+ sk->sk_prot->close(sk, 0);
+ }
+ return 0;
+}
+static int ieee802154_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len)
+{
+ struct sock *sk = sock->sk;
+
+ return sk->sk_prot->sendmsg(iocb, sk, msg, len);
+}
+
+static int ieee802154_sock_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+ struct sock *sk = sock->sk;
+
+ if (sk->sk_prot->bind)
+ return sk->sk_prot->bind(sk, uaddr, addr_len);
+
+ return sock_no_bind(sock, uaddr, addr_len);
+}
+
+static int ieee802154_sock_connect(struct socket *sock, struct sockaddr *uaddr,
+ int addr_len, int flags)
+{
+ struct sock *sk = sock->sk;
+
+ if (uaddr->sa_family == AF_UNSPEC)
+ return sk->sk_prot->disconnect(sk, flags);
+
+ return sk->sk_prot->connect(sk, uaddr, addr_len);
+}
+
+static int ieee802154_dev_ioctl(struct sock *sk, struct ifreq __user *arg, unsigned int cmd)
+{
+ struct ifreq ifr;
+ int ret = -EINVAL;
+ struct net_device *dev;
+
+ if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
+ return -EFAULT;
+
+ ifr.ifr_name[IFNAMSIZ-1] = 0;
+
+ dev_load(sock_net(sk), ifr.ifr_name);
+ dev = dev_get_by_name(sock_net(sk), ifr.ifr_name);
+ if (dev->type == ARPHRD_IEEE802154 || dev->type == ARPHRD_IEEE802154_PHY)
+ ret = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, cmd);
+
+ if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq)))
+ ret = -EFAULT;
+ dev_put(dev);
+
+ return ret;
+}
+
+static int ieee802154_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ struct sock *sk = sock->sk;
+
+ switch (cmd) {
+ case SIOCGSTAMP:
+ return sock_get_timestamp(sk, (struct timeval __user *)arg);
+ case SIOCGSTAMPNS:
+ return sock_get_timestampns(sk, (struct timespec __user *)arg);
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ return ieee802154_dev_ioctl(sk, (struct ifreq __user *)arg, cmd);
+ default:
+ if (!sk->sk_prot->ioctl)
+ return -ENOIOCTLCMD;
+ return sk->sk_prot->ioctl(sk, cmd, arg);
+ }
+}
+
+static const struct proto_ops ieee802154_raw_ops = {
+ .family = PF_IEEE802154,
+ .owner = THIS_MODULE,
+ .release = ieee802154_sock_release,
+ .bind = ieee802154_sock_bind,
+ .connect = ieee802154_sock_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = sock_no_getname,
+ .poll = datagram_poll,
+ .ioctl = ieee802154_sock_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_common_setsockopt,
+ .getsockopt = sock_common_getsockopt,
+ .sendmsg = ieee802154_sock_sendmsg,
+ .recvmsg = sock_common_recvmsg,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+#ifdef CONFIG_COMPAT
+ .compat_setsockopt = compat_sock_common_setsockopt,
+ .compat_getsockopt = compat_sock_common_getsockopt,
+#endif
+};
+
+static const struct proto_ops ieee802154_dgram_ops = {
+ .family = PF_IEEE802154,
+ .owner = THIS_MODULE,
+ .release = ieee802154_sock_release,
+ .bind = ieee802154_sock_bind,
+ .connect = ieee802154_sock_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = sock_no_getname,
+ .poll = datagram_poll,
+ .ioctl = ieee802154_sock_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_common_setsockopt,
+ .getsockopt = sock_common_getsockopt,
+ .sendmsg = ieee802154_sock_sendmsg,
+ .recvmsg = sock_common_recvmsg,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+#ifdef CONFIG_COMPAT
+ .compat_setsockopt = compat_sock_common_setsockopt,
+ .compat_getsockopt = compat_sock_common_getsockopt,
+#endif
+};
+
+
+/*
+ * Create a socket. Initialise the socket, blank the addresses
+ * set the state.
+ */
+static int ieee802154_create(struct net *net, struct socket *sock, int protocol)
+{
+ struct sock *sk;
+ int rc;
+ struct proto *proto;
+ const struct proto_ops *ops;
+
+ /* FIXME: init_net */
+ if (net != &init_net)
+ return -EAFNOSUPPORT;
+
+ switch (sock->type) {
+ case SOCK_RAW:
+ proto = &ieee802154_raw_prot;
+ ops = &ieee802154_raw_ops;
+ break;
+ case SOCK_DGRAM:
+ proto = &ieee802154_dgram_prot;
+ ops = &ieee802154_dgram_ops;
+ break;
+ default:
+ rc = -ESOCKTNOSUPPORT;
+ goto out;
+ }
+
+ rc = -ENOMEM;
+ sk = sk_alloc(net, PF_IEEE802154, GFP_KERNEL, proto);
+ if (!sk)
+ goto out;
+ rc = 0;
+
+ sock->ops = ops;
+
+ sock_init_data(sock, sk);
+ /* FIXME: sk->sk_destruct */
+ sk->sk_family = PF_IEEE802154;
+
+ /* Checksums on by default */
+ sock_set_flag(sk, SOCK_ZAPPED);
+
+ if (sk->sk_prot->hash)
+ sk->sk_prot->hash(sk);
+
+ if (sk->sk_prot->init) {
+ rc = sk->sk_prot->init(sk);
+ if (rc)
+ sk_common_release(sk);
+ }
+out:
+ return rc;
+}
+
+static struct net_proto_family ieee802154_family_ops = {
+ .family = PF_IEEE802154,
+ .create = ieee802154_create,
+ .owner = THIS_MODULE,
+};
+
+static int ieee802154_rcv(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *pt, struct net_device *orig_dev)
+{
+ DBG_DUMP(skb->data, skb->len);
+ if (!netif_running(dev))
+ return -ENODEV;
+ pr_debug("got frame, type %d, dev %p\n", dev->type, dev);
+ /* FIXME: init_net */
+ if (!net_eq(dev_net(dev), &init_net))
+ goto drop;
+
+ ieee802154_raw_deliver(dev, skb);
+
+ if (dev->type != ARPHRD_IEEE802154)
+ goto drop;
+
+ if (skb->pkt_type != PACKET_OTHERHOST)
+ return ieee802154_dgram_deliver(dev, skb);
+
+drop:
+ kfree_skb(skb);
+ return NET_RX_DROP;
+}
+
+
+static struct packet_type ieee802154_packet_type = {
+ .type = __constant_htons(ETH_P_IEEE802154),
+ .func = ieee802154_rcv,
+};
+
+static int __init af_ieee802154_init(void)
+{
+ int rc = -EINVAL;
+
+ rc = proto_register(&ieee802154_raw_prot, 1);
+ if (rc)
+ goto out;
+
+ rc = proto_register(&ieee802154_dgram_prot, 1);
+ if (rc)
+ goto err_dgram;
+
+ /* Tell SOCKET that we are alive */
+ rc = sock_register(&ieee802154_family_ops);
+ if (rc)
+ goto err_sock;
+ dev_add_pack(&ieee802154_packet_type);
+
+ rc = 0;
+ goto out;
+
+err_sock:
+ proto_unregister(&ieee802154_dgram_prot);
+err_dgram:
+ proto_unregister(&ieee802154_raw_prot);
+out:
+ return rc;
+}
+static void af_ieee802154_remove(void)
+{
+ dev_remove_pack(&ieee802154_packet_type);
+ sock_unregister(PF_IEEE802154);
+ proto_unregister(&ieee802154_dgram_prot);
+ proto_unregister(&ieee802154_raw_prot);
+}
+
+module_init(af_ieee802154_init);
+module_exit(af_ieee802154_remove);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_NETPROTO(PF_IEEE802154);
diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c
new file mode 100644
index 0000000..a4d6571
--- /dev/null
+++ b/net/ieee802154/dgram.c
@@ -0,0 +1,372 @@
+/*
+ * ZigBee socket interface
+ *
+ * Copyright 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ * Sergey Lapin <[email protected]>
+ * Dmitry Eremin-Solenikov <[email protected]>
+ */
+
+#include <linux/net.h>
+#include <linux/module.h>
+#include <linux/if_arp.h>
+#include <linux/list.h>
+#include <net/sock.h>
+#include <net/ieee802154/netdev.h>
+#include <net/ieee802154/af_ieee802154.h>
+#include <net/ieee802154/mac_def.h>
+#include <asm/ioctls.h>
+
+static HLIST_HEAD(dgram_head);
+static DEFINE_RWLOCK(dgram_lock);
+
+struct dgram_sock {
+ struct sock sk;
+
+ int bound;
+ struct ieee802154_addr src_addr;
+ struct ieee802154_addr dst_addr;
+};
+
+static inline struct dgram_sock *dgram_sk(const struct sock *sk)
+{
+ return (struct dgram_sock *)sk;
+}
+
+
+static void dgram_hash(struct sock *sk)
+{
+ write_lock_bh(&dgram_lock);
+ sk_add_node(sk, &dgram_head);
+ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
+ write_unlock_bh(&dgram_lock);
+}
+
+static void dgram_unhash(struct sock *sk)
+{
+ write_lock_bh(&dgram_lock);
+ if (sk_del_node_init(sk))
+ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
+ write_unlock_bh(&dgram_lock);
+}
+
+static int dgram_init(struct sock *sk)
+{
+ struct dgram_sock *ro = dgram_sk(sk);
+
+ ro->dst_addr.addr_type = IEEE802154_ADDR_LONG;
+ ro->dst_addr.pan_id = 0xffff;
+ memset(&ro->dst_addr.hwaddr, 0xff, sizeof(ro->dst_addr.hwaddr));
+ return 0;
+}
+
+static void dgram_close(struct sock *sk, long timeout)
+{
+ sk_common_release(sk);
+}
+
+static int dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len)
+{
+ struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr;
+ struct dgram_sock *ro = dgram_sk(sk);
+ int err = 0;
+ struct net_device *dev;
+
+ ro->bound = 0;
+
+ if (len < sizeof(*addr))
+ return -EINVAL;
+
+ if (addr->family != AF_IEEE802154)
+ return -EINVAL;
+
+ lock_sock(sk);
+
+ dev = ieee802154_get_dev(sock_net(sk), &addr->addr);
+ if (!dev) {
+ err = -ENODEV;
+ goto out;
+ }
+
+ if (dev->type != ARPHRD_IEEE802154) {
+ err = -ENODEV;
+ goto out_put;
+ }
+
+ memcpy(&ro->src_addr, &addr->addr, sizeof(struct ieee802154_addr));
+
+ ro->bound = 1;
+out_put:
+ dev_put(dev);
+out:
+ release_sock(sk);
+
+ return err;
+}
+
+static int dgram_ioctl(struct sock *sk, int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case SIOCOUTQ:
+ {
+ int amount = atomic_read(&sk->sk_wmem_alloc);
+ return put_user(amount, (int __user *)arg);
+ }
+
+ case SIOCINQ:
+ {
+ struct sk_buff *skb;
+ unsigned long amount;
+
+ amount = 0;
+ spin_lock_bh(&sk->sk_receive_queue.lock);
+ skb = skb_peek(&sk->sk_receive_queue);
+ if (skb != NULL) {
+ /*
+ * We will only return the amount
+ * of this packet since that is all
+ * that will be read.
+ */
+ /* FIXME: parse the header for more correct value */
+ amount = skb->len - (3+8+8);
+ }
+ spin_unlock_bh(&sk->sk_receive_queue.lock);
+ return put_user(amount, (int __user *)arg);
+ }
+
+ }
+ return -ENOIOCTLCMD;
+}
+
+/* FIXME: autobind */
+static int dgram_connect(struct sock *sk, struct sockaddr *uaddr,
+ int len)
+{
+ struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr;
+ struct dgram_sock *ro = dgram_sk(sk);
+ int err = 0;
+
+ if (len < sizeof(*addr))
+ return -EINVAL;
+
+ if (addr->family != AF_IEEE802154)
+ return -EINVAL;
+
+ lock_sock(sk);
+
+ if (!ro->bound) {
+ err = -ENETUNREACH;
+ goto out;
+ }
+
+ memcpy(&ro->dst_addr, &addr->addr, sizeof(struct ieee802154_addr));
+
+out:
+ release_sock(sk);
+ return err;
+}
+
+static int dgram_disconnect(struct sock *sk, int flags)
+{
+ struct dgram_sock *ro = dgram_sk(sk);
+
+ lock_sock(sk);
+
+ ro->dst_addr.addr_type = IEEE802154_ADDR_LONG;
+ memset(&ro->dst_addr.hwaddr, 0xff, sizeof(ro->dst_addr.hwaddr));
+
+ release_sock(sk);
+
+ return 0;
+}
+
+static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+ size_t size)
+{
+ struct net_device *dev;
+ unsigned mtu;
+ struct sk_buff *skb;
+ struct dgram_sock *ro = dgram_sk(sk);
+ int err;
+ struct ieee802154_priv *hw;
+
+ if (msg->msg_flags & MSG_OOB) {
+ pr_debug("msg->msg_flags = 0x%x\n", msg->msg_flags);
+ return -EOPNOTSUPP;
+ }
+
+ if (!ro->bound)
+ dev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_IEEE802154);
+ else
+ dev = ieee802154_get_dev(sock_net(sk), &ro->src_addr);
+
+ if (!dev) {
+ pr_debug("no dev\n");
+ return -ENXIO;
+ }
+ hw = ieee802154_slave_get_hw(dev);
+ mtu = dev->mtu;
+ pr_debug("name = %s, mtu = %u\n", dev->name, mtu);
+
+ skb = sock_alloc_send_skb(sk, LL_ALLOCATED_SPACE(dev) + size, msg->msg_flags & MSG_DONTWAIT,
+ &err);
+ if (!skb) {
+ dev_put(dev);
+ return err;
+ }
+ skb_reserve(skb, LL_RESERVED_SPACE(dev));
+
+ skb_reset_network_header(skb);
+
+ MAC_CB(skb)->flags = IEEE802154_FC_TYPE_DATA | MAC_CB_FLAG_ACKREQ;
+ MAC_CB(skb)->seq = hw->dsn;
+ err = dev_hard_header(skb, dev, ETH_P_IEEE802154, &ro->dst_addr, ro->bound ? &ro->src_addr : NULL, size);
+ if (err < 0) {
+ kfree_skb(skb);
+ dev_put(dev);
+ return err;
+ }
+
+ skb_reset_mac_header(skb);
+
+ err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
+ if (err < 0) {
+ kfree_skb(skb);
+ dev_put(dev);
+ return err;
+ }
+
+ if (size > mtu) {
+ pr_debug("size = %u, mtu = %u\n", size, mtu);
+ return -EINVAL;
+ }
+
+ skb->dev = dev;
+ skb->sk = sk;
+ skb->protocol = htons(ETH_P_IEEE802154);
+
+ err = dev_queue_xmit(skb);
+ hw->dsn++;
+
+ dev_put(dev);
+
+ if (err)
+ return err;
+
+ return size;
+}
+
+static int dgram_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+ size_t len, int noblock, int flags, int *addr_len)
+{
+ size_t copied = 0;
+ int err = -EOPNOTSUPP;
+ struct sk_buff *skb;
+
+ skb = skb_recv_datagram(sk, flags, noblock, &err);
+ if (!skb)
+ goto out;
+
+ copied = skb->len;
+ if (len < copied) {
+ msg->msg_flags |= MSG_TRUNC;
+ copied = len;
+ }
+
+ /* FIXME: skip headers if necessary ?! */
+ err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ if (err)
+ goto done;
+
+ sock_recv_timestamp(msg, sk, skb);
+
+ if (flags & MSG_TRUNC)
+ copied = skb->len;
+done:
+ skb_free_datagram(sk, skb);
+out:
+ if (err)
+ return err;
+ return copied;
+}
+
+static int dgram_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+ if (sock_queue_rcv_skb(sk, skb) < 0) {
+ atomic_inc(&sk->sk_drops);
+ kfree_skb(skb);
+ return NET_RX_DROP;
+ }
+
+ return NET_RX_SUCCESS;
+}
+
+int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb)
+{
+ struct sock *sk, *prev = NULL;
+ struct hlist_node *node;
+ int ret = NET_RX_SUCCESS;
+
+ /* Data frame processing */
+
+ read_lock(&dgram_lock);
+ sk_for_each(sk, node, &dgram_head) {
+ struct dgram_sock *ro = dgram_sk(sk);
+ if (!ro->bound ||
+ (ro->src_addr.addr_type == IEEE802154_ADDR_LONG &&
+ !memcmp(ro->src_addr.hwaddr, dev->dev_addr, IEEE802154_ADDR_LEN)) ||
+ (ro->src_addr.addr_type == IEEE802154_ADDR_SHORT &&
+ ieee802154_dev_get_pan_id(dev) == ro->src_addr.pan_id &&
+ ieee802154_dev_get_short_addr(dev) == ro->src_addr.short_addr)) {
+ if (prev) {
+ struct sk_buff *clone;
+ clone = skb_clone(skb, GFP_ATOMIC);
+ if (clone)
+ dgram_rcv_skb(prev, clone);
+ }
+
+ prev = sk;
+ }
+ }
+
+ if (prev)
+ dgram_rcv_skb(prev, skb);
+ else {
+ kfree_skb(skb);
+ ret = NET_RX_DROP;
+ }
+ read_unlock(&dgram_lock);
+
+ return ret;
+}
+
+struct proto ieee802154_dgram_prot = {
+ .name = "IEEE-802.15.4-MAC",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct dgram_sock),
+ .init = dgram_init,
+ .close = dgram_close,
+ .bind = dgram_bind,
+ .sendmsg = dgram_sendmsg,
+ .recvmsg = dgram_recvmsg,
+ .hash = dgram_hash,
+ .unhash = dgram_unhash,
+ .connect = dgram_connect,
+ .disconnect = dgram_disconnect,
+ .ioctl = dgram_ioctl,
+};
+
diff --git a/net/ieee802154/raw.c b/net/ieee802154/raw.c
new file mode 100644
index 0000000..4fe6f37
--- /dev/null
+++ b/net/ieee802154/raw.c
@@ -0,0 +1,249 @@
+/*
+ * Raw IEEE 802.15.4 sockets
+ *
+ * Copyright 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ * Sergey Lapin <[email protected]>
+ * Dmitry Eremin-Solenikov <[email protected]>
+ */
+
+#include <linux/net.h>
+#include <linux/module.h>
+#include <linux/if_arp.h>
+#include <linux/list.h>
+#include <net/sock.h>
+#include <net/ieee802154/netdev.h>
+#include <net/ieee802154/af_ieee802154.h>
+
+static HLIST_HEAD(raw_head);
+static DEFINE_RWLOCK(raw_lock);
+
+static void raw_hash(struct sock *sk)
+{
+ write_lock_bh(&raw_lock);
+ sk_add_node(sk, &raw_head);
+ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
+ write_unlock_bh(&raw_lock);
+}
+
+static void raw_unhash(struct sock *sk)
+{
+ write_lock_bh(&raw_lock);
+ if (sk_del_node_init(sk))
+ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
+ write_unlock_bh(&raw_lock);
+}
+
+static void raw_close(struct sock *sk, long timeout)
+{
+ sk_common_release(sk);
+}
+
+static int raw_bind(struct sock *sk, struct sockaddr *uaddr, int len)
+{
+ struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr;
+ int err = 0;
+ struct net_device *dev = NULL;
+
+ if (len < sizeof(*addr))
+ return -EINVAL;
+
+ if (addr->family != AF_IEEE802154)
+ return -EINVAL;
+
+ lock_sock(sk);
+
+ dev = ieee802154_get_dev(sock_net(sk), &addr->addr);
+ if (!dev) {
+ err = -ENODEV;
+ goto out;
+ }
+
+ if (dev->type != ARPHRD_IEEE802154_PHY && dev->type != ARPHRD_IEEE802154) {
+ err = -ENODEV;
+ goto out_put;
+ }
+
+ sk->sk_bound_dev_if = dev->ifindex;
+ sk_dst_reset(sk);
+
+out_put:
+ dev_put(dev);
+out:
+ release_sock(sk);
+
+ return err;
+}
+
+static int raw_connect(struct sock *sk, struct sockaddr *uaddr,
+ int addr_len)
+{
+ return -ENOTSUPP;
+}
+
+static int raw_disconnect(struct sock *sk, int flags)
+{
+ return 0;
+}
+
+static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+ size_t size)
+{
+ struct net_device *dev;
+ unsigned mtu;
+ struct sk_buff *skb;
+ int err;
+
+ if (msg->msg_flags & MSG_OOB) {
+ pr_debug("msg->msg_flags = 0x%x\n", msg->msg_flags);
+ return -EOPNOTSUPP;
+ }
+
+ lock_sock(sk);
+ if (!sk->sk_bound_dev_if)
+ dev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_IEEE802154_PHY);
+ else
+ dev = dev_get_by_index(sock_net(sk), sk->sk_bound_dev_if);
+ release_sock(sk);
+
+ if (!dev) {
+ pr_debug("no dev\n");
+ err = -ENXIO;
+ goto out;
+ }
+
+ mtu = dev->mtu;
+ pr_debug("name = %s, mtu = %u\n", dev->name, mtu);
+
+ if (size > mtu) {
+ pr_debug("size = %u, mtu = %u\n", size, mtu);
+ err = -EINVAL;
+ goto out_dev;
+ }
+
+ skb = sock_alloc_send_skb(sk, LL_ALLOCATED_SPACE(dev) + size, msg->msg_flags & MSG_DONTWAIT,
+ &err);
+ if (!skb)
+ goto out_dev;
+
+ skb_reserve(skb, LL_RESERVED_SPACE(dev));
+
+ skb_reset_mac_header(skb);
+ skb_reset_network_header(skb);
+
+ err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
+ if (err < 0)
+ goto out_skb;
+
+ skb->dev = dev;
+ skb->sk = sk;
+ skb->protocol = htons(ETH_P_IEEE802154);
+
+ err = dev_queue_xmit(skb);
+
+ dev_put(dev);
+
+ return size;
+
+out_skb:
+ kfree_skb(skb);
+out_dev:
+ dev_put(dev);
+out:
+ return err;
+}
+
+static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+ size_t len, int noblock, int flags, int *addr_len)
+{
+ size_t copied = 0;
+ int err = -EOPNOTSUPP;
+ struct sk_buff *skb;
+
+ skb = skb_recv_datagram(sk, flags, noblock, &err);
+ if (!skb)
+ goto out;
+
+ copied = skb->len;
+ if (len < copied) {
+ msg->msg_flags |= MSG_TRUNC;
+ copied = len;
+ }
+
+ err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ if (err)
+ goto done;
+
+ sock_recv_timestamp(msg, sk, skb);
+
+ if (flags & MSG_TRUNC)
+ copied = skb->len;
+done:
+ skb_free_datagram(sk, skb);
+out:
+ if (err)
+ return err;
+ return copied;
+}
+
+static int raw_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+ if (sock_queue_rcv_skb(sk, skb) < 0) {
+ atomic_inc(&sk->sk_drops);
+ kfree_skb(skb);
+ return NET_RX_DROP;
+ }
+
+ return NET_RX_SUCCESS;
+}
+
+
+void ieee802154_raw_deliver(struct net_device *dev, struct sk_buff *skb)
+{
+ struct sock *sk;
+ struct hlist_node *node;
+
+ read_lock(&raw_lock);
+ sk_for_each(sk, node, &raw_head) {
+ bh_lock_sock(sk);
+ if (!sk->sk_bound_dev_if || sk->sk_bound_dev_if == dev->ifindex) {
+
+ struct sk_buff *clone;
+
+ clone = skb_clone(skb, GFP_ATOMIC);
+ if (clone)
+ raw_rcv_skb(sk, clone);
+ }
+ bh_unlock_sock(sk);
+ }
+ read_unlock(&raw_lock);
+}
+
+struct proto ieee802154_raw_prot = {
+ .name = "IEEE-802.15.4-RAW",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct sock),
+ .close = raw_close,
+ .bind = raw_bind,
+ .sendmsg = raw_sendmsg,
+ .recvmsg = raw_recvmsg,
+ .hash = raw_hash,
+ .unhash = raw_unhash,
+ .connect = raw_connect,
+ .disconnect = raw_disconnect,
+};
+
--
1.6.2.4


2009-05-28 03:08:55

by Ben Hutchings

[permalink] [raw]
Subject: Re: [PATCH 2/5] net: add IEEE 802.15.4 partial implementation

On Tue, 2009-05-26 at 15:23 +0400, Dmitry Eremin-Solenikov wrote:
[...]
> diff --git a/include/net/ieee802154/af_ieee802154.h b/include/net/ieee802154/af_ieee802154.h
> new file mode 100644
> index 0000000..6eb7f51
> --- /dev/null
> +++ b/include/net/ieee802154/af_ieee802154.h
[...]
> +#ifdef __KERNEL__
> +#include <linux/skbuff.h>
> +#include <linux/netdevice.h>

The struct declarations would be sufficient.

> +extern struct proto ieee802154_raw_prot;
> +extern struct proto ieee802154_dgram_prot;
> +void ieee802154_raw_deliver(struct net_device *dev, struct sk_buff *skb);
> +int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb);
> +#endif
> +
> +#endif
> diff --git a/include/net/ieee802154/beacon.h b/include/net/ieee802154/beacon.h
> new file mode 100644
> index 0000000..baca263
> --- /dev/null
> +++ b/include/net/ieee802154/beacon.h
[...]
> +/* Per spec; optimizations are needed */
> +struct ieee802154_pandsc {
> + struct list_head list;
> + struct ieee802154_addr addr; /* Contains panid */
> + int channel;
> + u16 sf;
> + bool gts_permit;
> + u8 lqi;
> + u32 timestamp; /* FIXME */

When and how does this need to be fixed, if at all?

> + bool security;
> + u8 mac_sec;
> + bool sec_fail;
> +};
[...]
> --- /dev/null
> +++ b/include/net/ieee802154/const.h
[...]
> +/* Time related constants, in microseconds.
> + *
> + * The 1SYM_TIME values are shown how much time is needed to transmit one
> + * symbol across media.
> + * The callculation is following:
> + * For a 2450 MHZ radio freq rate is 62,5 ksym/sec. A byte (8 bit) transfered
> + * by low 4 bits in first symbol, high 4 bits in next symbol. So, to transmit
> + * 1 byte in 2450Mhz freq 2 symbols are needed. Therefore we have 31,25 kbyte/sec
> + * rate. 1 symbol transfered in 16*10^(-6) sec, or 16 microseconds.
> + * For a 868Mhz and 915Mhz, 1 symbol is equal to 1 byte. So, we have 20kbyte/sec
> + * and 40 kbyte/sec respectively. And 5*10^(-5) sec and 2,5*10(-5) sec,
> + * or 50 and 25 microseconds respectively for 868Mhz and 915Mhz freq.
> + */

This is so verbose as to be unhelpful. Since the following constants
only refer to symbol time, it should be sufficient to refer to the
specified symbol rates.

> +#define IEEE802154_2450MHZ_1SYM_TIME 16
> +#define IEEE802154_868MHZ_1SYM_TIME 50
> +#define IEEE802154_915MHZ_1SYM_TIME 25
> +
> +/******************************************************************************/
> +/* MAC constants */
> +/******************************************************************************/
> +/** The number of symbols forming a superframe slot when the superframe order
> + * is equal to 0.
> + */

Don't use two asterisks at the start of a comment unless it's in
kernel-doc format.

[...]
> +/**
> + * \brief PHY return codes description
> + *
> + * The return values of PHY operations
> + */

...and definitely don't use Doxygen format.

[...]
> +/* #define ZB_ED_EDGE 0x7f */

Define it or delete it.

> +/* I've got 0xBE on idle channel; let threshold be a little higher */
> +#define ZB_ED_EDGE 0xc8
> +/* In an ideal world this should be 1 */
> +#define IEEE802154_SLOW_SERIAL_FIXUP 75

Yes but what are these *for*?

> +#endif /* IEEE802154_CONST_H */
> diff --git a/include/net/ieee802154/crc.h b/include/net/ieee802154/crc.h
> new file mode 100644
> index 0000000..5b37218
> --- /dev/null
> +++ b/include/net/ieee802154/crc.h
> @@ -0,0 +1,38 @@
> +/*
> + * based on crc-itu-t.c.
> + * Basically it's CRC-ITU-T but with inverted bit numbering

This can probably be handled by crc_itu_t_bitreversed():

http://svn.debian.org/wsvn/kernel/dists/trunk/linux-2.6/debian/patches/features/all/lib-crcitut-bit-reversed.patch

(That patch hasn't gone anywhere yet as Greg K-H is dragging his heels
over actually fixing his crap, but feel free to resubmit it as part of
your own patch series.)

[...]
> --- /dev/null
> +++ b/include/net/ieee802154/nl.h
[...]
> +/* commands */
> +/* REQ should be responded with CONF
> + * and INDIC with RESP
> + */
> +enum {
> + __IEEE802154_COMMAND_INVALID,
> +
> + IEEE802154_ASSOCIATE_REQ,
> + IEEE802154_ASSOCIATE_CONF,
> + IEEE802154_DISASSOCIATE_REQ,
> + IEEE802154_DISASSOCIATE_CONF,
> + IEEE802154_GET_REQ,
> + IEEE802154_GET_CONF,
> +/* IEEE802154_GTS_REQ, */
> +/* IEEE802154_GTS_CONF, */

Why are some of these commented out? Since they're apparently exposed
to user-space you can't insert them later.

[...]
> --- /dev/null
> +++ b/net/ieee802154/beacon.c
[...]
> + /* TODO GTS */
> + gts = 0;
> +
> + if (flags & IEEE802154_BEACON_FLAG_GTSPERMIT)
> + gts |= IEEE802154_BEACON_GTS_PERMIT;
> + memcpy(skb_put(skb, sizeof(gts)), &gts, sizeof(gts));
> +
> + /* FIXME pending address */
> + addr16_cnt = 0;
> + addr64_cnt = 0;
> +#if 0
> + /* need more thinking about this */
> + list_for_each_entry(l, al->list, list) {
> + struct ieee802154_addr *s = container_of(l, struct list_head, list);
> + if (s->addr_type == IEEE802154_ADDR_LONG)
> + addr16_cnt++;
> +
> + if (s->addr_type == IEEE802154_ADDR_SHORT)
> + addr64_cnt++;
> + }
> +#endif

This all looks very unfinished - as do several other sections with
FIXME, #if 0, commented-out code...

[...]
> +#define IEEE802154_FETCH_U64(skb, var) \
> + do { \
> + if (skb->len < IEEE802154_ADDR_LEN) \

I see what you did there...

> + goto exit_error; \
> + fetch_skb_u64(skb, &var); \
> + } while (0)
[...]
> --- /dev/null
> +++ b/net/ieee802154/netlink.c
> @@ -0,0 +1,637 @@
> +/*
> + * Netlink intefcace for IEEE 802.15.4 stack
[...]

Speling misstake.

Ben.

--
Ben Hutchings
Teamwork is essential - it allows you to blame someone else.


Attachments:
signature.asc (189.00 B)
This is a digitally signed message part

2009-05-30 16:32:20

by Ben Hutchings

[permalink] [raw]
Subject: Re: [PATCH 2/5] net: add IEEE 802.15.4 partial implementation

On Thu, 2009-05-28 at 12:48 +0400, Dmitry Eremin-Solenikov wrote:
> On Thu, May 28, 2009 at 04:08:51AM +0100, Ben Hutchings wrote:
> > On Tue, 2009-05-26 at 15:23 +0400, Dmitry Eremin-Solenikov wrote:
> > [...]
> > > diff --git a/include/net/ieee802154/af_ieee802154.h b/include/net/ieee802154/af_ieee802154.h
> > > new file mode 100644
> > > index 0000000..6eb7f51
> > > --- /dev/null
> > > +++ b/include/net/ieee802154/af_ieee802154.h
> > [...]
> > > +#ifdef __KERNEL__
> > > +#include <linux/skbuff.h>
> > > +#include <linux/netdevice.h>
> >
> > The struct declarations would be sufficient.
>
> Do you mean just:
> struct sk_buff;
> struct net_device;

Right.

[...]
> >
> > [...]
> > > +#define IEEE802154_FETCH_U64(skb, var) \
> > > + do { \
> > > + if (skb->len < IEEE802154_ADDR_LEN) \
> >
> > I see what you did there...
>
> ???
[...]

This macro works with u64 values, not specifically addresses, which
happen to be 8 bytes long too. I infer that someone search-and-replaced
instances of the literal 8 with IEEE802154_ADDR_LEN and wrongly replaced
this instance.

Ben.

--
Ben Hutchings
Logic doesn't apply to the real world. - Marvin Minsky


Attachments:
signature.asc (189.00 B)
This is a digitally signed message part

2009-05-26 11:23:22

by Dmitry Baryshkov

[permalink] [raw]
Subject: [PATCH 1/5] Add constants for the ieee 802.15.4/ZigBee stack

IEEE 802.15.4/ZigBee stack requires several constants to be defined/adjusted.

Signed-off-by: Dmitry Eremin-Solenikov <[email protected]>
Signed-off-by: Sergey Lapin <[email protected]>
---
include/linux/if.h | 2 ++
include/linux/if_arp.h | 2 ++
include/linux/if_ether.h | 2 ++
include/linux/socket.h | 6 +++++-
net/core/dev.c | 6 ++++--
net/core/sock.c | 3 +++
6 files changed, 18 insertions(+), 3 deletions(-)

diff --git a/include/linux/if.h b/include/linux/if.h
index 1108f3e..3f574b8 100644
--- a/include/linux/if.h
+++ b/include/linux/if.h
@@ -68,6 +68,8 @@
#define IFF_MASTER_ARPMON 0x100 /* bonding master, ARP mon in use */
#define IFF_WAN_HDLC 0x200 /* WAN HDLC device */

+#define IFF_IEEE802154_COORD 0x100 /* IEEE802.15.4 PAN coordinator */
+
#define IF_GET_IFACE 0x0001 /* for querying only */
#define IF_GET_PROTO 0x0002

diff --git a/include/linux/if_arp.h b/include/linux/if_arp.h
index 5ff8980..b554300 100644
--- a/include/linux/if_arp.h
+++ b/include/linux/if_arp.h
@@ -86,6 +86,8 @@
#define ARPHRD_IEEE80211 801 /* IEEE 802.11 */
#define ARPHRD_IEEE80211_PRISM 802 /* IEEE 802.11 + Prism2 header */
#define ARPHRD_IEEE80211_RADIOTAP 803 /* IEEE 802.11 + radiotap header */
+#define ARPHRD_IEEE802154 804
+#define ARPHRD_IEEE802154_PHY 805

#define ARPHRD_PHONET 820 /* PhoNet media type */
#define ARPHRD_PHONET_PIPE 821 /* PhoNet pipe header */
diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h
index cfe4fe1..0a08e92 100644
--- a/include/linux/if_ether.h
+++ b/include/linux/if_ether.h
@@ -106,6 +106,8 @@
#define ETH_P_DSA 0x001B /* Distributed Switch Arch. */
#define ETH_P_TRAILER 0x001C /* Trailer switch tagging */
#define ETH_P_PHONET 0x00F5 /* Nokia Phonet frames */
+#define ETH_P_IEEE802154 0x00F6 /* IEEE802.15.4 frame */
+#define ETH_P_IEEE802154_MAC 0x00F7 /* IEEE802.15.4 MAC frame */

/*
* This is an Ethernet frame header.
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 421afb4..09d792e 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -194,7 +194,9 @@ struct ucred {
#define AF_RXRPC 33 /* RxRPC sockets */
#define AF_ISDN 34 /* mISDN sockets */
#define AF_PHONET 35 /* Phonet sockets */
-#define AF_MAX 36 /* For now.. */
+#define AF_IEEE802154 36 /* IEEE802154 sockets */
+#define AF_ZIGBEE 37 /* ZIGBEE sockets */
+#define AF_MAX 38 /* For now.. */

/* Protocol families, same as address families. */
#define PF_UNSPEC AF_UNSPEC
@@ -233,6 +235,8 @@ struct ucred {
#define PF_RXRPC AF_RXRPC
#define PF_ISDN AF_ISDN
#define PF_PHONET AF_PHONET
+#define PF_IEEE802154 AF_IEEE802154
+#define PF_ZIGBEE AF_ZIGBEE
#define PF_MAX AF_MAX

/* Maximum queue length specifiable by listen. */
diff --git a/net/core/dev.c b/net/core/dev.c
index e2e9e4a..4119dfc 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -268,7 +268,8 @@ static const unsigned short netdev_lock_type[] =
ARPHRD_IRDA, ARPHRD_FCPP, ARPHRD_FCAL, ARPHRD_FCPL,
ARPHRD_FCFABRIC, ARPHRD_IEEE802_TR, ARPHRD_IEEE80211,
ARPHRD_IEEE80211_PRISM, ARPHRD_IEEE80211_RADIOTAP, ARPHRD_PHONET,
- ARPHRD_PHONET_PIPE, ARPHRD_VOID, ARPHRD_NONE};
+ ARPHRD_PHONET_PIPE, ARPHRD_IEEE802154, ARPHRD_IEEE802154_PHY,
+ ARPHRD_VOID, ARPHRD_NONE};

static const char *netdev_lock_name[] =
{"_xmit_NETROM", "_xmit_ETHER", "_xmit_EETHER", "_xmit_AX25",
@@ -285,7 +286,8 @@ static const char *netdev_lock_name[] =
"_xmit_IRDA", "_xmit_FCPP", "_xmit_FCAL", "_xmit_FCPL",
"_xmit_FCFABRIC", "_xmit_IEEE802_TR", "_xmit_IEEE80211",
"_xmit_IEEE80211_PRISM", "_xmit_IEEE80211_RADIOTAP", "_xmit_PHONET",
- "_xmit_PHONET_PIPE", "_xmit_VOID", "_xmit_NONE"};
+ "_xmit_PHONET_PIPE", "_xmit_IEEE802154", "_xmit_IEEE802154_PHY",
+ "_xmit_VOID", "_xmit_NONE"};

static struct lock_class_key netdev_xmit_lock_key[ARRAY_SIZE(netdev_lock_type)];
static struct lock_class_key netdev_addr_lock_key[ARRAY_SIZE(netdev_lock_type)];
diff --git a/net/core/sock.c b/net/core/sock.c
index 7dbf3ff..72d593b 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -155,6 +155,7 @@ static const char *af_family_key_strings[AF_MAX+1] = {
"sk_lock-27" , "sk_lock-28" , "sk_lock-AF_CAN" ,
"sk_lock-AF_TIPC" , "sk_lock-AF_BLUETOOTH", "sk_lock-IUCV" ,
"sk_lock-AF_RXRPC" , "sk_lock-AF_ISDN" , "sk_lock-AF_PHONET" ,
+ "sk_lock-AF_IEEE802154", "sk_lock-AF_ZIGBEE",
"sk_lock-AF_MAX"
};
static const char *af_family_slock_key_strings[AF_MAX+1] = {
@@ -170,6 +171,7 @@ static const char *af_family_slock_key_strings[AF_MAX+1] = {
"slock-27" , "slock-28" , "slock-AF_CAN" ,
"slock-AF_TIPC" , "slock-AF_BLUETOOTH", "slock-AF_IUCV" ,
"slock-AF_RXRPC" , "slock-AF_ISDN" , "slock-AF_PHONET" ,
+ "slock-AF_IEEE802154", "slock-AF_ZIGBEE",
"slock-AF_MAX"
};
static const char *af_family_clock_key_strings[AF_MAX+1] = {
@@ -185,6 +187,7 @@ static const char *af_family_clock_key_strings[AF_MAX+1] = {
"clock-27" , "clock-28" , "clock-AF_CAN" ,
"clock-AF_TIPC" , "clock-AF_BLUETOOTH", "clock-AF_IUCV" ,
"clock-AF_RXRPC" , "clock-AF_ISDN" , "clock-AF_PHONET" ,
+ "clock-AF_IEEE802154", "clock-AF_ZIGBEE",
"clock-AF_MAX"
};

--
1.6.2.4


2009-05-28 06:35:21

by Maxim Osipov

[permalink] [raw]
Subject: Re: [PATCH 2/5] net: add IEEE 802.15.4 partial implementation

> [...]
>> --- /dev/null
>> +++ b/include/net/ieee802154/nl.h
> [...]
>> +/* commands */
>> +/* REQ should be responded with CONF
>> + * and INDIC with RESP
>> + */
>> +enum {
>> + ? ? __IEEE802154_COMMAND_INVALID,
>> +
>> + ? ? IEEE802154_ASSOCIATE_REQ,
>> + ? ? IEEE802154_ASSOCIATE_CONF,
>> + ? ? IEEE802154_DISASSOCIATE_REQ,
>> + ? ? IEEE802154_DISASSOCIATE_CONF,
>> + ? ? IEEE802154_GET_REQ,
>> + ? ? IEEE802154_GET_CONF,
>> +/* ? IEEE802154_GTS_REQ, */
>> +/* ? IEEE802154_GTS_CONF, */
>
> Why are some of these commented out? ?Since they're apparently exposed
> to user-space you can't insert them later.
>

We are working to define a next version of netlink API for the stack
right now. But taking into account, that iz tool & upper layers of
stack will be the only users of this API at least for next year, and
IEEE802.15.4 spec is evolving, it is probably not necessary to come up
with a very final version.

Kind regards,
Maxim

2009-05-26 11:23:34

by Dmitry Baryshkov

[permalink] [raw]
Subject: [PATCH 4/5] ieee802154: add virtual loopback driver

fakelb is a virtual loopback driver implementing one or several
interconnected radios. Packets from the radio are either sent
back to the node (if no other fake radio are registered) or to
all other fake radio.

Signed-off-by: Dmitry Eremin-Solenikov <[email protected]>
Signed-off-by: Sergey Lapin <[email protected]>
---
drivers/Makefile | 1 +
drivers/ieee802154/Kconfig | 23 +++
drivers/ieee802154/Makefile | 3 +
drivers/ieee802154/fakelb.c | 335 +++++++++++++++++++++++++++++++++++++++++++
drivers/net/Kconfig | 2 +
5 files changed, 364 insertions(+), 0 deletions(-)
create mode 100644 drivers/ieee802154/Kconfig
create mode 100644 drivers/ieee802154/Makefile
create mode 100644 drivers/ieee802154/fakelb.c

diff --git a/drivers/Makefile b/drivers/Makefile
index 1266ead..9e7d4e5 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -107,3 +107,4 @@ obj-$(CONFIG_SSB) += ssb/
obj-$(CONFIG_VIRTIO) += virtio/
obj-$(CONFIG_STAGING) += staging/
obj-y += platform/
+obj-y += ieee802154/
diff --git a/drivers/ieee802154/Kconfig b/drivers/ieee802154/Kconfig
new file mode 100644
index 0000000..6f03394
--- /dev/null
+++ b/drivers/ieee802154/Kconfig
@@ -0,0 +1,23 @@
+menuconfig IEEE802154_DRIVERS
+ bool "IEEE 802.15.4 drivers"
+ depends on NETDEVICES && IEEE802154
+ default y
+ ---help---
+ Say Y here to get to see options for IEEE 802.15.4 Low-Rate
+ Wireless Personal Area Network device drivers. This option alone
+ does not add any kernel code.
+
+ If you say N, all options in this submenu will be skipped and disabled.
+
+if IEEE802154_DRIVERS && NETDEVICES && IEEE802154
+config IEEE802154_FAKELB
+ tristate "Fake LR-WPAN driver with several interconnected devices"
+ ---help---
+ Say Y here to enable the fake driver that can emulate a net
+ of several interconnected radio devices.
+
+ This driver can also be built as a module. To do so say M here.
+ The module will be called 'fakelb'.
+
+endif
+
diff --git a/drivers/ieee802154/Makefile b/drivers/ieee802154/Makefile
new file mode 100644
index 0000000..c3d1db4
--- /dev/null
+++ b/drivers/ieee802154/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_IEEE802154_FAKELB) += fakelb.o
+
+EXTRA_CFLAGS += -DDEBUG -DCONFIG_FFD
diff --git a/drivers/ieee802154/fakelb.c b/drivers/ieee802154/fakelb.c
new file mode 100644
index 0000000..fa2c1d6
--- /dev/null
+++ b/drivers/ieee802154/fakelb.c
@@ -0,0 +1,335 @@
+/*
+ * Loopback IEEE 802.15.4 interface
+ *
+ * Copyright 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ * Sergey Lapin <[email protected]>
+ * Dmitry Eremin-Solenikov <[email protected]>
+ */
+
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/spinlock.h>
+#include <net/ieee802154/dev.h>
+
+struct fake_dev_priv {
+ struct ieee802154_dev *dev;
+ phy_status_t cur_state, pend_state;
+
+ struct list_head list;
+ struct fake_priv *fake;
+};
+
+struct fake_priv {
+ struct list_head list;
+ rwlock_t lock;
+};
+
+static int is_transmitting(struct ieee802154_dev *dev)
+{
+ return 0;
+}
+
+static int is_receiving(struct ieee802154_dev *dev)
+{
+ return 0;
+}
+
+static phy_status_t
+hw_ed(struct ieee802154_dev *dev, u8 *level)
+{
+ pr_debug("%s\n", __func__);
+ might_sleep();
+ BUG_ON(!level);
+ *level = 0xbe;
+ return PHY_SUCCESS;
+}
+
+static phy_status_t
+hw_cca(struct ieee802154_dev *dev)
+{
+ pr_debug("%s\n", __func__);
+ might_sleep();
+ return PHY_IDLE;
+}
+
+static phy_status_t
+hw_state(struct ieee802154_dev *dev, phy_status_t state)
+{
+ struct fake_dev_priv *priv = dev->priv;
+ pr_debug("%s %d %d\n", __func__, priv->cur_state, state);
+ might_sleep();
+ if (state != PHY_TRX_OFF && state != PHY_RX_ON && state != PHY_TX_ON && state != PHY_FORCE_TRX_OFF)
+ return PHY_INVAL;
+ else if (state == PHY_FORCE_TRX_OFF) {
+ priv->cur_state = PHY_TRX_OFF;
+ return PHY_SUCCESS;
+ } else if (priv->cur_state == state)
+ return state;
+ else if ((state == PHY_TRX_OFF || state == PHY_RX_ON) && is_transmitting(dev)) {
+ priv->pend_state = state;
+ return PHY_BUSY_TX;
+ } else if ((state == PHY_TRX_OFF || state == PHY_TX_ON) && is_receiving(dev)) {
+ priv->pend_state = state;
+ return PHY_BUSY_RX;
+ } else {
+ priv->cur_state = state;
+ return PHY_SUCCESS;
+ }
+}
+
+static phy_status_t
+hw_channel(struct ieee802154_dev *dev, int channel)
+{
+ pr_debug("%s %d\n", __func__, channel);
+ might_sleep();
+ dev->current_channel = channel;
+ return PHY_SUCCESS;
+}
+
+static void
+hw_deliver(struct fake_dev_priv *priv, struct sk_buff *skb)
+{
+ struct sk_buff *newskb;
+
+ newskb = pskb_copy(skb, GFP_ATOMIC);
+
+ ieee802154_rx_irqsafe(priv->dev, newskb, 0xcc);
+}
+
+static int
+hw_tx(struct ieee802154_dev *dev, struct sk_buff *skb)
+{
+ struct fake_dev_priv *priv = dev->priv;
+ struct fake_priv *fake = priv->fake;
+
+ might_sleep();
+
+ read_lock_bh(&fake->lock);
+ if (priv->list.next == priv->list.prev) {
+ /* we are the only one device */
+ hw_deliver(priv, skb);
+ } else {
+ struct fake_dev_priv *dp;
+ list_for_each_entry(dp, &priv->fake->list, list)
+ if (dp != priv && dp->dev->current_channel == priv->dev->current_channel)
+ hw_deliver(dp, skb);
+ }
+ read_unlock_bh(&fake->lock);
+
+ return PHY_SUCCESS;
+}
+
+static struct ieee802154_ops fake_ops = {
+ .owner = THIS_MODULE,
+ .tx = hw_tx,
+ .ed = hw_ed,
+ .cca = hw_cca,
+ .set_trx_state = hw_state,
+ .set_channel = hw_channel,
+};
+
+static int ieee802154fake_add_priv(struct device *dev, struct fake_priv *fake, const u8 *macaddr)
+{
+ struct fake_dev_priv *priv;
+ int err = -ENOMEM;
+
+ priv = kzalloc(sizeof(struct fake_dev_priv), GFP_KERNEL);
+ if (!priv)
+ goto err_alloc;
+
+ INIT_LIST_HEAD(&priv->list);
+
+ priv->dev = ieee802154_alloc_device();
+ if (!priv->dev)
+ goto err_alloc_dev;
+ priv->dev->name = "IEEE 802.15.4 fake";
+ priv->dev->priv = priv;
+ priv->dev->parent = dev;
+ priv->fake = fake;
+
+ err = ieee802154_register_device(priv->dev, &fake_ops);
+ if (err)
+ goto err_reg;
+ rtnl_lock();
+ err = ieee802154_add_slave(priv->dev, macaddr);
+ rtnl_unlock();
+ if (err < 0)
+ goto err_slave;
+
+ write_lock_bh(&fake->lock);
+ list_add_tail(&priv->list, &fake->list);
+ write_unlock_bh(&fake->lock);
+
+ return 0;
+
+err_slave:
+ ieee802154_unregister_device(priv->dev);
+err_reg:
+ ieee802154_free_device(priv->dev);
+err_alloc_dev:
+ kfree(priv);
+err_alloc:
+ return err;
+}
+
+static void ieee802154fake_del_priv(struct fake_dev_priv *priv)
+{
+ write_lock_bh(&priv->fake->lock);
+ list_del(&priv->list);
+ write_unlock_bh(&priv->fake->lock);
+
+ ieee802154_unregister_device(priv->dev);
+ ieee802154_free_device(priv->dev);
+ kfree(priv);
+}
+
+static ssize_t
+adddev_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct fake_priv *priv = platform_get_drvdata(pdev);
+ char hw[8] = {};
+ int i, j, ch, err;
+
+ for (i = 0, j = 0; i < 16 && j < n; j++) {
+ ch = buf[j];
+ switch (buf[j]) {
+ default:
+ return -EINVAL;
+ case '0'...'9':
+ ch -= '0';
+ break;
+ case 'A'...'F':
+ ch -= 'A' - 10;
+ break;
+ case 'a'...'f':
+ ch -= 'a' - 10;
+ break;
+ case ':':
+ case '.':
+ continue;
+ }
+ if (i % 2)
+ hw[i/2] = (hw[i/2] & 0xf0) | ch;
+ else
+ hw[i/2] = ch << 4;
+ i++;
+ }
+ if (i != 16)
+ return -EINVAL;
+ err = ieee802154fake_add_priv(dev, priv, hw);
+ if (err)
+ return err;
+ return n;
+}
+
+static DEVICE_ATTR(adddev, 0200, NULL, adddev_store);
+
+static struct attribute *fake_attrs[] = {
+ &dev_attr_adddev.attr,
+ NULL,
+};
+
+static struct attribute_group fake_group = {
+ .name = NULL /* fake */,
+ .attrs = fake_attrs,
+};
+
+
+static int __devinit ieee802154fake_probe(struct platform_device *pdev)
+{
+ struct fake_priv *priv;
+ struct fake_dev_priv *dp;
+
+ int err = -ENOMEM;
+ priv = kzalloc(sizeof(struct fake_priv), GFP_KERNEL);
+ if (!priv)
+ goto err_alloc;
+
+ INIT_LIST_HEAD(&priv->list);
+ rwlock_init(&priv->lock);
+
+ err = sysfs_create_group(&pdev->dev.kobj, &fake_group);
+ if (err)
+ goto err_grp;
+
+ err = ieee802154fake_add_priv(&pdev->dev, priv, "\xde\xad\xbe\xaf\xca\xfe\xba\xbe");
+ if (err < 0)
+ goto err_slave;
+
+/* err = ieee802154fake_add_priv(priv, "\x67\x45\x23\x01\x67\x45\x23\x01");
+ if (err < 0)
+ goto err_slave;*/
+
+ platform_set_drvdata(pdev, priv);
+ dev_info(&pdev->dev, "Added ieee802154 hardware\n");
+ return 0;
+
+err_slave:
+ list_for_each_entry(dp, &priv->list, list)
+ ieee802154fake_del_priv(dp);
+ sysfs_remove_group(&pdev->dev.kobj, &fake_group);
+err_grp:
+ kfree(priv);
+err_alloc:
+ return err;
+}
+
+static int __devexit ieee802154fake_remove(struct platform_device *pdev)
+{
+ struct fake_priv *priv = platform_get_drvdata(pdev);
+ struct fake_dev_priv *dp, *temp;
+
+ list_for_each_entry_safe(dp, temp, &priv->list, list)
+ ieee802154fake_del_priv(dp);
+ sysfs_remove_group(&pdev->dev.kobj, &fake_group);
+ kfree(priv);
+ return 0;
+}
+
+static struct platform_device *ieee802154fake_dev;
+
+static struct platform_driver ieee802154fake_driver = {
+ .probe = ieee802154fake_probe,
+ .remove = __devexit_p(ieee802154fake_remove),
+ .driver = {
+ .name = "ieee802154fakelb",
+ .owner = THIS_MODULE,
+ },
+};
+
+static __init int fake_init(void)
+{
+ ieee802154fake_dev = platform_device_register_simple("ieee802154fakelb", -1, NULL, 0);
+ return platform_driver_register(&ieee802154fake_driver);
+}
+
+static __exit void fake_exit(void)
+{
+ platform_driver_unregister(&ieee802154fake_driver);
+ platform_device_unregister(ieee802154fake_dev);
+}
+
+module_init(fake_init);
+module_exit(fake_exit);
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 214a92d..ec8cf21 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2704,6 +2704,8 @@ source "drivers/net/wan/Kconfig"

source "drivers/atm/Kconfig"

+source "drivers/ieee802154/Kconfig"
+
source "drivers/s390/net/Kconfig"

config XEN_NETDEV_FRONTEND
--
1.6.2.4


2009-05-27 09:59:00

by Florian Fainelli

[permalink] [raw]
Subject: Re: [RFC][WIP] IEEE 802.15.4 implementation for Linux

Hi Dmitry,

Le Tuesday 26 May 2009 13:21:57 Dmitry Eremin-Solenikov, vous avez ?crit?:
> Hi,
>
> As a part of research activities in the Embedded Systems - Open Platform
> Group from Siemens Corporate Technology we are working on adding support
> for the IEEE 802.15.4 Wireless Personal Area Networks to the Linux. Our
> current implementation is neither certified nor even feature complete.
> However we'd like to present current state of our patchset to the Linux
> developers community to gain comments, fixes, ideas, etc. This is not yet a
> pull request, but more like an RFC.

Let met just thank you for doing this, I am glad some people worked on this.
Got a couple of questions below.

>
> The project page is available at
> http://apps.sourceforge.net/trac/linux-zigbee with source code of kernel
> part available from git at
> http://zigbee-linux.git.sourceforge.net, mirrored for convenience at
> git://git.kernel.org/pub/scm/linux/kernel/git/lumag/lowpan.git
>
> The source code for userspace utils is available from git at
> http://linux-zigbee.git.sourceforge.net/

>From the project page I understand that you plan on providing a ZigBee stack,
based on this 802.15.4 stack ? Do you plan on submitting the ZigBee stack
later on ?

Maybe you should mention that this implementation is royalty-free, but that
commercial products using it should both respect the GPL license and pay a
ZigBee royalty as per the ZigBee Alliance . That is just for clarification
since right now it simply does 802.15.4 which is not subject to this royalty
thing.

>
> Several comments about our implementation:
> * As with 802.11 there are two types of devices: the smart ones that
> implement most parts of the protocol by themselves and more or less dumb
> ones which simply send and receive what they are told. Currently we do only
> support the second type of devices (SoftMAC)
> * The implementation is split between code driving radio, (master, mwpanX
> interface: mdev.c), code processing frames according the IEEE 802.15.4
> rules (slave wpanX devices) and finally sockets (af_ieee802154.c,
> dgram.c, raw.c).

This sounds good. Do you also plan on integrating some meshing algorithm on
top of 802.15.4 ? If so,

> * We do present two example drivers using our stack. One is purely virtual
> one either looping the packets back or connecting several virtual
> interfaces (the one at fakelb.c), and the driver for the Freescale MC13192
> evaluation boards (13192-SARD, 13192-EVK) using our custom firmware
> (currently only available at request, we are working on publishing it). The
> driver for the Atmel at86rf230/at86rf231 chips will follow in several
> weeks.

Is the virtual interface just like mac80211_hwsim for 802.11 ?h
--
Best regards, Florian Fainelli
Email : [email protected]
http://openwrt.org
-------------------------------

2009-05-28 08:48:53

by Dmitry Baryshkov

[permalink] [raw]
Subject: Re: [PATCH 2/5] net: add IEEE 802.15.4 partial implementation

On Thu, May 28, 2009 at 04:08:51AM +0100, Ben Hutchings wrote:
> On Tue, 2009-05-26 at 15:23 +0400, Dmitry Eremin-Solenikov wrote:
> [...]
> > diff --git a/include/net/ieee802154/af_ieee802154.h b/include/net/ieee802154/af_ieee802154.h
> > new file mode 100644
> > index 0000000..6eb7f51
> > --- /dev/null
> > +++ b/include/net/ieee802154/af_ieee802154.h
> [...]
> > +#ifdef __KERNEL__
> > +#include <linux/skbuff.h>
> > +#include <linux/netdevice.h>
>
> The struct declarations would be sufficient.

Do you mean just:
struct sk_buff;
struct net_device;

>
> > +extern struct proto ieee802154_raw_prot;
> > +extern struct proto ieee802154_dgram_prot;
> > +void ieee802154_raw_deliver(struct net_device *dev, struct sk_buff *skb);
> > +int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb);
> > +#endif
> > +
> > +#endif

> > --- /dev/null
> > +++ b/include/net/ieee802154/const.h
> [...]
> > +/* Time related constants, in microseconds.
> > + *
> > + * The 1SYM_TIME values are shown how much time is needed to transmit one
> > + * symbol across media.
> > + * The callculation is following:
> > + * For a 2450 MHZ radio freq rate is 62,5 ksym/sec. A byte (8 bit) transfered
> > + * by low 4 bits in first symbol, high 4 bits in next symbol. So, to transmit
> > + * 1 byte in 2450Mhz freq 2 symbols are needed. Therefore we have 31,25 kbyte/sec
> > + * rate. 1 symbol transfered in 16*10^(-6) sec, or 16 microseconds.
> > + * For a 868Mhz and 915Mhz, 1 symbol is equal to 1 byte. So, we have 20kbyte/sec
> > + * and 40 kbyte/sec respectively. And 5*10^(-5) sec and 2,5*10(-5) sec,
> > + * or 50 and 25 microseconds respectively for 868Mhz and 915Mhz freq.
> > + */
>
> This is so verbose as to be unhelpful. Since the following constants
> only refer to symbol time, it should be sufficient to refer to the
> specified symbol rates.

const.h was inherited from the previous implementation of our stack.
I somehow missed that it needs a bit of polishing. A great bit.

> > +#endif /* IEEE802154_CONST_H */
> > diff --git a/include/net/ieee802154/crc.h b/include/net/ieee802154/crc.h
> > new file mode 100644
> > index 0000000..5b37218
> > --- /dev/null
> > +++ b/include/net/ieee802154/crc.h
> > @@ -0,0 +1,38 @@
> > +/*
> > + * based on crc-itu-t.c.
> > + * Basically it's CRC-ITU-T but with inverted bit numbering
>
> This can probably be handled by crc_itu_t_bitreversed():
>
> http://svn.debian.org/wsvn/kernel/dists/trunk/linux-2.6/debian/patches/features/all/lib-crcitut-bit-reversed.patch
>
> (That patch hasn't gone anywhere yet as Greg K-H is dragging his heels
> over actually fixing his crap, but feel free to resubmit it as part of
> your own patch series.)

Fine, I'll use this patch.

> [...]
> > --- /dev/null
> > +++ b/include/net/ieee802154/nl.h
> [...]
> > +/* commands */
> > +/* REQ should be responded with CONF
> > + * and INDIC with RESP
> > + */
> > +enum {
> > + __IEEE802154_COMMAND_INVALID,
> > +
> > + IEEE802154_ASSOCIATE_REQ,
> > + IEEE802154_ASSOCIATE_CONF,
> > + IEEE802154_DISASSOCIATE_REQ,
> > + IEEE802154_DISASSOCIATE_CONF,
> > + IEEE802154_GET_REQ,
> > + IEEE802154_GET_CONF,
> > +/* IEEE802154_GTS_REQ, */
> > +/* IEEE802154_GTS_CONF, */
>
> Why are some of these commented out? Since they're apparently exposed
> to user-space you can't insert them later.

Those are not implemented yet. We planned on appending them later to the
end of this enum. Most probably I'll just uncomment them and add simple
stubs returning an error instead.

>
> [...]
> > --- /dev/null
> > +++ b/net/ieee802154/beacon.c
> [...]
> > + /* TODO GTS */
> > + gts = 0;
> > +
> > + if (flags & IEEE802154_BEACON_FLAG_GTSPERMIT)
> > + gts |= IEEE802154_BEACON_GTS_PERMIT;
> > + memcpy(skb_put(skb, sizeof(gts)), &gts, sizeof(gts));
> > +
> > + /* FIXME pending address */
> > + addr16_cnt = 0;
> > + addr64_cnt = 0;
> > +#if 0
> > + /* need more thinking about this */
> > + list_for_each_entry(l, al->list, list) {
> > + struct ieee802154_addr *s = container_of(l, struct list_head, list);
> > + if (s->addr_type == IEEE802154_ADDR_LONG)
> > + addr16_cnt++;
> > +
> > + if (s->addr_type == IEEE802154_ADDR_SHORT)
> > + addr64_cnt++;
> > + }
> > +#endif
>
> This all looks very unfinished - as do several other sections with
> FIXME, #if 0, commented-out code...

As I said, the code is in early stage of development.

>
> [...]
> > +#define IEEE802154_FETCH_U64(skb, var) \
> > + do { \
> > + if (skb->len < IEEE802154_ADDR_LEN) \
>
> I see what you did there...

???

> > + goto exit_error; \
> > + fetch_skb_u64(skb, &var); \
> > + } while (0)
> [...]


--
With best wishes
Dmitry