Enscript Output

extractedLnx/linux-2.5.9/drivers/isdn/i4l/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.