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