Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / drivers / isdn / i4l / isdn_common.c
index daec603..69aee26 100644 (file)
  *
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/init.h>
-#include <linux/version.h>
 #include <linux/poll.h>
 #include <linux/vmalloc.h>
 #include <linux/isdn.h>
@@ -64,10 +62,10 @@ static isdn_divert_if *divert_if; /* = NULL */
 #endif /* CONFIG_ISDN_DIVERSION */
 
 
-static int isdn_writebuf_stub(int, int, const u_char *, int, int);
+static int isdn_writebuf_stub(int, int, const u_char __user *, int);
 static void set_global_features(void);
 static int isdn_wildmat(char *s, char *p);
-
+static int isdn_add_channels(isdn_driver_t *d, int drvidx, int n, int adding);
 
 static inline void
 isdn_lock_driver(isdn_driver_t *drv)
@@ -341,6 +339,16 @@ isdn_command(isdn_ctrl *cmd)
                printk(KERN_WARNING "isdn_command command(%x) driver -1\n", cmd->command);
                return(1);
        }
+       if (!dev->drv[cmd->driver]) {
+               printk(KERN_WARNING "isdn_command command(%x) dev->drv[%d] NULL\n",
+                       cmd->command, cmd->driver);
+               return(1);
+       }
+       if (!dev->drv[cmd->driver]->interface) {
+               printk(KERN_WARNING "isdn_command command(%x) dev->drv[%d]->interface NULL\n",
+                       cmd->command, cmd->driver);
+               return(1);
+       }
        if (cmd->command == ISDN_CMD_SETL2) {
                int idx = isdn_dc2minor(cmd->driver, cmd->arg & 255);
                unsigned long l2prot = (cmd->arg >> 8) & 255;
@@ -386,9 +394,9 @@ isdn_all_eaz(int di, int ch)
  * Begin of a CAPI like LL<->HL interface, currently used only for 
  * supplementary service (CAPI 2.0 part III)
  */
-#include <linux/isdn//capicmd.h>
+#include <linux/isdn/capicmd.h>
 
-int
+static int
 isdn_capi_rec_hl_msg(capi_msg *cm) {
        
        int di;
@@ -858,6 +866,118 @@ isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, wait_que
        return count;
 }
 
+/*
+ * isdn_readbchan_tty() tries to get data from the read-queue.
+ * It MUST be called with interrupts off.
+ *
+ * Be aware that this is not an atomic operation when sleep != 0, even though
+ * interrupts are turned off! Well, like that we are currently only called
+ * on behalf of a read system call on raw device files (which are documented
+ * to be dangerous and for for debugging purpose only). The inode semaphore
+ * takes care that this is not called for the same minor device number while
+ * we are sleeping, but access is not serialized against simultaneous read()
+ * from the corresponding ttyI device. Can other ugly events, like changes
+ * of the mapping (di,ch)<->minor, happen during the sleep? --he
+ */
+int
+isdn_readbchan_tty(int di, int channel, struct tty_struct *tty, int cisco_hack)
+{
+       int count;
+       int count_pull;
+       int count_put;
+       int dflag;
+       struct sk_buff *skb;
+       char last = 0;
+       int len;
+
+       if (!dev->drv[di])
+               return 0;
+       if (skb_queue_empty(&dev->drv[di]->rpqueue[channel]))
+                       return 0;
+
+       len = tty_buffer_request_room(tty, dev->drv[di]->rcvcount[channel]);
+       if(len == 0)
+               return len;
+
+       count = 0;
+       while (len) {
+               if (!(skb = skb_peek(&dev->drv[di]->rpqueue[channel])))
+                       break;
+#ifdef CONFIG_ISDN_AUDIO
+               if (ISDN_AUDIO_SKB_LOCK(skb))
+                       break;
+               ISDN_AUDIO_SKB_LOCK(skb) = 1;
+               if ((ISDN_AUDIO_SKB_DLECOUNT(skb)) || (dev->drv[di]->DLEflag & (1 << channel))) {
+                       char *p = skb->data;
+                       unsigned long DLEmask = (1 << channel);
+
+                       dflag = 0;
+                       count_pull = count_put = 0;
+                       while ((count_pull < skb->len) && (len > 0)) {
+                               len--;
+                               if (dev->drv[di]->DLEflag & DLEmask) {
+                                       last = DLE;
+                                       dev->drv[di]->DLEflag &= ~DLEmask;
+                               } else {
+                                       last = *p;
+                                       if (last == DLE) {
+                                               dev->drv[di]->DLEflag |= DLEmask;
+                                               (ISDN_AUDIO_SKB_DLECOUNT(skb))--;
+                                       }
+                                       p++;
+                                       count_pull++;
+                               }
+                               count_put++;
+                       }
+                       if (count_pull >= skb->len)
+                               dflag = 1;
+               } else {
+#endif
+                       /* No DLE's in buff, so simply copy it */
+                       dflag = 1;
+                       if ((count_pull = skb->len) > len) {
+                               count_pull = len;
+                               dflag = 0;
+                       }
+                       count_put = count_pull;
+                       if(count_put > 1)
+                               tty_insert_flip_string(tty, skb->data, count_put - 1);
+                       last = skb->data[count_put - 1];
+                       len -= count_put;
+#ifdef CONFIG_ISDN_AUDIO
+               }
+#endif
+               count += count_put;
+               if (dflag) {
+                       /* We got all the data in this buff.
+                        * Now we can dequeue it.
+                        */
+                       if(cisco_hack)
+                               tty_insert_flip_char(tty, last, 0xFF);
+                       else
+                               tty_insert_flip_char(tty, last, TTY_NORMAL);
+#ifdef CONFIG_ISDN_AUDIO
+                       ISDN_AUDIO_SKB_LOCK(skb) = 0;
+#endif
+                       skb = skb_dequeue(&dev->drv[di]->rpqueue[channel]);
+                       dev_kfree_skb(skb);
+               } else {
+                       tty_insert_flip_char(tty, last, TTY_NORMAL);
+                       /* Not yet emptied this buff, so it
+                        * must stay in the queue, for further calls
+                        * but we pull off the data we got until now.
+                        */
+                       skb_pull(skb, count_pull);
+#ifdef CONFIG_ISDN_AUDIO
+                       ISDN_AUDIO_SKB_LOCK(skb) = 0;
+#endif
+               }
+               dev->drv[di]->rcvcount[channel] -= count_put;
+       }
+       return count;
+}
+
+
 static __inline int
 isdn_minor2drv(int minor)
 {
@@ -937,18 +1057,15 @@ isdn_info_update(void)
 }
 
 static ssize_t
-isdn_read(struct file *file, char *buf, size_t count, loff_t * off)
+isdn_read(struct file *file, char __user *buf, size_t count, loff_t * off)
 {
-       uint minor = MINOR(file->f_dentry->d_inode->i_rdev);
+       uint minor = iminor(file->f_dentry->d_inode);
        int len = 0;
        int drvidx;
        int chidx;
        int retval;
        char *p;
 
-       if (off != &file->f_pos)
-               return -ESPIPE;
-
        lock_kernel();
        if (minor == ISDN_MINOR_STATUS) {
                if (!file->private_data) {
@@ -959,7 +1076,7 @@ isdn_read(struct file *file, char *buf, size_t count, loff_t * off)
                        interruptible_sleep_on(&(dev->info_waitq));
                }
                p = isdn_statstr();
-               file->private_data = 0;
+               file->private_data = NULL;
                if ((len = strlen(p)) <= count) {
                        if (copy_to_user(buf, p, len)) {
                                retval = -EFAULT;
@@ -992,7 +1109,7 @@ isdn_read(struct file *file, char *buf, size_t count, loff_t * off)
                        retval = -ENOMEM;
                        goto out;
                }
-               len = isdn_readbchan(drvidx, chidx, p, 0, count,
+               len = isdn_readbchan(drvidx, chidx, p, NULL, count,
                                     &dev->drv[drvidx]->rcv_waitq[chidx]);
                *off += len;
                if (copy_to_user(buf,p,len)) 
@@ -1017,9 +1134,12 @@ isdn_read(struct file *file, char *buf, size_t count, loff_t * off)
                if (dev->drv[drvidx]->interface->readstat) {
                        if (count > dev->drv[drvidx]->stavail)
                                count = dev->drv[drvidx]->stavail;
-                       len = dev->drv[drvidx]->interface->
-                               readstat(buf, count, 1, drvidx,
-                                        isdn_minor2chan(minor));
+                       len = dev->drv[drvidx]->interface->readstat(buf, count,
+                                               drvidx, isdn_minor2chan(minor));
+                       if (len < 0) {
+                               retval = len;
+                               goto out;
+                       }
                } else {
                        len = 0;
                }
@@ -1044,16 +1164,13 @@ isdn_read(struct file *file, char *buf, size_t count, loff_t * off)
 }
 
 static ssize_t
-isdn_write(struct file *file, const char *buf, size_t count, loff_t * off)
+isdn_write(struct file *file, const char __user *buf, size_t count, loff_t * off)
 {
-       uint minor = MINOR(file->f_dentry->d_inode->i_rdev);
+       uint minor = iminor(file->f_dentry->d_inode);
        int drvidx;
        int chidx;
        int retval;
 
-       if (off != &file->f_pos)
-               return -ESPIPE;
-
        if (minor == ISDN_MINOR_STATUS)
                return -EPERM;
        if (!dev->drivers)
@@ -1072,9 +1189,8 @@ isdn_write(struct file *file, const char *buf, size_t count, loff_t * off)
                        goto out;
                }
                chidx = isdn_minor2chan(minor);
-               while (isdn_writebuf_stub(drvidx, chidx, buf, count, 1) != count)
+               while ((retval = isdn_writebuf_stub(drvidx, chidx, buf, count)) == 0)
                        interruptible_sleep_on(&dev->drv[drvidx]->snd_waitq[chidx]);
-               retval = count;
                goto out;
        }
        if (minor <= ISDN_MINOR_CTRLMAX) {
@@ -1091,7 +1207,7 @@ isdn_write(struct file *file, const char *buf, size_t count, loff_t * off)
                 */
                if (dev->drv[drvidx]->interface->writecmd)
                        retval = dev->drv[drvidx]->interface->
-                               writecmd(buf, count, 1, drvidx, isdn_minor2chan(minor));
+                               writecmd(buf, count, drvidx, isdn_minor2chan(minor));
                else
                        retval = count;
                goto out;
@@ -1112,7 +1228,7 @@ static unsigned int
 isdn_poll(struct file *file, poll_table * wait)
 {
        unsigned int mask = 0;
-       unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+       unsigned int minor = iminor(file->f_dentry->d_inode);
        int drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
 
        lock_kernel();
@@ -1153,13 +1269,13 @@ isdn_poll(struct file *file, poll_table * wait)
 static int
 isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
 {
-       uint minor = MINOR(inode->i_rdev);
+       uint minor = iminor(inode);
        isdn_ctrl c;
        int drvidx;
        int chidx;
        int ret;
        int i;
-       char *p;
+       char __user *p;
        char *s;
        union iocpar {
                char name[10];
@@ -1168,6 +1284,7 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
                isdn_net_ioctl_phone phone;
                isdn_net_ioctl_cfg cfg;
        } iocpar;
+       void __user *argp = (void __user *)arg;
 
 #define name  iocpar.name
 #define bname iocpar.bname
@@ -1183,11 +1300,11 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
                                        (INF_DV << 16));
                        case IIOCGETCPS:
                                if (arg) {
-                                       ulong *p = (ulong *) arg;
+                                       ulong __user *p = argp;
                                        int i;
-                                       if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
-                                                              sizeof(ulong) * ISDN_MAX_CHANNELS * 2)))
-                                               return ret;
+                                       if (!access_ok(VERIFY_WRITE, p,
+                                                       sizeof(ulong) * ISDN_MAX_CHANNELS * 2))
+                                               return -EFAULT;
                                        for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
                                                put_user(dev->ibytes[i], p++);
                                                put_user(dev->obytes[i], p++);
@@ -1201,9 +1318,9 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
                                /* Get peer phone number of a connected 
                                 * isdn network interface */
                                if (arg) {
-                                       if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
+                                       if (copy_from_user(&phone, argp, sizeof(phone)))
                                                return -EFAULT;
-                                       return isdn_net_getpeer(&phone, (isdn_net_ioctl_phone *) arg);
+                                       return isdn_net_getpeer(&phone, argp);
                                } else
                                        return -EINVAL;
 #endif
@@ -1241,7 +1358,7 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
                        case IIOCNETAIF:
                                /* Add a network-interface */
                                if (arg) {
-                                       if (copy_from_user(name, (char *) arg, sizeof(name)))
+                                       if (copy_from_user(name, argp, sizeof(name)))
                                                return -EFAULT;
                                        s = name;
                                } else {
@@ -1250,7 +1367,7 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
                                ret = down_interruptible(&dev->sem);
                                if( ret ) return ret;
                                if ((s = isdn_net_new(s, NULL))) {
-                                       if (copy_to_user((char *) arg, s, strlen(s) + 1)){
+                                       if (copy_to_user(argp, s, strlen(s) + 1)){
                                                ret = -EFAULT;
                                        } else {
                                                ret = 0;
@@ -1262,14 +1379,14 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
                        case IIOCNETASL:
                                /* Add a slave to a network-interface */
                                if (arg) {
-                                       if (copy_from_user(bname, (char *) arg, sizeof(bname) - 1))
+                                       if (copy_from_user(bname, argp, sizeof(bname) - 1))
                                                return -EFAULT;
                                } else
                                        return -EINVAL;
                                ret = down_interruptible(&dev->sem);
                                if( ret ) return ret;
                                if ((s = isdn_net_newslave(bname))) {
-                                       if (copy_to_user((char *) arg, s, strlen(s) + 1)){
+                                       if (copy_to_user(argp, s, strlen(s) + 1)){
                                                ret = -EFAULT;
                                        } else {
                                                ret = 0;
@@ -1281,7 +1398,7 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
                        case IIOCNETDIF:
                                /* Delete a network-interface */
                                if (arg) {
-                                       if (copy_from_user(name, (char *) arg, sizeof(name)))
+                                       if (copy_from_user(name, argp, sizeof(name)))
                                                return -EFAULT;
                                        ret = down_interruptible(&dev->sem);
                                        if( ret ) return ret;
@@ -1293,7 +1410,7 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
                        case IIOCNETSCF:
                                /* Set configurable parameters of a network-interface */
                                if (arg) {
-                                       if (copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg)))
+                                       if (copy_from_user(&cfg, argp, sizeof(cfg)))
                                                return -EFAULT;
                                        return isdn_net_setcfg(&cfg);
                                } else
@@ -1301,10 +1418,10 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
                        case IIOCNETGCF:
                                /* Get configurable parameters of a network-interface */
                                if (arg) {
-                                       if (copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg)))
+                                       if (copy_from_user(&cfg, argp, sizeof(cfg)))
                                                return -EFAULT;
                                        if (!(ret = isdn_net_getcfg(&cfg))) {
-                                               if (copy_to_user((char *) arg, (char *) &cfg, sizeof(cfg)))
+                                               if (copy_to_user(argp, &cfg, sizeof(cfg)))
                                                        return -EFAULT;
                                        }
                                        return ret;
@@ -1313,7 +1430,7 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
                        case IIOCNETANM:
                                /* Add a phone-number to a network-interface */
                                if (arg) {
-                                       if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
+                                       if (copy_from_user(&phone, argp, sizeof(phone)))
                                                return -EFAULT;
                                        ret = down_interruptible(&dev->sem);
                                        if( ret ) return ret;
@@ -1325,11 +1442,11 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
                        case IIOCNETGNM:
                                /* Get list of phone-numbers of a network-interface */
                                if (arg) {
-                                       if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
+                                       if (copy_from_user(&phone, argp, sizeof(phone)))
                                                return -EFAULT;
                                        ret = down_interruptible(&dev->sem);
                                        if( ret ) return ret;
-                                       ret = isdn_net_getphones(&phone, (char *) arg);
+                                       ret = isdn_net_getphones(&phone, argp);
                                        up(&dev->sem);
                                        return ret;
                                } else
@@ -1337,7 +1454,7 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
                        case IIOCNETDNM:
                                /* Delete a phone-number of a network-interface */
                                if (arg) {
-                                       if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
+                                       if (copy_from_user(&phone, argp, sizeof(phone)))
                                                return -EFAULT;
                                        ret = down_interruptible(&dev->sem);
                                        if( ret ) return ret;
@@ -1349,7 +1466,7 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
                        case IIOCNETDIL:
                                /* Force dialing of a network-interface */
                                if (arg) {
-                                       if (copy_from_user(name, (char *) arg, sizeof(name)))
+                                       if (copy_from_user(name, argp, sizeof(name)))
                                                return -EFAULT;
                                        return isdn_net_force_dial(name);
                                } else
@@ -1358,13 +1475,13 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
                        case IIOCNETALN:
                                if (!arg)
                                        return -EINVAL;
-                               if (copy_from_user(name, (char *) arg, sizeof(name)))
+                               if (copy_from_user(name, argp, sizeof(name)))
                                        return -EFAULT;
                                return isdn_ppp_dial_slave(name);
                        case IIOCNETDLN:
                                if (!arg)
                                        return -EINVAL;
-                               if (copy_from_user(name, (char *) arg, sizeof(name)))
+                               if (copy_from_user(name, argp, sizeof(name)))
                                        return -EFAULT;
                                return isdn_ppp_hangup_slave(name);
 #endif
@@ -1372,7 +1489,7 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
                                /* Force hangup of a network-interface */
                                if (!arg)
                                        return -EINVAL;
-                               if (copy_from_user(name, (char *) arg, sizeof(name)))
+                               if (copy_from_user(name, argp, sizeof(name)))
                                        return -EFAULT;
                                return isdn_net_force_hangup(name);
                                break;
@@ -1394,7 +1511,7 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
                                if (arg) {
                                        int i;
                                        char *p;
-                                       if (copy_from_user((char *) &iocts, (char *) arg,
+                                       if (copy_from_user(&iocts, argp,
                                             sizeof(isdn_ioctl_struct)))
                                                return -EFAULT;
                                        if (strlen(iocts.drvid)) {
@@ -1422,13 +1539,13 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
                        case IIOCGETPRF:
                                /* Get all Modem-Profiles */
                                if (arg) {
-                                       char *p = (char *) arg;
+                                       char __user *p = argp;
                                        int i;
 
-                                       if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
+                                       if (!access_ok(VERIFY_WRITE, argp,
                                        (ISDN_MODEM_NUMREG + ISDN_MSNLEN + ISDN_LMSNLEN)
-                                                  * ISDN_MAX_CHANNELS)))
-                                               return ret;
+                                                  * ISDN_MAX_CHANNELS))
+                                               return -EFAULT;
 
                                        for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
                                                if (copy_to_user(p, dev->mdm.info[i].emu.profile,
@@ -1449,13 +1566,13 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
                        case IIOCSETPRF:
                                /* Set all Modem-Profiles */
                                if (arg) {
-                                       char *p = (char *) arg;
+                                       char __user *p = argp;
                                        int i;
 
-                                       if ((ret = verify_area(VERIFY_READ, (void *) arg,
+                                       if (!access_ok(VERIFY_READ, argp,
                                        (ISDN_MODEM_NUMREG + ISDN_MSNLEN + ISDN_LMSNLEN)
-                                                  * ISDN_MAX_CHANNELS)))
-                                               return ret;
+                                                  * ISDN_MAX_CHANNELS))
+                                               return -EFAULT;
 
                                        for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
                                                if (copy_from_user(dev->mdm.info[i].emu.profile, p,
@@ -1478,8 +1595,7 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
                                /* Set/Get MSN->EAZ-Mapping for a driver */
                                if (arg) {
 
-                                       if (copy_from_user((char *) &iocts,
-                                                           (char *) arg,
+                                       if (copy_from_user(&iocts, argp,
                                             sizeof(isdn_ioctl_struct)))
                                                return -EFAULT;
                                        if (strlen(iocts.drvid)) {
@@ -1496,14 +1612,14 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
                                        if (cmd == IIOCSETMAP) {
                                                int loop = 1;
 
-                                               p = (char *) iocts.arg;
+                                               p = (char __user *) iocts.arg;
                                                i = 0;
                                                while (loop) {
                                                        int j = 0;
 
                                                        while (1) {
-                                                               if ((ret = verify_area(VERIFY_READ, p, 1)))
-                                                                       return ret;
+                                                               if (!access_ok(VERIFY_READ, p, 1))
+                                                                       return -EFAULT;
                                                                get_user(bname[j], p++);
                                                                switch (bname[j]) {
                                                                        case '\0':
@@ -1524,7 +1640,7 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
                                                                break;
                                                }
                                        } else {
-                                               p = (char *) iocts.arg;
+                                               p = (char __user *) iocts.arg;
                                                for (i = 0; i < 10; i++) {
                                                        sprintf(bname, "%s%s",
                                                                strlen(dev->drv[drvidx]->msn2eaz[i]) ?
@@ -1540,7 +1656,7 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
                                        return -EINVAL;
                        case IIOCDBGVAR:
                                if (arg) {
-                                       if (copy_to_user((char *) arg, (char *) &dev, sizeof(ulong)))
+                                       if (copy_to_user(argp, &dev, sizeof(ulong)))
                                                return -EFAULT;
                                        return 0;
                                } else
@@ -1554,7 +1670,7 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
                                if (arg) {
                                        int i;
                                        char *p;
-                                       if (copy_from_user((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct)))
+                                       if (copy_from_user(&iocts, argp, sizeof(isdn_ioctl_struct)))
                                                return -EFAULT;
                                        if (strlen(iocts.drvid)) {
                                                if ((p = strchr(iocts.drvid, ',')))
@@ -1569,16 +1685,16 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
                                                drvidx = 0;
                                        if (drvidx == -1)
                                                return -ENODEV;
-                                       if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
-                                            sizeof(isdn_ioctl_struct))))
-                                               return ret;
+                                       if (!access_ok(VERIFY_WRITE, argp,
+                                            sizeof(isdn_ioctl_struct)))
+                                               return -EFAULT;
                                        c.driver = drvidx;
                                        c.command = ISDN_CMD_IOCTL;
                                        c.arg = cmd;
-                                       memcpy(c.parm.num, (char *) &iocts.arg, sizeof(ulong));
+                                       memcpy(c.parm.num, &iocts.arg, sizeof(ulong));
                                        ret = isdn_command(&c);
-                                       memcpy((char *) &iocts.arg, c.parm.num, sizeof(ulong));
-                                       if (copy_to_user((char *) arg, &iocts, sizeof(isdn_ioctl_struct)))
+                                       memcpy(&iocts.arg, c.parm.num, sizeof(ulong));
+                                       if (copy_to_user(argp, &iocts, sizeof(isdn_ioctl_struct)))
                                                return -EFAULT;
                                        return ret;
                                } else
@@ -1604,7 +1720,7 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
 static int
 isdn_open(struct inode *ino, struct file *filep)
 {
-       uint minor = MINOR(ino->i_rdev);
+       uint minor = iminor(ino);
        int drvidx;
        int chidx;
        int retval = -ENODEV;
@@ -1659,13 +1775,14 @@ isdn_open(struct inode *ino, struct file *filep)
        }
 #endif
  out:
+       nonseekable_open(ino, filep);
        return retval;
 }
 
 static int
 isdn_close(struct inode *ino, struct file *filep)
 {
-       uint minor = MINOR(ino->i_rdev);
+       uint minor = iminor(ino);
 
        lock_kernel();
        if (minor == ISDN_MINOR_STATUS) {
@@ -1798,6 +1915,11 @@ isdn_free_channel(int di, int ch, int usage)
 {
        int i;
 
+       if ((di < 0) || (ch < 0)) {
+               printk(KERN_WARNING "%s: called with invalid drv(%d) or channel(%d)\n",
+                       __FUNCTION__, di, ch);
+               return;
+       }
        for (i = 0; i < ISDN_MAX_CHANNELS; i++)
                if (((!usage) || ((dev->usage[i] & ISDN_USAGE_MASK) == usage)) &&
                    (dev->drvmap[i] == di) &&
@@ -1813,7 +1935,8 @@ isdn_free_channel(int di, int ch, int usage)
                        dev->v110[i] = NULL;
 // 20.10.99 JIM, try to reinitialize v110 !
                        isdn_info_update();
-                       skb_queue_purge(&dev->drv[di]->rpqueue[ch]);
+                       if (dev->drv[di])
+                               skb_queue_purge(&dev->drv[di]->rpqueue[ch]);
                }
 }
 
@@ -1838,20 +1961,17 @@ isdn_unexclusive_channel(int di, int ch)
  *  writebuf replacement for SKB_ABLE drivers
  */
 static int
-isdn_writebuf_stub(int drvidx, int chan, const u_char * buf, int len,
-                  int user)
+isdn_writebuf_stub(int drvidx, int chan, const u_char __user * buf, int len)
 {
        int ret;
        int hl = dev->drv[drvidx]->interface->hl_hdrlen;
        struct sk_buff *skb = alloc_skb(hl + len, GFP_ATOMIC);
 
        if (!skb)
-               return 0;
+               return -ENOMEM;
        skb_reserve(skb, hl);
-       if (user)
-               copy_from_user(skb_put(skb, len), buf, len);
-       else
-               memcpy(skb_put(skb, len), buf, len);
+       if (copy_from_user(skb_put(skb, len), buf, len))
+               return -EFAULT;
        ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, 1, skb);
        if (ret <= 0)
                dev_kfree_skb(skb);
@@ -1932,7 +2052,7 @@ isdn_writebuf_skb_stub(int drvidx, int chan, int ack, struct sk_buff *skb)
        return ret;
 }
 
-int
+static int
 isdn_add_channels(isdn_driver_t *d, int drvidx, int n, int adding)
 {
        int j, k, m;
@@ -1962,7 +2082,8 @@ isdn_add_channels(isdn_driver_t *d, int drvidx, int n, int adding)
                kfree(d->rcvcount);
        if (!(d->rcvcount = kmalloc(sizeof(int) * m, GFP_ATOMIC))) {
                printk(KERN_WARNING "register_isdn: Could not alloc rcvcount\n");
-               if (!adding) kfree(d->rcverr);
+               if (!adding)
+                       kfree(d->rcverr);
                return -1;
        }
        memset((char *) d->rcvcount, 0, sizeof(int) * m);