Import 2.1.118
[davej-history.git] / drivers / isdn / avmb1 / capi.c
blobf4f5c70391254ec9a0a46f2c68433c0b60bd80a7
1 /*
2 * $Id: capi.c,v 1.10 1998/02/13 07:09:13 calle 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.10 1998/02/13 07:09:13 calle
10 * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
12 * Revision 1.9 1998/01/31 11:14:44 calle
13 * merged changes to 2.0 tree, prepare 2.1.82 to work.
15 * Revision 1.8 1997/11/04 06:12:08 calle
16 * capi.c: new read/write in file_ops since 2.1.60
17 * capidrv.c: prepared isdnlog interface for d2-trace in newer firmware.
18 * capiutil.c: needs config.h (CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON)
19 * compat.h: added #define LinuxVersionCode
21 * Revision 1.7 1997/10/11 10:29:34 calle
22 * llseek() parameters changed in 2.1.56.
24 * Revision 1.6 1997/10/01 09:21:15 fritz
25 * Removed old compatibility stuff for 2.0.X kernels.
26 * From now on, this code is for 2.1.X ONLY!
27 * Old stuff is still in the separate branch.
29 * Revision 1.5 1997/08/21 23:11:55 fritz
30 * Added changes for kernels >= 2.1.45
32 * Revision 1.4 1997/05/27 15:17:50 fritz
33 * Added changes for recent 2.1.x kernels:
34 * changed return type of isdn_close
35 * queue_task_* -> queue_task
36 * clear/set_bit -> test_and_... where apropriate.
37 * changed type of hard_header_cache parameter.
39 * Revision 1.3 1997/05/18 09:24:14 calle
40 * added verbose disconnect reason reporting to avmb1.
41 * some fixes in capi20 interface.
42 * changed info messages for B1-PCI
44 * Revision 1.2 1997/03/05 21:17:59 fritz
45 * Added capi_poll for compiling under 2.1.27
47 * Revision 1.1 1997/03/04 21:50:29 calle
48 * Frirst version in isdn4linux
50 * Revision 2.2 1997/02/12 09:31:39 calle
51 * new version
53 * Revision 1.1 1997/01/31 10:32:20 calle
54 * Initial revision
58 #include <linux/module.h>
59 #include <linux/errno.h>
60 #include <linux/kernel.h>
61 #include <linux/major.h>
62 #include <linux/sched.h>
63 #include <linux/malloc.h>
64 #include <linux/fcntl.h>
65 #include <linux/fs.h>
66 #include <linux/signal.h>
67 #include <linux/mm.h>
68 #include <linux/timer.h>
69 #include <linux/wait.h>
70 #include <linux/skbuff.h>
71 #include <linux/poll.h>
72 #include <linux/capi.h>
73 #include <linux/kernelcapi.h>
75 #include "compat.h"
76 #include "capiutil.h"
77 #include "capicmd.h"
78 #include "capidev.h"
80 MODULE_AUTHOR("Carsten Paeth (calle@calle.in-berlin.de)");
82 /* -------- driver information -------------------------------------- */
84 int capi_major = 68; /* allocated */
86 MODULE_PARM(capi_major, "i");
88 /* -------- global variables ---------------------------------------- */
90 static struct capidev capidevs[CAPI_MAXMINOR + 1];
91 struct capi_interface *capifuncs;
93 /* -------- function called by lower level -------------------------- */
95 static void capi_signal(__u16 applid, __u32 minor)
97 struct capidev *cdev;
98 struct sk_buff *skb = 0;
100 if (minor > CAPI_MAXMINOR || !capidevs[minor].is_registered) {
101 printk(KERN_ERR "BUG: capi_signal: illegal minor %d\n", minor);
102 return;
104 cdev = &capidevs[minor];
105 (void) (*capifuncs->capi_get_message) (applid, &skb);
106 if (skb) {
107 skb_queue_tail(&cdev->recv_queue, skb);
108 wake_up_interruptible(&cdev->recv_wait);
109 } else {
110 printk(KERN_ERR "BUG: capi_signal: no skb\n");
114 /* -------- file_operations ----------------------------------------- */
116 static long long capi_llseek(struct file *file,
117 long long offset, int origin)
119 return -ESPIPE;
122 static ssize_t capi_read(struct file *file, char *buf,
123 size_t count, loff_t *ppos)
125 struct inode *inode = file->f_dentry->d_inode;
126 unsigned int minor = MINOR(inode->i_rdev);
127 struct capidev *cdev;
128 struct sk_buff *skb;
129 int retval;
130 size_t copied;
132 if (ppos != &file->f_pos)
133 return -ESPIPE;
135 if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered)
136 return -ENODEV;
138 cdev = &capidevs[minor];
140 if ((skb = skb_dequeue(&cdev->recv_queue)) == 0) {
142 if (file->f_flags & O_NONBLOCK)
143 return -EAGAIN;
145 for (;;) {
146 interruptible_sleep_on(&cdev->recv_wait);
147 if ((skb = skb_dequeue(&cdev->recv_queue)) != 0)
148 break;
149 if (signal_pending(current))
150 break;
152 if (skb == 0)
153 return -ERESTARTNOHAND;
155 if (skb->len > count) {
156 skb_queue_head(&cdev->recv_queue, skb);
157 return -EMSGSIZE;
159 if (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3
160 && CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND)
161 CAPIMSG_SETDATA(skb->data, buf + CAPIMSG_LEN(skb->data));
162 retval = copy_to_user(buf, skb->data, skb->len);
163 if (retval) {
164 skb_queue_head(&cdev->recv_queue, skb);
165 return retval;
167 copied = skb->len;
170 kfree_skb(skb);
172 return copied;
175 static ssize_t capi_write(struct file *file, const char *buf,
176 size_t count, loff_t *ppos)
178 struct inode *inode = file->f_dentry->d_inode;
179 unsigned int minor = MINOR(inode->i_rdev);
180 struct capidev *cdev;
181 struct sk_buff *skb;
182 int retval;
183 __u8 cmd;
184 __u8 subcmd;
185 __u16 mlen;
187 if (ppos != &file->f_pos)
188 return -ESPIPE;
190 if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered)
191 return -ENODEV;
193 cdev = &capidevs[minor];
195 skb = alloc_skb(count, GFP_USER);
197 if ((retval = copy_from_user(skb_put(skb, count), buf, count))) {
198 dev_kfree_skb(skb);
199 return retval;
201 cmd = CAPIMSG_COMMAND(skb->data);
202 subcmd = CAPIMSG_SUBCOMMAND(skb->data);
203 mlen = CAPIMSG_LEN(skb->data);
204 if (cmd == CAPI_DATA_B3 && subcmd == CAPI_REQ) {
205 __u16 dlen = CAPIMSG_DATALEN(skb->data);
206 if (mlen + dlen != count) {
207 dev_kfree_skb(skb);
208 return -EINVAL;
210 } else if (mlen != count) {
211 dev_kfree_skb(skb);
212 return -EINVAL;
214 CAPIMSG_SETAPPID(skb->data, cdev->applid);
216 cdev->errcode = (*capifuncs->capi_put_message) (cdev->applid, skb);
218 if (cdev->errcode) {
219 dev_kfree_skb(skb);
220 return -EIO;
222 return count;
225 static unsigned int
226 capi_poll(struct file *file, poll_table * wait)
228 unsigned int mask = 0;
229 #if (LINUX_VERSION_CODE >= 0x02012d)
230 unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
231 #else
232 unsigned int minor = MINOR(file->f_inode->i_rdev);
233 #endif
234 struct capidev *cdev;
236 if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered)
237 return POLLERR;
239 cdev = &capidevs[minor];
240 poll_wait(file, &(cdev->recv_wait), wait);
241 mask = POLLOUT | POLLWRNORM;
242 if (!skb_queue_empty(&cdev->recv_queue))
243 mask |= POLLIN | POLLRDNORM;
244 return mask;
247 static int capi_ioctl(struct inode *inode, struct file *file,
248 unsigned int cmd, unsigned long arg)
250 unsigned int minor = MINOR(inode->i_rdev);
251 struct capidev *cdev;
252 capi_ioctl_struct data;
253 int retval;
256 if (minor >= CAPI_MAXMINOR || !capidevs[minor].is_open)
257 return -ENODEV;
259 cdev = &capidevs[minor];
261 switch (cmd) {
262 case CAPI_REGISTER:
264 if (!minor)
265 return -EINVAL;
266 retval = copy_from_user((void *) &data.rparams,
267 (void *) arg, sizeof(struct capi_register_params));
268 if (retval)
269 return -EFAULT;
270 if (cdev->is_registered)
271 return -EEXIST;
272 cdev->errcode = (*capifuncs->capi_register) (&data.rparams,
273 &cdev->applid);
274 if (cdev->errcode)
275 return -EIO;
276 (void) (*capifuncs->capi_set_signal) (cdev->applid, capi_signal, minor);
277 cdev->is_registered = 1;
279 return 0;
281 case CAPI_GET_VERSION:
283 retval = copy_from_user((void *) &data.contr,
284 (void *) arg,
285 sizeof(data.contr));
286 if (retval)
287 return -EFAULT;
288 cdev->errcode = (*capifuncs->capi_get_version) (data.contr, &data.version);
289 if (cdev->errcode)
290 return -EIO;
291 retval = copy_to_user((void *) arg,
292 (void *) &data.version,
293 sizeof(data.version));
294 if (retval)
295 return -EFAULT;
297 return 0;
299 case CAPI_GET_SERIAL:
301 retval = copy_from_user((void *) &data.contr,
302 (void *) arg,
303 sizeof(data.contr));
304 if (retval)
305 return -EFAULT;
306 cdev->errcode = (*capifuncs->capi_get_serial) (data.contr, data.serial);
307 if (cdev->errcode)
308 return -EIO;
309 retval = copy_to_user((void *) arg,
310 (void *) data.serial,
311 sizeof(data.serial));
312 if (retval)
313 return -EFAULT;
315 return 0;
316 case CAPI_GET_PROFILE:
318 retval = copy_from_user((void *) &data.contr,
319 (void *) arg,
320 sizeof(data.contr));
321 if (retval)
322 return -EFAULT;
324 if (data.contr == 0) {
325 cdev->errcode = (*capifuncs->capi_get_profile) (data.contr, &data.profile);
326 if (cdev->errcode)
327 return -EIO;
329 retval = copy_to_user((void *) arg,
330 (void *) &data.profile.ncontroller,
331 sizeof(data.profile.ncontroller));
333 } else {
334 cdev->errcode = (*capifuncs->capi_get_profile) (data.contr, &data.profile);
335 if (cdev->errcode)
336 return -EIO;
338 retval = copy_to_user((void *) arg,
339 (void *) &data.profile,
340 sizeof(data.profile));
342 if (retval)
343 return -EFAULT;
345 return 0;
347 case CAPI_GET_MANUFACTURER:
349 retval = copy_from_user((void *) &data.contr,
350 (void *) arg,
351 sizeof(data.contr));
352 if (retval)
353 return -EFAULT;
354 cdev->errcode = (*capifuncs->capi_get_manufacturer) (data.contr, data.manufacturer);
355 if (cdev->errcode)
356 return -EIO;
358 retval = copy_to_user((void *) arg, (void *) data.manufacturer,
359 sizeof(data.manufacturer));
360 if (retval)
361 return -EFAULT;
364 return 0;
365 case CAPI_GET_ERRCODE:
366 data.errcode = cdev->errcode;
367 cdev->errcode = CAPI_NOERROR;
368 if (arg) {
369 retval = copy_to_user((void *) arg,
370 (void *) &data.errcode,
371 sizeof(data.errcode));
372 if (retval)
373 return -EFAULT;
375 return data.errcode;
377 case CAPI_INSTALLED:
378 if ((*capifuncs->capi_installed) ())
379 return 0;
380 return -ENXIO;
382 case CAPI_MANUFACTURER_CMD:
384 struct capi_manufacturer_cmd mcmd;
385 if (minor)
386 return -EINVAL;
387 if (!capable(CAP_SYS_ADMIN))
388 return -EPERM;
389 retval = copy_from_user((void *) &mcmd, (void *) arg,
390 sizeof(mcmd));
391 if (retval)
392 return -EFAULT;
393 return (*capifuncs->capi_manufacturer) (mcmd.cmd, mcmd.data);
395 return 0;
397 return -EINVAL;
400 static int capi_open(struct inode *inode, struct file *file)
402 unsigned int minor = MINOR(inode->i_rdev);
404 if (minor >= CAPI_MAXMINOR)
405 return -ENXIO;
407 if (minor) {
408 if (capidevs[minor].is_open)
409 return -EEXIST;
411 capidevs[minor].is_open = 1;
412 skb_queue_head_init(&capidevs[minor].recv_queue);
413 MOD_INC_USE_COUNT;
415 } else {
417 if (!capidevs[minor].is_open) {
418 capidevs[minor].is_open = 1;
419 MOD_INC_USE_COUNT;
424 return 0;
427 static int
428 capi_release(struct inode *inode, struct file *file)
430 unsigned int minor = MINOR(inode->i_rdev);
431 struct capidev *cdev;
432 struct sk_buff *skb;
434 if (minor >= CAPI_MAXMINOR || !capidevs[minor].is_open) {
435 printk(KERN_ERR "capi20: release minor %d ???\n", minor);
436 return 0;
438 cdev = &capidevs[minor];
440 if (minor) {
442 if (cdev->is_registered)
443 (*capifuncs->capi_release) (cdev->applid);
445 cdev->is_registered = 0;
446 cdev->applid = 0;
448 while ((skb = skb_dequeue(&cdev->recv_queue)) != 0)
449 kfree_skb(skb);
451 cdev->is_open = 0;
453 MOD_DEC_USE_COUNT;
454 return 0;
457 static struct file_operations capi_fops =
459 capi_llseek,
460 capi_read,
461 capi_write,
462 NULL, /* capi_readdir */
463 capi_poll,
464 capi_ioctl,
465 NULL, /* capi_mmap */
466 capi_open,
467 NULL, /* flush */
468 capi_release,
469 NULL, /* capi_fsync */
470 NULL, /* capi_fasync */
474 /* -------- init function and module interface ---------------------- */
476 #ifdef MODULE
477 #define capi_init init_module
478 #endif
480 static struct capi_interface_user cuser = {
481 "capi20",
485 int capi_init(void)
487 memset(capidevs, 0, sizeof(capidevs));
489 if (register_chrdev(capi_major, "capi20", &capi_fops)) {
490 printk(KERN_ERR "capi20: unable to get major %d\n", capi_major);
491 return -EIO;
493 printk(KERN_NOTICE "capi20: started up with major %d\n", capi_major);
495 if ((capifuncs = attach_capi_interface(&cuser)) == 0) {
496 unregister_chrdev(capi_major, "capi20");
497 return -EIO;
499 return 0;
502 #ifdef MODULE
503 void cleanup_module(void)
505 unregister_chrdev(capi_major, "capi20");
506 (void) detach_capi_interface(&cuser);
509 #endif