Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /********************************************************************* |
6819bc2e | 2 | * |
1da177e4 LT |
3 | * Filename: irlan_common.c |
4 | * Version: 0.9 | |
5 | * Description: IrDA LAN Access Protocol Implementation | |
6 | * Status: Experimental. | |
7 | * Author: Dag Brattli <dagb@cs.uit.no> | |
8 | * Created at: Sun Aug 31 20:14:37 1997 | |
9 | * Modified at: Sun Dec 26 21:53:10 1999 | |
10 | * Modified by: Dag Brattli <dagb@cs.uit.no> | |
6819bc2e YH |
11 | * |
12 | * Copyright (c) 1997, 1999 Dag Brattli <dagb@cs.uit.no>, | |
1da177e4 | 13 | * All Rights Reserved. |
6819bc2e YH |
14 | * |
15 | * This program is free software; you can redistribute it and/or | |
16 | * modify it under the terms of the GNU General Public License as | |
17 | * published by the Free Software Foundation; either version 2 of | |
1da177e4 LT |
18 | * the License, or (at your option) any later version. |
19 | * | |
96de0e25 | 20 | * Neither Dag Brattli nor University of Tromsø admit liability nor |
6819bc2e | 21 | * provide warranty for any of this software. This material is |
1da177e4 LT |
22 | * provided "AS-IS" and at no charge. |
23 | * | |
24 | ********************************************************************/ | |
25 | ||
1da177e4 LT |
26 | #include <linux/module.h> |
27 | ||
28 | #include <linux/kernel.h> | |
29 | #include <linux/string.h> | |
5a0e3ad6 | 30 | #include <linux/gfp.h> |
1da177e4 LT |
31 | #include <linux/init.h> |
32 | #include <linux/errno.h> | |
33 | #include <linux/proc_fs.h> | |
d43c36dc | 34 | #include <linux/sched.h> |
1da177e4 LT |
35 | #include <linux/seq_file.h> |
36 | #include <linux/random.h> | |
37 | #include <linux/netdevice.h> | |
38 | #include <linux/etherdevice.h> | |
39 | #include <linux/rtnetlink.h> | |
40 | #include <linux/moduleparam.h> | |
41 | #include <linux/bitops.h> | |
42 | ||
1da177e4 LT |
43 | #include <asm/byteorder.h> |
44 | ||
45 | #include <net/irda/irda.h> | |
46 | #include <net/irda/irttp.h> | |
47 | #include <net/irda/irlmp.h> | |
48 | #include <net/irda/iriap.h> | |
49 | #include <net/irda/timer.h> | |
50 | ||
51 | #include <net/irda/irlan_common.h> | |
52 | #include <net/irda/irlan_client.h> | |
6819bc2e | 53 | #include <net/irda/irlan_provider.h> |
1da177e4 LT |
54 | #include <net/irda/irlan_eth.h> |
55 | #include <net/irda/irlan_filter.h> | |
56 | ||
57 | ||
1da177e4 LT |
58 | /* extern char sysctl_devname[]; */ |
59 | ||
60 | /* | |
61 | * Master structure | |
62 | */ | |
63 | static LIST_HEAD(irlans); | |
64 | ||
65 | static void *ckey; | |
66 | static void *skey; | |
67 | ||
68 | /* Module parameters */ | |
eb939922 | 69 | static bool eth; /* Use "eth" or "irlan" name for devices */ |
1da177e4 LT |
70 | static int access = ACCESS_PEER; /* PEER, DIRECT or HOSTED */ |
71 | ||
72 | #ifdef CONFIG_PROC_FS | |
36cbd3dc | 73 | static const char *const irlan_access[] = { |
1da177e4 LT |
74 | "UNKNOWN", |
75 | "DIRECT", | |
76 | "PEER", | |
77 | "HOSTED" | |
78 | }; | |
79 | ||
36cbd3dc | 80 | static const char *const irlan_media[] = { |
1da177e4 LT |
81 | "UNKNOWN", |
82 | "802.3", | |
83 | "802.5" | |
84 | }; | |
85 | ||
86 | extern struct proc_dir_entry *proc_irda; | |
87 | ||
88 | static int irlan_seq_open(struct inode *inode, struct file *file); | |
89 | ||
da7071d7 | 90 | static const struct file_operations irlan_fops = { |
1da177e4 LT |
91 | .owner = THIS_MODULE, |
92 | .open = irlan_seq_open, | |
93 | .read = seq_read, | |
94 | .llseek = seq_lseek, | |
95 | .release = seq_release, | |
96 | }; | |
97 | ||
98 | extern struct proc_dir_entry *proc_irda; | |
99 | #endif /* CONFIG_PROC_FS */ | |
100 | ||
101 | static struct irlan_cb *irlan_open(__u32 saddr, __u32 daddr); | |
102 | static void __irlan_close(struct irlan_cb *self); | |
6819bc2e YH |
103 | static int __irlan_insert_param(struct sk_buff *skb, char *param, int type, |
104 | __u8 value_byte, __u16 value_short, | |
1da177e4 LT |
105 | __u8 *value_array, __u16 value_len); |
106 | static void irlan_open_unicast_addr(struct irlan_cb *self); | |
107 | static void irlan_get_unicast_addr(struct irlan_cb *self); | |
108 | void irlan_close_tsaps(struct irlan_cb *self); | |
109 | ||
110 | /* | |
111 | * Function irlan_init (void) | |
112 | * | |
113 | * Initialize IrLAN layer | |
114 | * | |
115 | */ | |
116 | static int __init irlan_init(void) | |
117 | { | |
118 | struct irlan_cb *new; | |
119 | __u16 hints; | |
120 | ||
0dc47877 | 121 | IRDA_DEBUG(2, "%s()\n", __func__ ); |
1da177e4 LT |
122 | |
123 | #ifdef CONFIG_PROC_FS | |
124 | { struct proc_dir_entry *proc; | |
5e47879f | 125 | proc = proc_create("irlan", 0, proc_irda, &irlan_fops); |
1da177e4 LT |
126 | if (!proc) { |
127 | printk(KERN_ERR "irlan_init: can't create /proc entry!\n"); | |
128 | return -ENODEV; | |
129 | } | |
1da177e4 LT |
130 | } |
131 | #endif /* CONFIG_PROC_FS */ | |
132 | ||
0dc47877 | 133 | IRDA_DEBUG(4, "%s()\n", __func__ ); |
1da177e4 LT |
134 | hints = irlmp_service_to_hint(S_LAN); |
135 | ||
136 | /* Register with IrLMP as a client */ | |
137 | ckey = irlmp_register_client(hints, &irlan_client_discovery_indication, | |
138 | NULL, NULL); | |
719647e2 AM |
139 | if (!ckey) |
140 | goto err_ckey; | |
141 | ||
1da177e4 | 142 | /* Register with IrLMP as a service */ |
719647e2 AM |
143 | skey = irlmp_register_service(hints); |
144 | if (!skey) | |
145 | goto err_skey; | |
1da177e4 LT |
146 | |
147 | /* Start the master IrLAN instance (the only one for now) */ | |
719647e2 AM |
148 | new = irlan_open(DEV_ADDR_ANY, DEV_ADDR_ANY); |
149 | if (!new) | |
150 | goto err_open; | |
1da177e4 LT |
151 | |
152 | /* The master will only open its (listen) control TSAP */ | |
153 | irlan_provider_open_ctrl_tsap(new); | |
154 | ||
155 | /* Do some fast discovery! */ | |
156 | irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS); | |
157 | ||
158 | return 0; | |
719647e2 AM |
159 | |
160 | err_open: | |
161 | irlmp_unregister_service(skey); | |
162 | err_skey: | |
163 | irlmp_unregister_client(ckey); | |
164 | err_ckey: | |
165 | #ifdef CONFIG_PROC_FS | |
166 | remove_proc_entry("irlan", proc_irda); | |
167 | #endif /* CONFIG_PROC_FS */ | |
168 | ||
169 | return -ENOMEM; | |
1da177e4 LT |
170 | } |
171 | ||
6819bc2e | 172 | static void __exit irlan_cleanup(void) |
1da177e4 LT |
173 | { |
174 | struct irlan_cb *self, *next; | |
175 | ||
0dc47877 | 176 | IRDA_DEBUG(4, "%s()\n", __func__ ); |
1da177e4 LT |
177 | |
178 | irlmp_unregister_client(ckey); | |
179 | irlmp_unregister_service(skey); | |
180 | ||
181 | #ifdef CONFIG_PROC_FS | |
182 | remove_proc_entry("irlan", proc_irda); | |
183 | #endif /* CONFIG_PROC_FS */ | |
184 | ||
185 | /* Cleanup any leftover network devices */ | |
186 | rtnl_lock(); | |
187 | list_for_each_entry_safe(self, next, &irlans, dev_list) { | |
188 | __irlan_close(self); | |
189 | } | |
190 | rtnl_unlock(); | |
191 | } | |
192 | ||
193 | /* | |
194 | * Function irlan_open (void) | |
195 | * | |
6819bc2e | 196 | * Open new instance of a client/provider, we should only register the |
1da177e4 LT |
197 | * network device if this instance is ment for a particular client/provider |
198 | */ | |
199 | static struct irlan_cb *irlan_open(__u32 saddr, __u32 daddr) | |
200 | { | |
201 | struct net_device *dev; | |
202 | struct irlan_cb *self; | |
203 | ||
0dc47877 | 204 | IRDA_DEBUG(2, "%s()\n", __func__ ); |
1da177e4 LT |
205 | |
206 | /* Create network device with irlan */ | |
207 | dev = alloc_irlandev(eth ? "eth%d" : "irlan%d"); | |
208 | if (!dev) | |
209 | return NULL; | |
210 | ||
524ad0a7 | 211 | self = netdev_priv(dev); |
1da177e4 LT |
212 | self->dev = dev; |
213 | ||
214 | /* | |
215 | * Initialize local device structure | |
216 | */ | |
217 | self->magic = IRLAN_MAGIC; | |
218 | self->saddr = saddr; | |
219 | self->daddr = daddr; | |
220 | ||
221 | /* Provider access can only be PEER, DIRECT, or HOSTED */ | |
222 | self->provider.access_type = access; | |
223 | if (access == ACCESS_DIRECT) { | |
6819bc2e | 224 | /* |
1da177e4 | 225 | * Since we are emulating an IrLAN sever we will have to |
6819bc2e | 226 | * give ourself an ethernet address! |
1da177e4 LT |
227 | */ |
228 | dev->dev_addr[0] = 0x40; | |
229 | dev->dev_addr[1] = 0x00; | |
230 | dev->dev_addr[2] = 0x00; | |
231 | dev->dev_addr[3] = 0x00; | |
232 | get_random_bytes(dev->dev_addr+4, 1); | |
233 | get_random_bytes(dev->dev_addr+5, 1); | |
234 | } | |
235 | ||
236 | self->media = MEDIA_802_3; | |
237 | self->disconnect_reason = LM_USER_REQUEST; | |
238 | init_timer(&self->watchdog_timer); | |
239 | init_timer(&self->client.kick_timer); | |
6819bc2e YH |
240 | init_waitqueue_head(&self->open_wait); |
241 | ||
1da177e4 | 242 | skb_queue_head_init(&self->client.txq); |
6819bc2e | 243 | |
1da177e4 LT |
244 | irlan_next_client_state(self, IRLAN_IDLE); |
245 | irlan_next_provider_state(self, IRLAN_IDLE); | |
246 | ||
247 | if (register_netdev(dev)) { | |
6819bc2e | 248 | IRDA_DEBUG(2, "%s(), register_netdev() failed!\n", |
0dc47877 | 249 | __func__ ); |
1da177e4 LT |
250 | self = NULL; |
251 | free_netdev(dev); | |
252 | } else { | |
253 | rtnl_lock(); | |
254 | list_add_rcu(&self->dev_list, &irlans); | |
255 | rtnl_unlock(); | |
256 | } | |
257 | ||
258 | return self; | |
259 | } | |
260 | /* | |
261 | * Function __irlan_close (self) | |
262 | * | |
6819bc2e | 263 | * This function closes and deallocates the IrLAN client instances. Be |
1da177e4 LT |
264 | * aware that other functions which calls client_close() must |
265 | * remove self from irlans list first. | |
266 | */ | |
267 | static void __irlan_close(struct irlan_cb *self) | |
268 | { | |
0dc47877 | 269 | IRDA_DEBUG(2, "%s()\n", __func__ ); |
6819bc2e | 270 | |
1da177e4 LT |
271 | ASSERT_RTNL(); |
272 | IRDA_ASSERT(self != NULL, return;); | |
273 | IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); | |
274 | ||
275 | del_timer_sync(&self->watchdog_timer); | |
276 | del_timer_sync(&self->client.kick_timer); | |
277 | ||
278 | /* Close all open connections and remove TSAPs */ | |
279 | irlan_close_tsaps(self); | |
6819bc2e YH |
280 | |
281 | if (self->client.iriap) | |
1da177e4 LT |
282 | iriap_close(self->client.iriap); |
283 | ||
284 | /* Remove frames queued on the control channel */ | |
285 | skb_queue_purge(&self->client.txq); | |
286 | ||
287 | /* Unregister and free self via destructor */ | |
288 | unregister_netdevice(self->dev); | |
289 | } | |
290 | ||
291 | /* Find any instance of irlan, used for client discovery wakeup */ | |
292 | struct irlan_cb *irlan_get_any(void) | |
293 | { | |
294 | struct irlan_cb *self; | |
295 | ||
296 | list_for_each_entry_rcu(self, &irlans, dev_list) { | |
297 | return self; | |
298 | } | |
299 | return NULL; | |
300 | } | |
301 | ||
302 | /* | |
303 | * Function irlan_connect_indication (instance, sap, qos, max_sdu_size, skb) | |
304 | * | |
305 | * Here we receive the connect indication for the data channel | |
306 | * | |
307 | */ | |
308 | static void irlan_connect_indication(void *instance, void *sap, | |
309 | struct qos_info *qos, | |
310 | __u32 max_sdu_size, | |
6819bc2e | 311 | __u8 max_header_size, |
1da177e4 LT |
312 | struct sk_buff *skb) |
313 | { | |
314 | struct irlan_cb *self; | |
315 | struct tsap_cb *tsap; | |
316 | ||
0dc47877 | 317 | IRDA_DEBUG(2, "%s()\n", __func__ ); |
6819bc2e | 318 | |
ea110733 JP |
319 | self = instance; |
320 | tsap = sap; | |
6819bc2e | 321 | |
1da177e4 LT |
322 | IRDA_ASSERT(self != NULL, return;); |
323 | IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); | |
324 | IRDA_ASSERT(tsap == self->tsap_data,return;); | |
325 | ||
326 | self->max_sdu_size = max_sdu_size; | |
327 | self->max_header_size = max_header_size; | |
328 | ||
0dc47877 | 329 | IRDA_DEBUG(0, "%s: We are now connected!\n", __func__); |
1da177e4 LT |
330 | |
331 | del_timer(&self->watchdog_timer); | |
332 | ||
333 | /* If you want to pass the skb to *both* state machines, you will | |
334 | * need to skb_clone() it, so that you don't free it twice. | |
335 | * As the state machines don't need it, git rid of it here... | |
336 | * Jean II */ | |
337 | if (skb) | |
338 | dev_kfree_skb(skb); | |
339 | ||
340 | irlan_do_provider_event(self, IRLAN_DATA_CONNECT_INDICATION, NULL); | |
341 | irlan_do_client_event(self, IRLAN_DATA_CONNECT_INDICATION, NULL); | |
342 | ||
343 | if (self->provider.access_type == ACCESS_PEER) { | |
6819bc2e | 344 | /* |
1da177e4 | 345 | * Data channel is open, so we are now allowed to |
6819bc2e | 346 | * configure the remote filter |
1da177e4 LT |
347 | */ |
348 | irlan_get_unicast_addr(self); | |
349 | irlan_open_unicast_addr(self); | |
350 | } | |
351 | /* Ready to transfer Ethernet frames (at last) */ | |
352 | netif_start_queue(self->dev); /* Clear reason */ | |
353 | } | |
354 | ||
355 | static void irlan_connect_confirm(void *instance, void *sap, | |
6819bc2e | 356 | struct qos_info *qos, |
1da177e4 | 357 | __u32 max_sdu_size, |
6819bc2e YH |
358 | __u8 max_header_size, |
359 | struct sk_buff *skb) | |
1da177e4 LT |
360 | { |
361 | struct irlan_cb *self; | |
362 | ||
ea110733 | 363 | self = instance; |
1da177e4 LT |
364 | |
365 | IRDA_ASSERT(self != NULL, return;); | |
366 | IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); | |
367 | ||
368 | self->max_sdu_size = max_sdu_size; | |
369 | self->max_header_size = max_header_size; | |
370 | ||
371 | /* TODO: we could set the MTU depending on the max_sdu_size */ | |
372 | ||
0dc47877 | 373 | IRDA_DEBUG(0, "%s: We are now connected!\n", __func__); |
1da177e4 LT |
374 | del_timer(&self->watchdog_timer); |
375 | ||
6819bc2e | 376 | /* |
1da177e4 | 377 | * Data channel is open, so we are now allowed to configure the remote |
6819bc2e | 378 | * filter |
1da177e4 LT |
379 | */ |
380 | irlan_get_unicast_addr(self); | |
381 | irlan_open_unicast_addr(self); | |
6819bc2e | 382 | |
1da177e4 | 383 | /* Open broadcast and multicast filter by default */ |
6819bc2e YH |
384 | irlan_set_broadcast_filter(self, TRUE); |
385 | irlan_set_multicast_filter(self, TRUE); | |
1da177e4 LT |
386 | |
387 | /* Ready to transfer Ethernet frames */ | |
388 | netif_start_queue(self->dev); | |
389 | self->disconnect_reason = 0; /* Clear reason */ | |
1da177e4 LT |
390 | wake_up_interruptible(&self->open_wait); |
391 | } | |
392 | ||
393 | /* | |
394 | * Function irlan_client_disconnect_indication (handle) | |
395 | * | |
396 | * Callback function for the IrTTP layer. Indicates a disconnection of | |
397 | * the specified connection (handle) | |
398 | */ | |
399 | static void irlan_disconnect_indication(void *instance, | |
6819bc2e YH |
400 | void *sap, LM_REASON reason, |
401 | struct sk_buff *userdata) | |
1da177e4 LT |
402 | { |
403 | struct irlan_cb *self; | |
404 | struct tsap_cb *tsap; | |
405 | ||
0dc47877 | 406 | IRDA_DEBUG(0, "%s(), reason=%d\n", __func__ , reason); |
6819bc2e | 407 | |
ea110733 JP |
408 | self = instance; |
409 | tsap = sap; | |
1da177e4 LT |
410 | |
411 | IRDA_ASSERT(self != NULL, return;); | |
6819bc2e | 412 | IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); |
1da177e4 LT |
413 | IRDA_ASSERT(tsap != NULL, return;); |
414 | IRDA_ASSERT(tsap->magic == TTP_TSAP_MAGIC, return;); | |
6819bc2e | 415 | |
1da177e4 LT |
416 | IRDA_ASSERT(tsap == self->tsap_data, return;); |
417 | ||
418 | IRDA_DEBUG(2, "IrLAN, data channel disconnected by peer!\n"); | |
419 | ||
420 | /* Save reason so we know if we should try to reconnect or not */ | |
421 | self->disconnect_reason = reason; | |
6819bc2e | 422 | |
1da177e4 LT |
423 | switch (reason) { |
424 | case LM_USER_REQUEST: /* User request */ | |
0dc47877 | 425 | IRDA_DEBUG(2, "%s(), User requested\n", __func__ ); |
1da177e4 LT |
426 | break; |
427 | case LM_LAP_DISCONNECT: /* Unexpected IrLAP disconnect */ | |
0dc47877 | 428 | IRDA_DEBUG(2, "%s(), Unexpected IrLAP disconnect\n", __func__ ); |
1da177e4 LT |
429 | break; |
430 | case LM_CONNECT_FAILURE: /* Failed to establish IrLAP connection */ | |
0dc47877 | 431 | IRDA_DEBUG(2, "%s(), IrLAP connect failed\n", __func__ ); |
1da177e4 LT |
432 | break; |
433 | case LM_LAP_RESET: /* IrLAP reset */ | |
0dc47877 | 434 | IRDA_DEBUG(2, "%s(), IrLAP reset\n", __func__ ); |
1da177e4 LT |
435 | break; |
436 | case LM_INIT_DISCONNECT: | |
0dc47877 | 437 | IRDA_DEBUG(2, "%s(), IrLMP connect failed\n", __func__ ); |
1da177e4 LT |
438 | break; |
439 | default: | |
0dc47877 | 440 | IRDA_ERROR("%s(), Unknown disconnect reason\n", __func__); |
1da177e4 LT |
441 | break; |
442 | } | |
6819bc2e | 443 | |
1da177e4 LT |
444 | /* If you want to pass the skb to *both* state machines, you will |
445 | * need to skb_clone() it, so that you don't free it twice. | |
446 | * As the state machines don't need it, git rid of it here... | |
447 | * Jean II */ | |
448 | if (userdata) | |
449 | dev_kfree_skb(userdata); | |
450 | ||
451 | irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL); | |
452 | irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL); | |
6819bc2e | 453 | |
1da177e4 LT |
454 | wake_up_interruptible(&self->open_wait); |
455 | } | |
456 | ||
457 | void irlan_open_data_tsap(struct irlan_cb *self) | |
458 | { | |
459 | struct tsap_cb *tsap; | |
460 | notify_t notify; | |
461 | ||
0dc47877 | 462 | IRDA_DEBUG(2, "%s()\n", __func__ ); |
1da177e4 LT |
463 | |
464 | IRDA_ASSERT(self != NULL, return;); | |
465 | IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); | |
466 | ||
467 | /* Check if already open */ | |
468 | if (self->tsap_data) | |
469 | return; | |
470 | ||
471 | irda_notify_init(¬ify); | |
6819bc2e | 472 | |
1da177e4 LT |
473 | notify.data_indication = irlan_eth_receive; |
474 | notify.udata_indication = irlan_eth_receive; | |
475 | notify.connect_indication = irlan_connect_indication; | |
476 | notify.connect_confirm = irlan_connect_confirm; | |
6819bc2e | 477 | notify.flow_indication = irlan_eth_flow_indication; |
1da177e4 LT |
478 | notify.disconnect_indication = irlan_disconnect_indication; |
479 | notify.instance = self; | |
480 | strlcpy(notify.name, "IrLAN data", sizeof(notify.name)); | |
481 | ||
482 | tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, ¬ify); | |
483 | if (!tsap) { | |
0dc47877 | 484 | IRDA_DEBUG(2, "%s(), Got no tsap!\n", __func__ ); |
1da177e4 LT |
485 | return; |
486 | } | |
487 | self->tsap_data = tsap; | |
488 | ||
6819bc2e | 489 | /* |
1da177e4 LT |
490 | * This is the data TSAP selector which we will pass to the client |
491 | * when the client ask for it. | |
492 | */ | |
493 | self->stsap_sel_data = self->tsap_data->stsap_sel; | |
494 | } | |
495 | ||
496 | void irlan_close_tsaps(struct irlan_cb *self) | |
497 | { | |
0dc47877 | 498 | IRDA_DEBUG(4, "%s()\n", __func__ ); |
1da177e4 LT |
499 | |
500 | IRDA_ASSERT(self != NULL, return;); | |
501 | IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); | |
502 | ||
503 | /* Disconnect and close all open TSAP connections */ | |
504 | if (self->tsap_data) { | |
505 | irttp_disconnect_request(self->tsap_data, NULL, P_NORMAL); | |
506 | irttp_close_tsap(self->tsap_data); | |
507 | self->tsap_data = NULL; | |
508 | } | |
509 | if (self->client.tsap_ctrl) { | |
6819bc2e | 510 | irttp_disconnect_request(self->client.tsap_ctrl, NULL, |
1da177e4 LT |
511 | P_NORMAL); |
512 | irttp_close_tsap(self->client.tsap_ctrl); | |
513 | self->client.tsap_ctrl = NULL; | |
514 | } | |
515 | if (self->provider.tsap_ctrl) { | |
6819bc2e | 516 | irttp_disconnect_request(self->provider.tsap_ctrl, NULL, |
1da177e4 LT |
517 | P_NORMAL); |
518 | irttp_close_tsap(self->provider.tsap_ctrl); | |
519 | self->provider.tsap_ctrl = NULL; | |
520 | } | |
521 | self->disconnect_reason = LM_USER_REQUEST; | |
522 | } | |
523 | ||
524 | /* | |
525 | * Function irlan_ias_register (self, tsap_sel) | |
526 | * | |
527 | * Register with LM-IAS | |
528 | * | |
529 | */ | |
530 | void irlan_ias_register(struct irlan_cb *self, __u8 tsap_sel) | |
531 | { | |
532 | struct ias_object *obj; | |
533 | struct ias_value *new_value; | |
534 | ||
535 | IRDA_ASSERT(self != NULL, return;); | |
536 | IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); | |
6819bc2e YH |
537 | |
538 | /* | |
1da177e4 LT |
539 | * Check if object has already been registered by a previous provider. |
540 | * If that is the case, we just change the value of the attribute | |
541 | */ | |
542 | if (!irias_find_object("IrLAN")) { | |
543 | obj = irias_new_object("IrLAN", IAS_IRLAN_ID); | |
544 | irias_add_integer_attrib(obj, "IrDA:TinyTP:LsapSel", tsap_sel, | |
545 | IAS_KERNEL_ATTR); | |
546 | irias_insert_object(obj); | |
547 | } else { | |
548 | new_value = irias_new_integer_value(tsap_sel); | |
549 | irias_object_change_attribute("IrLAN", "IrDA:TinyTP:LsapSel", | |
550 | new_value); | |
551 | } | |
6819bc2e YH |
552 | |
553 | /* Register PnP object only if not registered before */ | |
554 | if (!irias_find_object("PnP")) { | |
1da177e4 LT |
555 | obj = irias_new_object("PnP", IAS_PNP_ID); |
556 | #if 0 | |
557 | irias_add_string_attrib(obj, "Name", sysctl_devname, | |
558 | IAS_KERNEL_ATTR); | |
559 | #else | |
560 | irias_add_string_attrib(obj, "Name", "Linux", IAS_KERNEL_ATTR); | |
561 | #endif | |
562 | irias_add_string_attrib(obj, "DeviceID", "HWP19F0", | |
563 | IAS_KERNEL_ATTR); | |
564 | irias_add_integer_attrib(obj, "CompCnt", 1, IAS_KERNEL_ATTR); | |
565 | if (self->provider.access_type == ACCESS_PEER) | |
566 | irias_add_string_attrib(obj, "Comp#01", "PNP8389", | |
567 | IAS_KERNEL_ATTR); | |
568 | else | |
569 | irias_add_string_attrib(obj, "Comp#01", "PNP8294", | |
570 | IAS_KERNEL_ATTR); | |
571 | ||
572 | irias_add_string_attrib(obj, "Manufacturer", | |
573 | "Linux-IrDA Project", IAS_KERNEL_ATTR); | |
574 | irias_insert_object(obj); | |
575 | } | |
576 | } | |
577 | ||
578 | /* | |
579 | * Function irlan_run_ctrl_tx_queue (self) | |
580 | * | |
581 | * Try to send the next command in the control transmit queue | |
582 | * | |
583 | */ | |
584 | int irlan_run_ctrl_tx_queue(struct irlan_cb *self) | |
585 | { | |
586 | struct sk_buff *skb; | |
587 | ||
0dc47877 | 588 | IRDA_DEBUG(2, "%s()\n", __func__ ); |
1da177e4 LT |
589 | |
590 | if (irda_lock(&self->client.tx_busy) == FALSE) | |
591 | return -EBUSY; | |
592 | ||
593 | skb = skb_dequeue(&self->client.txq); | |
594 | if (!skb) { | |
595 | self->client.tx_busy = FALSE; | |
596 | return 0; | |
597 | } | |
6819bc2e | 598 | |
1da177e4 | 599 | /* Check that it's really possible to send commands */ |
6819bc2e YH |
600 | if ((self->client.tsap_ctrl == NULL) || |
601 | (self->client.state == IRLAN_IDLE)) | |
1da177e4 LT |
602 | { |
603 | self->client.tx_busy = FALSE; | |
604 | dev_kfree_skb(skb); | |
605 | return -1; | |
606 | } | |
0dc47877 | 607 | IRDA_DEBUG(2, "%s(), sending ...\n", __func__ ); |
1da177e4 LT |
608 | |
609 | return irttp_data_request(self->client.tsap_ctrl, skb); | |
610 | } | |
611 | ||
612 | /* | |
613 | * Function irlan_ctrl_data_request (self, skb) | |
614 | * | |
615 | * This function makes sure that commands on the control channel is being | |
616 | * sent in a command/response fashion | |
617 | */ | |
618 | static void irlan_ctrl_data_request(struct irlan_cb *self, struct sk_buff *skb) | |
619 | { | |
0dc47877 | 620 | IRDA_DEBUG(2, "%s()\n", __func__ ); |
1da177e4 LT |
621 | |
622 | /* Queue command */ | |
623 | skb_queue_tail(&self->client.txq, skb); | |
624 | ||
625 | /* Try to send command */ | |
626 | irlan_run_ctrl_tx_queue(self); | |
627 | } | |
628 | ||
629 | /* | |
630 | * Function irlan_get_provider_info (self) | |
631 | * | |
632 | * Send Get Provider Information command to peer IrLAN layer | |
633 | * | |
634 | */ | |
635 | void irlan_get_provider_info(struct irlan_cb *self) | |
636 | { | |
637 | struct sk_buff *skb; | |
638 | __u8 *frame; | |
639 | ||
0dc47877 | 640 | IRDA_DEBUG(4, "%s()\n", __func__ ); |
6819bc2e | 641 | |
1da177e4 LT |
642 | IRDA_ASSERT(self != NULL, return;); |
643 | IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); | |
644 | ||
1b0fee7d SO |
645 | skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER, |
646 | GFP_ATOMIC); | |
1da177e4 LT |
647 | if (!skb) |
648 | return; | |
649 | ||
650 | /* Reserve space for TTP, LMP, and LAP header */ | |
651 | skb_reserve(skb, self->client.max_header_size); | |
652 | skb_put(skb, 2); | |
6819bc2e | 653 | |
1da177e4 | 654 | frame = skb->data; |
6819bc2e YH |
655 | |
656 | frame[0] = CMD_GET_PROVIDER_INFO; | |
1da177e4 | 657 | frame[1] = 0x00; /* Zero parameters */ |
6819bc2e | 658 | |
1da177e4 LT |
659 | irlan_ctrl_data_request(self, skb); |
660 | } | |
661 | ||
662 | /* | |
663 | * Function irlan_open_data_channel (self) | |
664 | * | |
665 | * Send an Open Data Command to provider | |
666 | * | |
667 | */ | |
6819bc2e | 668 | void irlan_open_data_channel(struct irlan_cb *self) |
1da177e4 LT |
669 | { |
670 | struct sk_buff *skb; | |
671 | __u8 *frame; | |
6819bc2e | 672 | |
0dc47877 | 673 | IRDA_DEBUG(4, "%s()\n", __func__ ); |
1da177e4 LT |
674 | |
675 | IRDA_ASSERT(self != NULL, return;); | |
676 | IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); | |
6819bc2e | 677 | |
1b0fee7d SO |
678 | skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + |
679 | IRLAN_STRING_PARAMETER_LEN("MEDIA", "802.3") + | |
680 | IRLAN_STRING_PARAMETER_LEN("ACCESS_TYPE", "DIRECT"), | |
681 | GFP_ATOMIC); | |
1da177e4 LT |
682 | if (!skb) |
683 | return; | |
684 | ||
685 | skb_reserve(skb, self->client.max_header_size); | |
686 | skb_put(skb, 2); | |
6819bc2e | 687 | |
1da177e4 | 688 | frame = skb->data; |
6819bc2e | 689 | |
1da177e4 | 690 | /* Build frame */ |
6819bc2e | 691 | frame[0] = CMD_OPEN_DATA_CHANNEL; |
1da177e4 LT |
692 | frame[1] = 0x02; /* Two parameters */ |
693 | ||
694 | irlan_insert_string_param(skb, "MEDIA", "802.3"); | |
695 | irlan_insert_string_param(skb, "ACCESS_TYPE", "DIRECT"); | |
696 | /* irlan_insert_string_param(skb, "MODE", "UNRELIABLE"); */ | |
697 | ||
698 | /* self->use_udata = TRUE; */ | |
699 | ||
700 | irlan_ctrl_data_request(self, skb); | |
701 | } | |
702 | ||
6819bc2e | 703 | void irlan_close_data_channel(struct irlan_cb *self) |
1da177e4 LT |
704 | { |
705 | struct sk_buff *skb; | |
706 | __u8 *frame; | |
6819bc2e | 707 | |
0dc47877 | 708 | IRDA_DEBUG(4, "%s()\n", __func__ ); |
1da177e4 LT |
709 | |
710 | IRDA_ASSERT(self != NULL, return;); | |
711 | IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); | |
712 | ||
713 | /* Check if the TSAP is still there */ | |
714 | if (self->client.tsap_ctrl == NULL) | |
715 | return; | |
716 | ||
1b0fee7d SO |
717 | skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + |
718 | IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN"), | |
719 | GFP_ATOMIC); | |
1da177e4 LT |
720 | if (!skb) |
721 | return; | |
722 | ||
723 | skb_reserve(skb, self->client.max_header_size); | |
724 | skb_put(skb, 2); | |
6819bc2e | 725 | |
1da177e4 | 726 | frame = skb->data; |
6819bc2e | 727 | |
1da177e4 | 728 | /* Build frame */ |
6819bc2e | 729 | frame[0] = CMD_CLOSE_DATA_CHAN; |
1b0fee7d | 730 | frame[1] = 0x01; /* One parameter */ |
1da177e4 LT |
731 | |
732 | irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data); | |
733 | ||
734 | irlan_ctrl_data_request(self, skb); | |
735 | } | |
736 | ||
737 | /* | |
738 | * Function irlan_open_unicast_addr (self) | |
739 | * | |
6819bc2e | 740 | * Make IrLAN provider accept ethernet frames addressed to the unicast |
1da177e4 LT |
741 | * address. |
742 | * | |
743 | */ | |
744 | static void irlan_open_unicast_addr(struct irlan_cb *self) | |
745 | { | |
746 | struct sk_buff *skb; | |
747 | __u8 *frame; | |
6819bc2e | 748 | |
0dc47877 | 749 | IRDA_DEBUG(4, "%s()\n", __func__ ); |
1da177e4 LT |
750 | |
751 | IRDA_ASSERT(self != NULL, return;); | |
6819bc2e YH |
752 | IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); |
753 | ||
1b0fee7d SO |
754 | skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + |
755 | IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") + | |
756 | IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "DIRECTED") + | |
757 | IRLAN_STRING_PARAMETER_LEN("FILTER_MODE", "FILTER"), | |
758 | GFP_ATOMIC); | |
1da177e4 LT |
759 | if (!skb) |
760 | return; | |
761 | ||
762 | /* Reserve space for TTP, LMP, and LAP header */ | |
763 | skb_reserve(skb, self->max_header_size); | |
764 | skb_put(skb, 2); | |
6819bc2e | 765 | |
1da177e4 | 766 | frame = skb->data; |
6819bc2e YH |
767 | |
768 | frame[0] = CMD_FILTER_OPERATION; | |
1da177e4 | 769 | frame[1] = 0x03; /* Three parameters */ |
6819bc2e YH |
770 | irlan_insert_byte_param(skb, "DATA_CHAN" , self->dtsap_sel_data); |
771 | irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED"); | |
772 | irlan_insert_string_param(skb, "FILTER_MODE", "FILTER"); | |
773 | ||
1da177e4 LT |
774 | irlan_ctrl_data_request(self, skb); |
775 | } | |
776 | ||
777 | /* | |
778 | * Function irlan_set_broadcast_filter (self, status) | |
779 | * | |
780 | * Make IrLAN provider accept ethernet frames addressed to the broadcast | |
781 | * address. Be careful with the use of this one, since there may be a lot | |
782 | * of broadcast traffic out there. We can still function without this | |
783 | * one but then _we_ have to initiate all communication with other | |
784 | * hosts, since ARP request for this host will not be answered. | |
785 | */ | |
6819bc2e | 786 | void irlan_set_broadcast_filter(struct irlan_cb *self, int status) |
1da177e4 LT |
787 | { |
788 | struct sk_buff *skb; | |
789 | __u8 *frame; | |
6819bc2e | 790 | |
0dc47877 | 791 | IRDA_DEBUG(2, "%s()\n", __func__ ); |
1da177e4 LT |
792 | |
793 | IRDA_ASSERT(self != NULL, return;); | |
794 | IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); | |
6819bc2e YH |
795 | |
796 | skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + | |
1b0fee7d SO |
797 | IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") + |
798 | IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "BROADCAST") + | |
799 | /* We may waste one byte here...*/ | |
800 | IRLAN_STRING_PARAMETER_LEN("FILTER_MODE", "FILTER"), | |
801 | GFP_ATOMIC); | |
1da177e4 LT |
802 | if (!skb) |
803 | return; | |
804 | ||
805 | /* Reserve space for TTP, LMP, and LAP header */ | |
806 | skb_reserve(skb, self->client.max_header_size); | |
807 | skb_put(skb, 2); | |
6819bc2e | 808 | |
1da177e4 | 809 | frame = skb->data; |
6819bc2e YH |
810 | |
811 | frame[0] = CMD_FILTER_OPERATION; | |
1da177e4 | 812 | frame[1] = 0x03; /* Three parameters */ |
6819bc2e YH |
813 | irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data); |
814 | irlan_insert_string_param(skb, "FILTER_TYPE", "BROADCAST"); | |
1da177e4 | 815 | if (status) |
6819bc2e | 816 | irlan_insert_string_param(skb, "FILTER_MODE", "FILTER"); |
1da177e4 | 817 | else |
6819bc2e | 818 | irlan_insert_string_param(skb, "FILTER_MODE", "NONE"); |
1da177e4 LT |
819 | |
820 | irlan_ctrl_data_request(self, skb); | |
821 | } | |
822 | ||
823 | /* | |
824 | * Function irlan_set_multicast_filter (self, status) | |
825 | * | |
826 | * Make IrLAN provider accept ethernet frames addressed to the multicast | |
6819bc2e | 827 | * address. |
1da177e4 LT |
828 | * |
829 | */ | |
6819bc2e | 830 | void irlan_set_multicast_filter(struct irlan_cb *self, int status) |
1da177e4 LT |
831 | { |
832 | struct sk_buff *skb; | |
833 | __u8 *frame; | |
6819bc2e | 834 | |
0dc47877 | 835 | IRDA_DEBUG(2, "%s()\n", __func__ ); |
1da177e4 LT |
836 | |
837 | IRDA_ASSERT(self != NULL, return;); | |
838 | IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); | |
839 | ||
1b0fee7d SO |
840 | skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + |
841 | IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") + | |
842 | IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "MULTICAST") + | |
843 | /* We may waste one byte here...*/ | |
844 | IRLAN_STRING_PARAMETER_LEN("FILTER_MODE", "NONE"), | |
845 | GFP_ATOMIC); | |
1da177e4 LT |
846 | if (!skb) |
847 | return; | |
6819bc2e | 848 | |
1da177e4 LT |
849 | /* Reserve space for TTP, LMP, and LAP header */ |
850 | skb_reserve(skb, self->client.max_header_size); | |
851 | skb_put(skb, 2); | |
6819bc2e | 852 | |
1da177e4 | 853 | frame = skb->data; |
6819bc2e YH |
854 | |
855 | frame[0] = CMD_FILTER_OPERATION; | |
1da177e4 | 856 | frame[1] = 0x03; /* Three parameters */ |
6819bc2e YH |
857 | irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data); |
858 | irlan_insert_string_param(skb, "FILTER_TYPE", "MULTICAST"); | |
1da177e4 | 859 | if (status) |
6819bc2e | 860 | irlan_insert_string_param(skb, "FILTER_MODE", "ALL"); |
1da177e4 | 861 | else |
6819bc2e | 862 | irlan_insert_string_param(skb, "FILTER_MODE", "NONE"); |
1da177e4 LT |
863 | |
864 | irlan_ctrl_data_request(self, skb); | |
865 | } | |
866 | ||
867 | /* | |
868 | * Function irlan_get_unicast_addr (self) | |
869 | * | |
870 | * Retrieves the unicast address from the IrLAN provider. This address | |
871 | * will be inserted into the devices structure, so the ethernet layer | |
872 | * can construct its packets. | |
873 | * | |
874 | */ | |
875 | static void irlan_get_unicast_addr(struct irlan_cb *self) | |
876 | { | |
877 | struct sk_buff *skb; | |
878 | __u8 *frame; | |
6819bc2e | 879 | |
0dc47877 | 880 | IRDA_DEBUG(2, "%s()\n", __func__ ); |
1da177e4 LT |
881 | |
882 | IRDA_ASSERT(self != NULL, return;); | |
883 | IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); | |
6819bc2e | 884 | |
1b0fee7d SO |
885 | skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + |
886 | IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") + | |
887 | IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "DIRECTED") + | |
888 | IRLAN_STRING_PARAMETER_LEN("FILTER_OPERATION", | |
889 | "DYNAMIC"), | |
890 | GFP_ATOMIC); | |
1da177e4 LT |
891 | if (!skb) |
892 | return; | |
893 | ||
894 | /* Reserve space for TTP, LMP, and LAP header */ | |
895 | skb_reserve(skb, self->client.max_header_size); | |
896 | skb_put(skb, 2); | |
6819bc2e | 897 | |
1da177e4 | 898 | frame = skb->data; |
6819bc2e YH |
899 | |
900 | frame[0] = CMD_FILTER_OPERATION; | |
1da177e4 | 901 | frame[1] = 0x03; /* Three parameters */ |
6819bc2e YH |
902 | irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data); |
903 | irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED"); | |
904 | irlan_insert_string_param(skb, "FILTER_OPERATION", "DYNAMIC"); | |
905 | ||
1da177e4 LT |
906 | irlan_ctrl_data_request(self, skb); |
907 | } | |
908 | ||
909 | /* | |
910 | * Function irlan_get_media_char (self) | |
911 | * | |
6819bc2e | 912 | * |
1da177e4 LT |
913 | * |
914 | */ | |
6819bc2e | 915 | void irlan_get_media_char(struct irlan_cb *self) |
1da177e4 LT |
916 | { |
917 | struct sk_buff *skb; | |
918 | __u8 *frame; | |
6819bc2e | 919 | |
0dc47877 | 920 | IRDA_DEBUG(4, "%s()\n", __func__ ); |
1da177e4 LT |
921 | |
922 | IRDA_ASSERT(self != NULL, return;); | |
923 | IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); | |
6819bc2e | 924 | |
1b0fee7d SO |
925 | skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + |
926 | IRLAN_STRING_PARAMETER_LEN("MEDIA", "802.3"), | |
927 | GFP_ATOMIC); | |
928 | ||
1da177e4 LT |
929 | if (!skb) |
930 | return; | |
931 | ||
932 | /* Reserve space for TTP, LMP, and LAP header */ | |
933 | skb_reserve(skb, self->client.max_header_size); | |
934 | skb_put(skb, 2); | |
6819bc2e | 935 | |
1da177e4 | 936 | frame = skb->data; |
6819bc2e | 937 | |
1da177e4 | 938 | /* Build frame */ |
6819bc2e | 939 | frame[0] = CMD_GET_MEDIA_CHAR; |
1da177e4 | 940 | frame[1] = 0x01; /* One parameter */ |
6819bc2e | 941 | |
1da177e4 LT |
942 | irlan_insert_string_param(skb, "MEDIA", "802.3"); |
943 | irlan_ctrl_data_request(self, skb); | |
944 | } | |
945 | ||
946 | /* | |
947 | * Function insert_byte_param (skb, param, value) | |
948 | * | |
949 | * Insert byte parameter into frame | |
950 | * | |
951 | */ | |
952 | int irlan_insert_byte_param(struct sk_buff *skb, char *param, __u8 value) | |
953 | { | |
954 | return __irlan_insert_param(skb, param, IRLAN_BYTE, value, 0, NULL, 0); | |
955 | } | |
956 | ||
957 | int irlan_insert_short_param(struct sk_buff *skb, char *param, __u16 value) | |
958 | { | |
959 | return __irlan_insert_param(skb, param, IRLAN_SHORT, 0, value, NULL, 0); | |
960 | } | |
961 | ||
962 | /* | |
963 | * Function insert_string (skb, param, value) | |
964 | * | |
965 | * Insert string parameter into frame | |
966 | * | |
967 | */ | |
968 | int irlan_insert_string_param(struct sk_buff *skb, char *param, char *string) | |
969 | { | |
970 | int string_len = strlen(string); | |
971 | ||
6819bc2e | 972 | return __irlan_insert_param(skb, param, IRLAN_ARRAY, 0, 0, string, |
1da177e4 LT |
973 | string_len); |
974 | } | |
975 | ||
976 | /* | |
977 | * Function insert_array_param(skb, param, value, len_value) | |
978 | * | |
979 | * Insert array parameter into frame | |
980 | * | |
981 | */ | |
982 | int irlan_insert_array_param(struct sk_buff *skb, char *name, __u8 *array, | |
983 | __u16 array_len) | |
984 | { | |
6819bc2e | 985 | return __irlan_insert_param(skb, name, IRLAN_ARRAY, 0, 0, array, |
1da177e4 LT |
986 | array_len); |
987 | } | |
988 | ||
989 | /* | |
990 | * Function insert_param (skb, param, value, byte) | |
991 | * | |
992 | * Insert parameter at end of buffer, structure of a parameter is: | |
993 | * | |
994 | * ----------------------------------------------------------------------- | |
995 | * | Name Length[1] | Param Name[1..255] | Val Length[2] | Value[0..1016]| | |
996 | * ----------------------------------------------------------------------- | |
997 | */ | |
6819bc2e YH |
998 | static int __irlan_insert_param(struct sk_buff *skb, char *param, int type, |
999 | __u8 value_byte, __u16 value_short, | |
1da177e4 LT |
1000 | __u8 *value_array, __u16 value_len) |
1001 | { | |
1002 | __u8 *frame; | |
1003 | __u8 param_len; | |
448c31aa | 1004 | __le16 tmp_le; /* Temporary value in little endian format */ |
1da177e4 | 1005 | int n=0; |
6819bc2e | 1006 | |
1da177e4 | 1007 | if (skb == NULL) { |
0dc47877 | 1008 | IRDA_DEBUG(2, "%s(), Got NULL skb\n", __func__ ); |
1da177e4 | 1009 | return 0; |
6819bc2e | 1010 | } |
1da177e4 LT |
1011 | |
1012 | param_len = strlen(param); | |
1013 | switch (type) { | |
1014 | case IRLAN_BYTE: | |
1015 | value_len = 1; | |
1016 | break; | |
1017 | case IRLAN_SHORT: | |
1018 | value_len = 2; | |
1019 | break; | |
1020 | case IRLAN_ARRAY: | |
1021 | IRDA_ASSERT(value_array != NULL, return 0;); | |
1022 | IRDA_ASSERT(value_len > 0, return 0;); | |
1023 | break; | |
1024 | default: | |
0dc47877 | 1025 | IRDA_DEBUG(2, "%s(), Unknown parameter type!\n", __func__ ); |
1da177e4 LT |
1026 | return 0; |
1027 | break; | |
1028 | } | |
6819bc2e | 1029 | |
1da177e4 | 1030 | /* Insert at end of sk-buffer */ |
27a884dc | 1031 | frame = skb_tail_pointer(skb); |
1da177e4 LT |
1032 | |
1033 | /* Make space for data */ | |
1034 | if (skb_tailroom(skb) < (param_len+value_len+3)) { | |
0dc47877 | 1035 | IRDA_DEBUG(2, "%s(), No more space at end of skb\n", __func__ ); |
1da177e4 | 1036 | return 0; |
6819bc2e | 1037 | } |
1da177e4 | 1038 | skb_put(skb, param_len+value_len+3); |
6819bc2e | 1039 | |
1da177e4 LT |
1040 | /* Insert parameter length */ |
1041 | frame[n++] = param_len; | |
6819bc2e | 1042 | |
1da177e4 LT |
1043 | /* Insert parameter */ |
1044 | memcpy(frame+n, param, param_len); n += param_len; | |
6819bc2e | 1045 | |
1da177e4 LT |
1046 | /* Insert value length (2 byte little endian format, LSB first) */ |
1047 | tmp_le = cpu_to_le16(value_len); | |
1048 | memcpy(frame+n, &tmp_le, 2); n += 2; /* To avoid alignment problems */ | |
1049 | ||
1050 | /* Insert value */ | |
1051 | switch (type) { | |
1052 | case IRLAN_BYTE: | |
1053 | frame[n++] = value_byte; | |
1054 | break; | |
1055 | case IRLAN_SHORT: | |
1056 | tmp_le = cpu_to_le16(value_short); | |
1057 | memcpy(frame+n, &tmp_le, 2); n += 2; | |
1058 | break; | |
1059 | case IRLAN_ARRAY: | |
1060 | memcpy(frame+n, value_array, value_len); n+=value_len; | |
1061 | break; | |
1062 | default: | |
1063 | break; | |
1064 | } | |
1065 | IRDA_ASSERT(n == (param_len+value_len+3), return 0;); | |
1066 | ||
1067 | return param_len+value_len+3; | |
1068 | } | |
1069 | ||
1070 | /* | |
1071 | * Function irlan_extract_param (buf, name, value, len) | |
1072 | * | |
1073 | * Extracts a single parameter name/value pair from buffer and updates | |
6819bc2e | 1074 | * the buffer pointer to point to the next name/value pair. |
1da177e4 LT |
1075 | */ |
1076 | int irlan_extract_param(__u8 *buf, char *name, char *value, __u16 *len) | |
1077 | { | |
1078 | __u8 name_len; | |
1079 | __u16 val_len; | |
1080 | int n=0; | |
6819bc2e | 1081 | |
0dc47877 | 1082 | IRDA_DEBUG(4, "%s()\n", __func__ ); |
6819bc2e | 1083 | |
1da177e4 LT |
1084 | /* get length of parameter name (1 byte) */ |
1085 | name_len = buf[n++]; | |
6819bc2e | 1086 | |
1da177e4 | 1087 | if (name_len > 254) { |
0dc47877 | 1088 | IRDA_DEBUG(2, "%s(), name_len > 254\n", __func__ ); |
1da177e4 LT |
1089 | return -RSP_INVALID_COMMAND_FORMAT; |
1090 | } | |
6819bc2e | 1091 | |
1da177e4 LT |
1092 | /* get parameter name */ |
1093 | memcpy(name, buf+n, name_len); | |
1094 | name[name_len] = '\0'; | |
1095 | n+=name_len; | |
6819bc2e YH |
1096 | |
1097 | /* | |
1098 | * Get length of parameter value (2 bytes in little endian | |
1099 | * format) | |
1da177e4 LT |
1100 | */ |
1101 | memcpy(&val_len, buf+n, 2); /* To avoid alignment problems */ | |
1102 | le16_to_cpus(&val_len); n+=2; | |
6819bc2e | 1103 | |
cf9b94f8 | 1104 | if (val_len >= 1016) { |
0dc47877 | 1105 | IRDA_DEBUG(2, "%s(), parameter length to long\n", __func__ ); |
1da177e4 LT |
1106 | return -RSP_INVALID_COMMAND_FORMAT; |
1107 | } | |
1108 | *len = val_len; | |
1109 | ||
1110 | /* get parameter value */ | |
1111 | memcpy(value, buf+n, val_len); | |
1112 | value[val_len] = '\0'; | |
1113 | n+=val_len; | |
6819bc2e YH |
1114 | |
1115 | IRDA_DEBUG(4, "Parameter: %s ", name); | |
1116 | IRDA_DEBUG(4, "Value: %s\n", value); | |
1da177e4 LT |
1117 | |
1118 | return n; | |
1119 | } | |
1120 | ||
1121 | #ifdef CONFIG_PROC_FS | |
1122 | ||
1123 | /* | |
1124 | * Start of reading /proc entries. | |
6819bc2e | 1125 | * Return entry at pos, |
1da177e4 LT |
1126 | * or start_token to indicate print header line |
1127 | * or NULL if end of file | |
1128 | */ | |
1129 | static void *irlan_seq_start(struct seq_file *seq, loff_t *pos) | |
1130 | { | |
1da177e4 | 1131 | rcu_read_lock(); |
216437eb | 1132 | return seq_list_start_head(&irlans, *pos); |
1da177e4 LT |
1133 | } |
1134 | ||
1135 | /* Return entry after v, and increment pos */ | |
1136 | static void *irlan_seq_next(struct seq_file *seq, void *v, loff_t *pos) | |
1137 | { | |
216437eb | 1138 | return seq_list_next(v, &irlans, pos); |
1da177e4 LT |
1139 | } |
1140 | ||
1141 | /* End of reading /proc file */ | |
1142 | static void irlan_seq_stop(struct seq_file *seq, void *v) | |
1143 | { | |
1144 | rcu_read_unlock(); | |
1145 | } | |
1146 | ||
1147 | ||
1148 | /* | |
1149 | * Show one entry in /proc file. | |
1150 | */ | |
1151 | static int irlan_seq_show(struct seq_file *seq, void *v) | |
1152 | { | |
216437eb | 1153 | if (v == &irlans) |
1da177e4 LT |
1154 | seq_puts(seq, "IrLAN instances:\n"); |
1155 | else { | |
216437eb | 1156 | struct irlan_cb *self = list_entry(v, struct irlan_cb, dev_list); |
6819bc2e | 1157 | |
1da177e4 LT |
1158 | IRDA_ASSERT(self != NULL, return -1;); |
1159 | IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); | |
1160 | ||
1161 | seq_printf(seq,"ifname: %s,\n", | |
1162 | self->dev->name); | |
1163 | seq_printf(seq,"client state: %s, ", | |
1164 | irlan_state[ self->client.state]); | |
1165 | seq_printf(seq,"provider state: %s,\n", | |
1166 | irlan_state[ self->provider.state]); | |
1167 | seq_printf(seq,"saddr: %#08x, ", | |
1168 | self->saddr); | |
1169 | seq_printf(seq,"daddr: %#08x\n", | |
1170 | self->daddr); | |
1171 | seq_printf(seq,"version: %d.%d,\n", | |
1172 | self->version[1], self->version[0]); | |
6819bc2e | 1173 | seq_printf(seq,"access type: %s\n", |
1da177e4 | 1174 | irlan_access[self->client.access_type]); |
6819bc2e | 1175 | seq_printf(seq,"media: %s\n", |
1da177e4 | 1176 | irlan_media[self->media]); |
6819bc2e | 1177 | |
1da177e4 LT |
1178 | seq_printf(seq,"local filter:\n"); |
1179 | seq_printf(seq,"remote filter: "); | |
1180 | irlan_print_filter(seq, self->client.filter_type); | |
6819bc2e | 1181 | seq_printf(seq,"tx busy: %s\n", |
1da177e4 | 1182 | netif_queue_stopped(self->dev) ? "TRUE" : "FALSE"); |
6819bc2e | 1183 | |
1da177e4 LT |
1184 | seq_putc(seq,'\n'); |
1185 | } | |
1186 | return 0; | |
1187 | } | |
1188 | ||
56b3d975 | 1189 | static const struct seq_operations irlan_seq_ops = { |
1da177e4 LT |
1190 | .start = irlan_seq_start, |
1191 | .next = irlan_seq_next, | |
1192 | .stop = irlan_seq_stop, | |
1193 | .show = irlan_seq_show, | |
1194 | }; | |
1195 | ||
1196 | static int irlan_seq_open(struct inode *inode, struct file *file) | |
1197 | { | |
1198 | return seq_open(file, &irlan_seq_ops); | |
1199 | } | |
1200 | #endif | |
1201 | ||
1202 | MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>"); | |
6819bc2e | 1203 | MODULE_DESCRIPTION("The Linux IrDA LAN protocol"); |
1da177e4 LT |
1204 | MODULE_LICENSE("GPL"); |
1205 | ||
1206 | module_param(eth, bool, 0); | |
1207 | MODULE_PARM_DESC(eth, "Name devices ethX (0) or irlanX (1)"); | |
1208 | module_param(access, int, 0); | |
1209 | MODULE_PARM_DESC(access, "Access type DIRECT=1, PEER=2, HOSTED=3"); | |
1210 | ||
1211 | module_init(irlan_init); | |
1212 | module_exit(irlan_cleanup); | |
1213 |