*
*/
-#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>
#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)
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;
*/
#include <linux/isdn/capicmd.h>
-int
+static int
isdn_capi_rec_hl_msg(capi_msg *cm) {
int di;
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)
{
}
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_path.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) {
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;
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))
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;
}
}
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_path.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)
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) {
*/
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;
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();
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];
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
(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++);
/* 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
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 {
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;
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;
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;
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
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;
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;
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
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;
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
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
/* 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;
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)) {
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,
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,
/* 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)) {
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':
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]) ?
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
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, ',')))
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
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;
}
#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) {
{
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) &&
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]);
}
}
* 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);
return ret;
}
-int
+static int
isdn_add_channels(isdn_driver_t *d, int drvidx, int n, int adding)
{
int j, k, m;
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++)
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;