patch-2.0.31 linux/drivers/isdn/avmb1/capi.c
Next file: linux/drivers/isdn/avmb1/capicmd.h
Previous file: linux/drivers/isdn/avmb1/b1pci.c
Back to the patch index
Back to the overall index
- Lines: 541
- Date:
Mon Aug 4 17:33:59 1997
- Orig file:
v2.0.30/linux/drivers/isdn/avmb1/capi.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/avmb1/capi.c linux/drivers/isdn/avmb1/capi.c
@@ -0,0 +1,540 @@
+/*
+ * $Id: capi.c,v 1.4 1997/05/27 15:17:50 fritz Exp $
+ *
+ * CAPI 2.0 Interface for Linux
+ *
+ * Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de)
+ *
+ * $Log: capi.c,v $
+ * Revision 1.4 1997/05/27 15:17:50 fritz
+ * Added changes for recent 2.1.x kernels:
+ * changed return type of isdn_close
+ * queue_task_* -> queue_task
+ * clear/set_bit -> test_and_... where apropriate.
+ * changed type of hard_header_cache parameter.
+ *
+ * Revision 1.3 1997/05/18 09:24:14 calle
+ * added verbose disconnect reason reporting to avmb1.
+ * some fixes in capi20 interface.
+ * changed info messages for B1-PCI
+ *
+ * Revision 1.2 1997/03/05 21:17:59 fritz
+ * Added capi_poll for compiling under 2.1.27
+ *
+ * Revision 1.1 1997/03/04 21:50:29 calle
+ * Frirst version in isdn4linux
+ *
+ * Revision 2.2 1997/02/12 09:31:39 calle
+ * new version
+ *
+ * Revision 1.1 1997/01/31 10:32:20 calle
+ * Initial revision
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/fcntl.h>
+#include <linux/fs.h>
+#include <linux/signal.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/skbuff.h>
+#if (LINUX_VERSION_CODE >= 0x020117)
+#include <asm/poll.h>
+#endif
+#include <linux/capi.h>
+#include <linux/kernelcapi.h>
+
+#include "compat.h"
+#include "capiutil.h"
+#include "capicmd.h"
+#include "capidev.h"
+
+#ifdef HAS_NEW_SYMTAB
+MODULE_AUTHOR("Carsten Paeth (calle@calle.in-berlin.de)");
+#endif
+
+/* -------- driver information -------------------------------------- */
+
+int capi_major = 68; /* allocated */
+
+#ifdef HAS_NEW_SYMTAB
+MODULE_PARM(capi_major, "i");
+#endif
+
+/* -------- global variables ---------------------------------------- */
+
+static struct capidev capidevs[CAPI_MAXMINOR + 1];
+struct capi_interface *capifuncs;
+
+/* -------- function called by lower level -------------------------- */
+
+static void capi_signal(__u16 applid, __u32 minor)
+{
+ struct capidev *cdev;
+ struct sk_buff *skb = 0;
+
+ if (minor > CAPI_MAXMINOR || !capidevs[minor].is_registered) {
+ printk(KERN_ERR "BUG: capi_signal: illegal minor %d\n", minor);
+ return;
+ }
+ cdev = &capidevs[minor];
+ (void) (*capifuncs->capi_get_message) (applid, &skb);
+ if (skb) {
+ skb_queue_tail(&cdev->recv_queue, skb);
+ wake_up_interruptible(&cdev->recv_wait);
+ } else {
+ printk(KERN_ERR "BUG: capi_signal: no skb\n");
+ }
+}
+
+/* -------- file_operations ----------------------------------------- */
+
+#if LINUX_VERSION_CODE < 0x020100
+static int capi_lseek(struct inode *inode, struct file *file,
+ off_t offset, int origin)
+{
+ return -ESPIPE;
+}
+#else
+static long long capi_llseek(struct inode *inode, struct file *file,
+ long long offset, int origin)
+{
+ return -ESPIPE;
+}
+#endif
+
+#if LINUX_VERSION_CODE < 0x020100
+static int capi_read(struct inode *inode, struct file *file,
+ char *buf, int count)
+#else
+static long capi_read(struct inode *inode, struct file *file,
+ char *buf, unsigned long count)
+#endif
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+ struct capidev *cdev;
+ struct sk_buff *skb;
+ int retval;
+ size_t copied;
+
+ if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered)
+ return -ENODEV;
+
+ cdev = &capidevs[minor];
+
+ if ((skb = skb_dequeue(&cdev->recv_queue)) == 0) {
+
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ for (;;) {
+ interruptible_sleep_on(&cdev->recv_wait);
+ if ((skb = skb_dequeue(&cdev->recv_queue)) != 0)
+ break;
+ if (current->signal & ~current->blocked)
+ break;
+ }
+ if (skb == 0)
+ return -ERESTARTNOHAND;
+ }
+ if (skb->len > count) {
+ skb_queue_head(&cdev->recv_queue, skb);
+ return -EMSGSIZE;
+ }
+ if (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3
+ && CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND)
+ CAPIMSG_SETDATA(skb->data, buf + CAPIMSG_LEN(skb->data));
+ retval = copy_to_user(buf, skb->data, skb->len);
+ if (retval) {
+ skb_queue_head(&cdev->recv_queue, skb);
+ return retval;
+ }
+ copied = skb->len;
+
+
+ kfree_skb(skb, FREE_READ);
+
+ return copied;
+}
+
+#if LINUX_VERSION_CODE < 0x020100
+static int capi_write(struct inode *inode, struct file *file,
+ const char *buf, int count)
+#else
+static long capi_write(struct inode *inode, struct file *file,
+ const char *buf, unsigned long count)
+#endif
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+ struct capidev *cdev;
+ struct sk_buff *skb;
+ int retval;
+ __u8 cmd;
+ __u8 subcmd;
+ __u16 mlen;
+
+ if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered)
+ return -ENODEV;
+
+ cdev = &capidevs[minor];
+
+ skb = alloc_skb(count, GFP_USER);
+
+ if ((retval = copy_from_user(skb_put(skb, count), buf, count))) {
+ dev_kfree_skb(skb, FREE_WRITE);
+ return retval;
+ }
+ cmd = CAPIMSG_COMMAND(skb->data);
+ subcmd = CAPIMSG_SUBCOMMAND(skb->data);
+ mlen = CAPIMSG_LEN(skb->data);
+ if (cmd == CAPI_DATA_B3 && subcmd == CAPI_REQ) {
+ __u16 dlen = CAPIMSG_DATALEN(skb->data);
+ if (mlen + dlen != count) {
+ dev_kfree_skb(skb, FREE_WRITE);
+ return -EINVAL;
+ }
+ } else if (mlen != count) {
+ dev_kfree_skb(skb, FREE_WRITE);
+ return -EINVAL;
+ }
+ CAPIMSG_SETAPPID(skb->data, cdev->applid);
+
+ cdev->errcode = (*capifuncs->capi_put_message) (cdev->applid, skb);
+
+ if (cdev->errcode) {
+ dev_kfree_skb(skb, FREE_WRITE);
+ return -EIO;
+ }
+ return count;
+}
+
+#if (LINUX_VERSION_CODE < 0x020117)
+static int capi_select(struct inode *inode, struct file *file,
+ int sel_type, select_table * wait)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+ struct capidev *cdev;
+
+ if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered)
+ return -ENODEV;
+
+ cdev = &capidevs[minor];
+
+ switch (sel_type) {
+ case SEL_IN:
+ if (!skb_queue_empty(&cdev->recv_queue))
+ return 1;
+ /* fall througth */
+ case SEL_EX:
+ /* error conditions ? */
+
+ select_wait(&cdev->recv_wait, wait);
+ return 0;
+ case SEL_OUT:
+ /*
+ if (!queue_full())
+ return 1;
+ select_wait(&cdev->send_wait, wait);
+ return 0;
+ */
+ return 1;
+ }
+ return 1;
+}
+#else
+static unsigned int
+capi_poll(struct file *file, poll_table * wait)
+{
+ unsigned int mask = 0;
+ unsigned int minor = MINOR(file->f_inode->i_rdev);
+ struct capidev *cdev;
+
+ if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered)
+ return POLLERR;
+
+ cdev = &capidevs[minor];
+ poll_wait(&(cdev->recv_wait), wait);
+ mask = POLLOUT | POLLWRNORM;
+ if (!skb_queue_empty(&cdev->recv_queue))
+ mask |= POLLIN | POLLRDNORM;
+ return mask;
+}
+#endif
+
+static int capi_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+ struct capidev *cdev;
+ capi_ioctl_struct data;
+ int retval;
+
+
+ if (minor >= CAPI_MAXMINOR || !capidevs[minor].is_open)
+ return -ENODEV;
+
+ cdev = &capidevs[minor];
+
+ switch (cmd) {
+ case CAPI_REGISTER:
+ {
+ if (!minor)
+ return -EINVAL;
+ retval = copy_from_user((void *) &data.rparams,
+ (void *) arg, sizeof(struct capi_register_params));
+ if (retval)
+ return -EFAULT;
+ if (cdev->is_registered)
+ return -EEXIST;
+ cdev->errcode = (*capifuncs->capi_register) (&data.rparams,
+ &cdev->applid);
+ if (cdev->errcode)
+ return -EIO;
+ (void) (*capifuncs->capi_set_signal) (cdev->applid, capi_signal, minor);
+ cdev->is_registered = 1;
+ }
+ return 0;
+
+ case CAPI_GET_VERSION:
+ {
+ retval = copy_from_user((void *) &data.contr,
+ (void *) arg,
+ sizeof(data.contr));
+ if (retval)
+ return -EFAULT;
+ cdev->errcode = (*capifuncs->capi_get_version) (data.contr, &data.version);
+ if (cdev->errcode)
+ return -EIO;
+ retval = copy_to_user((void *) arg,
+ (void *) &data.version,
+ sizeof(data.version));
+ if (retval)
+ return -EFAULT;
+ }
+ return 0;
+
+ case CAPI_GET_SERIAL:
+ {
+ retval = copy_from_user((void *) &data.contr,
+ (void *) arg,
+ sizeof(data.contr));
+ if (retval)
+ return -EFAULT;
+ cdev->errcode = (*capifuncs->capi_get_serial) (data.contr, data.serial);
+ if (cdev->errcode)
+ return -EIO;
+ retval = copy_to_user((void *) arg,
+ (void *) data.serial,
+ sizeof(data.serial));
+ if (retval)
+ return -EFAULT;
+ }
+ return 0;
+ case CAPI_GET_PROFILE:
+ {
+ retval = copy_from_user((void *) &data.contr,
+ (void *) arg,
+ sizeof(data.contr));
+ if (retval)
+ return -EFAULT;
+
+ if (data.contr == 0) {
+ cdev->errcode = (*capifuncs->capi_get_profile) (data.contr, &data.profile);
+ if (cdev->errcode)
+ return -EIO;
+
+ retval = copy_to_user((void *) arg,
+ (void *) &data.profile.ncontroller,
+ sizeof(data.profile.ncontroller));
+
+ } else {
+ cdev->errcode = (*capifuncs->capi_get_profile) (data.contr, &data.profile);
+ if (cdev->errcode)
+ return -EIO;
+
+ retval = copy_to_user((void *) arg,
+ (void *) &data.profile,
+ sizeof(data.profile));
+ }
+ if (retval)
+ return -EFAULT;
+ }
+ return 0;
+
+ case CAPI_GET_MANUFACTURER:
+ {
+ retval = copy_from_user((void *) &data.contr,
+ (void *) arg,
+ sizeof(data.contr));
+ if (retval)
+ return -EFAULT;
+ cdev->errcode = (*capifuncs->capi_get_manufacturer) (data.contr, data.manufacturer);
+ if (cdev->errcode)
+ return -EIO;
+
+ retval = copy_to_user((void *) arg, (void *) data.manufacturer,
+ sizeof(data.manufacturer));
+ if (retval)
+ return -EFAULT;
+
+ }
+ return 0;
+ case CAPI_GET_ERRCODE:
+ data.errcode = cdev->errcode;
+ cdev->errcode = CAPI_NOERROR;
+ if (arg) {
+ retval = copy_to_user((void *) arg,
+ (void *) &data.errcode,
+ sizeof(data.errcode));
+ if (retval)
+ return -EFAULT;
+ }
+ return data.errcode;
+
+ case CAPI_INSTALLED:
+ if ((*capifuncs->capi_installed) ())
+ return 0;
+ return -ENXIO;
+
+ case CAPI_MANUFACTURER_CMD:
+ {
+ struct capi_manufacturer_cmd mcmd;
+ if (minor)
+ return -EINVAL;
+ if (!suser())
+ return -EPERM;
+ retval = copy_from_user((void *) &mcmd, (void *) arg,
+ sizeof(mcmd));
+ if (retval)
+ return -EFAULT;
+ return (*capifuncs->capi_manufacturer) (mcmd.cmd, mcmd.data);
+ }
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int capi_open(struct inode *inode, struct file *file)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+
+ if (minor >= CAPI_MAXMINOR)
+ return -ENXIO;
+
+ if (minor) {
+ if (capidevs[minor].is_open)
+ return -EEXIST;
+
+ capidevs[minor].is_open = 1;
+ skb_queue_head_init(&capidevs[minor].recv_queue);
+ MOD_INC_USE_COUNT;
+
+ } else {
+
+ if (!capidevs[minor].is_open) {
+ capidevs[minor].is_open = 1;
+ MOD_INC_USE_COUNT;
+ }
+ }
+
+
+ return 0;
+}
+
+static CLOSETYPE
+capi_release(struct inode *inode, struct file *file)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+ struct capidev *cdev;
+ struct sk_buff *skb;
+
+ if (minor >= CAPI_MAXMINOR || !capidevs[minor].is_open) {
+ printk(KERN_ERR "capi20: release minor %d ???\n", minor);
+ return CLOSEVAL;
+ }
+ cdev = &capidevs[minor];
+
+ if (minor) {
+
+ if (cdev->is_registered)
+ (*capifuncs->capi_release) (cdev->applid);
+
+ cdev->is_registered = 0;
+ cdev->applid = 0;
+
+ while ((skb = skb_dequeue(&cdev->recv_queue)) != 0)
+ kfree_skb(skb, FREE_READ);
+ }
+ cdev->is_open = 0;
+
+ MOD_DEC_USE_COUNT;
+ return CLOSEVAL;
+}
+
+static struct file_operations capi_fops =
+{
+#if LINUX_VERSION_CODE < 0x020100
+ capi_lseek,
+#else
+ capi_llseek,
+#endif
+ capi_read,
+ capi_write,
+ NULL, /* capi_readdir */
+#if (LINUX_VERSION_CODE < 0x020117)
+ capi_select,
+#else
+ capi_poll,
+#endif
+ capi_ioctl,
+ NULL, /* capi_mmap */
+ capi_open,
+ capi_release,
+ NULL, /* capi_fsync */
+ NULL, /* capi_fasync */
+};
+
+
+/* -------- init function and module interface ---------------------- */
+
+#ifdef MODULE
+#define capi_init init_module
+#endif
+
+static struct capi_interface_user cuser = {
+ "capi20",
+ 0,
+};
+
+int capi_init(void)
+{
+ memset(capidevs, 0, sizeof(capidevs));
+
+ if (register_chrdev(capi_major, "capi20", &capi_fops)) {
+ printk(KERN_ERR "capi20: unable to get major %d\n", capi_major);
+ return -EIO;
+ }
+ printk(KERN_NOTICE "capi20: started up with major %d\n", capi_major);
+
+ if ((capifuncs = attach_capi_interface(&cuser)) == 0) {
+ unregister_chrdev(capi_major, "capi20");
+ return -EIO;
+ }
+ return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ unregister_chrdev(capi_major, "capi20");
+ (void) detach_capi_interface(&cuser);
+}
+
+#endif
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov