X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fisdn%2Fi4l%2Fisdn_common.c;fp=drivers%2Fisdn%2Fi4l%2Fisdn_common.c;h=22759c01746af5d71a1c016d79716d4ec3406c91;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=c406df6f268a5f630c051bea40c5a45351203de0;hpb=cee37fe97739d85991964371c1f3a745c00dd236;p=linux-2.6.git diff --git a/drivers/isdn/i4l/isdn_common.c b/drivers/isdn/i4l/isdn_common.c index c406df6f2..22759c017 100644 --- a/drivers/isdn/i4l/isdn_common.c +++ b/drivers/isdn/i4l/isdn_common.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -67,7 +66,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) @@ -388,7 +387,7 @@ isdn_all_eaz(int di, int ch) */ #include -int +static int isdn_capi_rec_hl_msg(capi_msg *cm) { int di; @@ -858,6 +857,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) { @@ -1923,7 +2034,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; @@ -1953,7 +2064,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);