isdn_common.c

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

C
2,372
字号
			if (isdn_tty_stat_callback(i, c)) {				cmd.driver = di;				cmd.arg = c->arg;				cmd.command = ISDN_CMD_ACCEPTB;				isdn_command(&cmd);				break;			}			break;		case ISDN_STAT_DHUP:			if (i < 0)				return -1;#ifdef ISDN_DEBUG_STATCALLB			printk(KERN_DEBUG "DHUP: %ld\n", c->arg);#endif			if (dev->global_flags & ISDN_GLOBAL_STOPPED)				return 0;			dev->drv[di]->online &= ~(1 << (c->arg));			isdn_info_update();			/* Signal hangup to network-devices */			if (isdn_net_stat_callback(i, c))				break;			isdn_v110_stat_callback(i, c);			if (isdn_tty_stat_callback(i, c))				break;#ifdef CONFIG_ISDN_DIVERSION                        if (divert_if)                         divert_if->stat_callback(c); #endif /* CONFIG_ISDN_DIVERSION */			break;			break;		case ISDN_STAT_BCONN:			if (i < 0)				return -1;#ifdef ISDN_DEBUG_STATCALLB			printk(KERN_DEBUG "BCONN: %ld\n", c->arg);#endif			/* Signal B-channel-connect to network-devices */			if (dev->global_flags & ISDN_GLOBAL_STOPPED)				return 0;			dev->drv[di]->online |= (1 << (c->arg));			isdn_info_update();			if (isdn_net_stat_callback(i, c))				break;			isdn_v110_stat_callback(i, c);			if (isdn_tty_stat_callback(i, c))				break;			break;		case ISDN_STAT_BHUP:			if (i < 0)				return -1;#ifdef ISDN_DEBUG_STATCALLB			printk(KERN_DEBUG "BHUP: %ld\n", c->arg);#endif			if (dev->global_flags & ISDN_GLOBAL_STOPPED)				return 0;			dev->drv[di]->online &= ~(1 << (c->arg));			isdn_info_update();#ifdef CONFIG_ISDN_X25			/* Signal hangup to network-devices */			if (isdn_net_stat_callback(i, c))				break;#endif			isdn_v110_stat_callback(i, c);			if (isdn_tty_stat_callback(i, c))				break;			break;		case ISDN_STAT_NODCH:			if (i < 0)				return -1;#ifdef ISDN_DEBUG_STATCALLB			printk(KERN_DEBUG "NODCH: %ld\n", c->arg);#endif			if (dev->global_flags & ISDN_GLOBAL_STOPPED)				return 0;			if (isdn_net_stat_callback(i, c))				break;			if (isdn_tty_stat_callback(i, c))				break;			break;		case ISDN_STAT_ADDCH:			if (isdn_add_channels(dev->drv[di], di, c->arg, 1))				return -1;			isdn_info_update();			break;		case ISDN_STAT_DISCH:			save_flags(flags);			cli();			for (i = 0; i < ISDN_MAX_CHANNELS; i++)				if ((dev->drvmap[i] == di) &&				    (dev->chanmap[i] == c->arg)) {				    if (c->parm.num[0])				      dev->usage[i] &= ~ISDN_USAGE_DISABLED;				    else				      if (USG_NONE(dev->usage[i])) {					dev->usage[i] |= ISDN_USAGE_DISABLED;				      }				      else 					retval = -1;				    break;				}			restore_flags(flags);			isdn_info_update();			break;		case ISDN_STAT_UNLOAD:			while (dev->drv[di]->locks > 0) {				isdn_ctrl cmd;				cmd.driver = di;				cmd.arg = 0;				cmd.command = ISDN_CMD_UNLOCK;				isdn_command(&cmd);				dev->drv[di]->locks--;			}			save_flags(flags);			cli();			isdn_tty_stat_callback(i, c);			for (i = 0; i < ISDN_MAX_CHANNELS; i++)				if (dev->drvmap[i] == di) {					dev->drvmap[i] = -1;					dev->chanmap[i] = -1;					dev->usage[i] &= ~ISDN_USAGE_DISABLED;					isdn_unregister_devfs(i);				}			dev->drivers--;			dev->channels -= dev->drv[di]->channels;			kfree(dev->drv[di]->rcverr);			kfree(dev->drv[di]->rcvcount);			for (i = 0; i < dev->drv[di]->channels; i++)				skb_queue_purge(&dev->drv[di]->rpqueue[i]);			kfree(dev->drv[di]->rpqueue);			kfree(dev->drv[di]->rcv_waitq);			kfree(dev->drv[di]);			dev->drv[di] = NULL;			dev->drvid[di][0] = '\0';			isdn_info_update();			set_global_features();			restore_flags(flags);			return 0;		case ISDN_STAT_L1ERR:			break;		case CAPI_PUT_MESSAGE:			return(isdn_capi_rec_hl_msg(&c->parm.cmsg));#ifdef CONFIG_ISDN_TTY_FAX		case ISDN_STAT_FAXIND:			isdn_tty_stat_callback(i, c);			break;#endif#ifdef CONFIG_ISDN_AUDIO		case ISDN_STAT_AUDIO:			isdn_tty_stat_callback(i, c);			break;#endif#ifdef CONFIG_ISDN_DIVERSION	        case ISDN_STAT_PROT:	        case ISDN_STAT_REDIR:                        if (divert_if)                          return(divert_if->stat_callback(c));#endif /* CONFIG_ISDN_DIVERSION */		default:			return -1;	}	return 0;}/* * Get integer from char-pointer, set pointer to end of number */intisdn_getnum(char **p){	int v = -1;	while (*p[0] >= '0' && *p[0] <= '9')		v = ((v < 0) ? 0 : (v * 10)) + (int) ((*p[0]++) - '0');	return v;}#define DLE 0x10/* * isdn_readbchan() 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  */intisdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, wait_queue_head_t *sleep){	int count;	int count_pull;	int count_put;	int dflag;	struct sk_buff *skb;	u_char *cp;	if (!dev->drv[di])		return 0;	if (skb_queue_empty(&dev->drv[di]->rpqueue[channel])) {		if (sleep)			interruptible_sleep_on(sleep);		else			return 0;	}	if (len > dev->drv[di]->rcvcount[channel])		len = dev->drv[di]->rcvcount[channel];	cp = buf;	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) {					*cp++ = DLE;					dev->drv[di]->DLEflag &= ~DLEmask;				} else {					*cp++ = *p;					if (*p == 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;			memcpy(cp, skb->data, count_put);			cp += count_put;			len -= count_put;#ifdef CONFIG_ISDN_AUDIO		}#endif		count += count_put;		if (fp) {			memset(fp, 0, count_put);			fp += count_put;		}		if (dflag) {			/* We got all the data in this buff.			 * Now we can dequeue it.			 */			if (fp)				*(fp - 1) = 0xff;#ifdef CONFIG_ISDN_AUDIO			ISDN_AUDIO_SKB_LOCK(skb) = 0;#endif			skb = skb_dequeue(&dev->drv[di]->rpqueue[channel]);			dev_kfree_skb(skb);		} else {			/* 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 intisdn_minor2drv(int minor){	return (dev->drvmap[minor]);}static __inline intisdn_minor2chan(int minor){	return (dev->chanmap[minor]);}static char *isdn_statstr(void){	static char istatbuf[2048];	char *p;	int i;	sprintf(istatbuf, "idmap:\t");	p = istatbuf + strlen(istatbuf);	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {		sprintf(p, "%s ", (dev->drvmap[i] < 0) ? "-" : dev->drvid[dev->drvmap[i]]);		p = istatbuf + strlen(istatbuf);	}	sprintf(p, "\nchmap:\t");	p = istatbuf + strlen(istatbuf);	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {		sprintf(p, "%d ", dev->chanmap[i]);		p = istatbuf + strlen(istatbuf);	}	sprintf(p, "\ndrmap:\t");	p = istatbuf + strlen(istatbuf);	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {		sprintf(p, "%d ", dev->drvmap[i]);		p = istatbuf + strlen(istatbuf);	}	sprintf(p, "\nusage:\t");	p = istatbuf + strlen(istatbuf);	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {		sprintf(p, "%d ", dev->usage[i]);		p = istatbuf + strlen(istatbuf);	}	sprintf(p, "\nflags:\t");	p = istatbuf + strlen(istatbuf);	for (i = 0; i < ISDN_MAX_DRIVERS; i++) {		if (dev->drv[i]) {			sprintf(p, "%ld ", dev->drv[i]->online);			p = istatbuf + strlen(istatbuf);		} else {			sprintf(p, "? ");			p = istatbuf + strlen(istatbuf);		}	}	sprintf(p, "\nphone:\t");	p = istatbuf + strlen(istatbuf);	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {		sprintf(p, "%s ", dev->num[i]);		p = istatbuf + strlen(istatbuf);	}	sprintf(p, "\n");	return istatbuf;}/*  * /dev/isdninfo */voidisdn_info_update(void){	infostruct *p = dev->infochain;	while (p) {		*(p->private) = 1;		p = (infostruct *) p->next;	}	wake_up_interruptible(&(dev->info_waitq));}static intisdn_status_open(struct inode *ino, struct file *filep){	infostruct *p;		p = kmalloc(sizeof(infostruct), GFP_KERNEL);	if (!p)		return -ENOMEM;	p->next = (char *) dev->infochain;	p->private = (char *) &(filep->private_data);	dev->infochain = p;	/* At opening we allow a single update */	filep->private_data = (char *) 1;	return 0;}static intisdn_status_release(struct inode *ino, struct file *filep){	infostruct *p = dev->infochain;	infostruct *q = NULL;		lock_kernel();	while (p) {		if (p->private == (char *) &(filep->private_data)) {			if (q)				q->next = p->next;			else				dev->infochain = (infostruct *) (p->next);			kfree(p);			goto out;		}		q = p;		p = (infostruct *) (p->next);	}	printk(KERN_WARNING "isdn: No private data while closing isdnctrl\n"); out:	unlock_kernel();	return 0;}static ssize_tisdn_status_read(struct file *file, char *buf, size_t count, loff_t * off){	int retval;	int len = 0;	char *p;	if (off != &file->f_pos)		return -ESPIPE;	lock_kernel();	if (!file->private_data) {		if (file->f_flags & O_NONBLOCK) {			retval = -EAGAIN;			goto out;		}		interruptible_sleep_on(&(dev->info_waitq));	}	p = isdn_statstr();	file->private_data = 0;	if ((len = strlen(p)) <= count) {		if (copy_to_user(buf, p, len)) {			retval = -EFAULT;			goto out;		}		*off += len;		retval = len;		goto out;	}	retval = 0;	goto out; out:	unlock_kernel();	return retval;}static ssize_tisdn_status_write(struct file *file, const char *buf, size_t count, loff_t *off){	return -EPERM;}static unsigned intisdn_status_poll(struct file *file, poll_table *wait){	unsigned int mask = 0;	lock_kernel();	poll_wait(file, &(dev->info_waitq), wait);	if (file->private_data)		mask |= POLLIN | POLLRDNORM;	unlock_kernel();	return mask;}static intisdn_status_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg){	int ret;	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	switch (cmd) {	case IIOCGETDVR:		return (TTY_DV +			(NET_DV << 8) +			(INF_DV << 16));	case IIOCGETCPS:		if (arg) {			ulong *p = (ulong *) arg;			int i;			if ((ret = verify_area(VERIFY_WRITE, (void *) arg,					       sizeof(ulong) * ISDN_MAX_CHANNELS * 2)))				return ret;			for (i = 0; i < ISDN_MAX_CHANNELS; i++) {				put_user(dev->ibytes[i], p++);				put_user(dev->obytes[i], p++);			}			return 0;		} else			return -EINVAL;		break;#ifdef CONFIG_NETDEVICES	case IIOCNETGPN:		/* Get peer phone number of a connected 		 * isdn network interface */		if (arg) {			if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))				return -EFAULT;			return isdn_net_getpeer(&phone, (isdn_net_ioctl_phone *) arg);		} else			return -EINVAL;#endif	default:		return -EINVAL;	}#undef name#undef bname#undef iocts#undef phone#undef cfg}static struct file_operations isdn_status_fops ={	owner:		THIS_MODULE,	llseek:		no_llseek,	read:		isdn_status_read,	write:		isdn_status_write,	poll:		isdn_status_poll,	ioctl:		isdn_status_ioctl,	open:		isdn_status_open,	release:	isdn_status_release,};/* * /dev/isdnctrlX */static intisdn_ctrl_open(struct inode *ino, struct file *filep){	uint minor = minor(ino->i_rdev);	int drvidx;	drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);	if (drvidx < 0)		return -ENODEV;	isdn_lock_drivers();	return 0;}static intisdn_ctrl_release(struct inode *ino, struct file *filep){	lock_kernel();	if (dev->profd == current)		dev->profd = NULL;	isdn_unlock_drivers();	unlock_kernel();	return 0;}static ssize_tisdn_ctrl_read(struct file *file, char *buf, size_t count, loff_t * off){	uint minor = minor(file->f_dentry->d_inode->i_rdev);	ulong flags;	int len = 0;	int drvidx;	int retval;	if (off != &file->f_pos)		return -ESPIPE;	lock_kernel();	drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);	if (drvidx < 0) {

⌨️ 快捷键说明

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