isdn_common.c

来自「是关于linux2.5.1的完全源码」· C语言 代码 · 共 2,372 行 · 第 1/4 页

C
2,372
字号
		retval = -ENODEV;		goto out;	}	if (!dev->drv[drvidx]->stavail) {		if (file->f_flags & O_NONBLOCK) {			retval = -EAGAIN;			goto out;		}		interruptible_sleep_on(&(dev->drv[drvidx]->st_waitq));	}	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));	} else {		len = 0;	}	save_flags(flags);	cli();	if (len)		dev->drv[drvidx]->stavail -= len;	else		dev->drv[drvidx]->stavail = 0;	restore_flags(flags);	*off += len;	retval = len;	 out:	unlock_kernel();	return retval;}static ssize_tisdn_ctrl_write(struct file *file, const char *buf, size_t count, loff_t *off){	uint minor = minor(file->f_dentry->d_inode->i_rdev);	int drvidx;	int retval;	if (off != &file->f_pos)		return -ESPIPE;	lock_kernel();	drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);	if (drvidx < 0) {		retval = -ENODEV;		goto out;	}	if (!dev->drv[drvidx]->interface->writecmd) {		retval = -EINVAL;		goto out;	}	retval = dev->drv[drvidx]->interface->		writecmd(buf, count, 1, drvidx, isdn_minor2chan(minor - ISDN_MINOR_CTRL)); out:	unlock_kernel();	return retval;}static unsigned intisdn_ctrl_poll(struct file *file, poll_table *wait){	unsigned int mask = 0;	unsigned int minor = minor(file->f_dentry->d_inode->i_rdev);	int drvidx;	drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);	if (drvidx < 0)		/* driver deregistered while file open */		return POLLHUP;	lock_kernel();	poll_wait(file, &(dev->drv[drvidx]->st_waitq), wait);	mask = POLLOUT | POLLWRNORM;	if (dev->drv[drvidx]->stavail)		mask |= POLLIN | POLLRDNORM;	unlock_kernel();	return mask;}static intisdn_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}static struct file_operations isdn_ctrl_fops ={	owner:		THIS_MODULE,	llseek:		no_llseek,	read:		isdn_ctrl_read,	write:		isdn_ctrl_write,	poll:		isdn_ctrl_poll,	ioctl:		isdn_ctrl_ioctl,	open:		isdn_ctrl_open,	release:	isdn_ctrl_release,};/* * file_operations for major 43, /dev/isdn* * stolen from drivers/char/misc.c */static intisdn_open(struct inode * inode, struct file * file){	int minor = minor(inode->i_rdev);	int err = -ENODEV;	struct file_operations *old_fops, *new_fops = NULL;		if (minor >= ISDN_MINOR_CTRL && minor <= ISDN_MINOR_CTRLMAX)		new_fops = fops_get(&isdn_ctrl_fops);#ifdef CONFIG_ISDN_PPP	else if (minor >= ISDN_MINOR_PPP && minor <= ISDN_MINOR_PPPMAX)		new_fops = fops_get(&isdn_ppp_fops);#endif	else if (minor == ISDN_MINOR_STATUS)		new_fops = fops_get(&isdn_status_fops);	if (!new_fops)		goto out;	err = 0;	old_fops = file->f_op;	file->f_op = new_fops;	if (file->f_op->open) {		err = file->f_op->open(inode,file);		if (err) {			fops_put(file->f_op);			file->f_op = fops_get(old_fops);		}	}	fops_put(old_fops);	 out:	return err;}static struct file_operations isdn_fops ={	owner:		THIS_MODULE,	open:		isdn_open,};char *isdn_map_eaz2msn(char *msn, int di){	driver *this = dev->drv[di];	int i;	if (strlen(msn) == 1) {		i = msn[0] - '0';		if ((i >= 0) && (i <= 9))			if (strlen(this->msn2eaz[i]))				return (this->msn2eaz[i]);	}	return (msn);}/* * Find an unused ISDN-channel, whose feature-flags match the * given L2- and L3-protocols. */#define L2V (~(ISDN_FEATURE_L2_V11096|ISDN_FEATURE_L2_V11019|ISDN_FEATURE_L2_V11038))intisdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev		      ,int pre_chan, char *msn){	int i;	ulong flags;	ulong features;	ulong vfeatures;	save_flags(flags);	cli();	features = ((1 << l2_proto) | (0x10000 << l3_proto));	vfeatures = (((1 << l2_proto) | (0x10000 << l3_proto)) &		     ~(ISDN_FEATURE_L2_V11096|ISDN_FEATURE_L2_V11019|ISDN_FEATURE_L2_V11038));	/* If Layer-2 protocol is V.110, accept drivers with	 * transparent feature even if these don't support V.110	 * because we can emulate this in linklevel.	 */	for (i = 0; i < ISDN_MAX_CHANNELS; i++)		if (USG_NONE(dev->usage[i]) &&		    (dev->drvmap[i] != -1)) {			int d = dev->drvmap[i];			if ((dev->usage[i] & ISDN_USAGE_EXCLUSIVE) &&			((pre_dev != d) || (pre_chan != dev->chanmap[i])))				continue;			if (!strcmp(isdn_map_eaz2msn(msn, d), "-"))				continue;			if (dev->usage[i] & ISDN_USAGE_DISABLED)			        continue; /* usage not allowed */			if (dev->drv[d]->flags & DRV_FLAG_RUNNING) {				if (((dev->drv[d]->interface->features & features) == features) ||				    (((dev->drv[d]->interface->features & vfeatures) == vfeatures) &&				     (dev->drv[d]->interface->features & ISDN_FEATURE_L2_TRANS))) {					if ((pre_dev < 0) || (pre_chan < 0)) {						dev->usage[i] &= ISDN_USAGE_EXCLUSIVE;						dev->usage[i] |= usage;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?