patch-2.1.42 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

diff -u --recursive --new-file v2.1.41/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