diff options
-rw-r--r-- | include/net/ieee802154/af_ieee802154.h | 60 | ||||
-rw-r--r-- | include/net/ieee802154/mac_def.h | 160 | ||||
-rw-r--r-- | include/net/ieee802154/netdevice.h | 115 | ||||
-rw-r--r-- | net/Kconfig | 1 | ||||
-rw-r--r-- | net/Makefile | 1 | ||||
-rw-r--r-- | net/ieee802154/Kconfig | 12 | ||||
-rw-r--r-- | net/ieee802154/Makefile | 4 | ||||
-rw-r--r-- | net/ieee802154/af802154.h | 36 | ||||
-rw-r--r-- | net/ieee802154/af_ieee802154.c | 372 | ||||
-rw-r--r-- | net/ieee802154/dgram.c | 394 | ||||
-rw-r--r-- | net/ieee802154/raw.c | 254 |
11 files changed, 1409 insertions, 0 deletions
diff --git a/include/net/ieee802154/af_ieee802154.h b/include/net/ieee802154/af_ieee802154.h new file mode 100644 index 000000000000..0d78605fb1a6 --- /dev/null +++ b/include/net/ieee802154/af_ieee802154.h | |||
@@ -0,0 +1,60 @@ | |||
1 | /* | ||
2 | * IEEE 802.15.4 inteface for userspace | ||
3 | * | ||
4 | * Copyright 2007, 2008 Siemens AG | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 | ||
8 | * as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License along | ||
16 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
18 | * | ||
19 | * Written by: | ||
20 | * Sergey Lapin <slapin@ossfans.org> | ||
21 | * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> | ||
22 | */ | ||
23 | |||
24 | #ifndef _AF_IEEE802154_H | ||
25 | #define _AF_IEEE802154_H | ||
26 | |||
27 | #include <linux/socket.h> /* for sa_family_t */ | ||
28 | |||
29 | enum { | ||
30 | IEEE802154_ADDR_NONE = 0x0, | ||
31 | /* RESERVED = 0x01, */ | ||
32 | IEEE802154_ADDR_SHORT = 0x2, /* 16-bit address + PANid */ | ||
33 | IEEE802154_ADDR_LONG = 0x3, /* 64-bit address + PANid */ | ||
34 | }; | ||
35 | |||
36 | /* address length, octets */ | ||
37 | #define IEEE802154_ADDR_LEN 8 | ||
38 | |||
39 | struct ieee802154_addr { | ||
40 | int addr_type; | ||
41 | u16 pan_id; | ||
42 | union { | ||
43 | u8 hwaddr[IEEE802154_ADDR_LEN]; | ||
44 | u16 short_addr; | ||
45 | }; | ||
46 | }; | ||
47 | |||
48 | #define IEEE802154_PANID_BROADCAST 0xffff | ||
49 | #define IEEE802154_ADDR_BROADCAST 0xffff | ||
50 | #define IEEE802154_ADDR_UNDEF 0xfffe | ||
51 | |||
52 | struct sockaddr_ieee802154 { | ||
53 | sa_family_t family; /* AF_IEEE802154 */ | ||
54 | struct ieee802154_addr addr; | ||
55 | }; | ||
56 | |||
57 | /* master device */ | ||
58 | #define IEEE802154_SIOC_ADD_SLAVE (SIOCDEVPRIVATE + 0) | ||
59 | |||
60 | #endif | ||
diff --git a/include/net/ieee802154/mac_def.h b/include/net/ieee802154/mac_def.h new file mode 100644 index 000000000000..8cb684635650 --- /dev/null +++ b/include/net/ieee802154/mac_def.h | |||
@@ -0,0 +1,160 @@ | |||
1 | /* | ||
2 | * IEEE802.15.4-2003 specification | ||
3 | * | ||
4 | * Copyright (C) 2007, 2008 Siemens AG | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 | ||
8 | * as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License along | ||
16 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
18 | * | ||
19 | * Written by: | ||
20 | * Pavel Smolenskiy <pavel.smolenskiy@gmail.com> | ||
21 | * Maxim Gorbachyov <maxim.gorbachev@siemens.com> | ||
22 | * Maxim Osipov <maxim.osipov@siemens.com> | ||
23 | * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> | ||
24 | */ | ||
25 | |||
26 | #ifndef IEEE802154_MAC_DEF_H | ||
27 | #define IEEE802154_MAC_DEF_H | ||
28 | |||
29 | #define IEEE802154_FC_TYPE_BEACON 0x0 /* Frame is beacon */ | ||
30 | #define IEEE802154_FC_TYPE_DATA 0x1 /* Frame is data */ | ||
31 | #define IEEE802154_FC_TYPE_ACK 0x2 /* Frame is acknowledgment */ | ||
32 | #define IEEE802154_FC_TYPE_MAC_CMD 0x3 /* Frame is MAC command */ | ||
33 | |||
34 | #define IEEE802154_FC_TYPE_SHIFT 0 | ||
35 | #define IEEE802154_FC_TYPE_MASK ((1 << 3) - 1) | ||
36 | #define IEEE802154_FC_TYPE(x) ((x & IEEE802154_FC_TYPE_MASK) >> IEEE802154_FC_TYPE_SHIFT) | ||
37 | #define IEEE802154_FC_SET_TYPE(v, x) do { \ | ||
38 | v = (((v) & ~IEEE802154_FC_TYPE_MASK) | \ | ||
39 | (((x) << IEEE802154_FC_TYPE_SHIFT) & IEEE802154_FC_TYPE_MASK)); \ | ||
40 | } while (0) | ||
41 | |||
42 | #define IEEE802154_FC_SECEN (1 << 3) | ||
43 | #define IEEE802154_FC_FRPEND (1 << 4) | ||
44 | #define IEEE802154_FC_ACK_REQ (1 << 5) | ||
45 | #define IEEE802154_FC_INTRA_PAN (1 << 6) | ||
46 | |||
47 | #define IEEE802154_FC_SAMODE_SHIFT 14 | ||
48 | #define IEEE802154_FC_SAMODE_MASK (3 << IEEE802154_FC_SAMODE_SHIFT) | ||
49 | #define IEEE802154_FC_DAMODE_SHIFT 10 | ||
50 | #define IEEE802154_FC_DAMODE_MASK (3 << IEEE802154_FC_DAMODE_SHIFT) | ||
51 | |||
52 | #define IEEE802154_FC_SAMODE(x) \ | ||
53 | (((x) & IEEE802154_FC_SAMODE_MASK) >> IEEE802154_FC_SAMODE_SHIFT) | ||
54 | |||
55 | #define IEEE802154_FC_DAMODE(x) \ | ||
56 | (((x) & IEEE802154_FC_DAMODE_MASK) >> IEEE802154_FC_DAMODE_SHIFT) | ||
57 | |||
58 | |||
59 | /* MAC's Command Frames Identifiers */ | ||
60 | #define IEEE802154_CMD_ASSOCIATION_REQ 0x01 | ||
61 | #define IEEE802154_CMD_ASSOCIATION_RESP 0x02 | ||
62 | #define IEEE802154_CMD_DISASSOCIATION_NOTIFY 0x03 | ||
63 | #define IEEE802154_CMD_DATA_REQ 0x04 | ||
64 | #define IEEE802154_CMD_PANID_CONFLICT_NOTIFY 0x05 | ||
65 | #define IEEE802154_CMD_ORPHAN_NOTIFY 0x06 | ||
66 | #define IEEE802154_CMD_BEACON_REQ 0x07 | ||
67 | #define IEEE802154_CMD_COORD_REALIGN_NOTIFY 0x08 | ||
68 | #define IEEE802154_CMD_GTS_REQ 0x09 | ||
69 | |||
70 | /* | ||
71 | * The return values of MAC operations | ||
72 | */ | ||
73 | enum { | ||
74 | /* | ||
75 | * The requested operation was completed successfully. | ||
76 | * For a transmission request, this value indicates | ||
77 | * a successful transmission. | ||
78 | */ | ||
79 | IEEE802154_SUCCESS = 0x0, | ||
80 | |||
81 | /* The beacon was lost following a synchronization request. */ | ||
82 | IEEE802154_BEACON_LOSS = 0xe0, | ||
83 | /* | ||
84 | * A transmission could not take place due to activity on the | ||
85 | * channel, i.e., the CSMA-CA mechanism has failed. | ||
86 | */ | ||
87 | IEEE802154_CHNL_ACCESS_FAIL = 0xe1, | ||
88 | /* The GTS request has been denied by the PAN coordinator. */ | ||
89 | IEEE802154_DENINED = 0xe2, | ||
90 | /* The attempt to disable the transceiver has failed. */ | ||
91 | IEEE802154_DISABLE_TRX_FAIL = 0xe3, | ||
92 | /* | ||
93 | * The received frame induces a failed security check according to | ||
94 | * the security suite. | ||
95 | */ | ||
96 | IEEE802154_FAILED_SECURITY_CHECK = 0xe4, | ||
97 | /* | ||
98 | * The frame resulting from secure processing has a length that is | ||
99 | * greater than aMACMaxFrameSize. | ||
100 | */ | ||
101 | IEEE802154_FRAME_TOO_LONG = 0xe5, | ||
102 | /* | ||
103 | * The requested GTS transmission failed because the specified GTS | ||
104 | * either did not have a transmit GTS direction or was not defined. | ||
105 | */ | ||
106 | IEEE802154_INVALID_GTS = 0xe6, | ||
107 | /* | ||
108 | * A request to purge an MSDU from the transaction queue was made using | ||
109 | * an MSDU handle that was not found in the transaction table. | ||
110 | */ | ||
111 | IEEE802154_INVALID_HANDLE = 0xe7, | ||
112 | /* A parameter in the primitive is out of the valid range.*/ | ||
113 | IEEE802154_INVALID_PARAMETER = 0xe8, | ||
114 | /* No acknowledgment was received after aMaxFrameRetries. */ | ||
115 | IEEE802154_NO_ACK = 0xe9, | ||
116 | /* A scan operation failed to find any network beacons.*/ | ||
117 | IEEE802154_NO_BEACON = 0xea, | ||
118 | /* No response data were available following a request. */ | ||
119 | IEEE802154_NO_DATA = 0xeb, | ||
120 | /* The operation failed because a short address was not allocated. */ | ||
121 | IEEE802154_NO_SHORT_ADDRESS = 0xec, | ||
122 | /* | ||
123 | * A receiver enable request was unsuccessful because it could not be | ||
124 | * completed within the CAP. | ||
125 | */ | ||
126 | IEEE802154_OUT_OF_CAP = 0xed, | ||
127 | /* | ||
128 | * A PAN identifier conflict has been detected and communicated to the | ||
129 | * PAN coordinator. | ||
130 | */ | ||
131 | IEEE802154_PANID_CONFLICT = 0xee, | ||
132 | /* A coordinator realignment command has been received. */ | ||
133 | IEEE802154_REALIGMENT = 0xef, | ||
134 | /* The transaction has expired and its information discarded. */ | ||
135 | IEEE802154_TRANSACTION_EXPIRED = 0xf0, | ||
136 | /* There is no capacity to store the transaction. */ | ||
137 | IEEE802154_TRANSACTION_OVERFLOW = 0xf1, | ||
138 | /* | ||
139 | * The transceiver was in the transmitter enabled state when the | ||
140 | * receiver was requested to be enabled. | ||
141 | */ | ||
142 | IEEE802154_TX_ACTIVE = 0xf2, | ||
143 | /* The appropriate key is not available in the ACL. */ | ||
144 | IEEE802154_UNAVAILABLE_KEY = 0xf3, | ||
145 | /* | ||
146 | * A SET/GET request was issued with the identifier of a PIB attribute | ||
147 | * that is not supported. | ||
148 | */ | ||
149 | IEEE802154_UNSUPPORTED_ATTR = 0xf4, | ||
150 | /* | ||
151 | * A request to perform a scan operation failed because the MLME was | ||
152 | * in the process of performing a previously initiated scan operation. | ||
153 | */ | ||
154 | IEEE802154_SCAN_IN_PROGRESS = 0xfc, | ||
155 | }; | ||
156 | |||
157 | |||
158 | #endif | ||
159 | |||
160 | |||
diff --git a/include/net/ieee802154/netdevice.h b/include/net/ieee802154/netdevice.h new file mode 100644 index 000000000000..e2506af3e7c8 --- /dev/null +++ b/include/net/ieee802154/netdevice.h | |||
@@ -0,0 +1,115 @@ | |||
1 | /* | ||
2 | * An interface between IEEE802.15.4 device and rest of the kernel. | ||
3 | * | ||
4 | * Copyright (C) 2007, 2008, 2009 Siemens AG | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 | ||
8 | * as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License along | ||
16 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
18 | * | ||
19 | * Written by: | ||
20 | * Pavel Smolenskiy <pavel.smolenskiy@gmail.com> | ||
21 | * Maxim Gorbachyov <maxim.gorbachev@siemens.com> | ||
22 | * Maxim Osipov <maxim.osipov@siemens.com> | ||
23 | * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> | ||
24 | */ | ||
25 | |||
26 | #ifndef IEEE802154_NETDEVICE_H | ||
27 | #define IEEE802154_NETDEVICE_H | ||
28 | |||
29 | /* | ||
30 | * A control block of skb passed between the ARPHRD_IEEE802154 device | ||
31 | * and other stack parts. | ||
32 | */ | ||
33 | struct ieee802154_mac_cb { | ||
34 | u8 lqi; | ||
35 | struct ieee802154_addr sa; | ||
36 | struct ieee802154_addr da; | ||
37 | u8 flags; | ||
38 | u8 seq; | ||
39 | }; | ||
40 | |||
41 | static inline struct ieee802154_mac_cb *mac_cb(struct sk_buff *skb) | ||
42 | { | ||
43 | return (struct ieee802154_mac_cb *)skb->cb; | ||
44 | } | ||
45 | |||
46 | #define MAC_CB_FLAG_TYPEMASK ((1 << 3) - 1) | ||
47 | |||
48 | #define MAC_CB_FLAG_ACKREQ (1 << 3) | ||
49 | #define MAC_CB_FLAG_SECEN (1 << 4) | ||
50 | #define MAC_CB_FLAG_INTRAPAN (1 << 5) | ||
51 | |||
52 | static inline int mac_cb_is_ackreq(struct sk_buff *skb) | ||
53 | { | ||
54 | return mac_cb(skb)->flags & MAC_CB_FLAG_ACKREQ; | ||
55 | } | ||
56 | |||
57 | static inline int mac_cb_is_secen(struct sk_buff *skb) | ||
58 | { | ||
59 | return mac_cb(skb)->flags & MAC_CB_FLAG_SECEN; | ||
60 | } | ||
61 | |||
62 | static inline int mac_cb_is_intrapan(struct sk_buff *skb) | ||
63 | { | ||
64 | return mac_cb(skb)->flags & MAC_CB_FLAG_INTRAPAN; | ||
65 | } | ||
66 | |||
67 | static inline int mac_cb_type(struct sk_buff *skb) | ||
68 | { | ||
69 | return mac_cb(skb)->flags & MAC_CB_FLAG_TYPEMASK; | ||
70 | } | ||
71 | |||
72 | #define IEEE802154_MAC_SCAN_ED 0 | ||
73 | #define IEEE802154_MAC_SCAN_ACTIVE 1 | ||
74 | #define IEEE802154_MAC_SCAN_PASSIVE 2 | ||
75 | #define IEEE802154_MAC_SCAN_ORPHAN 3 | ||
76 | |||
77 | /* | ||
78 | * This should be located at net_device->ml_priv | ||
79 | */ | ||
80 | struct ieee802154_mlme_ops { | ||
81 | int (*assoc_req)(struct net_device *dev, | ||
82 | struct ieee802154_addr *addr, | ||
83 | u8 channel, u8 cap); | ||
84 | int (*assoc_resp)(struct net_device *dev, | ||
85 | struct ieee802154_addr *addr, | ||
86 | u16 short_addr, u8 status); | ||
87 | int (*disassoc_req)(struct net_device *dev, | ||
88 | struct ieee802154_addr *addr, | ||
89 | u8 reason); | ||
90 | int (*start_req)(struct net_device *dev, | ||
91 | struct ieee802154_addr *addr, | ||
92 | u8 channel, u8 bcn_ord, u8 sf_ord, | ||
93 | u8 pan_coord, u8 blx, u8 coord_realign); | ||
94 | int (*scan_req)(struct net_device *dev, | ||
95 | u8 type, u32 channels, u8 duration); | ||
96 | |||
97 | /* | ||
98 | * FIXME: these should become the part of PIB/MIB interface. | ||
99 | * However we still don't have IB interface of any kind | ||
100 | */ | ||
101 | u16 (*get_pan_id)(struct net_device *dev); | ||
102 | u16 (*get_short_addr)(struct net_device *dev); | ||
103 | u8 (*get_dsn)(struct net_device *dev); | ||
104 | u8 (*get_bsn)(struct net_device *dev); | ||
105 | }; | ||
106 | |||
107 | static inline struct ieee802154_mlme_ops *ieee802154_mlme_ops( | ||
108 | struct net_device *dev) | ||
109 | { | ||
110 | return dev->ml_priv; | ||
111 | } | ||
112 | |||
113 | #endif | ||
114 | |||
115 | |||
diff --git a/net/Kconfig b/net/Kconfig index c19f549c8e74..7051b9710675 100644 --- a/net/Kconfig +++ b/net/Kconfig | |||
@@ -179,6 +179,7 @@ source "net/lapb/Kconfig" | |||
179 | source "net/econet/Kconfig" | 179 | source "net/econet/Kconfig" |
180 | source "net/wanrouter/Kconfig" | 180 | source "net/wanrouter/Kconfig" |
181 | source "net/phonet/Kconfig" | 181 | source "net/phonet/Kconfig" |
182 | source "net/ieee802154/Kconfig" | ||
182 | source "net/sched/Kconfig" | 183 | source "net/sched/Kconfig" |
183 | source "net/dcb/Kconfig" | 184 | source "net/dcb/Kconfig" |
184 | 185 | ||
diff --git a/net/Makefile b/net/Makefile index 9e00a55a901b..ba324aefda73 100644 --- a/net/Makefile +++ b/net/Makefile | |||
@@ -60,6 +60,7 @@ obj-$(CONFIG_NET_9P) += 9p/ | |||
60 | ifneq ($(CONFIG_DCB),) | 60 | ifneq ($(CONFIG_DCB),) |
61 | obj-y += dcb/ | 61 | obj-y += dcb/ |
62 | endif | 62 | endif |
63 | obj-y += ieee802154/ | ||
63 | 64 | ||
64 | ifeq ($(CONFIG_NET),y) | 65 | ifeq ($(CONFIG_NET),y) |
65 | obj-$(CONFIG_SYSCTL) += sysctl_net.o | 66 | obj-$(CONFIG_SYSCTL) += sysctl_net.o |
diff --git a/net/ieee802154/Kconfig b/net/ieee802154/Kconfig new file mode 100644 index 000000000000..1c1de97d264a --- /dev/null +++ b/net/ieee802154/Kconfig | |||
@@ -0,0 +1,12 @@ | |||
1 | config IEEE802154 | ||
2 | tristate "IEEE Std 802.15.4 Low-Rate Wireless Personal Area Networks support (EXPERIMENTAL)" | ||
3 | depends on EXPERIMENTAL | ||
4 | ---help--- | ||
5 | IEEE Std 802.15.4 defines a low data rate, low power and low | ||
6 | complexity short range wireless personal area networks. It was | ||
7 | designed to organise networks of sensors, switches, etc automation | ||
8 | devices. Maximum allowed data rate is 250 kb/s and typical personal | ||
9 | operating space around 10m. | ||
10 | |||
11 | Say Y here to compile LR-WPAN support into the kernel or say M to | ||
12 | compile it as modules. | ||
diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile new file mode 100644 index 000000000000..7451c7cc8c8e --- /dev/null +++ b/net/ieee802154/Makefile | |||
@@ -0,0 +1,4 @@ | |||
1 | obj-$(CONFIG_IEEE802154) += af_802154.o | ||
2 | af_802154-y := af_ieee802154.o raw.o dgram.o | ||
3 | |||
4 | ccflags-y += -Wall -DDEBUG | ||
diff --git a/net/ieee802154/af802154.h b/net/ieee802154/af802154.h new file mode 100644 index 000000000000..b1ec52537522 --- /dev/null +++ b/net/ieee802154/af802154.h | |||
@@ -0,0 +1,36 @@ | |||
1 | /* | ||
2 | * Internal interfaces for ieee 802.15.4 address family. | ||
3 | * | ||
4 | * Copyright 2007, 2008, 2009 Siemens AG | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 | ||
8 | * as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License along | ||
16 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
18 | * | ||
19 | * Written by: | ||
20 | * Sergey Lapin <slapin@ossfans.org> | ||
21 | * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> | ||
22 | */ | ||
23 | |||
24 | #ifndef AF802154_H | ||
25 | #define AF802154_H | ||
26 | |||
27 | struct sk_buff; | ||
28 | struct net_devce; | ||
29 | extern struct proto ieee802154_raw_prot; | ||
30 | extern struct proto ieee802154_dgram_prot; | ||
31 | void ieee802154_raw_deliver(struct net_device *dev, struct sk_buff *skb); | ||
32 | int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb); | ||
33 | struct net_device *ieee802154_get_dev(struct net *net, | ||
34 | struct ieee802154_addr *addr); | ||
35 | |||
36 | #endif | ||
diff --git a/net/ieee802154/af_ieee802154.c b/net/ieee802154/af_ieee802154.c new file mode 100644 index 000000000000..882a927cefae --- /dev/null +++ b/net/ieee802154/af_ieee802154.c | |||
@@ -0,0 +1,372 @@ | |||
1 | /* | ||
2 | * IEEE802154.4 socket interface | ||
3 | * | ||
4 | * Copyright 2007, 2008 Siemens AG | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 | ||
8 | * as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License along | ||
16 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
18 | * | ||
19 | * Written by: | ||
20 | * Sergey Lapin <slapin@ossfans.org> | ||
21 | * Maxim Gorbachyov <maxim.gorbachev@siemens.com> | ||
22 | */ | ||
23 | |||
24 | #include <linux/net.h> | ||
25 | #include <linux/capability.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/if_arp.h> | ||
28 | #include <linux/if.h> | ||
29 | #include <linux/termios.h> /* For TIOCOUTQ/INQ */ | ||
30 | #include <linux/list.h> | ||
31 | #include <net/datalink.h> | ||
32 | #include <net/psnap.h> | ||
33 | #include <net/sock.h> | ||
34 | #include <net/tcp_states.h> | ||
35 | #include <net/route.h> | ||
36 | |||
37 | #include <net/ieee802154/af_ieee802154.h> | ||
38 | #include <net/ieee802154/netdevice.h> | ||
39 | |||
40 | #include "af802154.h" | ||
41 | |||
42 | #define DBG_DUMP(data, len) { \ | ||
43 | int i; \ | ||
44 | pr_debug("function: %s: data: len %d:\n", __func__, len); \ | ||
45 | for (i = 0; i < len; i++) {\ | ||
46 | pr_debug("%02x: %02x\n", i, (data)[i]); \ | ||
47 | } \ | ||
48 | } | ||
49 | |||
50 | /* | ||
51 | * Utility function for families | ||
52 | */ | ||
53 | struct net_device *ieee802154_get_dev(struct net *net, | ||
54 | struct ieee802154_addr *addr) | ||
55 | { | ||
56 | struct net_device *dev = NULL; | ||
57 | struct net_device *tmp; | ||
58 | u16 pan_id, short_addr; | ||
59 | |||
60 | switch (addr->addr_type) { | ||
61 | case IEEE802154_ADDR_LONG: | ||
62 | rtnl_lock(); | ||
63 | dev = dev_getbyhwaddr(net, ARPHRD_IEEE802154, addr->hwaddr); | ||
64 | if (dev) | ||
65 | dev_hold(dev); | ||
66 | rtnl_unlock(); | ||
67 | break; | ||
68 | case IEEE802154_ADDR_SHORT: | ||
69 | if (addr->pan_id == 0xffff || | ||
70 | addr->short_addr == IEEE802154_ADDR_UNDEF || | ||
71 | addr->short_addr == 0xffff) | ||
72 | break; | ||
73 | |||
74 | rtnl_lock(); | ||
75 | |||
76 | for_each_netdev(net, tmp) { | ||
77 | if (tmp->type != ARPHRD_IEEE802154) | ||
78 | continue; | ||
79 | |||
80 | pan_id = ieee802154_mlme_ops(tmp)->get_pan_id(tmp); | ||
81 | short_addr = | ||
82 | ieee802154_mlme_ops(tmp)->get_short_addr(tmp); | ||
83 | |||
84 | if (pan_id == addr->pan_id && | ||
85 | short_addr == addr->short_addr) { | ||
86 | dev = tmp; | ||
87 | dev_hold(dev); | ||
88 | break; | ||
89 | } | ||
90 | } | ||
91 | |||
92 | rtnl_unlock(); | ||
93 | break; | ||
94 | default: | ||
95 | pr_warning("Unsupported ieee802154 address type: %d\n", | ||
96 | addr->addr_type); | ||
97 | break; | ||
98 | } | ||
99 | |||
100 | return dev; | ||
101 | } | ||
102 | |||
103 | static int ieee802154_sock_release(struct socket *sock) | ||
104 | { | ||
105 | struct sock *sk = sock->sk; | ||
106 | |||
107 | if (sk) { | ||
108 | sock->sk = NULL; | ||
109 | sk->sk_prot->close(sk, 0); | ||
110 | } | ||
111 | return 0; | ||
112 | } | ||
113 | static int ieee802154_sock_sendmsg(struct kiocb *iocb, struct socket *sock, | ||
114 | struct msghdr *msg, size_t len) | ||
115 | { | ||
116 | struct sock *sk = sock->sk; | ||
117 | |||
118 | return sk->sk_prot->sendmsg(iocb, sk, msg, len); | ||
119 | } | ||
120 | |||
121 | static int ieee802154_sock_bind(struct socket *sock, struct sockaddr *uaddr, | ||
122 | int addr_len) | ||
123 | { | ||
124 | struct sock *sk = sock->sk; | ||
125 | |||
126 | if (sk->sk_prot->bind) | ||
127 | return sk->sk_prot->bind(sk, uaddr, addr_len); | ||
128 | |||
129 | return sock_no_bind(sock, uaddr, addr_len); | ||
130 | } | ||
131 | |||
132 | static int ieee802154_sock_connect(struct socket *sock, struct sockaddr *uaddr, | ||
133 | int addr_len, int flags) | ||
134 | { | ||
135 | struct sock *sk = sock->sk; | ||
136 | |||
137 | if (uaddr->sa_family == AF_UNSPEC) | ||
138 | return sk->sk_prot->disconnect(sk, flags); | ||
139 | |||
140 | return sk->sk_prot->connect(sk, uaddr, addr_len); | ||
141 | } | ||
142 | |||
143 | static int ieee802154_dev_ioctl(struct sock *sk, struct ifreq __user *arg, | ||
144 | unsigned int cmd) | ||
145 | { | ||
146 | struct ifreq ifr; | ||
147 | int ret = -EINVAL; | ||
148 | struct net_device *dev; | ||
149 | |||
150 | if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) | ||
151 | return -EFAULT; | ||
152 | |||
153 | ifr.ifr_name[IFNAMSIZ-1] = 0; | ||
154 | |||
155 | dev_load(sock_net(sk), ifr.ifr_name); | ||
156 | dev = dev_get_by_name(sock_net(sk), ifr.ifr_name); | ||
157 | if (dev->type == ARPHRD_IEEE802154 || | ||
158 | dev->type == ARPHRD_IEEE802154_PHY) | ||
159 | ret = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, cmd); | ||
160 | |||
161 | if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq))) | ||
162 | ret = -EFAULT; | ||
163 | dev_put(dev); | ||
164 | |||
165 | return ret; | ||
166 | } | ||
167 | |||
168 | static int ieee802154_sock_ioctl(struct socket *sock, unsigned int cmd, | ||
169 | unsigned long arg) | ||
170 | { | ||
171 | struct sock *sk = sock->sk; | ||
172 | |||
173 | switch (cmd) { | ||
174 | case SIOCGSTAMP: | ||
175 | return sock_get_timestamp(sk, (struct timeval __user *)arg); | ||
176 | case SIOCGSTAMPNS: | ||
177 | return sock_get_timestampns(sk, (struct timespec __user *)arg); | ||
178 | case SIOCGIFADDR: | ||
179 | case SIOCSIFADDR: | ||
180 | return ieee802154_dev_ioctl(sk, (struct ifreq __user *)arg, | ||
181 | cmd); | ||
182 | default: | ||
183 | if (!sk->sk_prot->ioctl) | ||
184 | return -ENOIOCTLCMD; | ||
185 | return sk->sk_prot->ioctl(sk, cmd, arg); | ||
186 | } | ||
187 | } | ||
188 | |||
189 | static const struct proto_ops ieee802154_raw_ops = { | ||
190 | .family = PF_IEEE802154, | ||
191 | .owner = THIS_MODULE, | ||
192 | .release = ieee802154_sock_release, | ||
193 | .bind = ieee802154_sock_bind, | ||
194 | .connect = ieee802154_sock_connect, | ||
195 | .socketpair = sock_no_socketpair, | ||
196 | .accept = sock_no_accept, | ||
197 | .getname = sock_no_getname, | ||
198 | .poll = datagram_poll, | ||
199 | .ioctl = ieee802154_sock_ioctl, | ||
200 | .listen = sock_no_listen, | ||
201 | .shutdown = sock_no_shutdown, | ||
202 | .setsockopt = sock_common_setsockopt, | ||
203 | .getsockopt = sock_common_getsockopt, | ||
204 | .sendmsg = ieee802154_sock_sendmsg, | ||
205 | .recvmsg = sock_common_recvmsg, | ||
206 | .mmap = sock_no_mmap, | ||
207 | .sendpage = sock_no_sendpage, | ||
208 | #ifdef CONFIG_COMPAT | ||
209 | .compat_setsockopt = compat_sock_common_setsockopt, | ||
210 | .compat_getsockopt = compat_sock_common_getsockopt, | ||
211 | #endif | ||
212 | }; | ||
213 | |||
214 | static const struct proto_ops ieee802154_dgram_ops = { | ||
215 | .family = PF_IEEE802154, | ||
216 | .owner = THIS_MODULE, | ||
217 | .release = ieee802154_sock_release, | ||
218 | .bind = ieee802154_sock_bind, | ||
219 | .connect = ieee802154_sock_connect, | ||
220 | .socketpair = sock_no_socketpair, | ||
221 | .accept = sock_no_accept, | ||
222 | .getname = sock_no_getname, | ||
223 | .poll = datagram_poll, | ||
224 | .ioctl = ieee802154_sock_ioctl, | ||
225 | .listen = sock_no_listen, | ||
226 | .shutdown = sock_no_shutdown, | ||
227 | .setsockopt = sock_common_setsockopt, | ||
228 | .getsockopt = sock_common_getsockopt, | ||
229 | .sendmsg = ieee802154_sock_sendmsg, | ||
230 | .recvmsg = sock_common_recvmsg, | ||
231 | .mmap = sock_no_mmap, | ||
232 | .sendpage = sock_no_sendpage, | ||
233 | #ifdef CONFIG_COMPAT | ||
234 | .compat_setsockopt = compat_sock_common_setsockopt, | ||
235 | .compat_getsockopt = compat_sock_common_getsockopt, | ||
236 | #endif | ||
237 | }; | ||
238 | |||
239 | |||
240 | /* | ||
241 | * Create a socket. Initialise the socket, blank the addresses | ||
242 | * set the state. | ||
243 | */ | ||
244 | static int ieee802154_create(struct net *net, struct socket *sock, | ||
245 | int protocol) | ||
246 | { | ||
247 | struct sock *sk; | ||
248 | int rc; | ||
249 | struct proto *proto; | ||
250 | const struct proto_ops *ops; | ||
251 | |||
252 | if (net != &init_net) | ||
253 | return -EAFNOSUPPORT; | ||
254 | |||
255 | switch (sock->type) { | ||
256 | case SOCK_RAW: | ||
257 | proto = &ieee802154_raw_prot; | ||
258 | ops = &ieee802154_raw_ops; | ||
259 | break; | ||
260 | case SOCK_DGRAM: | ||
261 | proto = &ieee802154_dgram_prot; | ||
262 | ops = &ieee802154_dgram_ops; | ||
263 | break; | ||
264 | default: | ||
265 | rc = -ESOCKTNOSUPPORT; | ||
266 | goto out; | ||
267 | } | ||
268 | |||
269 | rc = -ENOMEM; | ||
270 | sk = sk_alloc(net, PF_IEEE802154, GFP_KERNEL, proto); | ||
271 | if (!sk) | ||
272 | goto out; | ||
273 | rc = 0; | ||
274 | |||
275 | sock->ops = ops; | ||
276 | |||
277 | sock_init_data(sock, sk); | ||
278 | /* FIXME: sk->sk_destruct */ | ||
279 | sk->sk_family = PF_IEEE802154; | ||
280 | |||
281 | /* Checksums on by default */ | ||
282 | sock_set_flag(sk, SOCK_ZAPPED); | ||
283 | |||
284 | if (sk->sk_prot->hash) | ||
285 | sk->sk_prot->hash(sk); | ||
286 | |||
287 | if (sk->sk_prot->init) { | ||
288 | rc = sk->sk_prot->init(sk); | ||
289 | if (rc) | ||
290 | sk_common_release(sk); | ||
291 | } | ||
292 | out: | ||
293 | return rc; | ||
294 | } | ||
295 | |||
296 | static struct net_proto_family ieee802154_family_ops = { | ||
297 | .family = PF_IEEE802154, | ||
298 | .create = ieee802154_create, | ||
299 | .owner = THIS_MODULE, | ||
300 | }; | ||
301 | |||
302 | static int ieee802154_rcv(struct sk_buff *skb, struct net_device *dev, | ||
303 | struct packet_type *pt, struct net_device *orig_dev) | ||
304 | { | ||
305 | DBG_DUMP(skb->data, skb->len); | ||
306 | if (!netif_running(dev)) | ||
307 | return -ENODEV; | ||
308 | pr_debug("got frame, type %d, dev %p\n", dev->type, dev); | ||
309 | |||
310 | if (!net_eq(dev_net(dev), &init_net)) | ||
311 | goto drop; | ||
312 | |||
313 | ieee802154_raw_deliver(dev, skb); | ||
314 | |||
315 | if (dev->type != ARPHRD_IEEE802154) | ||
316 | goto drop; | ||
317 | |||
318 | if (skb->pkt_type != PACKET_OTHERHOST) | ||
319 | return ieee802154_dgram_deliver(dev, skb); | ||
320 | |||
321 | drop: | ||
322 | kfree_skb(skb); | ||
323 | return NET_RX_DROP; | ||
324 | } | ||
325 | |||
326 | |||
327 | static struct packet_type ieee802154_packet_type = { | ||
328 | .type = __constant_htons(ETH_P_IEEE802154), | ||
329 | .func = ieee802154_rcv, | ||
330 | }; | ||
331 | |||
332 | static int __init af_ieee802154_init(void) | ||
333 | { | ||
334 | int rc = -EINVAL; | ||
335 | |||
336 | rc = proto_register(&ieee802154_raw_prot, 1); | ||
337 | if (rc) | ||
338 | goto out; | ||
339 | |||
340 | rc = proto_register(&ieee802154_dgram_prot, 1); | ||
341 | if (rc) | ||
342 | goto err_dgram; | ||
343 | |||
344 | /* Tell SOCKET that we are alive */ | ||
345 | rc = sock_register(&ieee802154_family_ops); | ||
346 | if (rc) | ||
347 | goto err_sock; | ||
348 | dev_add_pack(&ieee802154_packet_type); | ||
349 | |||
350 | rc = 0; | ||
351 | goto out; | ||
352 | |||
353 | err_sock: | ||
354 | proto_unregister(&ieee802154_dgram_prot); | ||
355 | err_dgram: | ||
356 | proto_unregister(&ieee802154_raw_prot); | ||
357 | out: | ||
358 | return rc; | ||
359 | } | ||
360 | static void __exit af_ieee802154_remove(void) | ||
361 | { | ||
362 | dev_remove_pack(&ieee802154_packet_type); | ||
363 | sock_unregister(PF_IEEE802154); | ||
364 | proto_unregister(&ieee802154_dgram_prot); | ||
365 | proto_unregister(&ieee802154_raw_prot); | ||
366 | } | ||
367 | |||
368 | module_init(af_ieee802154_init); | ||
369 | module_exit(af_ieee802154_remove); | ||
370 | |||
371 | MODULE_LICENSE("GPL"); | ||
372 | MODULE_ALIAS_NETPROTO(PF_IEEE802154); | ||
diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c new file mode 100644 index 000000000000..1f5ea11c2fdf --- /dev/null +++ b/net/ieee802154/dgram.c | |||
@@ -0,0 +1,394 @@ | |||
1 | /* | ||
2 | * ZigBee socket interface | ||
3 | * | ||
4 | * Copyright 2007, 2008 Siemens AG | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 | ||
8 | * as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License along | ||
16 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
18 | * | ||
19 | * Written by: | ||
20 | * Sergey Lapin <slapin@ossfans.org> | ||
21 | * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> | ||
22 | */ | ||
23 | |||
24 | #include <linux/net.h> | ||
25 | #include <linux/module.h> | ||
26 | #include <linux/if_arp.h> | ||
27 | #include <linux/list.h> | ||
28 | #include <net/sock.h> | ||
29 | #include <net/ieee802154/af_ieee802154.h> | ||
30 | #include <net/ieee802154/mac_def.h> | ||
31 | #include <net/ieee802154/netdevice.h> | ||
32 | |||
33 | #include <asm/ioctls.h> | ||
34 | |||
35 | #include "af802154.h" | ||
36 | |||
37 | static HLIST_HEAD(dgram_head); | ||
38 | static DEFINE_RWLOCK(dgram_lock); | ||
39 | |||
40 | struct dgram_sock { | ||
41 | struct sock sk; | ||
42 | |||
43 | int bound; | ||
44 | struct ieee802154_addr src_addr; | ||
45 | struct ieee802154_addr dst_addr; | ||
46 | }; | ||
47 | |||
48 | static inline struct dgram_sock *dgram_sk(const struct sock *sk) | ||
49 | { | ||
50 | return container_of(sk, struct dgram_sock, sk); | ||
51 | } | ||
52 | |||
53 | |||
54 | static void dgram_hash(struct sock *sk) | ||
55 | { | ||
56 | write_lock_bh(&dgram_lock); | ||
57 | sk_add_node(sk, &dgram_head); | ||
58 | sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); | ||
59 | write_unlock_bh(&dgram_lock); | ||
60 | } | ||
61 | |||
62 | static void dgram_unhash(struct sock *sk) | ||
63 | { | ||
64 | write_lock_bh(&dgram_lock); | ||
65 | if (sk_del_node_init(sk)) | ||
66 | sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); | ||
67 | write_unlock_bh(&dgram_lock); | ||
68 | } | ||
69 | |||
70 | static int dgram_init(struct sock *sk) | ||
71 | { | ||
72 | struct dgram_sock *ro = dgram_sk(sk); | ||
73 | |||
74 | ro->dst_addr.addr_type = IEEE802154_ADDR_LONG; | ||
75 | ro->dst_addr.pan_id = 0xffff; | ||
76 | memset(&ro->dst_addr.hwaddr, 0xff, sizeof(ro->dst_addr.hwaddr)); | ||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | static void dgram_close(struct sock *sk, long timeout) | ||
81 | { | ||
82 | sk_common_release(sk); | ||
83 | } | ||
84 | |||
85 | static int dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len) | ||
86 | { | ||
87 | struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr; | ||
88 | struct dgram_sock *ro = dgram_sk(sk); | ||
89 | int err = 0; | ||
90 | struct net_device *dev; | ||
91 | |||
92 | ro->bound = 0; | ||
93 | |||
94 | if (len < sizeof(*addr)) | ||
95 | return -EINVAL; | ||
96 | |||
97 | if (addr->family != AF_IEEE802154) | ||
98 | return -EINVAL; | ||
99 | |||
100 | lock_sock(sk); | ||
101 | |||
102 | dev = ieee802154_get_dev(sock_net(sk), &addr->addr); | ||
103 | if (!dev) { | ||
104 | err = -ENODEV; | ||
105 | goto out; | ||
106 | } | ||
107 | |||
108 | if (dev->type != ARPHRD_IEEE802154) { | ||
109 | err = -ENODEV; | ||
110 | goto out_put; | ||
111 | } | ||
112 | |||
113 | memcpy(&ro->src_addr, &addr->addr, sizeof(struct ieee802154_addr)); | ||
114 | |||
115 | ro->bound = 1; | ||
116 | out_put: | ||
117 | dev_put(dev); | ||
118 | out: | ||
119 | release_sock(sk); | ||
120 | |||
121 | return err; | ||
122 | } | ||
123 | |||
124 | static int dgram_ioctl(struct sock *sk, int cmd, unsigned long arg) | ||
125 | { | ||
126 | switch (cmd) { | ||
127 | case SIOCOUTQ: | ||
128 | { | ||
129 | int amount = atomic_read(&sk->sk_wmem_alloc); | ||
130 | return put_user(amount, (int __user *)arg); | ||
131 | } | ||
132 | |||
133 | case SIOCINQ: | ||
134 | { | ||
135 | struct sk_buff *skb; | ||
136 | unsigned long amount; | ||
137 | |||
138 | amount = 0; | ||
139 | spin_lock_bh(&sk->sk_receive_queue.lock); | ||
140 | skb = skb_peek(&sk->sk_receive_queue); | ||
141 | if (skb != NULL) { | ||
142 | /* | ||
143 | * We will only return the amount | ||
144 | * of this packet since that is all | ||
145 | * that will be read. | ||
146 | */ | ||
147 | /* FIXME: parse the header for more correct value */ | ||
148 | amount = skb->len - (3+8+8); | ||
149 | } | ||
150 | spin_unlock_bh(&sk->sk_receive_queue.lock); | ||
151 | return put_user(amount, (int __user *)arg); | ||
152 | } | ||
153 | |||
154 | } | ||
155 | return -ENOIOCTLCMD; | ||
156 | } | ||
157 | |||
158 | /* FIXME: autobind */ | ||
159 | static int dgram_connect(struct sock *sk, struct sockaddr *uaddr, | ||
160 | int len) | ||
161 | { | ||
162 | struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr; | ||
163 | struct dgram_sock *ro = dgram_sk(sk); | ||
164 | int err = 0; | ||
165 | |||
166 | if (len < sizeof(*addr)) | ||
167 | return -EINVAL; | ||
168 | |||
169 | if (addr->family != AF_IEEE802154) | ||
170 | return -EINVAL; | ||
171 | |||
172 | lock_sock(sk); | ||
173 | |||
174 | if (!ro->bound) { | ||
175 | err = -ENETUNREACH; | ||
176 | goto out; | ||
177 | } | ||
178 | |||
179 | memcpy(&ro->dst_addr, &addr->addr, sizeof(struct ieee802154_addr)); | ||
180 | |||
181 | out: | ||
182 | release_sock(sk); | ||
183 | return err; | ||
184 | } | ||
185 | |||
186 | static int dgram_disconnect(struct sock *sk, int flags) | ||
187 | { | ||
188 | struct dgram_sock *ro = dgram_sk(sk); | ||
189 | |||
190 | lock_sock(sk); | ||
191 | |||
192 | ro->dst_addr.addr_type = IEEE802154_ADDR_LONG; | ||
193 | memset(&ro->dst_addr.hwaddr, 0xff, sizeof(ro->dst_addr.hwaddr)); | ||
194 | |||
195 | release_sock(sk); | ||
196 | |||
197 | return 0; | ||
198 | } | ||
199 | |||
200 | static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk, | ||
201 | struct msghdr *msg, size_t size) | ||
202 | { | ||
203 | struct net_device *dev; | ||
204 | unsigned mtu; | ||
205 | struct sk_buff *skb; | ||
206 | struct dgram_sock *ro = dgram_sk(sk); | ||
207 | int err; | ||
208 | |||
209 | if (msg->msg_flags & MSG_OOB) { | ||
210 | pr_debug("msg->msg_flags = 0x%x\n", msg->msg_flags); | ||
211 | return -EOPNOTSUPP; | ||
212 | } | ||
213 | |||
214 | if (!ro->bound) | ||
215 | dev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_IEEE802154); | ||
216 | else | ||
217 | dev = ieee802154_get_dev(sock_net(sk), &ro->src_addr); | ||
218 | |||
219 | if (!dev) { | ||
220 | pr_debug("no dev\n"); | ||
221 | err = -ENXIO; | ||
222 | goto out; | ||
223 | } | ||
224 | mtu = dev->mtu; | ||
225 | pr_debug("name = %s, mtu = %u\n", dev->name, mtu); | ||
226 | |||
227 | skb = sock_alloc_send_skb(sk, LL_ALLOCATED_SPACE(dev) + size, | ||
228 | msg->msg_flags & MSG_DONTWAIT, | ||
229 | &err); | ||
230 | if (!skb) | ||
231 | goto out_dev; | ||
232 | |||
233 | skb_reserve(skb, LL_RESERVED_SPACE(dev)); | ||
234 | |||
235 | skb_reset_network_header(skb); | ||
236 | |||
237 | mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA | MAC_CB_FLAG_ACKREQ; | ||
238 | mac_cb(skb)->seq = ieee802154_mlme_ops(dev)->get_dsn(dev); | ||
239 | err = dev_hard_header(skb, dev, ETH_P_IEEE802154, &ro->dst_addr, | ||
240 | ro->bound ? &ro->src_addr : NULL, size); | ||
241 | if (err < 0) | ||
242 | goto out_skb; | ||
243 | |||
244 | skb_reset_mac_header(skb); | ||
245 | |||
246 | err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); | ||
247 | if (err < 0) | ||
248 | goto out_skb; | ||
249 | |||
250 | if (size > mtu) { | ||
251 | pr_debug("size = %u, mtu = %u\n", size, mtu); | ||
252 | err = -EINVAL; | ||
253 | goto out_skb; | ||
254 | } | ||
255 | |||
256 | skb->dev = dev; | ||
257 | skb->sk = sk; | ||
258 | skb->protocol = htons(ETH_P_IEEE802154); | ||
259 | |||
260 | dev_put(dev); | ||
261 | |||
262 | err = dev_queue_xmit(skb); | ||
263 | if (err > 0) | ||
264 | err = net_xmit_errno(err); | ||
265 | |||
266 | return err ?: size; | ||
267 | |||
268 | out_skb: | ||
269 | kfree_skb(skb); | ||
270 | out_dev: | ||
271 | dev_put(dev); | ||
272 | out: | ||
273 | return err; | ||
274 | } | ||
275 | |||
276 | static int dgram_recvmsg(struct kiocb *iocb, struct sock *sk, | ||
277 | struct msghdr *msg, size_t len, int noblock, int flags, | ||
278 | int *addr_len) | ||
279 | { | ||
280 | size_t copied = 0; | ||
281 | int err = -EOPNOTSUPP; | ||
282 | struct sk_buff *skb; | ||
283 | |||
284 | skb = skb_recv_datagram(sk, flags, noblock, &err); | ||
285 | if (!skb) | ||
286 | goto out; | ||
287 | |||
288 | copied = skb->len; | ||
289 | if (len < copied) { | ||
290 | msg->msg_flags |= MSG_TRUNC; | ||
291 | copied = len; | ||
292 | } | ||
293 | |||
294 | /* FIXME: skip headers if necessary ?! */ | ||
295 | err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); | ||
296 | if (err) | ||
297 | goto done; | ||
298 | |||
299 | sock_recv_timestamp(msg, sk, skb); | ||
300 | |||
301 | if (flags & MSG_TRUNC) | ||
302 | copied = skb->len; | ||
303 | done: | ||
304 | skb_free_datagram(sk, skb); | ||
305 | out: | ||
306 | if (err) | ||
307 | return err; | ||
308 | return copied; | ||
309 | } | ||
310 | |||
311 | static int dgram_rcv_skb(struct sock *sk, struct sk_buff *skb) | ||
312 | { | ||
313 | if (sock_queue_rcv_skb(sk, skb) < 0) { | ||
314 | atomic_inc(&sk->sk_drops); | ||
315 | kfree_skb(skb); | ||
316 | return NET_RX_DROP; | ||
317 | } | ||
318 | |||
319 | return NET_RX_SUCCESS; | ||
320 | } | ||
321 | |||
322 | static inline int ieee802154_match_sock(u8 *hw_addr, u16 pan_id, | ||
323 | u16 short_addr, struct dgram_sock *ro) | ||
324 | { | ||
325 | if (!ro->bound) | ||
326 | return 1; | ||
327 | |||
328 | if (ro->src_addr.addr_type == IEEE802154_ADDR_LONG && | ||
329 | !memcmp(ro->src_addr.hwaddr, hw_addr, IEEE802154_ADDR_LEN)) | ||
330 | return 1; | ||
331 | |||
332 | if (ro->src_addr.addr_type == IEEE802154_ADDR_SHORT && | ||
333 | pan_id == ro->src_addr.pan_id && | ||
334 | short_addr == ro->src_addr.short_addr) | ||
335 | return 1; | ||
336 | |||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb) | ||
341 | { | ||
342 | struct sock *sk, *prev = NULL; | ||
343 | struct hlist_node *node; | ||
344 | int ret = NET_RX_SUCCESS; | ||
345 | u16 pan_id, short_addr; | ||
346 | |||
347 | /* Data frame processing */ | ||
348 | BUG_ON(dev->type != ARPHRD_IEEE802154); | ||
349 | |||
350 | pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); | ||
351 | short_addr = ieee802154_mlme_ops(dev)->get_short_addr(dev); | ||
352 | |||
353 | read_lock(&dgram_lock); | ||
354 | sk_for_each(sk, node, &dgram_head) { | ||
355 | if (ieee802154_match_sock(dev->dev_addr, pan_id, short_addr, | ||
356 | dgram_sk(sk))) { | ||
357 | if (prev) { | ||
358 | struct sk_buff *clone; | ||
359 | clone = skb_clone(skb, GFP_ATOMIC); | ||
360 | if (clone) | ||
361 | dgram_rcv_skb(prev, clone); | ||
362 | } | ||
363 | |||
364 | prev = sk; | ||
365 | } | ||
366 | } | ||
367 | |||
368 | if (prev) | ||
369 | dgram_rcv_skb(prev, skb); | ||
370 | else { | ||
371 | kfree_skb(skb); | ||
372 | ret = NET_RX_DROP; | ||
373 | } | ||
374 | read_unlock(&dgram_lock); | ||
375 | |||
376 | return ret; | ||
377 | } | ||
378 | |||
379 | struct proto ieee802154_dgram_prot = { | ||
380 | .name = "IEEE-802.15.4-MAC", | ||
381 | .owner = THIS_MODULE, | ||
382 | .obj_size = sizeof(struct dgram_sock), | ||
383 | .init = dgram_init, | ||
384 | .close = dgram_close, | ||
385 | .bind = dgram_bind, | ||
386 | .sendmsg = dgram_sendmsg, | ||
387 | .recvmsg = dgram_recvmsg, | ||
388 | .hash = dgram_hash, | ||
389 | .unhash = dgram_unhash, | ||
390 | .connect = dgram_connect, | ||
391 | .disconnect = dgram_disconnect, | ||
392 | .ioctl = dgram_ioctl, | ||
393 | }; | ||
394 | |||
diff --git a/net/ieee802154/raw.c b/net/ieee802154/raw.c new file mode 100644 index 000000000000..8b892e060491 --- /dev/null +++ b/net/ieee802154/raw.c | |||
@@ -0,0 +1,254 @@ | |||
1 | /* | ||
2 | * Raw IEEE 802.15.4 sockets | ||
3 | * | ||
4 | * Copyright 2007, 2008 Siemens AG | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 | ||
8 | * as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License along | ||
16 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
18 | * | ||
19 | * Written by: | ||
20 | * Sergey Lapin <slapin@ossfans.org> | ||
21 | * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> | ||
22 | */ | ||
23 | |||
24 | #include <linux/net.h> | ||
25 | #include <linux/module.h> | ||
26 | #include <linux/if_arp.h> | ||
27 | #include <linux/list.h> | ||
28 | #include <net/sock.h> | ||
29 | #include <net/ieee802154/af_ieee802154.h> | ||
30 | |||
31 | #include "af802154.h" | ||
32 | |||
33 | static HLIST_HEAD(raw_head); | ||
34 | static DEFINE_RWLOCK(raw_lock); | ||
35 | |||
36 | static void raw_hash(struct sock *sk) | ||
37 | { | ||
38 | write_lock_bh(&raw_lock); | ||
39 | sk_add_node(sk, &raw_head); | ||
40 | sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); | ||
41 | write_unlock_bh(&raw_lock); | ||
42 | } | ||
43 | |||
44 | static void raw_unhash(struct sock *sk) | ||
45 | { | ||
46 | write_lock_bh(&raw_lock); | ||
47 | if (sk_del_node_init(sk)) | ||
48 | sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); | ||
49 | write_unlock_bh(&raw_lock); | ||
50 | } | ||
51 | |||
52 | static void raw_close(struct sock *sk, long timeout) | ||
53 | { | ||
54 | sk_common_release(sk); | ||
55 | } | ||
56 | |||
57 | static int raw_bind(struct sock *sk, struct sockaddr *uaddr, int len) | ||
58 | { | ||
59 | struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr; | ||
60 | int err = 0; | ||
61 | struct net_device *dev = NULL; | ||
62 | |||
63 | if (len < sizeof(*addr)) | ||
64 | return -EINVAL; | ||
65 | |||
66 | if (addr->family != AF_IEEE802154) | ||
67 | return -EINVAL; | ||
68 | |||
69 | lock_sock(sk); | ||
70 | |||
71 | dev = ieee802154_get_dev(sock_net(sk), &addr->addr); | ||
72 | if (!dev) { | ||
73 | err = -ENODEV; | ||
74 | goto out; | ||
75 | } | ||
76 | |||
77 | if (dev->type != ARPHRD_IEEE802154_PHY && | ||
78 | dev->type != ARPHRD_IEEE802154) { | ||
79 | err = -ENODEV; | ||
80 | goto out_put; | ||
81 | } | ||
82 | |||
83 | sk->sk_bound_dev_if = dev->ifindex; | ||
84 | sk_dst_reset(sk); | ||
85 | |||
86 | out_put: | ||
87 | dev_put(dev); | ||
88 | out: | ||
89 | release_sock(sk); | ||
90 | |||
91 | return err; | ||
92 | } | ||
93 | |||
94 | static int raw_connect(struct sock *sk, struct sockaddr *uaddr, | ||
95 | int addr_len) | ||
96 | { | ||
97 | return -ENOTSUPP; | ||
98 | } | ||
99 | |||
100 | static int raw_disconnect(struct sock *sk, int flags) | ||
101 | { | ||
102 | return 0; | ||
103 | } | ||
104 | |||
105 | static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | ||
106 | size_t size) | ||
107 | { | ||
108 | struct net_device *dev; | ||
109 | unsigned mtu; | ||
110 | struct sk_buff *skb; | ||
111 | int err; | ||
112 | |||
113 | if (msg->msg_flags & MSG_OOB) { | ||
114 | pr_debug("msg->msg_flags = 0x%x\n", msg->msg_flags); | ||
115 | return -EOPNOTSUPP; | ||
116 | } | ||
117 | |||
118 | lock_sock(sk); | ||
119 | if (!sk->sk_bound_dev_if) | ||
120 | dev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_IEEE802154); | ||
121 | else | ||
122 | dev = dev_get_by_index(sock_net(sk), sk->sk_bound_dev_if); | ||
123 | release_sock(sk); | ||
124 | |||
125 | if (!dev) { | ||
126 | pr_debug("no dev\n"); | ||
127 | err = -ENXIO; | ||
128 | goto out; | ||
129 | } | ||
130 | |||
131 | mtu = dev->mtu; | ||
132 | pr_debug("name = %s, mtu = %u\n", dev->name, mtu); | ||
133 | |||
134 | if (size > mtu) { | ||
135 | pr_debug("size = %u, mtu = %u\n", size, mtu); | ||
136 | err = -EINVAL; | ||
137 | goto out_dev; | ||
138 | } | ||
139 | |||
140 | skb = sock_alloc_send_skb(sk, LL_ALLOCATED_SPACE(dev) + size, | ||
141 | msg->msg_flags & MSG_DONTWAIT, &err); | ||
142 | if (!skb) | ||
143 | goto out_dev; | ||
144 | |||
145 | skb_reserve(skb, LL_RESERVED_SPACE(dev)); | ||
146 | |||
147 | skb_reset_mac_header(skb); | ||
148 | skb_reset_network_header(skb); | ||
149 | |||
150 | err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); | ||
151 | if (err < 0) | ||
152 | goto out_skb; | ||
153 | |||
154 | skb->dev = dev; | ||
155 | skb->sk = sk; | ||
156 | skb->protocol = htons(ETH_P_IEEE802154); | ||
157 | |||
158 | dev_put(dev); | ||
159 | |||
160 | err = dev_queue_xmit(skb); | ||
161 | if (err > 0) | ||
162 | err = net_xmit_errno(err); | ||
163 | |||
164 | return err ?: size; | ||
165 | |||
166 | out_skb: | ||
167 | kfree_skb(skb); | ||
168 | out_dev: | ||
169 | dev_put(dev); | ||
170 | out: | ||
171 | return err; | ||
172 | } | ||
173 | |||
174 | static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | ||
175 | size_t len, int noblock, int flags, int *addr_len) | ||
176 | { | ||
177 | size_t copied = 0; | ||
178 | int err = -EOPNOTSUPP; | ||
179 | struct sk_buff *skb; | ||
180 | |||
181 | skb = skb_recv_datagram(sk, flags, noblock, &err); | ||
182 | if (!skb) | ||
183 | goto out; | ||
184 | |||
185 | copied = skb->len; | ||
186 | if (len < copied) { | ||
187 | msg->msg_flags |= MSG_TRUNC; | ||
188 | copied = len; | ||
189 | } | ||
190 | |||
191 | err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); | ||
192 | if (err) | ||
193 | goto done; | ||
194 | |||
195 | sock_recv_timestamp(msg, sk, skb); | ||
196 | |||
197 | if (flags & MSG_TRUNC) | ||
198 | copied = skb->len; | ||
199 | done: | ||
200 | skb_free_datagram(sk, skb); | ||
201 | out: | ||
202 | if (err) | ||
203 | return err; | ||
204 | return copied; | ||
205 | } | ||
206 | |||
207 | static int raw_rcv_skb(struct sock *sk, struct sk_buff *skb) | ||
208 | { | ||
209 | if (sock_queue_rcv_skb(sk, skb) < 0) { | ||
210 | atomic_inc(&sk->sk_drops); | ||
211 | kfree_skb(skb); | ||
212 | return NET_RX_DROP; | ||
213 | } | ||
214 | |||
215 | return NET_RX_SUCCESS; | ||
216 | } | ||
217 | |||
218 | |||
219 | void ieee802154_raw_deliver(struct net_device *dev, struct sk_buff *skb) | ||
220 | { | ||
221 | struct sock *sk; | ||
222 | struct hlist_node *node; | ||
223 | |||
224 | read_lock(&raw_lock); | ||
225 | sk_for_each(sk, node, &raw_head) { | ||
226 | bh_lock_sock(sk); | ||
227 | if (!sk->sk_bound_dev_if || | ||
228 | sk->sk_bound_dev_if == dev->ifindex) { | ||
229 | |||
230 | struct sk_buff *clone; | ||
231 | |||
232 | clone = skb_clone(skb, GFP_ATOMIC); | ||
233 | if (clone) | ||
234 | raw_rcv_skb(sk, clone); | ||
235 | } | ||
236 | bh_unlock_sock(sk); | ||
237 | } | ||
238 | read_unlock(&raw_lock); | ||
239 | } | ||
240 | |||
241 | struct proto ieee802154_raw_prot = { | ||
242 | .name = "IEEE-802.15.4-RAW", | ||
243 | .owner = THIS_MODULE, | ||
244 | .obj_size = sizeof(struct sock), | ||
245 | .close = raw_close, | ||
246 | .bind = raw_bind, | ||
247 | .sendmsg = raw_sendmsg, | ||
248 | .recvmsg = raw_recvmsg, | ||
249 | .hash = raw_hash, | ||
250 | .unhash = raw_unhash, | ||
251 | .connect = raw_connect, | ||
252 | .disconnect = raw_disconnect, | ||
253 | }; | ||
254 | |||