diff options
Diffstat (limited to 'drivers/ieee802154/fakehard.c')
-rw-r--r-- | drivers/ieee802154/fakehard.c | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/drivers/ieee802154/fakehard.c b/drivers/ieee802154/fakehard.c new file mode 100644 index 000000000000..0384144c0b34 --- /dev/null +++ b/drivers/ieee802154/fakehard.c | |||
@@ -0,0 +1,270 @@ | |||
1 | /* | ||
2 | * Sample driver for HardMAC IEEE 802.15.4 devices | ||
3 | * | ||
4 | * Copyright (C) 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 | * Dmitry Eremin-Solenikov <dmitry.baryshkov@siemens.com> | ||
21 | */ | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | #include <linux/netdevice.h> | ||
26 | #include <linux/skbuff.h> | ||
27 | #include <linux/if_arp.h> | ||
28 | |||
29 | #include <net/ieee802154/af_ieee802154.h> | ||
30 | #include <net/ieee802154/netdevice.h> | ||
31 | #include <net/ieee802154/mac_def.h> | ||
32 | #include <net/ieee802154/nl802154.h> | ||
33 | |||
34 | static u16 fake_get_pan_id(struct net_device *dev) | ||
35 | { | ||
36 | BUG_ON(dev->type != ARPHRD_IEEE802154); | ||
37 | |||
38 | return 0xeba1; | ||
39 | } | ||
40 | |||
41 | static u16 fake_get_short_addr(struct net_device *dev) | ||
42 | { | ||
43 | BUG_ON(dev->type != ARPHRD_IEEE802154); | ||
44 | |||
45 | return 0x1; | ||
46 | } | ||
47 | |||
48 | static u8 fake_get_dsn(struct net_device *dev) | ||
49 | { | ||
50 | BUG_ON(dev->type != ARPHRD_IEEE802154); | ||
51 | |||
52 | return 0x00; /* DSN are implemented in HW, so return just 0 */ | ||
53 | } | ||
54 | |||
55 | static u8 fake_get_bsn(struct net_device *dev) | ||
56 | { | ||
57 | BUG_ON(dev->type != ARPHRD_IEEE802154); | ||
58 | |||
59 | return 0x00; /* BSN are implemented in HW, so return just 0 */ | ||
60 | } | ||
61 | |||
62 | static int fake_assoc_req(struct net_device *dev, | ||
63 | struct ieee802154_addr *addr, u8 channel, u8 cap) | ||
64 | { | ||
65 | /* We simply emulate it here */ | ||
66 | return ieee802154_nl_assoc_confirm(dev, fake_get_short_addr(dev), | ||
67 | IEEE802154_SUCCESS); | ||
68 | } | ||
69 | |||
70 | static int fake_assoc_resp(struct net_device *dev, | ||
71 | struct ieee802154_addr *addr, u16 short_addr, u8 status) | ||
72 | { | ||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | static int fake_disassoc_req(struct net_device *dev, | ||
77 | struct ieee802154_addr *addr, u8 reason) | ||
78 | { | ||
79 | return ieee802154_nl_disassoc_confirm(dev, IEEE802154_SUCCESS); | ||
80 | } | ||
81 | |||
82 | static int fake_start_req(struct net_device *dev, struct ieee802154_addr *addr, | ||
83 | u8 channel, | ||
84 | u8 bcn_ord, u8 sf_ord, u8 pan_coord, u8 blx, | ||
85 | u8 coord_realign) | ||
86 | { | ||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | static int fake_scan_req(struct net_device *dev, u8 type, u32 channels, | ||
91 | u8 duration) | ||
92 | { | ||
93 | u8 edl[27] = {}; | ||
94 | return ieee802154_nl_scan_confirm(dev, IEEE802154_SUCCESS, type, | ||
95 | channels, | ||
96 | type == IEEE802154_MAC_SCAN_ED ? edl : NULL); | ||
97 | } | ||
98 | |||
99 | static struct ieee802154_mlme_ops fake_mlme = { | ||
100 | .assoc_req = fake_assoc_req, | ||
101 | .assoc_resp = fake_assoc_resp, | ||
102 | .disassoc_req = fake_disassoc_req, | ||
103 | .start_req = fake_start_req, | ||
104 | .scan_req = fake_scan_req, | ||
105 | |||
106 | .get_pan_id = fake_get_pan_id, | ||
107 | .get_short_addr = fake_get_short_addr, | ||
108 | .get_dsn = fake_get_dsn, | ||
109 | .get_bsn = fake_get_bsn, | ||
110 | }; | ||
111 | |||
112 | static int ieee802154_fake_open(struct net_device *dev) | ||
113 | { | ||
114 | netif_start_queue(dev); | ||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | static int ieee802154_fake_close(struct net_device *dev) | ||
119 | { | ||
120 | netif_stop_queue(dev); | ||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | static int ieee802154_fake_xmit(struct sk_buff *skb, struct net_device *dev) | ||
125 | { | ||
126 | skb->iif = dev->ifindex; | ||
127 | skb->dev = dev; | ||
128 | dev->stats.tx_packets++; | ||
129 | dev->stats.tx_bytes += skb->len; | ||
130 | |||
131 | dev->trans_start = jiffies; | ||
132 | |||
133 | /* FIXME: do hardware work here ... */ | ||
134 | |||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | |||
139 | static int ieee802154_fake_ioctl(struct net_device *dev, struct ifreq *ifr, | ||
140 | int cmd) | ||
141 | { | ||
142 | struct sockaddr_ieee802154 *sa = | ||
143 | (struct sockaddr_ieee802154 *)&ifr->ifr_addr; | ||
144 | u16 pan_id, short_addr; | ||
145 | |||
146 | switch (cmd) { | ||
147 | case SIOCGIFADDR: | ||
148 | /* FIXME: fixed here, get from device IRL */ | ||
149 | pan_id = fake_get_pan_id(dev); | ||
150 | short_addr = fake_get_short_addr(dev); | ||
151 | if (pan_id == IEEE802154_PANID_BROADCAST || | ||
152 | short_addr == IEEE802154_ADDR_BROADCAST) | ||
153 | return -EADDRNOTAVAIL; | ||
154 | |||
155 | sa->family = AF_IEEE802154; | ||
156 | sa->addr.addr_type = IEEE802154_ADDR_SHORT; | ||
157 | sa->addr.pan_id = pan_id; | ||
158 | sa->addr.short_addr = short_addr; | ||
159 | return 0; | ||
160 | } | ||
161 | return -ENOIOCTLCMD; | ||
162 | } | ||
163 | |||
164 | static int ieee802154_fake_mac_addr(struct net_device *dev, void *p) | ||
165 | { | ||
166 | return -EBUSY; /* HW address is built into the device */ | ||
167 | } | ||
168 | |||
169 | static const struct net_device_ops fake_ops = { | ||
170 | .ndo_open = ieee802154_fake_open, | ||
171 | .ndo_stop = ieee802154_fake_close, | ||
172 | .ndo_start_xmit = ieee802154_fake_xmit, | ||
173 | .ndo_do_ioctl = ieee802154_fake_ioctl, | ||
174 | .ndo_set_mac_address = ieee802154_fake_mac_addr, | ||
175 | }; | ||
176 | |||
177 | |||
178 | static void ieee802154_fake_setup(struct net_device *dev) | ||
179 | { | ||
180 | dev->addr_len = IEEE802154_ADDR_LEN; | ||
181 | memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN); | ||
182 | dev->features = NETIF_F_NO_CSUM; | ||
183 | dev->needed_tailroom = 2; /* FCS */ | ||
184 | dev->mtu = 127; | ||
185 | dev->tx_queue_len = 10; | ||
186 | dev->type = ARPHRD_IEEE802154; | ||
187 | dev->flags = IFF_NOARP | IFF_BROADCAST; | ||
188 | dev->watchdog_timeo = 0; | ||
189 | } | ||
190 | |||
191 | |||
192 | static int __devinit ieee802154fake_probe(struct platform_device *pdev) | ||
193 | { | ||
194 | struct net_device *dev = | ||
195 | alloc_netdev(0, "hardwpan%d", ieee802154_fake_setup); | ||
196 | int err; | ||
197 | |||
198 | if (!dev) | ||
199 | return -ENOMEM; | ||
200 | |||
201 | memcpy(dev->dev_addr, "\xba\xbe\xca\xfe\xde\xad\xbe\xef", | ||
202 | dev->addr_len); | ||
203 | memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); | ||
204 | |||
205 | dev->netdev_ops = &fake_ops; | ||
206 | dev->ml_priv = &fake_mlme; | ||
207 | |||
208 | /* | ||
209 | * If the name is a format string the caller wants us to do a | ||
210 | * name allocation. | ||
211 | */ | ||
212 | if (strchr(dev->name, '%')) { | ||
213 | err = dev_alloc_name(dev, dev->name); | ||
214 | if (err < 0) | ||
215 | goto out; | ||
216 | } | ||
217 | |||
218 | SET_NETDEV_DEV(dev, &pdev->dev); | ||
219 | |||
220 | platform_set_drvdata(pdev, dev); | ||
221 | |||
222 | err = register_netdev(dev); | ||
223 | if (err < 0) | ||
224 | goto out; | ||
225 | |||
226 | |||
227 | dev_info(&pdev->dev, "Added ieee802154 HardMAC hardware\n"); | ||
228 | return 0; | ||
229 | |||
230 | out: | ||
231 | unregister_netdev(dev); | ||
232 | return err; | ||
233 | } | ||
234 | |||
235 | static int __devexit ieee802154fake_remove(struct platform_device *pdev) | ||
236 | { | ||
237 | struct net_device *dev = platform_get_drvdata(pdev); | ||
238 | unregister_netdev(dev); | ||
239 | free_netdev(dev); | ||
240 | return 0; | ||
241 | } | ||
242 | |||
243 | static struct platform_device *ieee802154fake_dev; | ||
244 | |||
245 | static struct platform_driver ieee802154fake_driver = { | ||
246 | .probe = ieee802154fake_probe, | ||
247 | .remove = __devexit_p(ieee802154fake_remove), | ||
248 | .driver = { | ||
249 | .name = "ieee802154hardmac", | ||
250 | .owner = THIS_MODULE, | ||
251 | }, | ||
252 | }; | ||
253 | |||
254 | static __init int fake_init(void) | ||
255 | { | ||
256 | ieee802154fake_dev = platform_device_register_simple( | ||
257 | "ieee802154hardmac", -1, NULL, 0); | ||
258 | return platform_driver_register(&ieee802154fake_driver); | ||
259 | } | ||
260 | |||
261 | static __exit void fake_exit(void) | ||
262 | { | ||
263 | platform_driver_unregister(&ieee802154fake_driver); | ||
264 | platform_device_unregister(ieee802154fake_dev); | ||
265 | } | ||
266 | |||
267 | module_init(fake_init); | ||
268 | module_exit(fake_exit); | ||
269 | MODULE_LICENSE("GPL"); | ||
270 | |||