diff options
Diffstat (limited to 'drivers/isdn/capi/capi.c')
-rw-r--r-- | drivers/isdn/capi/capi.c | 1554 |
1 files changed, 1554 insertions, 0 deletions
diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c new file mode 100644 index 000000000000..06163538bb20 --- /dev/null +++ b/drivers/isdn/capi/capi.c | |||
@@ -0,0 +1,1554 @@ | |||
1 | /* $Id: capi.c,v 1.1.2.7 2004/04/28 09:48:59 armin Exp $ | ||
2 | * | ||
3 | * CAPI 2.0 Interface for Linux | ||
4 | * | ||
5 | * Copyright 1996 by Carsten Paeth <calle@calle.de> | ||
6 | * | ||
7 | * This software may be used and distributed according to the terms | ||
8 | * of the GNU General Public License, incorporated herein by reference. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/config.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/errno.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/major.h> | ||
17 | #include <linux/sched.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <linux/fcntl.h> | ||
20 | #include <linux/fs.h> | ||
21 | #include <linux/signal.h> | ||
22 | #include <linux/mm.h> | ||
23 | #include <linux/smp_lock.h> | ||
24 | #include <linux/timer.h> | ||
25 | #include <linux/wait.h> | ||
26 | #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE | ||
27 | #include <linux/tty.h> | ||
28 | #ifdef CONFIG_PPP | ||
29 | #include <linux/netdevice.h> | ||
30 | #include <linux/ppp_defs.h> | ||
31 | #include <linux/if_ppp.h> | ||
32 | #endif /* CONFIG_PPP */ | ||
33 | #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ | ||
34 | #include <linux/skbuff.h> | ||
35 | #include <linux/proc_fs.h> | ||
36 | #include <linux/poll.h> | ||
37 | #include <linux/capi.h> | ||
38 | #include <linux/kernelcapi.h> | ||
39 | #include <linux/init.h> | ||
40 | #include <linux/device.h> | ||
41 | #include <linux/moduleparam.h> | ||
42 | #include <linux/devfs_fs_kernel.h> | ||
43 | #include <linux/isdn/capiutil.h> | ||
44 | #include <linux/isdn/capicmd.h> | ||
45 | #if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE) | ||
46 | #include "capifs.h" | ||
47 | #endif | ||
48 | |||
49 | static char *revision = "$Revision: 1.1.2.7 $"; | ||
50 | |||
51 | MODULE_DESCRIPTION("CAPI4Linux: Userspace /dev/capi20 interface"); | ||
52 | MODULE_AUTHOR("Carsten Paeth"); | ||
53 | MODULE_LICENSE("GPL"); | ||
54 | |||
55 | #undef _DEBUG_REFCOUNT /* alloc/free and open/close debug */ | ||
56 | #undef _DEBUG_TTYFUNCS /* call to tty_driver */ | ||
57 | #undef _DEBUG_DATAFLOW /* data flow */ | ||
58 | |||
59 | /* -------- driver information -------------------------------------- */ | ||
60 | |||
61 | static struct class_simple *capi_class; | ||
62 | |||
63 | int capi_major = 68; /* allocated */ | ||
64 | #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE | ||
65 | #define CAPINC_NR_PORTS 32 | ||
66 | #define CAPINC_MAX_PORTS 256 | ||
67 | int capi_ttymajor = 191; | ||
68 | int capi_ttyminors = CAPINC_NR_PORTS; | ||
69 | #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ | ||
70 | |||
71 | module_param_named(major, capi_major, uint, 0); | ||
72 | #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE | ||
73 | module_param_named(ttymajor, capi_ttymajor, uint, 0); | ||
74 | module_param_named(ttyminors, capi_ttyminors, uint, 0); | ||
75 | #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ | ||
76 | |||
77 | /* -------- defines ------------------------------------------------- */ | ||
78 | |||
79 | #define CAPINC_MAX_RECVQUEUE 10 | ||
80 | #define CAPINC_MAX_SENDQUEUE 10 | ||
81 | #define CAPI_MAX_BLKSIZE 2048 | ||
82 | |||
83 | /* -------- data structures ----------------------------------------- */ | ||
84 | |||
85 | struct capidev; | ||
86 | struct capincci; | ||
87 | #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE | ||
88 | struct capiminor; | ||
89 | |||
90 | struct capiminor { | ||
91 | struct list_head list; | ||
92 | struct capincci *nccip; | ||
93 | unsigned int minor; | ||
94 | |||
95 | struct capi20_appl *ap; | ||
96 | u32 ncci; | ||
97 | u16 datahandle; | ||
98 | u16 msgid; | ||
99 | |||
100 | struct tty_struct *tty; | ||
101 | int ttyinstop; | ||
102 | int ttyoutstop; | ||
103 | struct sk_buff *ttyskb; | ||
104 | atomic_t ttyopencount; | ||
105 | |||
106 | struct sk_buff_head inqueue; | ||
107 | int inbytes; | ||
108 | struct sk_buff_head outqueue; | ||
109 | int outbytes; | ||
110 | |||
111 | /* transmit path */ | ||
112 | struct datahandle_queue { | ||
113 | struct datahandle_queue *next; | ||
114 | u16 datahandle; | ||
115 | } *ackqueue; | ||
116 | int nack; | ||
117 | |||
118 | }; | ||
119 | #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ | ||
120 | |||
121 | struct capincci { | ||
122 | struct capincci *next; | ||
123 | u32 ncci; | ||
124 | struct capidev *cdev; | ||
125 | #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE | ||
126 | struct capiminor *minorp; | ||
127 | #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ | ||
128 | }; | ||
129 | |||
130 | struct capidev { | ||
131 | struct list_head list; | ||
132 | struct capi20_appl ap; | ||
133 | u16 errcode; | ||
134 | unsigned userflags; | ||
135 | |||
136 | struct sk_buff_head recvqueue; | ||
137 | wait_queue_head_t recvwait; | ||
138 | |||
139 | struct capincci *nccis; | ||
140 | |||
141 | struct semaphore ncci_list_sem; | ||
142 | }; | ||
143 | |||
144 | /* -------- global variables ---------------------------------------- */ | ||
145 | |||
146 | static DEFINE_RWLOCK(capidev_list_lock); | ||
147 | static LIST_HEAD(capidev_list); | ||
148 | |||
149 | #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE | ||
150 | static DEFINE_RWLOCK(capiminor_list_lock); | ||
151 | static LIST_HEAD(capiminor_list); | ||
152 | #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ | ||
153 | |||
154 | #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE | ||
155 | /* -------- datahandles --------------------------------------------- */ | ||
156 | |||
157 | static int capincci_add_ack(struct capiminor *mp, u16 datahandle) | ||
158 | { | ||
159 | struct datahandle_queue *n, **pp; | ||
160 | |||
161 | n = kmalloc(sizeof(*n), GFP_ATOMIC); | ||
162 | if (!n) { | ||
163 | printk(KERN_ERR "capi: alloc datahandle failed\n"); | ||
164 | return -1; | ||
165 | } | ||
166 | n->next = NULL; | ||
167 | n->datahandle = datahandle; | ||
168 | for (pp = &mp->ackqueue; *pp; pp = &(*pp)->next) ; | ||
169 | *pp = n; | ||
170 | mp->nack++; | ||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | static int capiminor_del_ack(struct capiminor *mp, u16 datahandle) | ||
175 | { | ||
176 | struct datahandle_queue **pp, *p; | ||
177 | |||
178 | for (pp = &mp->ackqueue; *pp; pp = &(*pp)->next) { | ||
179 | if ((*pp)->datahandle == datahandle) { | ||
180 | p = *pp; | ||
181 | *pp = (*pp)->next; | ||
182 | kfree(p); | ||
183 | mp->nack--; | ||
184 | return 0; | ||
185 | } | ||
186 | } | ||
187 | return -1; | ||
188 | } | ||
189 | |||
190 | static void capiminor_del_all_ack(struct capiminor *mp) | ||
191 | { | ||
192 | struct datahandle_queue **pp, *p; | ||
193 | |||
194 | pp = &mp->ackqueue; | ||
195 | while (*pp) { | ||
196 | p = *pp; | ||
197 | *pp = (*pp)->next; | ||
198 | kfree(p); | ||
199 | mp->nack--; | ||
200 | } | ||
201 | } | ||
202 | |||
203 | |||
204 | /* -------- struct capiminor ---------------------------------------- */ | ||
205 | |||
206 | static struct capiminor *capiminor_alloc(struct capi20_appl *ap, u32 ncci) | ||
207 | { | ||
208 | struct capiminor *mp, *p; | ||
209 | unsigned int minor = 0; | ||
210 | unsigned long flags; | ||
211 | |||
212 | mp = kmalloc(sizeof(*mp), GFP_ATOMIC); | ||
213 | if (!mp) { | ||
214 | printk(KERN_ERR "capi: can't alloc capiminor\n"); | ||
215 | return NULL; | ||
216 | } | ||
217 | |||
218 | memset(mp, 0, sizeof(struct capiminor)); | ||
219 | mp->ap = ap; | ||
220 | mp->ncci = ncci; | ||
221 | mp->msgid = 0; | ||
222 | atomic_set(&mp->ttyopencount,0); | ||
223 | |||
224 | skb_queue_head_init(&mp->inqueue); | ||
225 | skb_queue_head_init(&mp->outqueue); | ||
226 | |||
227 | /* Allocate the least unused minor number. | ||
228 | */ | ||
229 | write_lock_irqsave(&capiminor_list_lock, flags); | ||
230 | if (list_empty(&capiminor_list)) | ||
231 | list_add(&mp->list, &capiminor_list); | ||
232 | else { | ||
233 | list_for_each_entry(p, &capiminor_list, list) { | ||
234 | if (p->minor > minor) | ||
235 | break; | ||
236 | minor++; | ||
237 | } | ||
238 | |||
239 | if (minor < capi_ttyminors) { | ||
240 | mp->minor = minor; | ||
241 | list_add(&mp->list, p->list.prev); | ||
242 | } | ||
243 | } | ||
244 | write_unlock_irqrestore(&capiminor_list_lock, flags); | ||
245 | |||
246 | if (!(minor < capi_ttyminors)) { | ||
247 | printk(KERN_NOTICE "capi: out of minors\n"); | ||
248 | kfree(mp); | ||
249 | return NULL; | ||
250 | } | ||
251 | |||
252 | return mp; | ||
253 | } | ||
254 | |||
255 | static void capiminor_free(struct capiminor *mp) | ||
256 | { | ||
257 | unsigned long flags; | ||
258 | |||
259 | write_lock_irqsave(&capiminor_list_lock, flags); | ||
260 | list_del(&mp->list); | ||
261 | write_unlock_irqrestore(&capiminor_list_lock, flags); | ||
262 | |||
263 | if (mp->ttyskb) kfree_skb(mp->ttyskb); | ||
264 | mp->ttyskb = NULL; | ||
265 | skb_queue_purge(&mp->inqueue); | ||
266 | skb_queue_purge(&mp->outqueue); | ||
267 | capiminor_del_all_ack(mp); | ||
268 | kfree(mp); | ||
269 | } | ||
270 | |||
271 | struct capiminor *capiminor_find(unsigned int minor) | ||
272 | { | ||
273 | struct list_head *l; | ||
274 | struct capiminor *p = NULL; | ||
275 | |||
276 | read_lock(&capiminor_list_lock); | ||
277 | list_for_each(l, &capiminor_list) { | ||
278 | p = list_entry(l, struct capiminor, list); | ||
279 | if (p->minor == minor) | ||
280 | break; | ||
281 | } | ||
282 | read_unlock(&capiminor_list_lock); | ||
283 | if (l == &capiminor_list) | ||
284 | return NULL; | ||
285 | |||
286 | return p; | ||
287 | } | ||
288 | #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ | ||
289 | |||
290 | /* -------- struct capincci ----------------------------------------- */ | ||
291 | |||
292 | static struct capincci *capincci_alloc(struct capidev *cdev, u32 ncci) | ||
293 | { | ||
294 | struct capincci *np, **pp; | ||
295 | #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE | ||
296 | struct capiminor *mp = NULL; | ||
297 | #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ | ||
298 | |||
299 | np = kmalloc(sizeof(*np), GFP_ATOMIC); | ||
300 | if (!np) | ||
301 | return NULL; | ||
302 | memset(np, 0, sizeof(struct capincci)); | ||
303 | np->ncci = ncci; | ||
304 | np->cdev = cdev; | ||
305 | #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE | ||
306 | mp = NULL; | ||
307 | if (cdev->userflags & CAPIFLAG_HIGHJACKING) | ||
308 | mp = np->minorp = capiminor_alloc(&cdev->ap, ncci); | ||
309 | if (mp) { | ||
310 | mp->nccip = np; | ||
311 | #ifdef _DEBUG_REFCOUNT | ||
312 | printk(KERN_DEBUG "set mp->nccip\n"); | ||
313 | #endif | ||
314 | #if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE) | ||
315 | capifs_new_ncci(mp->minor, MKDEV(capi_ttymajor, mp->minor)); | ||
316 | #endif | ||
317 | } | ||
318 | #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ | ||
319 | for (pp=&cdev->nccis; *pp; pp = &(*pp)->next) | ||
320 | ; | ||
321 | *pp = np; | ||
322 | return np; | ||
323 | } | ||
324 | |||
325 | static void capincci_free(struct capidev *cdev, u32 ncci) | ||
326 | { | ||
327 | struct capincci *np, **pp; | ||
328 | #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE | ||
329 | struct capiminor *mp; | ||
330 | #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ | ||
331 | |||
332 | pp=&cdev->nccis; | ||
333 | while (*pp) { | ||
334 | np = *pp; | ||
335 | if (ncci == 0xffffffff || np->ncci == ncci) { | ||
336 | *pp = (*pp)->next; | ||
337 | #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE | ||
338 | if ((mp = np->minorp) != 0) { | ||
339 | #if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE) | ||
340 | capifs_free_ncci(mp->minor); | ||
341 | #endif | ||
342 | if (mp->tty) { | ||
343 | mp->nccip = NULL; | ||
344 | #ifdef _DEBUG_REFCOUNT | ||
345 | printk(KERN_DEBUG "reset mp->nccip\n"); | ||
346 | #endif | ||
347 | tty_hangup(mp->tty); | ||
348 | } else { | ||
349 | capiminor_free(mp); | ||
350 | } | ||
351 | } | ||
352 | #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ | ||
353 | kfree(np); | ||
354 | if (*pp == 0) return; | ||
355 | } else { | ||
356 | pp = &(*pp)->next; | ||
357 | } | ||
358 | } | ||
359 | } | ||
360 | |||
361 | static struct capincci *capincci_find(struct capidev *cdev, u32 ncci) | ||
362 | { | ||
363 | struct capincci *p; | ||
364 | |||
365 | for (p=cdev->nccis; p ; p = p->next) { | ||
366 | if (p->ncci == ncci) | ||
367 | break; | ||
368 | } | ||
369 | return p; | ||
370 | } | ||
371 | |||
372 | /* -------- struct capidev ------------------------------------------ */ | ||
373 | |||
374 | static struct capidev *capidev_alloc(void) | ||
375 | { | ||
376 | struct capidev *cdev; | ||
377 | unsigned long flags; | ||
378 | |||
379 | cdev = kmalloc(sizeof(*cdev), GFP_KERNEL); | ||
380 | if (!cdev) | ||
381 | return NULL; | ||
382 | memset(cdev, 0, sizeof(struct capidev)); | ||
383 | |||
384 | init_MUTEX(&cdev->ncci_list_sem); | ||
385 | skb_queue_head_init(&cdev->recvqueue); | ||
386 | init_waitqueue_head(&cdev->recvwait); | ||
387 | write_lock_irqsave(&capidev_list_lock, flags); | ||
388 | list_add_tail(&cdev->list, &capidev_list); | ||
389 | write_unlock_irqrestore(&capidev_list_lock, flags); | ||
390 | return cdev; | ||
391 | } | ||
392 | |||
393 | static void capidev_free(struct capidev *cdev) | ||
394 | { | ||
395 | unsigned long flags; | ||
396 | |||
397 | if (cdev->ap.applid) { | ||
398 | capi20_release(&cdev->ap); | ||
399 | cdev->ap.applid = 0; | ||
400 | } | ||
401 | skb_queue_purge(&cdev->recvqueue); | ||
402 | |||
403 | down(&cdev->ncci_list_sem); | ||
404 | capincci_free(cdev, 0xffffffff); | ||
405 | up(&cdev->ncci_list_sem); | ||
406 | |||
407 | write_lock_irqsave(&capidev_list_lock, flags); | ||
408 | list_del(&cdev->list); | ||
409 | write_unlock_irqrestore(&capidev_list_lock, flags); | ||
410 | kfree(cdev); | ||
411 | } | ||
412 | |||
413 | #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE | ||
414 | /* -------- handle data queue --------------------------------------- */ | ||
415 | |||
416 | static struct sk_buff * | ||
417 | gen_data_b3_resp_for(struct capiminor *mp, struct sk_buff *skb) | ||
418 | { | ||
419 | struct sk_buff *nskb; | ||
420 | nskb = alloc_skb(CAPI_DATA_B3_RESP_LEN, GFP_ATOMIC); | ||
421 | if (nskb) { | ||
422 | u16 datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4+4+2); | ||
423 | unsigned char *s = skb_put(nskb, CAPI_DATA_B3_RESP_LEN); | ||
424 | capimsg_setu16(s, 0, CAPI_DATA_B3_RESP_LEN); | ||
425 | capimsg_setu16(s, 2, mp->ap->applid); | ||
426 | capimsg_setu8 (s, 4, CAPI_DATA_B3); | ||
427 | capimsg_setu8 (s, 5, CAPI_RESP); | ||
428 | capimsg_setu16(s, 6, mp->msgid++); | ||
429 | capimsg_setu32(s, 8, mp->ncci); | ||
430 | capimsg_setu16(s, 12, datahandle); | ||
431 | } | ||
432 | return nskb; | ||
433 | } | ||
434 | |||
435 | static int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb) | ||
436 | { | ||
437 | struct sk_buff *nskb; | ||
438 | int datalen; | ||
439 | u16 errcode, datahandle; | ||
440 | struct tty_ldisc *ld; | ||
441 | |||
442 | datalen = skb->len - CAPIMSG_LEN(skb->data); | ||
443 | if (mp->tty == NULL) | ||
444 | { | ||
445 | #ifdef _DEBUG_DATAFLOW | ||
446 | printk(KERN_DEBUG "capi: currently no receiver\n"); | ||
447 | #endif | ||
448 | return -1; | ||
449 | } | ||
450 | |||
451 | ld = tty_ldisc_ref(mp->tty); | ||
452 | if (ld == NULL) | ||
453 | return -1; | ||
454 | if (ld->receive_buf == NULL) { | ||
455 | #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) | ||
456 | printk(KERN_DEBUG "capi: ldisc has no receive_buf function\n"); | ||
457 | #endif | ||
458 | goto bad; | ||
459 | } | ||
460 | if (mp->ttyinstop) { | ||
461 | #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) | ||
462 | printk(KERN_DEBUG "capi: recv tty throttled\n"); | ||
463 | #endif | ||
464 | goto bad; | ||
465 | } | ||
466 | if (ld->receive_room && | ||
467 | ld->receive_room(mp->tty) < datalen) { | ||
468 | #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) | ||
469 | printk(KERN_DEBUG "capi: no room in tty\n"); | ||
470 | #endif | ||
471 | goto bad; | ||
472 | } | ||
473 | if ((nskb = gen_data_b3_resp_for(mp, skb)) == 0) { | ||
474 | printk(KERN_ERR "capi: gen_data_b3_resp failed\n"); | ||
475 | goto bad; | ||
476 | } | ||
477 | datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4); | ||
478 | errcode = capi20_put_message(mp->ap, nskb); | ||
479 | if (errcode != CAPI_NOERROR) { | ||
480 | printk(KERN_ERR "capi: send DATA_B3_RESP failed=%x\n", | ||
481 | errcode); | ||
482 | kfree_skb(nskb); | ||
483 | goto bad; | ||
484 | } | ||
485 | (void)skb_pull(skb, CAPIMSG_LEN(skb->data)); | ||
486 | #ifdef _DEBUG_DATAFLOW | ||
487 | printk(KERN_DEBUG "capi: DATA_B3_RESP %u len=%d => ldisc\n", | ||
488 | datahandle, skb->len); | ||
489 | #endif | ||
490 | ld->receive_buf(mp->tty, skb->data, NULL, skb->len); | ||
491 | kfree_skb(skb); | ||
492 | tty_ldisc_deref(ld); | ||
493 | return 0; | ||
494 | bad: | ||
495 | tty_ldisc_deref(ld); | ||
496 | return -1; | ||
497 | } | ||
498 | |||
499 | static void handle_minor_recv(struct capiminor *mp) | ||
500 | { | ||
501 | struct sk_buff *skb; | ||
502 | while ((skb = skb_dequeue(&mp->inqueue)) != 0) { | ||
503 | unsigned int len = skb->len; | ||
504 | mp->inbytes -= len; | ||
505 | if (handle_recv_skb(mp, skb) < 0) { | ||
506 | skb_queue_head(&mp->inqueue, skb); | ||
507 | mp->inbytes += len; | ||
508 | return; | ||
509 | } | ||
510 | } | ||
511 | } | ||
512 | |||
513 | static int handle_minor_send(struct capiminor *mp) | ||
514 | { | ||
515 | struct sk_buff *skb; | ||
516 | u16 len; | ||
517 | int count = 0; | ||
518 | u16 errcode; | ||
519 | u16 datahandle; | ||
520 | |||
521 | if (mp->tty && mp->ttyoutstop) { | ||
522 | #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) | ||
523 | printk(KERN_DEBUG "capi: send: tty stopped\n"); | ||
524 | #endif | ||
525 | return 0; | ||
526 | } | ||
527 | |||
528 | while ((skb = skb_dequeue(&mp->outqueue)) != 0) { | ||
529 | datahandle = mp->datahandle; | ||
530 | len = (u16)skb->len; | ||
531 | skb_push(skb, CAPI_DATA_B3_REQ_LEN); | ||
532 | memset(skb->data, 0, CAPI_DATA_B3_REQ_LEN); | ||
533 | capimsg_setu16(skb->data, 0, CAPI_DATA_B3_REQ_LEN); | ||
534 | capimsg_setu16(skb->data, 2, mp->ap->applid); | ||
535 | capimsg_setu8 (skb->data, 4, CAPI_DATA_B3); | ||
536 | capimsg_setu8 (skb->data, 5, CAPI_REQ); | ||
537 | capimsg_setu16(skb->data, 6, mp->msgid++); | ||
538 | capimsg_setu32(skb->data, 8, mp->ncci); /* NCCI */ | ||
539 | capimsg_setu32(skb->data, 12, (u32) skb->data); /* Data32 */ | ||
540 | capimsg_setu16(skb->data, 16, len); /* Data length */ | ||
541 | capimsg_setu16(skb->data, 18, datahandle); | ||
542 | capimsg_setu16(skb->data, 20, 0); /* Flags */ | ||
543 | |||
544 | if (capincci_add_ack(mp, datahandle) < 0) { | ||
545 | skb_pull(skb, CAPI_DATA_B3_REQ_LEN); | ||
546 | skb_queue_head(&mp->outqueue, skb); | ||
547 | return count; | ||
548 | } | ||
549 | errcode = capi20_put_message(mp->ap, skb); | ||
550 | if (errcode == CAPI_NOERROR) { | ||
551 | mp->datahandle++; | ||
552 | count++; | ||
553 | mp->outbytes -= len; | ||
554 | #ifdef _DEBUG_DATAFLOW | ||
555 | printk(KERN_DEBUG "capi: DATA_B3_REQ %u len=%u\n", | ||
556 | datahandle, len); | ||
557 | #endif | ||
558 | continue; | ||
559 | } | ||
560 | capiminor_del_ack(mp, datahandle); | ||
561 | |||
562 | if (errcode == CAPI_SENDQUEUEFULL) { | ||
563 | skb_pull(skb, CAPI_DATA_B3_REQ_LEN); | ||
564 | skb_queue_head(&mp->outqueue, skb); | ||
565 | break; | ||
566 | } | ||
567 | |||
568 | /* ups, drop packet */ | ||
569 | printk(KERN_ERR "capi: put_message = %x\n", errcode); | ||
570 | mp->outbytes -= len; | ||
571 | kfree_skb(skb); | ||
572 | } | ||
573 | return count; | ||
574 | } | ||
575 | |||
576 | #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ | ||
577 | /* -------- function called by lower level -------------------------- */ | ||
578 | |||
579 | static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb) | ||
580 | { | ||
581 | struct capidev *cdev = ap->private; | ||
582 | #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE | ||
583 | struct capiminor *mp; | ||
584 | u16 datahandle; | ||
585 | #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ | ||
586 | struct capincci *np; | ||
587 | u32 ncci; | ||
588 | |||
589 | if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_CONF) { | ||
590 | u16 info = CAPIMSG_U16(skb->data, 12); // Info field | ||
591 | if (info == 0) { | ||
592 | down(&cdev->ncci_list_sem); | ||
593 | capincci_alloc(cdev, CAPIMSG_NCCI(skb->data)); | ||
594 | up(&cdev->ncci_list_sem); | ||
595 | } | ||
596 | } | ||
597 | if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_IND) { | ||
598 | down(&cdev->ncci_list_sem); | ||
599 | capincci_alloc(cdev, CAPIMSG_NCCI(skb->data)); | ||
600 | up(&cdev->ncci_list_sem); | ||
601 | } | ||
602 | if (CAPIMSG_COMMAND(skb->data) != CAPI_DATA_B3) { | ||
603 | skb_queue_tail(&cdev->recvqueue, skb); | ||
604 | wake_up_interruptible(&cdev->recvwait); | ||
605 | return; | ||
606 | } | ||
607 | ncci = CAPIMSG_CONTROL(skb->data); | ||
608 | for (np = cdev->nccis; np && np->ncci != ncci; np = np->next) | ||
609 | ; | ||
610 | if (!np) { | ||
611 | printk(KERN_ERR "BUG: capi_signal: ncci not found\n"); | ||
612 | skb_queue_tail(&cdev->recvqueue, skb); | ||
613 | wake_up_interruptible(&cdev->recvwait); | ||
614 | return; | ||
615 | } | ||
616 | #ifndef CONFIG_ISDN_CAPI_MIDDLEWARE | ||
617 | skb_queue_tail(&cdev->recvqueue, skb); | ||
618 | wake_up_interruptible(&cdev->recvwait); | ||
619 | #else /* CONFIG_ISDN_CAPI_MIDDLEWARE */ | ||
620 | mp = np->minorp; | ||
621 | if (!mp) { | ||
622 | skb_queue_tail(&cdev->recvqueue, skb); | ||
623 | wake_up_interruptible(&cdev->recvwait); | ||
624 | return; | ||
625 | } | ||
626 | |||
627 | |||
628 | if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND) { | ||
629 | |||
630 | datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4+4+2); | ||
631 | #ifdef _DEBUG_DATAFLOW | ||
632 | printk(KERN_DEBUG "capi_signal: DATA_B3_IND %u len=%d\n", | ||
633 | datahandle, skb->len-CAPIMSG_LEN(skb->data)); | ||
634 | #endif | ||
635 | skb_queue_tail(&mp->inqueue, skb); | ||
636 | mp->inbytes += skb->len; | ||
637 | handle_minor_recv(mp); | ||
638 | |||
639 | } else if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_CONF) { | ||
640 | |||
641 | datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4); | ||
642 | #ifdef _DEBUG_DATAFLOW | ||
643 | printk(KERN_DEBUG "capi_signal: DATA_B3_CONF %u 0x%x\n", | ||
644 | datahandle, | ||
645 | CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4+2)); | ||
646 | #endif | ||
647 | kfree_skb(skb); | ||
648 | (void)capiminor_del_ack(mp, datahandle); | ||
649 | if (mp->tty) | ||
650 | tty_wakeup(mp->tty); | ||
651 | (void)handle_minor_send(mp); | ||
652 | |||
653 | } else { | ||
654 | /* ups, let capi application handle it :-) */ | ||
655 | skb_queue_tail(&cdev->recvqueue, skb); | ||
656 | wake_up_interruptible(&cdev->recvwait); | ||
657 | } | ||
658 | #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ | ||
659 | } | ||
660 | |||
661 | /* -------- file_operations for capidev ----------------------------- */ | ||
662 | |||
663 | static ssize_t | ||
664 | capi_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | ||
665 | { | ||
666 | struct capidev *cdev = (struct capidev *)file->private_data; | ||
667 | struct sk_buff *skb; | ||
668 | size_t copied; | ||
669 | |||
670 | if (!cdev->ap.applid) | ||
671 | return -ENODEV; | ||
672 | |||
673 | if ((skb = skb_dequeue(&cdev->recvqueue)) == 0) { | ||
674 | |||
675 | if (file->f_flags & O_NONBLOCK) | ||
676 | return -EAGAIN; | ||
677 | |||
678 | for (;;) { | ||
679 | interruptible_sleep_on(&cdev->recvwait); | ||
680 | if ((skb = skb_dequeue(&cdev->recvqueue)) != 0) | ||
681 | break; | ||
682 | if (signal_pending(current)) | ||
683 | break; | ||
684 | } | ||
685 | if (skb == 0) | ||
686 | return -ERESTARTNOHAND; | ||
687 | } | ||
688 | if (skb->len > count) { | ||
689 | skb_queue_head(&cdev->recvqueue, skb); | ||
690 | return -EMSGSIZE; | ||
691 | } | ||
692 | if (copy_to_user(buf, skb->data, skb->len)) { | ||
693 | skb_queue_head(&cdev->recvqueue, skb); | ||
694 | return -EFAULT; | ||
695 | } | ||
696 | copied = skb->len; | ||
697 | |||
698 | kfree_skb(skb); | ||
699 | |||
700 | return copied; | ||
701 | } | ||
702 | |||
703 | static ssize_t | ||
704 | capi_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) | ||
705 | { | ||
706 | struct capidev *cdev = (struct capidev *)file->private_data; | ||
707 | struct sk_buff *skb; | ||
708 | u16 mlen; | ||
709 | |||
710 | if (!cdev->ap.applid) | ||
711 | return -ENODEV; | ||
712 | |||
713 | skb = alloc_skb(count, GFP_USER); | ||
714 | if (!skb) | ||
715 | return -ENOMEM; | ||
716 | |||
717 | if (copy_from_user(skb_put(skb, count), buf, count)) { | ||
718 | kfree_skb(skb); | ||
719 | return -EFAULT; | ||
720 | } | ||
721 | mlen = CAPIMSG_LEN(skb->data); | ||
722 | if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) { | ||
723 | if ((size_t)(mlen + CAPIMSG_DATALEN(skb->data)) != count) { | ||
724 | kfree_skb(skb); | ||
725 | return -EINVAL; | ||
726 | } | ||
727 | } else { | ||
728 | if (mlen != count) { | ||
729 | kfree_skb(skb); | ||
730 | return -EINVAL; | ||
731 | } | ||
732 | } | ||
733 | CAPIMSG_SETAPPID(skb->data, cdev->ap.applid); | ||
734 | |||
735 | if (CAPIMSG_CMD(skb->data) == CAPI_DISCONNECT_B3_RESP) { | ||
736 | down(&cdev->ncci_list_sem); | ||
737 | capincci_free(cdev, CAPIMSG_NCCI(skb->data)); | ||
738 | up(&cdev->ncci_list_sem); | ||
739 | } | ||
740 | |||
741 | cdev->errcode = capi20_put_message(&cdev->ap, skb); | ||
742 | |||
743 | if (cdev->errcode) { | ||
744 | kfree_skb(skb); | ||
745 | return -EIO; | ||
746 | } | ||
747 | return count; | ||
748 | } | ||
749 | |||
750 | static unsigned int | ||
751 | capi_poll(struct file *file, poll_table * wait) | ||
752 | { | ||
753 | struct capidev *cdev = (struct capidev *)file->private_data; | ||
754 | unsigned int mask = 0; | ||
755 | |||
756 | if (!cdev->ap.applid) | ||
757 | return POLLERR; | ||
758 | |||
759 | poll_wait(file, &(cdev->recvwait), wait); | ||
760 | mask = POLLOUT | POLLWRNORM; | ||
761 | if (!skb_queue_empty(&cdev->recvqueue)) | ||
762 | mask |= POLLIN | POLLRDNORM; | ||
763 | return mask; | ||
764 | } | ||
765 | |||
766 | static int | ||
767 | capi_ioctl(struct inode *inode, struct file *file, | ||
768 | unsigned int cmd, unsigned long arg) | ||
769 | { | ||
770 | struct capidev *cdev = file->private_data; | ||
771 | struct capi20_appl *ap = &cdev->ap; | ||
772 | capi_ioctl_struct data; | ||
773 | int retval = -EINVAL; | ||
774 | void __user *argp = (void __user *)arg; | ||
775 | |||
776 | switch (cmd) { | ||
777 | case CAPI_REGISTER: | ||
778 | { | ||
779 | if (ap->applid) | ||
780 | return -EEXIST; | ||
781 | |||
782 | if (copy_from_user(&cdev->ap.rparam, argp, | ||
783 | sizeof(struct capi_register_params))) | ||
784 | return -EFAULT; | ||
785 | |||
786 | cdev->ap.private = cdev; | ||
787 | cdev->ap.recv_message = capi_recv_message; | ||
788 | cdev->errcode = capi20_register(ap); | ||
789 | if (cdev->errcode) { | ||
790 | ap->applid = 0; | ||
791 | return -EIO; | ||
792 | } | ||
793 | } | ||
794 | return (int)ap->applid; | ||
795 | |||
796 | case CAPI_GET_VERSION: | ||
797 | { | ||
798 | if (copy_from_user(&data.contr, argp, | ||
799 | sizeof(data.contr))) | ||
800 | return -EFAULT; | ||
801 | cdev->errcode = capi20_get_version(data.contr, &data.version); | ||
802 | if (cdev->errcode) | ||
803 | return -EIO; | ||
804 | if (copy_to_user(argp, &data.version, | ||
805 | sizeof(data.version))) | ||
806 | return -EFAULT; | ||
807 | } | ||
808 | return 0; | ||
809 | |||
810 | case CAPI_GET_SERIAL: | ||
811 | { | ||
812 | if (copy_from_user(&data.contr, argp, | ||
813 | sizeof(data.contr))) | ||
814 | return -EFAULT; | ||
815 | cdev->errcode = capi20_get_serial (data.contr, data.serial); | ||
816 | if (cdev->errcode) | ||
817 | return -EIO; | ||
818 | if (copy_to_user(argp, data.serial, | ||
819 | sizeof(data.serial))) | ||
820 | return -EFAULT; | ||
821 | } | ||
822 | return 0; | ||
823 | case CAPI_GET_PROFILE: | ||
824 | { | ||
825 | if (copy_from_user(&data.contr, argp, | ||
826 | sizeof(data.contr))) | ||
827 | return -EFAULT; | ||
828 | |||
829 | if (data.contr == 0) { | ||
830 | cdev->errcode = capi20_get_profile(data.contr, &data.profile); | ||
831 | if (cdev->errcode) | ||
832 | return -EIO; | ||
833 | |||
834 | retval = copy_to_user(argp, | ||
835 | &data.profile.ncontroller, | ||
836 | sizeof(data.profile.ncontroller)); | ||
837 | |||
838 | } else { | ||
839 | cdev->errcode = capi20_get_profile(data.contr, &data.profile); | ||
840 | if (cdev->errcode) | ||
841 | return -EIO; | ||
842 | |||
843 | retval = copy_to_user(argp, &data.profile, | ||
844 | sizeof(data.profile)); | ||
845 | } | ||
846 | if (retval) | ||
847 | return -EFAULT; | ||
848 | } | ||
849 | return 0; | ||
850 | |||
851 | case CAPI_GET_MANUFACTURER: | ||
852 | { | ||
853 | if (copy_from_user(&data.contr, argp, | ||
854 | sizeof(data.contr))) | ||
855 | return -EFAULT; | ||
856 | cdev->errcode = capi20_get_manufacturer(data.contr, data.manufacturer); | ||
857 | if (cdev->errcode) | ||
858 | return -EIO; | ||
859 | |||
860 | if (copy_to_user(argp, data.manufacturer, | ||
861 | sizeof(data.manufacturer))) | ||
862 | return -EFAULT; | ||
863 | |||
864 | } | ||
865 | return 0; | ||
866 | case CAPI_GET_ERRCODE: | ||
867 | data.errcode = cdev->errcode; | ||
868 | cdev->errcode = CAPI_NOERROR; | ||
869 | if (arg) { | ||
870 | if (copy_to_user(argp, &data.errcode, | ||
871 | sizeof(data.errcode))) | ||
872 | return -EFAULT; | ||
873 | } | ||
874 | return data.errcode; | ||
875 | |||
876 | case CAPI_INSTALLED: | ||
877 | if (capi20_isinstalled() == CAPI_NOERROR) | ||
878 | return 0; | ||
879 | return -ENXIO; | ||
880 | |||
881 | case CAPI_MANUFACTURER_CMD: | ||
882 | { | ||
883 | struct capi_manufacturer_cmd mcmd; | ||
884 | if (!capable(CAP_SYS_ADMIN)) | ||
885 | return -EPERM; | ||
886 | if (copy_from_user(&mcmd, argp, sizeof(mcmd))) | ||
887 | return -EFAULT; | ||
888 | return capi20_manufacturer(mcmd.cmd, mcmd.data); | ||
889 | } | ||
890 | return 0; | ||
891 | |||
892 | case CAPI_SET_FLAGS: | ||
893 | case CAPI_CLR_FLAGS: | ||
894 | { | ||
895 | unsigned userflags; | ||
896 | if (copy_from_user(&userflags, argp, | ||
897 | sizeof(userflags))) | ||
898 | return -EFAULT; | ||
899 | if (cmd == CAPI_SET_FLAGS) | ||
900 | cdev->userflags |= userflags; | ||
901 | else | ||
902 | cdev->userflags &= ~userflags; | ||
903 | } | ||
904 | return 0; | ||
905 | |||
906 | case CAPI_GET_FLAGS: | ||
907 | if (copy_to_user(argp, &cdev->userflags, | ||
908 | sizeof(cdev->userflags))) | ||
909 | return -EFAULT; | ||
910 | return 0; | ||
911 | |||
912 | case CAPI_NCCI_OPENCOUNT: | ||
913 | { | ||
914 | struct capincci *nccip; | ||
915 | #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE | ||
916 | struct capiminor *mp; | ||
917 | #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ | ||
918 | unsigned ncci; | ||
919 | int count = 0; | ||
920 | if (copy_from_user(&ncci, argp, sizeof(ncci))) | ||
921 | return -EFAULT; | ||
922 | |||
923 | down(&cdev->ncci_list_sem); | ||
924 | if ((nccip = capincci_find(cdev, (u32) ncci)) == 0) { | ||
925 | up(&cdev->ncci_list_sem); | ||
926 | return 0; | ||
927 | } | ||
928 | #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE | ||
929 | if ((mp = nccip->minorp) != 0) { | ||
930 | count += atomic_read(&mp->ttyopencount); | ||
931 | } | ||
932 | #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ | ||
933 | up(&cdev->ncci_list_sem); | ||
934 | return count; | ||
935 | } | ||
936 | return 0; | ||
937 | |||
938 | #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE | ||
939 | case CAPI_NCCI_GETUNIT: | ||
940 | { | ||
941 | struct capincci *nccip; | ||
942 | struct capiminor *mp; | ||
943 | unsigned ncci; | ||
944 | int unit = 0; | ||
945 | if (copy_from_user(&ncci, argp, | ||
946 | sizeof(ncci))) | ||
947 | return -EFAULT; | ||
948 | down(&cdev->ncci_list_sem); | ||
949 | nccip = capincci_find(cdev, (u32) ncci); | ||
950 | if (!nccip || (mp = nccip->minorp) == 0) { | ||
951 | up(&cdev->ncci_list_sem); | ||
952 | return -ESRCH; | ||
953 | } | ||
954 | unit = mp->minor; | ||
955 | up(&cdev->ncci_list_sem); | ||
956 | return unit; | ||
957 | } | ||
958 | return 0; | ||
959 | #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ | ||
960 | } | ||
961 | return -EINVAL; | ||
962 | } | ||
963 | |||
964 | static int | ||
965 | capi_open(struct inode *inode, struct file *file) | ||
966 | { | ||
967 | if (file->private_data) | ||
968 | return -EEXIST; | ||
969 | |||
970 | if ((file->private_data = capidev_alloc()) == 0) | ||
971 | return -ENOMEM; | ||
972 | |||
973 | return nonseekable_open(inode, file); | ||
974 | } | ||
975 | |||
976 | static int | ||
977 | capi_release(struct inode *inode, struct file *file) | ||
978 | { | ||
979 | struct capidev *cdev = (struct capidev *)file->private_data; | ||
980 | |||
981 | capidev_free(cdev); | ||
982 | file->private_data = NULL; | ||
983 | |||
984 | return 0; | ||
985 | } | ||
986 | |||
987 | static struct file_operations capi_fops = | ||
988 | { | ||
989 | .owner = THIS_MODULE, | ||
990 | .llseek = no_llseek, | ||
991 | .read = capi_read, | ||
992 | .write = capi_write, | ||
993 | .poll = capi_poll, | ||
994 | .ioctl = capi_ioctl, | ||
995 | .open = capi_open, | ||
996 | .release = capi_release, | ||
997 | }; | ||
998 | |||
999 | #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE | ||
1000 | /* -------- tty_operations for capincci ----------------------------- */ | ||
1001 | |||
1002 | static int capinc_tty_open(struct tty_struct * tty, struct file * file) | ||
1003 | { | ||
1004 | struct capiminor *mp; | ||
1005 | |||
1006 | if ((mp = capiminor_find(iminor(file->f_dentry->d_inode))) == 0) | ||
1007 | return -ENXIO; | ||
1008 | if (mp->nccip == 0) | ||
1009 | return -ENXIO; | ||
1010 | |||
1011 | tty->driver_data = (void *)mp; | ||
1012 | |||
1013 | if (atomic_read(&mp->ttyopencount) == 0) | ||
1014 | mp->tty = tty; | ||
1015 | atomic_inc(&mp->ttyopencount); | ||
1016 | #ifdef _DEBUG_REFCOUNT | ||
1017 | printk(KERN_DEBUG "capinc_tty_open ocount=%d\n", atomic_read(&mp->ttyopencount)); | ||
1018 | #endif | ||
1019 | handle_minor_recv(mp); | ||
1020 | return 0; | ||
1021 | } | ||
1022 | |||
1023 | static void capinc_tty_close(struct tty_struct * tty, struct file * file) | ||
1024 | { | ||
1025 | struct capiminor *mp; | ||
1026 | |||
1027 | mp = (struct capiminor *)tty->driver_data; | ||
1028 | if (mp) { | ||
1029 | if (atomic_dec_and_test(&mp->ttyopencount)) { | ||
1030 | #ifdef _DEBUG_REFCOUNT | ||
1031 | printk(KERN_DEBUG "capinc_tty_close lastclose\n"); | ||
1032 | #endif | ||
1033 | tty->driver_data = NULL; | ||
1034 | mp->tty = NULL; | ||
1035 | } | ||
1036 | #ifdef _DEBUG_REFCOUNT | ||
1037 | printk(KERN_DEBUG "capinc_tty_close ocount=%d\n", atomic_read(&mp->ttyopencount)); | ||
1038 | #endif | ||
1039 | if (mp->nccip == 0) | ||
1040 | capiminor_free(mp); | ||
1041 | } | ||
1042 | |||
1043 | #ifdef _DEBUG_REFCOUNT | ||
1044 | printk(KERN_DEBUG "capinc_tty_close\n"); | ||
1045 | #endif | ||
1046 | } | ||
1047 | |||
1048 | static int capinc_tty_write(struct tty_struct * tty, | ||
1049 | const unsigned char *buf, int count) | ||
1050 | { | ||
1051 | struct capiminor *mp = (struct capiminor *)tty->driver_data; | ||
1052 | struct sk_buff *skb; | ||
1053 | |||
1054 | #ifdef _DEBUG_TTYFUNCS | ||
1055 | printk(KERN_DEBUG "capinc_tty_write(count=%d)\n", count); | ||
1056 | #endif | ||
1057 | |||
1058 | if (!mp || !mp->nccip) { | ||
1059 | #ifdef _DEBUG_TTYFUNCS | ||
1060 | printk(KERN_DEBUG "capinc_tty_write: mp or mp->ncci NULL\n"); | ||
1061 | #endif | ||
1062 | return 0; | ||
1063 | } | ||
1064 | |||
1065 | skb = mp->ttyskb; | ||
1066 | if (skb) { | ||
1067 | mp->ttyskb = NULL; | ||
1068 | skb_queue_tail(&mp->outqueue, skb); | ||
1069 | mp->outbytes += skb->len; | ||
1070 | } | ||
1071 | |||
1072 | skb = alloc_skb(CAPI_DATA_B3_REQ_LEN+count, GFP_ATOMIC); | ||
1073 | if (!skb) { | ||
1074 | printk(KERN_ERR "capinc_tty_write: alloc_skb failed\n"); | ||
1075 | return -ENOMEM; | ||
1076 | } | ||
1077 | |||
1078 | skb_reserve(skb, CAPI_DATA_B3_REQ_LEN); | ||
1079 | memcpy(skb_put(skb, count), buf, count); | ||
1080 | |||
1081 | skb_queue_tail(&mp->outqueue, skb); | ||
1082 | mp->outbytes += skb->len; | ||
1083 | (void)handle_minor_send(mp); | ||
1084 | (void)handle_minor_recv(mp); | ||
1085 | return count; | ||
1086 | } | ||
1087 | |||
1088 | static void capinc_tty_put_char(struct tty_struct *tty, unsigned char ch) | ||
1089 | { | ||
1090 | struct capiminor *mp = (struct capiminor *)tty->driver_data; | ||
1091 | struct sk_buff *skb; | ||
1092 | |||
1093 | #ifdef _DEBUG_TTYFUNCS | ||
1094 | printk(KERN_DEBUG "capinc_put_char(%u)\n", ch); | ||
1095 | #endif | ||
1096 | |||
1097 | if (!mp || !mp->nccip) { | ||
1098 | #ifdef _DEBUG_TTYFUNCS | ||
1099 | printk(KERN_DEBUG "capinc_tty_put_char: mp or mp->ncci NULL\n"); | ||
1100 | #endif | ||
1101 | return; | ||
1102 | } | ||
1103 | |||
1104 | skb = mp->ttyskb; | ||
1105 | if (skb) { | ||
1106 | if (skb_tailroom(skb) > 0) { | ||
1107 | *(skb_put(skb, 1)) = ch; | ||
1108 | return; | ||
1109 | } | ||
1110 | mp->ttyskb = NULL; | ||
1111 | skb_queue_tail(&mp->outqueue, skb); | ||
1112 | mp->outbytes += skb->len; | ||
1113 | (void)handle_minor_send(mp); | ||
1114 | } | ||
1115 | skb = alloc_skb(CAPI_DATA_B3_REQ_LEN+CAPI_MAX_BLKSIZE, GFP_ATOMIC); | ||
1116 | if (skb) { | ||
1117 | skb_reserve(skb, CAPI_DATA_B3_REQ_LEN); | ||
1118 | *(skb_put(skb, 1)) = ch; | ||
1119 | mp->ttyskb = skb; | ||
1120 | } else { | ||
1121 | printk(KERN_ERR "capinc_put_char: char %u lost\n", ch); | ||
1122 | } | ||
1123 | } | ||
1124 | |||
1125 | static void capinc_tty_flush_chars(struct tty_struct *tty) | ||
1126 | { | ||
1127 | struct capiminor *mp = (struct capiminor *)tty->driver_data; | ||
1128 | struct sk_buff *skb; | ||
1129 | |||
1130 | #ifdef _DEBUG_TTYFUNCS | ||
1131 | printk(KERN_DEBUG "capinc_tty_flush_chars\n"); | ||
1132 | #endif | ||
1133 | |||
1134 | if (!mp || !mp->nccip) { | ||
1135 | #ifdef _DEBUG_TTYFUNCS | ||
1136 | printk(KERN_DEBUG "capinc_tty_flush_chars: mp or mp->ncci NULL\n"); | ||
1137 | #endif | ||
1138 | return; | ||
1139 | } | ||
1140 | |||
1141 | skb = mp->ttyskb; | ||
1142 | if (skb) { | ||
1143 | mp->ttyskb = NULL; | ||
1144 | skb_queue_tail(&mp->outqueue, skb); | ||
1145 | mp->outbytes += skb->len; | ||
1146 | (void)handle_minor_send(mp); | ||
1147 | } | ||
1148 | (void)handle_minor_recv(mp); | ||
1149 | } | ||
1150 | |||
1151 | static int capinc_tty_write_room(struct tty_struct *tty) | ||
1152 | { | ||
1153 | struct capiminor *mp = (struct capiminor *)tty->driver_data; | ||
1154 | int room; | ||
1155 | if (!mp || !mp->nccip) { | ||
1156 | #ifdef _DEBUG_TTYFUNCS | ||
1157 | printk(KERN_DEBUG "capinc_tty_write_room: mp or mp->ncci NULL\n"); | ||
1158 | #endif | ||
1159 | return 0; | ||
1160 | } | ||
1161 | room = CAPINC_MAX_SENDQUEUE-skb_queue_len(&mp->outqueue); | ||
1162 | room *= CAPI_MAX_BLKSIZE; | ||
1163 | #ifdef _DEBUG_TTYFUNCS | ||
1164 | printk(KERN_DEBUG "capinc_tty_write_room = %d\n", room); | ||
1165 | #endif | ||
1166 | return room; | ||
1167 | } | ||
1168 | |||
1169 | int capinc_tty_chars_in_buffer(struct tty_struct *tty) | ||
1170 | { | ||
1171 | struct capiminor *mp = (struct capiminor *)tty->driver_data; | ||
1172 | if (!mp || !mp->nccip) { | ||
1173 | #ifdef _DEBUG_TTYFUNCS | ||
1174 | printk(KERN_DEBUG "capinc_tty_chars_in_buffer: mp or mp->ncci NULL\n"); | ||
1175 | #endif | ||
1176 | return 0; | ||
1177 | } | ||
1178 | #ifdef _DEBUG_TTYFUNCS | ||
1179 | printk(KERN_DEBUG "capinc_tty_chars_in_buffer = %d nack=%d sq=%d rq=%d\n", | ||
1180 | mp->outbytes, mp->nack, | ||
1181 | skb_queue_len(&mp->outqueue), | ||
1182 | skb_queue_len(&mp->inqueue)); | ||
1183 | #endif | ||
1184 | return mp->outbytes; | ||
1185 | } | ||
1186 | |||
1187 | static int capinc_tty_ioctl(struct tty_struct *tty, struct file * file, | ||
1188 | unsigned int cmd, unsigned long arg) | ||
1189 | { | ||
1190 | int error = 0; | ||
1191 | switch (cmd) { | ||
1192 | default: | ||
1193 | error = n_tty_ioctl (tty, file, cmd, arg); | ||
1194 | break; | ||
1195 | } | ||
1196 | return error; | ||
1197 | } | ||
1198 | |||
1199 | static void capinc_tty_set_termios(struct tty_struct *tty, struct termios * old) | ||
1200 | { | ||
1201 | #ifdef _DEBUG_TTYFUNCS | ||
1202 | printk(KERN_DEBUG "capinc_tty_set_termios\n"); | ||
1203 | #endif | ||
1204 | } | ||
1205 | |||
1206 | static void capinc_tty_throttle(struct tty_struct * tty) | ||
1207 | { | ||
1208 | struct capiminor *mp = (struct capiminor *)tty->driver_data; | ||
1209 | #ifdef _DEBUG_TTYFUNCS | ||
1210 | printk(KERN_DEBUG "capinc_tty_throttle\n"); | ||
1211 | #endif | ||
1212 | if (mp) | ||
1213 | mp->ttyinstop = 1; | ||
1214 | } | ||
1215 | |||
1216 | static void capinc_tty_unthrottle(struct tty_struct * tty) | ||
1217 | { | ||
1218 | struct capiminor *mp = (struct capiminor *)tty->driver_data; | ||
1219 | #ifdef _DEBUG_TTYFUNCS | ||
1220 | printk(KERN_DEBUG "capinc_tty_unthrottle\n"); | ||
1221 | #endif | ||
1222 | if (mp) { | ||
1223 | mp->ttyinstop = 0; | ||
1224 | handle_minor_recv(mp); | ||
1225 | } | ||
1226 | } | ||
1227 | |||
1228 | static void capinc_tty_stop(struct tty_struct *tty) | ||
1229 | { | ||
1230 | struct capiminor *mp = (struct capiminor *)tty->driver_data; | ||
1231 | #ifdef _DEBUG_TTYFUNCS | ||
1232 | printk(KERN_DEBUG "capinc_tty_stop\n"); | ||
1233 | #endif | ||
1234 | if (mp) { | ||
1235 | mp->ttyoutstop = 1; | ||
1236 | } | ||
1237 | } | ||
1238 | |||
1239 | static void capinc_tty_start(struct tty_struct *tty) | ||
1240 | { | ||
1241 | struct capiminor *mp = (struct capiminor *)tty->driver_data; | ||
1242 | #ifdef _DEBUG_TTYFUNCS | ||
1243 | printk(KERN_DEBUG "capinc_tty_start\n"); | ||
1244 | #endif | ||
1245 | if (mp) { | ||
1246 | mp->ttyoutstop = 0; | ||
1247 | (void)handle_minor_send(mp); | ||
1248 | } | ||
1249 | } | ||
1250 | |||
1251 | static void capinc_tty_hangup(struct tty_struct *tty) | ||
1252 | { | ||
1253 | #ifdef _DEBUG_TTYFUNCS | ||
1254 | printk(KERN_DEBUG "capinc_tty_hangup\n"); | ||
1255 | #endif | ||
1256 | } | ||
1257 | |||
1258 | static void capinc_tty_break_ctl(struct tty_struct *tty, int state) | ||
1259 | { | ||
1260 | #ifdef _DEBUG_TTYFUNCS | ||
1261 | printk(KERN_DEBUG "capinc_tty_break_ctl(%d)\n", state); | ||
1262 | #endif | ||
1263 | } | ||
1264 | |||
1265 | static void capinc_tty_flush_buffer(struct tty_struct *tty) | ||
1266 | { | ||
1267 | #ifdef _DEBUG_TTYFUNCS | ||
1268 | printk(KERN_DEBUG "capinc_tty_flush_buffer\n"); | ||
1269 | #endif | ||
1270 | } | ||
1271 | |||
1272 | static void capinc_tty_set_ldisc(struct tty_struct *tty) | ||
1273 | { | ||
1274 | #ifdef _DEBUG_TTYFUNCS | ||
1275 | printk(KERN_DEBUG "capinc_tty_set_ldisc\n"); | ||
1276 | #endif | ||
1277 | } | ||
1278 | |||
1279 | static void capinc_tty_send_xchar(struct tty_struct *tty, char ch) | ||
1280 | { | ||
1281 | #ifdef _DEBUG_TTYFUNCS | ||
1282 | printk(KERN_DEBUG "capinc_tty_send_xchar(%d)\n", ch); | ||
1283 | #endif | ||
1284 | } | ||
1285 | |||
1286 | static int capinc_tty_read_proc(char *page, char **start, off_t off, | ||
1287 | int count, int *eof, void *data) | ||
1288 | { | ||
1289 | return 0; | ||
1290 | } | ||
1291 | |||
1292 | static struct tty_driver *capinc_tty_driver; | ||
1293 | |||
1294 | static struct tty_operations capinc_ops = { | ||
1295 | .open = capinc_tty_open, | ||
1296 | .close = capinc_tty_close, | ||
1297 | .write = capinc_tty_write, | ||
1298 | .put_char = capinc_tty_put_char, | ||
1299 | .flush_chars = capinc_tty_flush_chars, | ||
1300 | .write_room = capinc_tty_write_room, | ||
1301 | .chars_in_buffer = capinc_tty_chars_in_buffer, | ||
1302 | .ioctl = capinc_tty_ioctl, | ||
1303 | .set_termios = capinc_tty_set_termios, | ||
1304 | .throttle = capinc_tty_throttle, | ||
1305 | .unthrottle = capinc_tty_unthrottle, | ||
1306 | .stop = capinc_tty_stop, | ||
1307 | .start = capinc_tty_start, | ||
1308 | .hangup = capinc_tty_hangup, | ||
1309 | .break_ctl = capinc_tty_break_ctl, | ||
1310 | .flush_buffer = capinc_tty_flush_buffer, | ||
1311 | .set_ldisc = capinc_tty_set_ldisc, | ||
1312 | .send_xchar = capinc_tty_send_xchar, | ||
1313 | .read_proc = capinc_tty_read_proc, | ||
1314 | }; | ||
1315 | |||
1316 | static int capinc_tty_init(void) | ||
1317 | { | ||
1318 | struct tty_driver *drv; | ||
1319 | |||
1320 | if (capi_ttyminors > CAPINC_MAX_PORTS) | ||
1321 | capi_ttyminors = CAPINC_MAX_PORTS; | ||
1322 | if (capi_ttyminors <= 0) | ||
1323 | capi_ttyminors = CAPINC_NR_PORTS; | ||
1324 | |||
1325 | drv = alloc_tty_driver(capi_ttyminors); | ||
1326 | if (!drv) | ||
1327 | return -ENOMEM; | ||
1328 | |||
1329 | drv->owner = THIS_MODULE; | ||
1330 | drv->driver_name = "capi_nc"; | ||
1331 | drv->devfs_name = "capi/"; | ||
1332 | drv->name = "capi"; | ||
1333 | drv->major = capi_ttymajor; | ||
1334 | drv->minor_start = 0; | ||
1335 | drv->type = TTY_DRIVER_TYPE_SERIAL; | ||
1336 | drv->subtype = SERIAL_TYPE_NORMAL; | ||
1337 | drv->init_termios = tty_std_termios; | ||
1338 | drv->init_termios.c_iflag = ICRNL; | ||
1339 | drv->init_termios.c_oflag = OPOST | ONLCR; | ||
1340 | drv->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; | ||
1341 | drv->init_termios.c_lflag = 0; | ||
1342 | drv->flags = TTY_DRIVER_REAL_RAW|TTY_DRIVER_RESET_TERMIOS; | ||
1343 | tty_set_operations(drv, &capinc_ops); | ||
1344 | if (tty_register_driver(drv)) { | ||
1345 | put_tty_driver(drv); | ||
1346 | printk(KERN_ERR "Couldn't register capi_nc driver\n"); | ||
1347 | return -1; | ||
1348 | } | ||
1349 | capinc_tty_driver = drv; | ||
1350 | return 0; | ||
1351 | } | ||
1352 | |||
1353 | static void capinc_tty_exit(void) | ||
1354 | { | ||
1355 | struct tty_driver *drv = capinc_tty_driver; | ||
1356 | int retval; | ||
1357 | if ((retval = tty_unregister_driver(drv))) | ||
1358 | printk(KERN_ERR "capi: failed to unregister capi_nc driver (%d)\n", retval); | ||
1359 | put_tty_driver(drv); | ||
1360 | } | ||
1361 | |||
1362 | #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ | ||
1363 | |||
1364 | /* -------- /proc functions ----------------------------------------- */ | ||
1365 | |||
1366 | /* | ||
1367 | * /proc/capi/capi20: | ||
1368 | * minor applid nrecvctlpkt nrecvdatapkt nsendctlpkt nsenddatapkt | ||
1369 | */ | ||
1370 | static int proc_capidev_read_proc(char *page, char **start, off_t off, | ||
1371 | int count, int *eof, void *data) | ||
1372 | { | ||
1373 | struct capidev *cdev; | ||
1374 | struct list_head *l; | ||
1375 | int len = 0; | ||
1376 | |||
1377 | read_lock(&capidev_list_lock); | ||
1378 | list_for_each(l, &capidev_list) { | ||
1379 | cdev = list_entry(l, struct capidev, list); | ||
1380 | len += sprintf(page+len, "0 %d %lu %lu %lu %lu\n", | ||
1381 | cdev->ap.applid, | ||
1382 | cdev->ap.nrecvctlpkt, | ||
1383 | cdev->ap.nrecvdatapkt, | ||
1384 | cdev->ap.nsentctlpkt, | ||
1385 | cdev->ap.nsentdatapkt); | ||
1386 | if (len <= off) { | ||
1387 | off -= len; | ||
1388 | len = 0; | ||
1389 | } else { | ||
1390 | if (len-off > count) | ||
1391 | goto endloop; | ||
1392 | } | ||
1393 | } | ||
1394 | |||
1395 | endloop: | ||
1396 | read_unlock(&capidev_list_lock); | ||
1397 | if (len < count) | ||
1398 | *eof = 1; | ||
1399 | if (len > count) len = count; | ||
1400 | if (len < 0) len = 0; | ||
1401 | return len; | ||
1402 | } | ||
1403 | |||
1404 | /* | ||
1405 | * /proc/capi/capi20ncci: | ||
1406 | * applid ncci | ||
1407 | */ | ||
1408 | static int proc_capincci_read_proc(char *page, char **start, off_t off, | ||
1409 | int count, int *eof, void *data) | ||
1410 | { | ||
1411 | struct capidev *cdev; | ||
1412 | struct capincci *np; | ||
1413 | struct list_head *l; | ||
1414 | int len = 0; | ||
1415 | |||
1416 | read_lock(&capidev_list_lock); | ||
1417 | list_for_each(l, &capidev_list) { | ||
1418 | cdev = list_entry(l, struct capidev, list); | ||
1419 | for (np=cdev->nccis; np; np = np->next) { | ||
1420 | len += sprintf(page+len, "%d 0x%x\n", | ||
1421 | cdev->ap.applid, | ||
1422 | np->ncci); | ||
1423 | if (len <= off) { | ||
1424 | off -= len; | ||
1425 | len = 0; | ||
1426 | } else { | ||
1427 | if (len-off > count) | ||
1428 | goto endloop; | ||
1429 | } | ||
1430 | } | ||
1431 | } | ||
1432 | endloop: | ||
1433 | read_unlock(&capidev_list_lock); | ||
1434 | *start = page+off; | ||
1435 | if (len < count) | ||
1436 | *eof = 1; | ||
1437 | if (len>count) len = count; | ||
1438 | if (len<0) len = 0; | ||
1439 | return len; | ||
1440 | } | ||
1441 | |||
1442 | static struct procfsentries { | ||
1443 | char *name; | ||
1444 | mode_t mode; | ||
1445 | int (*read_proc)(char *page, char **start, off_t off, | ||
1446 | int count, int *eof, void *data); | ||
1447 | struct proc_dir_entry *procent; | ||
1448 | } procfsentries[] = { | ||
1449 | /* { "capi", S_IFDIR, 0 }, */ | ||
1450 | { "capi/capi20", 0 , proc_capidev_read_proc }, | ||
1451 | { "capi/capi20ncci", 0 , proc_capincci_read_proc }, | ||
1452 | }; | ||
1453 | |||
1454 | static void __init proc_init(void) | ||
1455 | { | ||
1456 | int nelem = sizeof(procfsentries)/sizeof(procfsentries[0]); | ||
1457 | int i; | ||
1458 | |||
1459 | for (i=0; i < nelem; i++) { | ||
1460 | struct procfsentries *p = procfsentries + i; | ||
1461 | p->procent = create_proc_entry(p->name, p->mode, NULL); | ||
1462 | if (p->procent) p->procent->read_proc = p->read_proc; | ||
1463 | } | ||
1464 | } | ||
1465 | |||
1466 | static void __exit proc_exit(void) | ||
1467 | { | ||
1468 | int nelem = sizeof(procfsentries)/sizeof(procfsentries[0]); | ||
1469 | int i; | ||
1470 | |||
1471 | for (i=nelem-1; i >= 0; i--) { | ||
1472 | struct procfsentries *p = procfsentries + i; | ||
1473 | if (p->procent) { | ||
1474 | remove_proc_entry(p->name, NULL); | ||
1475 | p->procent = NULL; | ||
1476 | } | ||
1477 | } | ||
1478 | } | ||
1479 | |||
1480 | /* -------- init function and module interface ---------------------- */ | ||
1481 | |||
1482 | |||
1483 | static char rev[32]; | ||
1484 | |||
1485 | static int __init capi_init(void) | ||
1486 | { | ||
1487 | char *p; | ||
1488 | char *compileinfo; | ||
1489 | |||
1490 | if ((p = strchr(revision, ':')) != 0 && p[1]) { | ||
1491 | strlcpy(rev, p + 2, sizeof(rev)); | ||
1492 | if ((p = strchr(rev, '$')) != 0 && p > rev) | ||
1493 | *(p-1) = 0; | ||
1494 | } else | ||
1495 | strcpy(rev, "1.0"); | ||
1496 | |||
1497 | if (register_chrdev(capi_major, "capi20", &capi_fops)) { | ||
1498 | printk(KERN_ERR "capi20: unable to get major %d\n", capi_major); | ||
1499 | return -EIO; | ||
1500 | } | ||
1501 | |||
1502 | capi_class = class_simple_create(THIS_MODULE, "capi"); | ||
1503 | if (IS_ERR(capi_class)) { | ||
1504 | unregister_chrdev(capi_major, "capi20"); | ||
1505 | return PTR_ERR(capi_class); | ||
1506 | } | ||
1507 | |||
1508 | class_simple_device_add(capi_class, MKDEV(capi_major, 0), NULL, "capi"); | ||
1509 | devfs_mk_cdev(MKDEV(capi_major, 0), S_IFCHR | S_IRUSR | S_IWUSR, | ||
1510 | "isdn/capi20"); | ||
1511 | |||
1512 | #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE | ||
1513 | if (capinc_tty_init() < 0) { | ||
1514 | class_simple_device_remove(MKDEV(capi_major, 0)); | ||
1515 | class_simple_destroy(capi_class); | ||
1516 | unregister_chrdev(capi_major, "capi20"); | ||
1517 | return -ENOMEM; | ||
1518 | } | ||
1519 | #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ | ||
1520 | |||
1521 | proc_init(); | ||
1522 | |||
1523 | #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE | ||
1524 | #if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE) | ||
1525 | compileinfo = " (middleware+capifs)"; | ||
1526 | #else | ||
1527 | compileinfo = " (no capifs)"; | ||
1528 | #endif | ||
1529 | #else | ||
1530 | compileinfo = " (no middleware)"; | ||
1531 | #endif | ||
1532 | printk(KERN_NOTICE "capi20: Rev %s: started up with major %d%s\n", | ||
1533 | rev, capi_major, compileinfo); | ||
1534 | |||
1535 | return 0; | ||
1536 | } | ||
1537 | |||
1538 | static void __exit capi_exit(void) | ||
1539 | { | ||
1540 | proc_exit(); | ||
1541 | |||
1542 | class_simple_device_remove(MKDEV(capi_major, 0)); | ||
1543 | class_simple_destroy(capi_class); | ||
1544 | unregister_chrdev(capi_major, "capi20"); | ||
1545 | devfs_remove("isdn/capi20"); | ||
1546 | |||
1547 | #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE | ||
1548 | capinc_tty_exit(); | ||
1549 | #endif | ||
1550 | printk(KERN_NOTICE "capi: Rev %s: unloaded\n", rev); | ||
1551 | } | ||
1552 | |||
1553 | module_init(capi_init); | ||
1554 | module_exit(capi_exit); | ||