X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fisdn%2Fi4l%2Fisdn_common.c;h=6a2ef0a87ed9a5143ba61a396cd35bdfd4908f68;hb=refs%2Fheads%2Fvserver;hp=c406df6f268a5f630c051bea40c5a45351203de0;hpb=f7f1b0f1e2fbadeab12d24236000e778aa9b1ead;p=linux-2.6.git diff --git a/drivers/isdn/i4l/isdn_common.c b/drivers/isdn/i4l/isdn_common.c index c406df6f2..6a2ef0a87 100644 --- a/drivers/isdn/i4l/isdn_common.c +++ b/drivers/isdn/i4l/isdn_common.c @@ -11,10 +11,8 @@ * */ -#include #include #include -#include #include #include #include @@ -67,7 +65,7 @@ static isdn_divert_if *divert_if; /* = NULL */ 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; @@ -388,7 +396,7 @@ isdn_all_eaz(int di, int ch) */ #include -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) { @@ -939,7 +1059,7 @@ isdn_info_update(void) static ssize_t 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_path.dentry->d_inode); int len = 0; int drvidx; int chidx; @@ -1014,9 +1134,12 @@ isdn_read(struct file *file, char __user *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, 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; } @@ -1043,7 +1166,7 @@ isdn_read(struct file *file, char __user *buf, size_t count, loff_t * off) static ssize_t 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_path.dentry->d_inode); int drvidx; int chidx; int retval; @@ -1066,9 +1189,8 @@ isdn_write(struct file *file, const char __user *buf, size_t count, loff_t * off goto out; } chidx = isdn_minor2chan(minor); - while (isdn_writebuf_stub(drvidx, chidx, buf, count) != 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) { @@ -1106,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_path.dentry->d_inode); int drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL); lock_kernel(); @@ -1147,7 +1269,7 @@ 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; @@ -1598,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; @@ -1660,7 +1782,7 @@ isdn_open(struct inode *ino, struct file *filep) 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) { @@ -1793,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) && @@ -1808,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]); } } @@ -1840,9 +1968,10 @@ isdn_writebuf_stub(int drvidx, int chan, const u_char __user * buf, int len) struct sk_buff *skb = alloc_skb(hl + len, GFP_ATOMIC); if (!skb) - return 0; + return -ENOMEM; skb_reserve(skb, hl); - copy_from_user(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); @@ -1923,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; @@ -1943,20 +2072,19 @@ isdn_add_channels(isdn_driver_t *d, int drvidx, int n, int adding) if ((adding) && (d->rcverr)) kfree(d->rcverr); - if (!(d->rcverr = kmalloc(sizeof(int) * m, GFP_ATOMIC))) { + if (!(d->rcverr = kzalloc(sizeof(int) * m, GFP_ATOMIC))) { printk(KERN_WARNING "register_isdn: Could not alloc rcverr\n"); return -1; } - memset((char *) d->rcverr, 0, sizeof(int) * m); if ((adding) && (d->rcvcount)) kfree(d->rcvcount); - if (!(d->rcvcount = kmalloc(sizeof(int) * m, GFP_ATOMIC))) { + if (!(d->rcvcount = kzalloc(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); if ((adding) && (d->rpqueue)) { for (j = 0; j < d->channels; j++) @@ -2096,11 +2224,10 @@ register_isdn(isdn_if * i) printk(KERN_WARNING "register_isdn: No write routine given.\n"); return 0; } - if (!(d = kmalloc(sizeof(isdn_driver_t), GFP_KERNEL))) { + if (!(d = kzalloc(sizeof(isdn_driver_t), GFP_KERNEL))) { printk(KERN_WARNING "register_isdn: Could not alloc driver-struct\n"); return 0; } - memset((char *) d, 0, sizeof(isdn_driver_t)); d->maxbufsize = i->maxbufsize; d->pktcount = 0;