Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/net/irda/ircomm/ircomm_core.c
15112 views
1
/*********************************************************************
2
*
3
* Filename: ircomm_core.c
4
* Version: 1.0
5
* Description: IrCOMM service interface
6
* Status: Experimental.
7
* Author: Dag Brattli <[email protected]>
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 <[email protected]>
11
*
12
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
13
* Copyright (c) 2000-2003 Jean Tourrilhes <[email protected]>
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/proc_fs.h>
34
#include <linux/seq_file.h>
35
#include <linux/init.h>
36
#include <linux/slab.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", __func__);
75
return -ENOMEM;
76
}
77
78
#ifdef CONFIG_PROC_FS
79
{ struct proc_dir_entry *ent;
80
ent = proc_create("ircomm", 0, proc_irda, &ircomm_proc_fops);
81
if (!ent) {
82
printk(KERN_ERR "ircomm_init: can't create /proc entry!\n");
83
return -ENODEV;
84
}
85
}
86
#endif /* CONFIG_PROC_FS */
87
88
IRDA_MESSAGE("IrCOMM protocol (Dag Brattli)\n");
89
90
return 0;
91
}
92
93
static void __exit ircomm_cleanup(void)
94
{
95
IRDA_DEBUG(2, "%s()\n", __func__ );
96
97
hashbin_delete(ircomm, (FREE_FUNC) __ircomm_close);
98
99
#ifdef CONFIG_PROC_FS
100
remove_proc_entry("ircomm", proc_irda);
101
#endif /* CONFIG_PROC_FS */
102
}
103
104
/*
105
* Function ircomm_open (client_notify)
106
*
107
* Start a new IrCOMM instance
108
*
109
*/
110
struct ircomm_cb *ircomm_open(notify_t *notify, __u8 service_type, int line)
111
{
112
struct ircomm_cb *self = NULL;
113
int ret;
114
115
IRDA_DEBUG(2, "%s(), service_type=0x%02x\n", __func__ ,
116
service_type);
117
118
IRDA_ASSERT(ircomm != NULL, return NULL;);
119
120
self = kzalloc(sizeof(struct ircomm_cb), GFP_ATOMIC);
121
if (self == NULL)
122
return NULL;
123
124
self->notify = *notify;
125
self->magic = IRCOMM_MAGIC;
126
127
/* Check if we should use IrLMP or IrTTP */
128
if (service_type & IRCOMM_3_WIRE_RAW) {
129
self->flow_status = FLOW_START;
130
ret = ircomm_open_lsap(self);
131
} else
132
ret = ircomm_open_tsap(self);
133
134
if (ret < 0) {
135
kfree(self);
136
return NULL;
137
}
138
139
self->service_type = service_type;
140
self->line = line;
141
142
hashbin_insert(ircomm, (irda_queue_t *) self, line, NULL);
143
144
ircomm_next_state(self, IRCOMM_IDLE);
145
146
return self;
147
}
148
149
EXPORT_SYMBOL(ircomm_open);
150
151
/*
152
* Function ircomm_close_instance (self)
153
*
154
* Remove IrCOMM instance
155
*
156
*/
157
static int __ircomm_close(struct ircomm_cb *self)
158
{
159
IRDA_DEBUG(2, "%s()\n", __func__ );
160
161
/* Disconnect link if any */
162
ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, NULL, NULL);
163
164
/* Remove TSAP */
165
if (self->tsap) {
166
irttp_close_tsap(self->tsap);
167
self->tsap = NULL;
168
}
169
170
/* Remove LSAP */
171
if (self->lsap) {
172
irlmp_close_lsap(self->lsap);
173
self->lsap = NULL;
174
}
175
self->magic = 0;
176
177
kfree(self);
178
179
return 0;
180
}
181
182
/*
183
* Function ircomm_close (self)
184
*
185
* Closes and removes the specified IrCOMM instance
186
*
187
*/
188
int ircomm_close(struct ircomm_cb *self)
189
{
190
struct ircomm_cb *entry;
191
192
IRDA_ASSERT(self != NULL, return -EIO;);
193
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EIO;);
194
195
IRDA_DEBUG(0, "%s()\n", __func__ );
196
197
entry = hashbin_remove(ircomm, self->line, NULL);
198
199
IRDA_ASSERT(entry == self, return -1;);
200
201
return __ircomm_close(self);
202
}
203
204
EXPORT_SYMBOL(ircomm_close);
205
206
/*
207
* Function ircomm_connect_request (self, service_type)
208
*
209
* Impl. of this function is differ from one of the reference. This
210
* function does discovery as well as sending connect request
211
*
212
*/
213
int ircomm_connect_request(struct ircomm_cb *self, __u8 dlsap_sel,
214
__u32 saddr, __u32 daddr, struct sk_buff *skb,
215
__u8 service_type)
216
{
217
struct ircomm_info info;
218
int ret;
219
220
IRDA_DEBUG(2 , "%s()\n", __func__ );
221
222
IRDA_ASSERT(self != NULL, return -1;);
223
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
224
225
self->service_type= service_type;
226
227
info.dlsap_sel = dlsap_sel;
228
info.saddr = saddr;
229
info.daddr = daddr;
230
231
ret = ircomm_do_event(self, IRCOMM_CONNECT_REQUEST, skb, &info);
232
233
return ret;
234
}
235
236
EXPORT_SYMBOL(ircomm_connect_request);
237
238
/*
239
* Function ircomm_connect_indication (self, qos, skb)
240
*
241
* Notify user layer about the incoming connection
242
*
243
*/
244
void ircomm_connect_indication(struct ircomm_cb *self, struct sk_buff *skb,
245
struct ircomm_info *info)
246
{
247
IRDA_DEBUG(2, "%s()\n", __func__ );
248
249
/*
250
* If there are any data hiding in the control channel, we must
251
* deliver it first. The side effect is that the control channel
252
* will be removed from the skb
253
*/
254
if (self->notify.connect_indication)
255
self->notify.connect_indication(self->notify.instance, self,
256
info->qos, info->max_data_size,
257
info->max_header_size, skb);
258
else {
259
IRDA_DEBUG(0, "%s(), missing handler\n", __func__ );
260
}
261
}
262
263
/*
264
* Function ircomm_connect_response (self, userdata, max_sdu_size)
265
*
266
* User accepts connection
267
*
268
*/
269
int ircomm_connect_response(struct ircomm_cb *self, struct sk_buff *userdata)
270
{
271
int ret;
272
273
IRDA_ASSERT(self != NULL, return -1;);
274
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
275
276
IRDA_DEBUG(4, "%s()\n", __func__ );
277
278
ret = ircomm_do_event(self, IRCOMM_CONNECT_RESPONSE, userdata, NULL);
279
280
return ret;
281
}
282
283
EXPORT_SYMBOL(ircomm_connect_response);
284
285
/*
286
* Function connect_confirm (self, skb)
287
*
288
* Notify user layer that the link is now connected
289
*
290
*/
291
void ircomm_connect_confirm(struct ircomm_cb *self, struct sk_buff *skb,
292
struct ircomm_info *info)
293
{
294
IRDA_DEBUG(4, "%s()\n", __func__ );
295
296
if (self->notify.connect_confirm )
297
self->notify.connect_confirm(self->notify.instance,
298
self, info->qos,
299
info->max_data_size,
300
info->max_header_size, skb);
301
else {
302
IRDA_DEBUG(0, "%s(), missing handler\n", __func__ );
303
}
304
}
305
306
/*
307
* Function ircomm_data_request (self, userdata)
308
*
309
* Send IrCOMM data to peer device
310
*
311
*/
312
int ircomm_data_request(struct ircomm_cb *self, struct sk_buff *skb)
313
{
314
int ret;
315
316
IRDA_DEBUG(4, "%s()\n", __func__ );
317
318
IRDA_ASSERT(self != NULL, return -EFAULT;);
319
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;);
320
IRDA_ASSERT(skb != NULL, return -EFAULT;);
321
322
ret = ircomm_do_event(self, IRCOMM_DATA_REQUEST, skb, NULL);
323
324
return ret;
325
}
326
327
EXPORT_SYMBOL(ircomm_data_request);
328
329
/*
330
* Function ircomm_data_indication (self, skb)
331
*
332
* Data arrived, so deliver it to user
333
*
334
*/
335
void ircomm_data_indication(struct ircomm_cb *self, struct sk_buff *skb)
336
{
337
IRDA_DEBUG(4, "%s()\n", __func__ );
338
339
IRDA_ASSERT(skb->len > 0, return;);
340
341
if (self->notify.data_indication)
342
self->notify.data_indication(self->notify.instance, self, skb);
343
else {
344
IRDA_DEBUG(0, "%s(), missing handler\n", __func__ );
345
}
346
}
347
348
/*
349
* Function ircomm_process_data (self, skb)
350
*
351
* Data arrived which may contain control channel data
352
*
353
*/
354
void ircomm_process_data(struct ircomm_cb *self, struct sk_buff *skb)
355
{
356
int clen;
357
358
IRDA_ASSERT(skb->len > 0, return;);
359
360
clen = skb->data[0];
361
362
/*
363
* Input validation check: a stir4200/mcp2150 combinations sometimes
364
* results in frames with clen > remaining packet size. These are
365
* illegal; if we throw away just this frame then it seems to carry on
366
* fine
367
*/
368
if (unlikely(skb->len < (clen + 1))) {
369
IRDA_DEBUG(2, "%s() throwing away illegal frame\n",
370
__func__ );
371
return;
372
}
373
374
/*
375
* If there are any data hiding in the control channel, we must
376
* deliver it first. The side effect is that the control channel
377
* will be removed from the skb
378
*/
379
if (clen > 0)
380
ircomm_control_indication(self, skb, clen);
381
382
/* Remove control channel from data channel */
383
skb_pull(skb, clen+1);
384
385
if (skb->len)
386
ircomm_data_indication(self, skb);
387
else {
388
IRDA_DEBUG(4, "%s(), data was control info only!\n",
389
__func__ );
390
}
391
}
392
393
/*
394
* Function ircomm_control_request (self, params)
395
*
396
* Send control data to peer device
397
*
398
*/
399
int ircomm_control_request(struct ircomm_cb *self, struct sk_buff *skb)
400
{
401
int ret;
402
403
IRDA_DEBUG(2, "%s()\n", __func__ );
404
405
IRDA_ASSERT(self != NULL, return -EFAULT;);
406
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;);
407
IRDA_ASSERT(skb != NULL, return -EFAULT;);
408
409
ret = ircomm_do_event(self, IRCOMM_CONTROL_REQUEST, skb, NULL);
410
411
return ret;
412
}
413
414
EXPORT_SYMBOL(ircomm_control_request);
415
416
/*
417
* Function ircomm_control_indication (self, skb)
418
*
419
* Data has arrived on the control channel
420
*
421
*/
422
static void ircomm_control_indication(struct ircomm_cb *self,
423
struct sk_buff *skb, int clen)
424
{
425
IRDA_DEBUG(2, "%s()\n", __func__ );
426
427
/* Use udata for delivering data on the control channel */
428
if (self->notify.udata_indication) {
429
struct sk_buff *ctrl_skb;
430
431
/* We don't own the skb, so clone it */
432
ctrl_skb = skb_clone(skb, GFP_ATOMIC);
433
if (!ctrl_skb)
434
return;
435
436
/* Remove data channel from control channel */
437
skb_trim(ctrl_skb, clen+1);
438
439
self->notify.udata_indication(self->notify.instance, self,
440
ctrl_skb);
441
442
/* Drop reference count -
443
* see ircomm_tty_control_indication(). */
444
dev_kfree_skb(ctrl_skb);
445
} else {
446
IRDA_DEBUG(0, "%s(), missing handler\n", __func__ );
447
}
448
}
449
450
/*
451
* Function ircomm_disconnect_request (self, userdata, priority)
452
*
453
* User layer wants to disconnect the IrCOMM connection
454
*
455
*/
456
int ircomm_disconnect_request(struct ircomm_cb *self, struct sk_buff *userdata)
457
{
458
struct ircomm_info info;
459
int ret;
460
461
IRDA_DEBUG(2, "%s()\n", __func__ );
462
463
IRDA_ASSERT(self != NULL, return -1;);
464
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
465
466
ret = ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, userdata,
467
&info);
468
return ret;
469
}
470
471
EXPORT_SYMBOL(ircomm_disconnect_request);
472
473
/*
474
* Function disconnect_indication (self, skb)
475
*
476
* Tell user that the link has been disconnected
477
*
478
*/
479
void ircomm_disconnect_indication(struct ircomm_cb *self, struct sk_buff *skb,
480
struct ircomm_info *info)
481
{
482
IRDA_DEBUG(2, "%s()\n", __func__ );
483
484
IRDA_ASSERT(info != NULL, return;);
485
486
if (self->notify.disconnect_indication) {
487
self->notify.disconnect_indication(self->notify.instance, self,
488
info->reason, skb);
489
} else {
490
IRDA_DEBUG(0, "%s(), missing handler\n", __func__ );
491
}
492
}
493
494
/*
495
* Function ircomm_flow_request (self, flow)
496
*
497
*
498
*
499
*/
500
void ircomm_flow_request(struct ircomm_cb *self, LOCAL_FLOW flow)
501
{
502
IRDA_DEBUG(2, "%s()\n", __func__ );
503
504
IRDA_ASSERT(self != NULL, return;);
505
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
506
507
if (self->service_type == IRCOMM_3_WIRE_RAW)
508
return;
509
510
irttp_flow_request(self->tsap, flow);
511
}
512
513
EXPORT_SYMBOL(ircomm_flow_request);
514
515
#ifdef CONFIG_PROC_FS
516
static void *ircomm_seq_start(struct seq_file *seq, loff_t *pos)
517
{
518
struct ircomm_cb *self;
519
loff_t off = 0;
520
521
spin_lock_irq(&ircomm->hb_spinlock);
522
523
for (self = (struct ircomm_cb *) hashbin_get_first(ircomm);
524
self != NULL;
525
self = (struct ircomm_cb *) hashbin_get_next(ircomm)) {
526
if (off++ == *pos)
527
break;
528
529
}
530
return self;
531
}
532
533
static void *ircomm_seq_next(struct seq_file *seq, void *v, loff_t *pos)
534
{
535
++*pos;
536
537
return (void *) hashbin_get_next(ircomm);
538
}
539
540
static void ircomm_seq_stop(struct seq_file *seq, void *v)
541
{
542
spin_unlock_irq(&ircomm->hb_spinlock);
543
}
544
545
static int ircomm_seq_show(struct seq_file *seq, void *v)
546
{
547
const struct ircomm_cb *self = v;
548
549
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EINVAL; );
550
551
if(self->line < 0x10)
552
seq_printf(seq, "ircomm%d", self->line);
553
else
554
seq_printf(seq, "irlpt%d", self->line - 0x10);
555
556
seq_printf(seq,
557
" state: %s, slsap_sel: %#02x, dlsap_sel: %#02x, mode:",
558
ircomm_state[ self->state],
559
self->slsap_sel, self->dlsap_sel);
560
561
if(self->service_type & IRCOMM_3_WIRE_RAW)
562
seq_printf(seq, " 3-wire-raw");
563
if(self->service_type & IRCOMM_3_WIRE)
564
seq_printf(seq, " 3-wire");
565
if(self->service_type & IRCOMM_9_WIRE)
566
seq_printf(seq, " 9-wire");
567
if(self->service_type & IRCOMM_CENTRONICS)
568
seq_printf(seq, " Centronics");
569
seq_putc(seq, '\n');
570
571
return 0;
572
}
573
574
static const struct seq_operations ircomm_seq_ops = {
575
.start = ircomm_seq_start,
576
.next = ircomm_seq_next,
577
.stop = ircomm_seq_stop,
578
.show = ircomm_seq_show,
579
};
580
581
static int ircomm_seq_open(struct inode *inode, struct file *file)
582
{
583
return seq_open(file, &ircomm_seq_ops);
584
}
585
#endif /* CONFIG_PROC_FS */
586
587
MODULE_AUTHOR("Dag Brattli <[email protected]>");
588
MODULE_DESCRIPTION("IrCOMM protocol");
589
MODULE_LICENSE("GPL");
590
591
module_init(ircomm_init);
592
module_exit(ircomm_cleanup);
593
594