* add p cc
[mascara-docs.git] / i386 / linux / linux-2.3.21 / drivers / isdn / avmb1 / capi.c
blobc8cab69193e0f4bfa8dc589a8b6b76c5b650be09
1 /*
2 * $Id: capi.c,v 1.19 1999/07/09 15:05:42 keil Exp $
4 * CAPI 2.0 Interface for Linux
6 * Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de)
8 * $Log: capi.c,v $
9 * Revision 1.19 1999/07/09 15:05:42 keil
10 * compat.h is now isdn_compat.h
12 * Revision 1.18 1999/07/06 07:42:01 calle
13 * - changes in /proc interface
14 * - check and changed calls to [dev_]kfree_skb and [dev_]alloc_skb.
16 * Revision 1.17 1999/07/01 15:26:30 calle
17 * complete new version (I love it):
18 * + new hardware independed "capi_driver" interface that will make it easy to:
19 * - support other controllers with CAPI-2.0 (i.e. USB Controller)
20 * - write a CAPI-2.0 for the passive cards
21 * - support serial link CAPI-2.0 boxes.
22 * + wrote "capi_driver" for all supported cards.
23 * + "capi_driver" (supported cards) now have to be configured with
24 * make menuconfig, in the past all supported cards where included
25 * at once.
26 * + new and better informations in /proc/capi/
27 * + new ioctl to switch trace of capi messages per controller
28 * using "avmcapictrl trace [contr] on|off|...."
29 * + complete testcircle with all supported cards and also the
30 * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done.
32 * Revision 1.16 1999/07/01 08:22:57 keil
33 * compatibility macros now in <linux/isdn_compat.h>
35 * Revision 1.15 1999/06/21 15:24:11 calle
36 * extend information in /proc.
38 * Revision 1.14 1999/06/10 16:51:03 calle
39 * Bugfix: open/release of control device was not handled correct.
41 * Revision 1.13 1998/08/28 04:32:25 calle
42 * Added patch send by Michael.Mueller4@post.rwth-aachen.de, to get AVM B1
43 * driver running with 2.1.118.
45 * Revision 1.12 1998/05/26 22:39:34 he
46 * sync'ed with 2.1.102 where appropriate (CAPABILITY changes)
47 * concap typo
48 * cleared dev.tbusy in isdn_net BCONN status callback
50 * Revision 1.11 1998/03/09 17:46:37 he
51 * merged in 2.1.89 changes
53 * Revision 1.10 1998/02/13 07:09:13 calle
54 * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
56 * Revision 1.9 1998/01/31 11:14:44 calle
57 * merged changes to 2.0 tree, prepare 2.1.82 to work.
59 * Revision 1.8 1997/11/04 06:12:08 calle
60 * capi.c: new read/write in file_ops since 2.1.60
61 * capidrv.c: prepared isdnlog interface for d2-trace in newer firmware.
62 * capiutil.c: needs config.h (CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON)
63 * compat.h: added #define LinuxVersionCode
65 * Revision 1.7 1997/10/11 10:29:34 calle
66 * llseek() parameters changed in 2.1.56.
68 * Revision 1.6 1997/10/01 09:21:15 fritz
69 * Removed old compatibility stuff for 2.0.X kernels.
70 * From now on, this code is for 2.1.X ONLY!
71 * Old stuff is still in the separate branch.
73 * Revision 1.5 1997/08/21 23:11:55 fritz
74 * Added changes for kernels >= 2.1.45
76 * Revision 1.4 1997/05/27 15:17:50 fritz
77 * Added changes for recent 2.1.x kernels:
78 * changed return type of isdn_close
79 * queue_task_* -> queue_task
80 * clear/set_bit -> test_and_... where apropriate.
81 * changed type of hard_header_cache parameter.
83 * Revision 1.3 1997/05/18 09:24:14 calle
84 * added verbose disconnect reason reporting to avmb1.
85 * some fixes in capi20 interface.
86 * changed info messages for B1-PCI
88 * Revision 1.2 1997/03/05 21:17:59 fritz
89 * Added capi_poll for compiling under 2.1.27
91 * Revision 1.1 1997/03/04 21:50:29 calle
92 * Frirst version in isdn4linux
94 * Revision 2.2 1997/02/12 09:31:39 calle
95 * new version
97 * Revision 1.1 1997/01/31 10:32:20 calle
98 * Initial revision
102 #include <linux/module.h>
103 #include <linux/errno.h>
104 #include <linux/kernel.h>
105 #include <linux/major.h>
106 #include <linux/sched.h>
107 #include <linux/malloc.h>
108 #include <linux/fcntl.h>
109 #include <linux/fs.h>
110 #include <linux/signal.h>
111 #include <linux/mm.h>
112 #include <linux/timer.h>
113 #include <linux/wait.h>
114 #include <linux/skbuff.h>
115 #include <linux/proc_fs.h>
116 #include <linux/poll.h>
117 #include <linux/capi.h>
118 #include <linux/kernelcapi.h>
120 #include <linux/isdn_compat.h>
121 #include "capiutil.h"
122 #include "capicmd.h"
123 #include "capidev.h"
125 MODULE_AUTHOR("Carsten Paeth (calle@calle.in-berlin.de)");
127 /* -------- driver information -------------------------------------- */
129 int capi_major = 68; /* allocated */
131 MODULE_PARM(capi_major, "i");
133 /* -------- global variables ---------------------------------------- */
135 static struct capidev capidevs[CAPI_MAXMINOR + 1];
136 struct capi_interface *capifuncs;
138 /* -------- function called by lower level -------------------------- */
140 static void capi_signal(__u16 applid, __u32 minor)
142 struct capidev *cdev;
143 struct sk_buff *skb = 0;
145 if (minor > CAPI_MAXMINOR || !capidevs[minor].is_registered) {
146 printk(KERN_ERR "BUG: capi_signal: illegal minor %d\n", minor);
147 return;
149 cdev = &capidevs[minor];
150 (void) (*capifuncs->capi_get_message) (applid, &skb);
151 if (skb) {
152 skb_queue_tail(&cdev->recv_queue, skb);
153 wake_up_interruptible(&cdev->recv_wait);
154 } else {
155 printk(KERN_ERR "BUG: capi_signal: no skb\n");
159 /* -------- file_operations ----------------------------------------- */
161 static long long capi_llseek(struct file *file,
162 long long offset, int origin)
164 return -ESPIPE;
167 static ssize_t capi_read(struct file *file, char *buf,
168 size_t count, loff_t *ppos)
170 struct inode *inode = file->f_dentry->d_inode;
171 unsigned int minor = MINOR(inode->i_rdev);
172 struct capidev *cdev;
173 struct sk_buff *skb;
174 int retval;
175 size_t copied;
177 if (ppos != &file->f_pos)
178 return -ESPIPE;
180 if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered)
181 return -ENODEV;
183 cdev = &capidevs[minor];
185 if ((skb = skb_dequeue(&cdev->recv_queue)) == 0) {
187 if (file->f_flags & O_NONBLOCK)
188 return -EAGAIN;
190 for (;;) {
191 interruptible_sleep_on(&cdev->recv_wait);
192 if ((skb = skb_dequeue(&cdev->recv_queue)) != 0)
193 break;
194 if (signal_pending(current))
195 break;
197 if (skb == 0)
198 return -ERESTARTNOHAND;
200 if (skb->len > count) {
201 skb_queue_head(&cdev->recv_queue, skb);
202 return -EMSGSIZE;
204 if (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3
205 && CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND)
206 CAPIMSG_SETDATA(skb->data, buf + CAPIMSG_LEN(skb->data));
207 retval = copy_to_user(buf, skb->data, skb->len);
208 if (retval) {
209 skb_queue_head(&cdev->recv_queue, skb);
210 return retval;
212 copied = skb->len;
214 if (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3
215 && CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND)
216 cdev->nrecvdatapkt++;
217 else cdev->nrecvctlpkt++;
218 kfree_skb(skb);
220 return copied;
223 static ssize_t capi_write(struct file *file, const char *buf,
224 size_t count, loff_t *ppos)
226 struct inode *inode = file->f_dentry->d_inode;
227 unsigned int minor = MINOR(inode->i_rdev);
228 struct capidev *cdev;
229 struct sk_buff *skb;
230 int retval;
231 __u8 cmd;
232 __u8 subcmd;
233 __u16 mlen;
235 if (ppos != &file->f_pos)
236 return -ESPIPE;
238 if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered)
239 return -ENODEV;
241 cdev = &capidevs[minor];
243 skb = alloc_skb(count, GFP_USER);
245 if ((retval = copy_from_user(skb_put(skb, count), buf, count))) {
246 kfree_skb(skb);
247 return retval;
249 cmd = CAPIMSG_COMMAND(skb->data);
250 subcmd = CAPIMSG_SUBCOMMAND(skb->data);
251 mlen = CAPIMSG_LEN(skb->data);
252 if (cmd == CAPI_DATA_B3 && subcmd == CAPI_REQ) {
253 __u16 dlen = CAPIMSG_DATALEN(skb->data);
254 if (mlen + dlen != count) {
255 kfree_skb(skb);
256 return -EINVAL;
258 } else if (mlen != count) {
259 kfree_skb(skb);
260 return -EINVAL;
262 CAPIMSG_SETAPPID(skb->data, cdev->applid);
264 cdev->errcode = (*capifuncs->capi_put_message) (cdev->applid, skb);
266 if (cdev->errcode) {
267 kfree_skb(skb);
268 return -EIO;
270 if (cmd == CAPI_DATA_B3 && subcmd == CAPI_REQ)
271 cdev->nsentdatapkt++;
272 else cdev->nsentctlpkt++;
273 return count;
276 static unsigned int
277 capi_poll(struct file *file, poll_table * wait)
279 unsigned int mask = 0;
280 #if (LINUX_VERSION_CODE >= 0x02012d)
281 unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
282 #else
283 unsigned int minor = MINOR(file->f_inode->i_rdev);
284 #endif
285 struct capidev *cdev;
287 if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered)
288 return POLLERR;
290 cdev = &capidevs[minor];
291 #if (LINUX_VERSION_CODE < 0x020159) /* 2.1.89 */
292 #define poll_wait(f,wq,w) poll_wait((wq),(w))
293 #endif
294 poll_wait(file, &(cdev->recv_wait), wait);
295 mask = POLLOUT | POLLWRNORM;
296 if (!skb_queue_empty(&cdev->recv_queue))
297 mask |= POLLIN | POLLRDNORM;
298 return mask;
301 static int capi_ioctl(struct inode *inode, struct file *file,
302 unsigned int cmd, unsigned long arg)
304 unsigned int minor = MINOR(inode->i_rdev);
305 struct capidev *cdev;
306 capi_ioctl_struct data;
307 int retval;
310 if (minor >= CAPI_MAXMINOR || !capidevs[minor].is_open)
311 return -ENODEV;
313 cdev = &capidevs[minor];
315 switch (cmd) {
316 case CAPI_REGISTER:
318 if (!minor)
319 return -EINVAL;
320 retval = copy_from_user((void *) &data.rparams,
321 (void *) arg, sizeof(struct capi_register_params));
322 if (retval)
323 return -EFAULT;
324 if (cdev->is_registered)
325 return -EEXIST;
326 cdev->errcode = (*capifuncs->capi_register) (&data.rparams,
327 &cdev->applid);
328 if (cdev->errcode)
329 return -EIO;
330 (void) (*capifuncs->capi_set_signal) (cdev->applid, capi_signal, minor);
331 cdev->is_registered = 1;
333 return 0;
335 case CAPI_GET_VERSION:
337 retval = copy_from_user((void *) &data.contr,
338 (void *) arg,
339 sizeof(data.contr));
340 if (retval)
341 return -EFAULT;
342 cdev->errcode = (*capifuncs->capi_get_version) (data.contr, &data.version);
343 if (cdev->errcode)
344 return -EIO;
345 retval = copy_to_user((void *) arg,
346 (void *) &data.version,
347 sizeof(data.version));
348 if (retval)
349 return -EFAULT;
351 return 0;
353 case CAPI_GET_SERIAL:
355 retval = copy_from_user((void *) &data.contr,
356 (void *) arg,
357 sizeof(data.contr));
358 if (retval)
359 return -EFAULT;
360 cdev->errcode = (*capifuncs->capi_get_serial) (data.contr, data.serial);
361 if (cdev->errcode)
362 return -EIO;
363 retval = copy_to_user((void *) arg,
364 (void *) data.serial,
365 sizeof(data.serial));
366 if (retval)
367 return -EFAULT;
369 return 0;
370 case CAPI_GET_PROFILE:
372 retval = copy_from_user((void *) &data.contr,
373 (void *) arg,
374 sizeof(data.contr));
375 if (retval)
376 return -EFAULT;
378 if (data.contr == 0) {
379 cdev->errcode = (*capifuncs->capi_get_profile) (data.contr, &data.profile);
380 if (cdev->errcode)
381 return -EIO;
383 retval = copy_to_user((void *) arg,
384 (void *) &data.profile.ncontroller,
385 sizeof(data.profile.ncontroller));
387 } else {
388 cdev->errcode = (*capifuncs->capi_get_profile) (data.contr, &data.profile);
389 if (cdev->errcode)
390 return -EIO;
392 retval = copy_to_user((void *) arg,
393 (void *) &data.profile,
394 sizeof(data.profile));
396 if (retval)
397 return -EFAULT;
399 return 0;
401 case CAPI_GET_MANUFACTURER:
403 retval = copy_from_user((void *) &data.contr,
404 (void *) arg,
405 sizeof(data.contr));
406 if (retval)
407 return -EFAULT;
408 cdev->errcode = (*capifuncs->capi_get_manufacturer) (data.contr, data.manufacturer);
409 if (cdev->errcode)
410 return -EIO;
412 retval = copy_to_user((void *) arg, (void *) data.manufacturer,
413 sizeof(data.manufacturer));
414 if (retval)
415 return -EFAULT;
418 return 0;
419 case CAPI_GET_ERRCODE:
420 data.errcode = cdev->errcode;
421 cdev->errcode = CAPI_NOERROR;
422 if (arg) {
423 retval = copy_to_user((void *) arg,
424 (void *) &data.errcode,
425 sizeof(data.errcode));
426 if (retval)
427 return -EFAULT;
429 return data.errcode;
431 case CAPI_INSTALLED:
432 if ((*capifuncs->capi_installed) ())
433 return 0;
434 return -ENXIO;
436 case CAPI_MANUFACTURER_CMD:
438 struct capi_manufacturer_cmd mcmd;
439 if (minor)
440 return -EINVAL;
441 if (!capable(CAP_SYS_ADMIN))
442 return -EPERM;
443 retval = copy_from_user((void *) &mcmd, (void *) arg,
444 sizeof(mcmd));
445 if (retval)
446 return -EFAULT;
447 return (*capifuncs->capi_manufacturer) (mcmd.cmd, mcmd.data);
449 return 0;
451 return -EINVAL;
454 static int capi_open(struct inode *inode, struct file *file)
456 unsigned int minor = MINOR(inode->i_rdev);
458 if (minor >= CAPI_MAXMINOR)
459 return -ENXIO;
461 if (minor) {
462 if (capidevs[minor].is_open)
463 return -EEXIST;
465 capidevs[minor].is_open = 1;
466 skb_queue_head_init(&capidevs[minor].recv_queue);
467 MOD_INC_USE_COUNT;
468 capidevs[minor].nopen++;
470 } else {
471 capidevs[minor].is_open++;
472 MOD_INC_USE_COUNT;
475 return 0;
478 static int
479 capi_release(struct inode *inode, struct file *file)
481 unsigned int minor = MINOR(inode->i_rdev);
482 struct capidev *cdev;
483 struct sk_buff *skb;
485 if (minor >= CAPI_MAXMINOR || !capidevs[minor].is_open) {
486 printk(KERN_ERR "capi20: release minor %d ???\n", minor);
487 return 0;
489 cdev = &capidevs[minor];
491 if (minor) {
493 if (cdev->is_registered)
494 (*capifuncs->capi_release) (cdev->applid);
496 cdev->is_registered = 0;
497 cdev->applid = 0;
499 while ((skb = skb_dequeue(&cdev->recv_queue)) != 0) {
500 kfree_skb(skb);
502 cdev->is_open = 0;
503 } else {
504 cdev->is_open--;
507 MOD_DEC_USE_COUNT;
508 return 0;
511 static struct file_operations capi_fops =
513 capi_llseek,
514 capi_read,
515 capi_write,
516 NULL, /* capi_readdir */
517 capi_poll,
518 capi_ioctl,
519 NULL, /* capi_mmap */
520 capi_open,
521 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,118)
522 NULL, /* capi_flush */
523 #endif
524 capi_release,
525 NULL, /* capi_fsync */
526 NULL, /* capi_fasync */
529 /* -------- /proc functions ----------------------------------- */
532 * /proc/capi/capi20:
533 * minor opencount nrecvctlpkt nrecvdatapkt nsendctlpkt nsenddatapkt
535 static int proc_capidev_read_proc(char *page, char **start, off_t off,
536 int count, int *eof, void *data)
538 struct capidev *cp;
539 int i;
540 int len = 0;
541 off_t begin = 0;
543 for (i=0; i < CAPI_MAXMINOR; i++) {
544 cp = &capidevs[i+1];
545 if (cp->nopen == 0) continue;
546 len += sprintf(page+len, "%d %lu %lu %lu %lu %lu\n",
547 i+1,
548 cp->nopen,
549 cp->nrecvctlpkt,
550 cp->nrecvdatapkt,
551 cp->nsentctlpkt,
552 cp->nsentdatapkt);
553 if (len+begin > off+count)
554 goto endloop;
555 if (len+begin < off) {
556 begin += len;
557 len = 0;
560 endloop:
561 if (i >= CAPI_MAXMINOR)
562 *eof = 1;
563 if (off >= len+begin)
564 return 0;
565 *start = page + (begin-off);
566 return ((count < begin+len-off) ? count : begin+len-off);
569 static struct procfsentries {
570 char *name;
571 mode_t mode;
572 int (*read_proc)(char *page, char **start, off_t off,
573 int count, int *eof, void *data);
574 struct proc_dir_entry *procent;
575 } procfsentries[] = {
576 /* { "capi", S_IFDIR, 0 }, */
577 { "capi/capi20", 0 , proc_capidev_read_proc },
580 static void proc_init(void)
582 int nelem = sizeof(procfsentries)/sizeof(procfsentries[0]);
583 int i;
585 for (i=0; i < nelem; i++) {
586 struct procfsentries *p = procfsentries + i;
587 p->procent = create_proc_entry(p->name, p->mode, 0);
588 if (p->procent) p->procent->read_proc = p->read_proc;
592 static void proc_exit(void)
594 int nelem = sizeof(procfsentries)/sizeof(procfsentries[0]);
595 int i;
597 for (i=nelem-1; i >= 0; i--) {
598 struct procfsentries *p = procfsentries + i;
599 if (p->procent) {
600 remove_proc_entry(p->name, 0);
601 p->procent = 0;
605 /* -------- init function and module interface ---------------------- */
607 #ifdef MODULE
608 #define capi_init init_module
609 #endif
611 static struct capi_interface_user cuser = {
612 "capi20",
616 int capi_init(void)
618 #ifdef COMPAT_HAS_NEW_WAITQ
619 int j;
620 #endif
622 memset(capidevs, 0, sizeof(capidevs));
623 #ifdef COMPAT_HAS_NEW_WAITQ
624 for ( j = 0; j < CAPI_MAXMINOR+1; j++ ) {
625 init_waitqueue_head(&capidevs[j].recv_wait);
627 #endif
629 if (register_chrdev(capi_major, "capi20", &capi_fops)) {
630 printk(KERN_ERR "capi20: unable to get major %d\n", capi_major);
631 return -EIO;
633 printk(KERN_NOTICE "capi20: started up with major %d\n", capi_major);
635 if ((capifuncs = attach_capi_interface(&cuser)) == 0) {
636 unregister_chrdev(capi_major, "capi20");
637 return -EIO;
639 (void)proc_init();
640 return 0;
643 #ifdef MODULE
644 void cleanup_module(void)
646 (void)proc_exit();
647 unregister_chrdev(capi_major, "capi20");
648 (void) detach_capi_interface(&cuser);
651 #endif