Merge branch 'gfar' of master.kernel.org:/pub/scm/linux/kernel/git/galak/powerpc...
[linux-2.6] / net / irda / ircomm / ircomm_core.c
1 /*********************************************************************
2  *
3  * Filename:      ircomm_core.c
4  * Version:       1.0
5  * Description:   IrCOMM service interface
6  * Status:        Experimental.
7  * Author:        Dag Brattli <dagb@cs.uit.no>
8  * Created at:    Sun Jun  6 20:37:34 1999
9  * Modified at:   Tue Dec 21 13:26:41 1999
10  * Modified by:   Dag Brattli <dagb@cs.uit.no>
11  *
12  *     Copyright (c) 1999 Dag Brattli, All Rights Reserved.
13  *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
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  *     This program is distributed in the hope that it will be useful,
21  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
22  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23  *     GNU General Public License for more details.
24  *
25  *     You should have received a copy of the GNU General Public License
26  *     along with this program; if not, write to the Free Software
27  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston,
28  *     MA 02111-1307 USA
29  *
30  ********************************************************************/
31
32 #include <linux/module.h>
33 #include <linux/sched.h>
34 #include <linux/proc_fs.h>
35 #include <linux/seq_file.h>
36 #include <linux/init.h>
37
38 #include <net/irda/irda.h>
39 #include <net/irda/irmod.h>
40 #include <net/irda/irlmp.h>
41 #include <net/irda/iriap.h>
42 #include <net/irda/irttp.h>
43 #include <net/irda/irias_object.h>
44
45 #include <net/irda/ircomm_event.h>
46 #include <net/irda/ircomm_lmp.h>
47 #include <net/irda/ircomm_ttp.h>
48 #include <net/irda/ircomm_param.h>
49 #include <net/irda/ircomm_core.h>
50
51 static int __ircomm_close(struct ircomm_cb *self);
52 static void ircomm_control_indication(struct ircomm_cb *self,
53                                       struct sk_buff *skb, int clen);
54
55 #ifdef CONFIG_PROC_FS
56 extern struct proc_dir_entry *proc_irda;
57 static int ircomm_seq_open(struct inode *, struct file *);
58
59 static const struct file_operations ircomm_proc_fops = {
60         .owner          = THIS_MODULE,
61         .open           = ircomm_seq_open,
62         .read           = seq_read,
63         .llseek         = seq_lseek,
64         .release        = seq_release,
65 };
66 #endif /* CONFIG_PROC_FS */
67
68 hashbin_t *ircomm = NULL;
69
70 static int __init ircomm_init(void)
71 {
72         ircomm = hashbin_new(HB_LOCK);
73         if (ircomm == NULL) {
74                 IRDA_ERROR("%s(), can't allocate hashbin!\n", __FUNCTION__);
75                 return -ENOMEM;
76         }
77
78 #ifdef CONFIG_PROC_FS
79         { struct proc_dir_entry *ent;
80         ent = create_proc_entry("ircomm", 0, proc_irda);
81         if (ent)
82                 ent->proc_fops = &ircomm_proc_fops;
83         }
84 #endif /* CONFIG_PROC_FS */
85
86         IRDA_MESSAGE("IrCOMM protocol (Dag Brattli)\n");
87
88         return 0;
89 }
90
91 static void __exit ircomm_cleanup(void)
92 {
93         IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
94
95         hashbin_delete(ircomm, (FREE_FUNC) __ircomm_close);
96
97 #ifdef CONFIG_PROC_FS
98         remove_proc_entry("ircomm", proc_irda);
99 #endif /* CONFIG_PROC_FS */
100 }
101
102 /*
103  * Function ircomm_open (client_notify)
104  *
105  *    Start a new IrCOMM instance
106  *
107  */
108 struct ircomm_cb *ircomm_open(notify_t *notify, __u8 service_type, int line)
109 {
110         struct ircomm_cb *self = NULL;
111         int ret;
112
113         IRDA_DEBUG(2, "%s(), service_type=0x%02x\n", __FUNCTION__ ,
114                    service_type);
115
116         IRDA_ASSERT(ircomm != NULL, return NULL;);
117
118         self = kzalloc(sizeof(struct ircomm_cb), GFP_ATOMIC);
119         if (self == NULL)
120                 return NULL;
121
122         self->notify = *notify;
123         self->magic = IRCOMM_MAGIC;
124
125         /* Check if we should use IrLMP or IrTTP */
126         if (service_type & IRCOMM_3_WIRE_RAW) {
127                 self->flow_status = FLOW_START;
128                 ret = ircomm_open_lsap(self);
129         } else
130                 ret = ircomm_open_tsap(self);
131
132         if (ret < 0) {
133                 kfree(self);
134                 return NULL;
135         }
136
137         self->service_type = service_type;
138         self->line = line;
139
140         hashbin_insert(ircomm, (irda_queue_t *) self, line, NULL);
141
142         ircomm_next_state(self, IRCOMM_IDLE);
143
144         return self;
145 }
146
147 EXPORT_SYMBOL(ircomm_open);
148
149 /*
150  * Function ircomm_close_instance (self)
151  *
152  *    Remove IrCOMM instance
153  *
154  */
155 static int __ircomm_close(struct ircomm_cb *self)
156 {
157         IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
158
159         /* Disconnect link if any */
160         ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, NULL, NULL);
161
162         /* Remove TSAP */
163         if (self->tsap) {
164                 irttp_close_tsap(self->tsap);
165                 self->tsap = NULL;
166         }
167
168         /* Remove LSAP */
169         if (self->lsap) {
170                 irlmp_close_lsap(self->lsap);
171                 self->lsap = NULL;
172         }
173         self->magic = 0;
174
175         kfree(self);
176
177         return 0;
178 }
179
180 /*
181  * Function ircomm_close (self)
182  *
183  *    Closes and removes the specified IrCOMM instance
184  *
185  */
186 int ircomm_close(struct ircomm_cb *self)
187 {
188         struct ircomm_cb *entry;
189
190         IRDA_ASSERT(self != NULL, return -EIO;);
191         IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EIO;);
192
193         IRDA_DEBUG(0, "%s()\n", __FUNCTION__ );
194
195         entry = hashbin_remove(ircomm, self->line, NULL);
196
197         IRDA_ASSERT(entry == self, return -1;);
198
199         return __ircomm_close(self);
200 }
201
202 EXPORT_SYMBOL(ircomm_close);
203
204 /*
205  * Function ircomm_connect_request (self, service_type)
206  *
207  *    Impl. of this function is differ from one of the reference. This
208  *    function does discovery as well as sending connect request
209  *
210  */
211 int ircomm_connect_request(struct ircomm_cb *self, __u8 dlsap_sel,
212                            __u32 saddr, __u32 daddr, struct sk_buff *skb,
213                            __u8 service_type)
214 {
215         struct ircomm_info info;
216         int ret;
217
218         IRDA_DEBUG(2 , "%s()\n", __FUNCTION__ );
219
220         IRDA_ASSERT(self != NULL, return -1;);
221         IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
222
223         self->service_type= service_type;
224
225         info.dlsap_sel = dlsap_sel;
226         info.saddr = saddr;
227         info.daddr = daddr;
228
229         ret = ircomm_do_event(self, IRCOMM_CONNECT_REQUEST, skb, &info);
230
231         return ret;
232 }
233
234 EXPORT_SYMBOL(ircomm_connect_request);
235
236 /*
237  * Function ircomm_connect_indication (self, qos, skb)
238  *
239  *    Notify user layer about the incoming connection
240  *
241  */
242 void ircomm_connect_indication(struct ircomm_cb *self, struct sk_buff *skb,
243                                struct ircomm_info *info)
244 {
245         int clen = 0;
246
247         IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
248
249         /* Check if the packet contains data on the control channel */
250         if (skb->len > 0)
251                 clen = skb->data[0];
252
253         /*
254          * If there are any data hiding in the control channel, we must
255          * deliver it first. The side effect is that the control channel
256          * will be removed from the skb
257          */
258         if (self->notify.connect_indication)
259                 self->notify.connect_indication(self->notify.instance, self,
260                                                 info->qos, info->max_data_size,
261                                                 info->max_header_size, skb);
262         else {
263                 IRDA_DEBUG(0, "%s(), missing handler\n", __FUNCTION__ );
264         }
265 }
266
267 /*
268  * Function ircomm_connect_response (self, userdata, max_sdu_size)
269  *
270  *    User accepts connection
271  *
272  */
273 int ircomm_connect_response(struct ircomm_cb *self, struct sk_buff *userdata)
274 {
275         int ret;
276
277         IRDA_ASSERT(self != NULL, return -1;);
278         IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
279
280         IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
281
282         ret = ircomm_do_event(self, IRCOMM_CONNECT_RESPONSE, userdata, NULL);
283
284         return ret;
285 }
286
287 EXPORT_SYMBOL(ircomm_connect_response);
288
289 /*
290  * Function connect_confirm (self, skb)
291  *
292  *    Notify user layer that the link is now connected
293  *
294  */
295 void ircomm_connect_confirm(struct ircomm_cb *self, struct sk_buff *skb,
296                             struct ircomm_info *info)
297 {
298         IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
299
300         if (self->notify.connect_confirm )
301                 self->notify.connect_confirm(self->notify.instance,
302                                              self, info->qos,
303                                              info->max_data_size,
304                                              info->max_header_size, skb);
305         else {
306                 IRDA_DEBUG(0, "%s(), missing handler\n", __FUNCTION__ );
307         }
308 }
309
310 /*
311  * Function ircomm_data_request (self, userdata)
312  *
313  *    Send IrCOMM data to peer device
314  *
315  */
316 int ircomm_data_request(struct ircomm_cb *self, struct sk_buff *skb)
317 {
318         int ret;
319
320         IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
321
322         IRDA_ASSERT(self != NULL, return -EFAULT;);
323         IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;);
324         IRDA_ASSERT(skb != NULL, return -EFAULT;);
325
326         ret = ircomm_do_event(self, IRCOMM_DATA_REQUEST, skb, NULL);
327
328         return ret;
329 }
330
331 EXPORT_SYMBOL(ircomm_data_request);
332
333 /*
334  * Function ircomm_data_indication (self, skb)
335  *
336  *    Data arrived, so deliver it to user
337  *
338  */
339 void ircomm_data_indication(struct ircomm_cb *self, struct sk_buff *skb)
340 {
341         IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
342
343         IRDA_ASSERT(skb->len > 0, return;);
344
345         if (self->notify.data_indication)
346                 self->notify.data_indication(self->notify.instance, self, skb);
347         else {
348                 IRDA_DEBUG(0, "%s(), missing handler\n", __FUNCTION__ );
349         }
350 }
351
352 /*
353  * Function ircomm_process_data (self, skb)
354  *
355  *    Data arrived which may contain control channel data
356  *
357  */
358 void ircomm_process_data(struct ircomm_cb *self, struct sk_buff *skb)
359 {
360         int clen;
361
362         IRDA_ASSERT(skb->len > 0, return;);
363
364         clen = skb->data[0];
365
366         /*
367          * If there are any data hiding in the control channel, we must
368          * deliver it first. The side effect is that the control channel
369          * will be removed from the skb
370          */
371         if (clen > 0)
372                 ircomm_control_indication(self, skb, clen);
373
374         /* Remove control channel from data channel */
375         skb_pull(skb, clen+1);
376
377         if (skb->len)
378                 ircomm_data_indication(self, skb);
379         else {
380                 IRDA_DEBUG(4, "%s(), data was control info only!\n",
381                            __FUNCTION__ );
382         }
383 }
384
385 /*
386  * Function ircomm_control_request (self, params)
387  *
388  *    Send control data to peer device
389  *
390  */
391 int ircomm_control_request(struct ircomm_cb *self, struct sk_buff *skb)
392 {
393         int ret;
394
395         IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
396
397         IRDA_ASSERT(self != NULL, return -EFAULT;);
398         IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;);
399         IRDA_ASSERT(skb != NULL, return -EFAULT;);
400
401         ret = ircomm_do_event(self, IRCOMM_CONTROL_REQUEST, skb, NULL);
402
403         return ret;
404 }
405
406 EXPORT_SYMBOL(ircomm_control_request);
407
408 /*
409  * Function ircomm_control_indication (self, skb)
410  *
411  *    Data has arrived on the control channel
412  *
413  */
414 static void ircomm_control_indication(struct ircomm_cb *self,
415                                       struct sk_buff *skb, int clen)
416 {
417         IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
418
419         /* Use udata for delivering data on the control channel */
420         if (self->notify.udata_indication) {
421                 struct sk_buff *ctrl_skb;
422
423                 /* We don't own the skb, so clone it */
424                 ctrl_skb = skb_clone(skb, GFP_ATOMIC);
425                 if (!ctrl_skb)
426                         return;
427
428                 /* Remove data channel from control channel */
429                 skb_trim(ctrl_skb, clen+1);
430
431                 self->notify.udata_indication(self->notify.instance, self,
432                                               ctrl_skb);
433
434                 /* Drop reference count -
435                  * see ircomm_tty_control_indication(). */
436                 dev_kfree_skb(ctrl_skb);
437         } else {
438                 IRDA_DEBUG(0, "%s(), missing handler\n", __FUNCTION__ );
439         }
440 }
441
442 /*
443  * Function ircomm_disconnect_request (self, userdata, priority)
444  *
445  *    User layer wants to disconnect the IrCOMM connection
446  *
447  */
448 int ircomm_disconnect_request(struct ircomm_cb *self, struct sk_buff *userdata)
449 {
450         struct ircomm_info info;
451         int ret;
452
453         IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
454
455         IRDA_ASSERT(self != NULL, return -1;);
456         IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
457
458         ret = ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, userdata,
459                               &info);
460         return ret;
461 }
462
463 EXPORT_SYMBOL(ircomm_disconnect_request);
464
465 /*
466  * Function disconnect_indication (self, skb)
467  *
468  *    Tell user that the link has been disconnected
469  *
470  */
471 void ircomm_disconnect_indication(struct ircomm_cb *self, struct sk_buff *skb,
472                                   struct ircomm_info *info)
473 {
474         IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
475
476         IRDA_ASSERT(info != NULL, return;);
477
478         if (self->notify.disconnect_indication) {
479                 self->notify.disconnect_indication(self->notify.instance, self,
480                                                    info->reason, skb);
481         } else {
482                 IRDA_DEBUG(0, "%s(), missing handler\n", __FUNCTION__ );
483         }
484 }
485
486 /*
487  * Function ircomm_flow_request (self, flow)
488  *
489  *
490  *
491  */
492 void ircomm_flow_request(struct ircomm_cb *self, LOCAL_FLOW flow)
493 {
494         IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
495
496         IRDA_ASSERT(self != NULL, return;);
497         IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
498
499         if (self->service_type == IRCOMM_3_WIRE_RAW)
500                 return;
501
502         irttp_flow_request(self->tsap, flow);
503 }
504
505 EXPORT_SYMBOL(ircomm_flow_request);
506
507 #ifdef CONFIG_PROC_FS
508 static void *ircomm_seq_start(struct seq_file *seq, loff_t *pos)
509 {
510         struct ircomm_cb *self;
511         loff_t off = 0;
512
513         spin_lock_irq(&ircomm->hb_spinlock);
514
515         for (self = (struct ircomm_cb *) hashbin_get_first(ircomm);
516              self != NULL;
517              self = (struct ircomm_cb *) hashbin_get_next(ircomm)) {
518                 if (off++ == *pos)
519                         break;
520
521         }
522         return self;
523 }
524
525 static void *ircomm_seq_next(struct seq_file *seq, void *v, loff_t *pos)
526 {
527         ++*pos;
528
529         return (void *) hashbin_get_next(ircomm);
530 }
531
532 static void ircomm_seq_stop(struct seq_file *seq, void *v)
533 {
534         spin_unlock_irq(&ircomm->hb_spinlock);
535 }
536
537 static int ircomm_seq_show(struct seq_file *seq, void *v)
538 {
539         const struct ircomm_cb *self = v;
540
541         IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EINVAL; );
542
543         if(self->line < 0x10)
544                 seq_printf(seq, "ircomm%d", self->line);
545         else
546                 seq_printf(seq, "irlpt%d", self->line - 0x10);
547
548         seq_printf(seq,
549                    " state: %s, slsap_sel: %#02x, dlsap_sel: %#02x, mode:",
550                    ircomm_state[ self->state],
551                    self->slsap_sel, self->dlsap_sel);
552
553         if(self->service_type & IRCOMM_3_WIRE_RAW)
554                 seq_printf(seq, " 3-wire-raw");
555         if(self->service_type & IRCOMM_3_WIRE)
556                 seq_printf(seq, " 3-wire");
557         if(self->service_type & IRCOMM_9_WIRE)
558                 seq_printf(seq, " 9-wire");
559         if(self->service_type & IRCOMM_CENTRONICS)
560                 seq_printf(seq, " Centronics");
561         seq_putc(seq, '\n');
562
563         return 0;
564 }
565
566 static struct seq_operations ircomm_seq_ops = {
567         .start  = ircomm_seq_start,
568         .next   = ircomm_seq_next,
569         .stop   = ircomm_seq_stop,
570         .show   = ircomm_seq_show,
571 };
572
573 static int ircomm_seq_open(struct inode *inode, struct file *file)
574 {
575         return seq_open(file, &ircomm_seq_ops);
576 }
577 #endif /* CONFIG_PROC_FS */
578
579 MODULE_AUTHOR("Dag Brattli <dag@brattli.net>");
580 MODULE_DESCRIPTION("IrCOMM protocol");
581 MODULE_LICENSE("GPL");
582
583 module_init(ircomm_init);
584 module_exit(ircomm_cleanup);