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