extractedLnx/linux-2.5.8/drivers/isdn/isdn_common.c_isdn_ctrl_ioctl.c
static int
isdn_ctrl_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
{
isdn_ctrl c;
int drvidx;
int ret;
int i;
char *p;
char *s;
union iocpar {
char name[10];
char bname[22];
isdn_ioctl_struct iocts;
isdn_net_ioctl_phone phone;
isdn_net_ioctl_cfg cfg;
} iocpar;
#define name iocpar.name
#define bname iocpar.bname
#define iocts iocpar.iocts
#define phone iocpar.phone
#define cfg iocpar.cfg
/*
* isdn net devices manage lots of configuration variables as linked lists.
* Those lists must only be manipulated from user space. Some of the ioctl's
* service routines access user space and are not atomic. Therefor, ioctl's
* manipulating the lists and ioctl's sleeping while accessing the lists
* are serialized by means of a semaphore.
*/
switch (cmd) {
case IIOCNETDWRSET:
printk(KERN_INFO "INFO: ISDN_DW_ABC_EXTENSION not enabled\n");
return(-EINVAL);
case IIOCNETLCR:
printk(KERN_INFO "INFO: ISDN_ABC_LCR_SUPPORT not enabled\n");
return -ENODEV;
#ifdef CONFIG_NETDEVICES
case IIOCNETAIF:
/* Add a network-interface */
if (arg) {
if (copy_from_user(name, (char *) arg, sizeof(name)))
return -EFAULT;
s = name;
} else {
s = NULL;
}
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)){
ret = -EFAULT;
} else {
ret = 0;
}
} else
ret = -ENODEV;
up(&dev->sem);
return ret;
case IIOCNETASL:
/* Add a slave to a network-interface */
if (arg) {
if (copy_from_user(bname, (char *) arg, 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)){
ret = -EFAULT;
} else {
ret = 0;
}
} else
ret = -ENODEV;
up(&dev->sem);
return ret;
case IIOCNETDIF:
/* Delete a network-interface */
if (arg) {
if (copy_from_user(name, (char *) arg, sizeof(name)))
return -EFAULT;
ret = down_interruptible(&dev->sem);
if( ret ) return ret;
ret = isdn_net_rm(name);
up(&dev->sem);
return ret;
} else
return -EINVAL;
case IIOCNETSCF:
/* Set configurable parameters of a network-interface */
if (arg) {
if (copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg)))
return -EFAULT;
return isdn_net_setcfg(&cfg);
} else
return -EINVAL;
case IIOCNETGCF:
/* Get configurable parameters of a network-interface */
if (arg) {
if (copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg)))
return -EFAULT;
if (!(ret = isdn_net_getcfg(&cfg))) {
if (copy_to_user((char *) arg, (char *) &cfg, sizeof(cfg)))
return -EFAULT;
}
return ret;
} else
return -EINVAL;
case IIOCNETANM:
/* Add a phone-number to a network-interface */
if (arg) {
if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
return -EFAULT;
ret = down_interruptible(&dev->sem);
if( ret ) return ret;
ret = isdn_net_addphone(&phone);
up(&dev->sem);
return ret;
} else
return -EINVAL;
case IIOCNETGNM:
/* Get list of phone-numbers of a network-interface */
if (arg) {
if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
return -EFAULT;
ret = down_interruptible(&dev->sem);
if( ret ) return ret;
ret = isdn_net_getphones(&phone, (char *) arg);
up(&dev->sem);
return ret;
} else
return -EINVAL;
case IIOCNETDNM:
/* Delete a phone-number of a network-interface */
if (arg) {
if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
return -EFAULT;
ret = down_interruptible(&dev->sem);
if( ret ) return ret;
ret = isdn_net_delphone(&phone);
up(&dev->sem);
return ret;
} else
return -EINVAL;
case IIOCNETDIL:
/* Force dialing of a network-interface */
if (arg) {
if (copy_from_user(name, (char *) arg, sizeof(name)))
return -EFAULT;
return isdn_net_force_dial(name);
} else
return -EINVAL;
#ifdef CONFIG_ISDN_PPP
case IIOCNETALN:
if (!arg)
return -EINVAL;
if (copy_from_user(name, (char *) arg, 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)))
return -EFAULT;
return isdn_ppp_hangup_slave(name);
#endif
case IIOCNETHUP:
/* Force hangup of a network-interface */
if (!arg)
return -EINVAL;
if (copy_from_user(name, (char *) arg, sizeof(name)))
return -EFAULT;
return isdn_net_force_hangup(name);
break;
#endif /* CONFIG_NETDEVICES */
case IIOCSETVER:
dev->net_verbose = arg;
printk(KERN_INFO "isdn: Verbose-Level is %d\n", dev->net_verbose);
return 0;
case IIOCSETGST:
if (arg)
dev->global_flags |= ISDN_GLOBAL_STOPPED;
else
dev->global_flags &= ~ISDN_GLOBAL_STOPPED;
printk(KERN_INFO "isdn: Global Mode %s\n",
(dev->global_flags & ISDN_GLOBAL_STOPPED) ? "stopped" : "running");
return 0;
case IIOCSETBRJ:
drvidx = -1;
if (arg) {
int i;
char *p;
if (copy_from_user((char *) &iocts, (char *) arg,
sizeof(isdn_ioctl_struct)))
return -EFAULT;
if (strlen(iocts.drvid)) {
if ((p = strchr(iocts.drvid, ',')))
*p = 0;
drvidx = -1;
for (i = 0; i < ISDN_MAX_DRIVERS; i++)
if (!(strcmp(dev->drvid[i], iocts.drvid))) {
drvidx = i;
break;
}
}
}
if (drvidx == -1)
return -ENODEV;
if (iocts.arg)
dev->drv[drvidx]->flags |= DRV_FLAG_REJBUS;
else
dev->drv[drvidx]->flags &= ~DRV_FLAG_REJBUS;
return 0;
case IIOCSIGPRF:
dev->profd = current;
return 0;
break;
case IIOCGETPRF:
/* Get all Modem-Profiles */
if (arg) {
char *p = (char *) arg;
int i;
if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
(ISDN_MODEM_NUMREG + ISDN_MSNLEN + ISDN_LMSNLEN)
* ISDN_MAX_CHANNELS)))
return ret;
for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
if (copy_to_user(p, dev->mdm.info[i].emu.profile,
ISDN_MODEM_NUMREG))
return -EFAULT;
p += ISDN_MODEM_NUMREG;
if (copy_to_user(p, dev->mdm.info[i].emu.pmsn, ISDN_MSNLEN))
return -EFAULT;
p += ISDN_MSNLEN;
if (copy_to_user(p, dev->mdm.info[i].emu.plmsn, ISDN_LMSNLEN))
return -EFAULT;
p += ISDN_LMSNLEN;
}
return (ISDN_MODEM_NUMREG + ISDN_MSNLEN + ISDN_LMSNLEN) * ISDN_MAX_CHANNELS;
} else
return -EINVAL;
break;
case IIOCSETPRF:
/* Set all Modem-Profiles */
if (arg) {
char *p = (char *) arg;
int i;
if ((ret = verify_area(VERIFY_READ, (void *) arg,
(ISDN_MODEM_NUMREG + ISDN_MSNLEN + ISDN_LMSNLEN)
* ISDN_MAX_CHANNELS)))
return ret;
for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
if (copy_from_user(dev->mdm.info[i].emu.profile, p,
ISDN_MODEM_NUMREG))
return -EFAULT;
p += ISDN_MODEM_NUMREG;
if (copy_from_user(dev->mdm.info[i].emu.plmsn, p, ISDN_LMSNLEN))
return -EFAULT;
p += ISDN_LMSNLEN;
if (copy_from_user(dev->mdm.info[i].emu.pmsn, p, ISDN_MSNLEN))
return -EFAULT;
p += ISDN_MSNLEN;
}
return 0;
} else
return -EINVAL;
break;
case IIOCSETMAP:
case IIOCGETMAP:
/* Set/Get MSN->EAZ-Mapping for a driver */
if (arg) {
if (copy_from_user((char *) &iocts,
(char *) arg,
sizeof(isdn_ioctl_struct)))
return -EFAULT;
if (strlen(iocts.drvid)) {
drvidx = -1;
for (i = 0; i < ISDN_MAX_DRIVERS; i++)
if (!(strcmp(dev->drvid[i], iocts.drvid))) {
drvidx = i;
break;
}
} else
drvidx = 0;
if (drvidx == -1)
return -ENODEV;
if (cmd == IIOCSETMAP) {
int loop = 1;
p = (char *) iocts.arg;
i = 0;
while (loop) {
int j = 0;
while (1) {
if ((ret = verify_area(VERIFY_READ, p, 1)))
return ret;
get_user(bname[j], p++);
switch (bname[j]) {
case '\0':
loop = 0;
/* Fall through */
case ',':
bname[j] = '\0';
strcpy(dev->drv[drvidx]->msn2eaz[i], bname);
j = ISDN_MSNLEN;
break;
default:
j++;
}
if (j >= ISDN_MSNLEN)
break;
}
if (++i > 9)
break;
}
} else {
p = (char *) iocts.arg;
for (i = 0; i < 10; i++) {
sprintf(bname, "%s%s",
strlen(dev->drv[drvidx]->msn2eaz[i]) ?
dev->drv[drvidx]->msn2eaz[i] : "_",
(i < 9) ? "," : "\0");
if (copy_to_user(p, bname, strlen(bname) + 1))
return -EFAULT;
p += strlen(bname);
}
}
return 0;
} else
return -EINVAL;
case IIOCDBGVAR:
if (arg) {
if (copy_to_user((char *) arg, (char *) &dev, sizeof(ulong)))
return -EFAULT;
return 0;
} else
return -EINVAL;
break;
default:
if ((cmd & IIOCDRVCTL) == IIOCDRVCTL)
cmd = ((cmd >> _IOC_NRSHIFT) & _IOC_NRMASK) & ISDN_DRVIOCTL_MASK;
else
return -EINVAL;
if (arg) {
int i;
char *p;
if (copy_from_user((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct)))
return -EFAULT;
if (strlen(iocts.drvid)) {
if ((p = strchr(iocts.drvid, ',')))
*p = 0;
drvidx = -1;
for (i = 0; i < ISDN_MAX_DRIVERS; i++)
if (!(strcmp(dev->drvid[i], iocts.drvid))) {
drvidx = i;
break;
}
} else
drvidx = 0;
if (drvidx == -1)
return -ENODEV;
if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
sizeof(isdn_ioctl_struct))))
return ret;
c.driver = drvidx;
c.command = ISDN_CMD_IOCTL;
c.arg = cmd;
memcpy(c.parm.num, (char *) &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)))
return -EFAULT;
return ret;
} else
return -EINVAL;
}
#undef name
#undef bname
#undef iocts
#undef phone
#undef cfg
}
Generated by GNU enscript 1.6.4.