epca.c

来自「linux 内核源代码」· C语言 代码 · 共 2,328 行 · 第 1/5 页

C
2,328
字号
			/* Set baud rate, char size, stop bits, parity */			fepcmd(ch, SETCTRLFLAGS, (unsigned) cflag, 0, 0, 0);		}		/*		 * If the user has not forced CLOCAL and if the device is not a		 * CALLOUT device (Which is always CLOCAL) we set flags such		 * that the driver will wait on carrier detect.		 */		if (ts->c_cflag & CLOCAL)			ch->asyncflags &= ~ASYNC_CHECK_CD;		else			ch->asyncflags |= ASYNC_CHECK_CD;		mval = ch->m_dtr | ch->m_rts;	} /* End CBAUD not detected */	iflag = termios2digi_i(ch, ts->c_iflag);	/* Check input mode flags */	if (iflag != ch->fepiflag)  {		ch->fepiflag = iflag;		/*		 * Command sets channels iflag structure on the board. Such		 * things as input soft flow control, handling of parity		 * errors, and break handling are all set here.		 */		/* break handling, parity handling, input stripping, flow control chars */		fepcmd(ch, SETIFLAGS, (unsigned int) ch->fepiflag, 0, 0, 0);	}	/*	 * Set the board mint value for this channel. This will cause hardware	 * events to be generated each time the DCD signal (Described in mint)	 * changes.	 */	writeb(ch->dcd, &bc->mint);	if ((ts->c_cflag & CLOCAL) || (ch->digiext.digi_flags & DIGI_FORCEDCD))		if (ch->digiext.digi_flags & DIGI_FORCEDCD)			writeb(0, &bc->mint);	ch->imodem = readb(&bc->mstat);	hflow = termios2digi_h(ch, ts->c_cflag);	if (hflow != ch->hflow)  {		ch->hflow = hflow;		/*		 * Hard flow control has been selected but the board is not		 * using it. Activate hard flow control now.		 */		fepcmd(ch, SETHFLOW, hflow, 0xff, 0, 1);	}	mval ^= ch->modemfake & (mval ^ ch->modem);	if (ch->omodem ^ mval)  {		ch->omodem = mval;		/*		 * The below command sets the DTR and RTS mstat structure. If		 * hard flow control is NOT active these changes will drive the		 * output of the actual DTR and RTS lines. If hard flow control		 * is active, the changes will be saved in the mstat structure		 * and only asserted when hard flow control is turned off.		 */		/* First reset DTR & RTS; then set them */		fepcmd(ch, SETMODEM, 0, ((ch->m_dtr)|(ch->m_rts)), 0, 1);		fepcmd(ch, SETMODEM, mval, 0, 0, 1);	}	if (ch->startc != ch->fepstartc || ch->stopc != ch->fepstopc)  {		ch->fepstartc = ch->startc;		ch->fepstopc = ch->stopc;		/*		 * The XON / XOFF characters have changed; propagate these		 * changes to the card.		 */		fepcmd(ch, SONOFFC, ch->fepstartc, ch->fepstopc, 0, 1);	}	if (ch->startca != ch->fepstartca || ch->stopca != ch->fepstopca)  {		ch->fepstartca = ch->startca;		ch->fepstopca = ch->stopca;		/*		 * Similar to the above, this time the auxilarly XON / XOFF		 * characters have changed; propagate these changes to the card.		 */		fepcmd(ch, SAUXONOFFC, ch->fepstartca, ch->fepstopca, 0, 1);	}}/* Caller holds lock */static void receive_data(struct channel *ch){	unchar *rptr;	struct ktermios *ts = NULL;	struct tty_struct *tty;	struct board_chan __iomem *bc;	int dataToRead, wrapgap, bytesAvailable;	unsigned int tail, head;	unsigned int wrapmask;	/*	 * This routine is called by doint when a receive data event has taken	 * place.	 */	globalwinon(ch);	if (ch->statusflags & RXSTOPPED)		return;	tty = ch->tty;	if (tty)		ts = tty->termios;	bc = ch->brdchan;	BUG_ON(!bc);	wrapmask = ch->rxbufsize - 1;	/*	 * Get the head and tail pointers to the receiver queue. Wrap the head	 * pointer if it has reached the end of the buffer.	 */	head = readw(&bc->rin);	head &= wrapmask;	tail = readw(&bc->rout) & wrapmask;	bytesAvailable = (head - tail) & wrapmask;	if (bytesAvailable == 0)		return;	/* If CREAD bit is off or device not open, set TX tail to head */	if (!tty || !ts || !(ts->c_cflag & CREAD))  {		writew(head, &bc->rout);		return;	}	if (tty_buffer_request_room(tty, bytesAvailable + 1) == 0)		return;	if (readb(&bc->orun)) {		writeb(0, &bc->orun);		printk(KERN_WARNING "epca; overrun! DigiBoard device %s\n",tty->name);		tty_insert_flip_char(tty, 0, TTY_OVERRUN);	}	rxwinon(ch);	while (bytesAvailable > 0)  { /* Begin while there is data on the card */		wrapgap = (head >= tail) ? head - tail : ch->rxbufsize - tail;		/*		 * Even if head has wrapped around only report the amount of		 * data to be equal to the size - tail. Remember memcpy can't		 * automaticly wrap around the receive buffer.		 */		dataToRead = (wrapgap < bytesAvailable) ? wrapgap : bytesAvailable;		/* Make sure we don't overflow the buffer */		dataToRead = tty_prepare_flip_string(tty, &rptr, dataToRead);		if (dataToRead == 0)			break;		/*		 * Move data read from our card into the line disciplines		 * buffer for translation if necessary.		 */		memcpy_fromio(rptr, ch->rxptr + tail, dataToRead);		tail = (tail + dataToRead) & wrapmask;		bytesAvailable -= dataToRead;	} /* End while there is data on the card */	globalwinon(ch);	writew(tail, &bc->rout);	/* Must be called with global data */	tty_schedule_flip(ch->tty);}static int info_ioctl(struct tty_struct *tty, struct file *file,		    unsigned int cmd, unsigned long arg){	switch (cmd) {	case DIGI_GETINFO:		{			struct digi_info di;			int brd;			if (get_user(brd, (unsigned int __user *)arg))				return -EFAULT;			if (brd < 0 || brd >= num_cards || num_cards == 0)				return -ENODEV;			memset(&di, 0, sizeof(di));			di.board = brd;			di.status = boards[brd].status;			di.type = boards[brd].type ;			di.numports = boards[brd].numports ;			/* Legacy fixups - just move along nothing to see */			di.port = (unsigned char *)boards[brd].port ;			di.membase = (unsigned char *)boards[brd].membase ;			if (copy_to_user((void __user *)arg, &di, sizeof(di)))				return -EFAULT;			break;		}	case DIGI_POLLER:		{			int brd = arg & 0xff000000 >> 16;			unsigned char state = arg & 0xff;			if (brd < 0 || brd >= num_cards) {				printk(KERN_ERR "epca: DIGI POLLER : brd not valid!\n");				return -ENODEV;			}			digi_poller_inhibited = state;			break;		}	case DIGI_INIT:		{			/*			 * This call is made by the apps to complete the			 * initilization of the board(s). This routine is			 * responsible for setting the card to its initial			 * state and setting the drivers control fields to the			 * sutianle settings for the card in question.			 */			int crd;			for (crd = 0; crd < num_cards; crd++)				post_fep_init(crd);			break;		}	default:		return -ENOTTY;	}	return 0;}static int pc_tiocmget(struct tty_struct *tty, struct file *file){	struct channel *ch = (struct channel *) tty->driver_data;	struct board_chan __iomem *bc;	unsigned int mstat, mflag = 0;	unsigned long flags;	if (ch)		bc = ch->brdchan;	else		return -EINVAL;	spin_lock_irqsave(&epca_lock, flags);	globalwinon(ch);	mstat = readb(&bc->mstat);	memoff(ch);	spin_unlock_irqrestore(&epca_lock, flags);	if (mstat & ch->m_dtr)		mflag |= TIOCM_DTR;	if (mstat & ch->m_rts)		mflag |= TIOCM_RTS;	if (mstat & ch->m_cts)		mflag |= TIOCM_CTS;	if (mstat & ch->dsr)		mflag |= TIOCM_DSR;	if (mstat & ch->m_ri)		mflag |= TIOCM_RI;	if (mstat & ch->dcd)		mflag |= TIOCM_CD;	return mflag;}static int pc_tiocmset(struct tty_struct *tty, struct file *file,		       unsigned int set, unsigned int clear){	struct channel *ch = (struct channel *) tty->driver_data;	unsigned long flags;	if (!ch)		return -EINVAL;	spin_lock_irqsave(&epca_lock, flags);	/*	 * I think this modemfake stuff is broken. It doesn't correctly reflect	 * the behaviour desired by the TIOCM* ioctls. Therefore this is	 * probably broken.	 */	if (set & TIOCM_RTS) {		ch->modemfake |= ch->m_rts;		ch->modem |= ch->m_rts;	}	if (set & TIOCM_DTR) {		ch->modemfake |= ch->m_dtr;		ch->modem |= ch->m_dtr;	}	if (clear & TIOCM_RTS) {		ch->modemfake |= ch->m_rts;		ch->modem &= ~ch->m_rts;	}	if (clear & TIOCM_DTR) {		ch->modemfake |= ch->m_dtr;		ch->modem &= ~ch->m_dtr;	}	globalwinon(ch);	/*	 * The below routine generally sets up parity, baud, flow control	 * issues, etc.... It effect both control flags and input flags.	 */	epcaparam(tty,ch);	memoff(ch);	spin_unlock_irqrestore(&epca_lock, flags);	return 0;}static int pc_ioctl(struct tty_struct *tty, struct file * file,		    unsigned int cmd, unsigned long arg){	digiflow_t dflow;	int retval;	unsigned long flags;	unsigned int mflag, mstat;	unsigned char startc, stopc;	struct board_chan __iomem *bc;	struct channel *ch = (struct channel *) tty->driver_data;	void __user *argp = (void __user *)arg;	if (ch)		bc = ch->brdchan;	else		return -EINVAL;	/*	 * For POSIX compliance we need to add more ioctls. See tty_ioctl.c in	 * /usr/src/linux/drivers/char for a good example. In particular think	 * about adding TCSETAF, TCSETAW, TCSETA, TCSETSF, TCSETSW, TCSETS.	 */	switch (cmd) {	case TCSBRK:	/* SVID version: non-zero arg --> no break */		retval = tty_check_change(tty);		if (retval)			return retval;		/* Setup an event to indicate when the transmit buffer empties */		spin_lock_irqsave(&epca_lock, flags);		setup_empty_event(tty,ch);		spin_unlock_irqrestore(&epca_lock, flags);		tty_wait_until_sent(tty, 0);		if (!arg)			digi_send_break(ch, HZ / 4);    /* 1/4 second */		return 0;	case TCSBRKP:	/* support for POSIX tcsendbreak() */		retval = tty_check_change(tty);		if (retval)			return retval;		/* Setup an event to indicate when the transmit buffer empties */		spin_lock_irqsave(&epca_lock, flags);		setup_empty_event(tty,ch);		spin_unlock_irqrestore(&epca_lock, flags);		tty_wait_until_sent(tty, 0);		digi_send_break(ch, arg ? arg*(HZ/10) : HZ/4);		return 0;	case TIOCGSOFTCAR:		if (put_user(C_CLOCAL(tty)?1:0, (unsigned long __user *)arg))			return -EFAULT;		return 0;	case TIOCSSOFTCAR:		{			unsigned int value;			if (get_user(value, (unsigned __user *)argp))				return -EFAULT;			tty->termios->c_cflag =				((tty->termios->c_cflag & ~CLOCAL) |				 (value ? CLOCAL : 0));			return 0;		}	case TIOCMODG:		mflag = pc_tiocmget(tty, file);		if (put_user(mflag, (unsigned long __user *)argp))			return -EFAULT;		break;	case TIOCMODS:		if (get_user(mstat, (unsigned __user *)argp))			return -EFAULT;		return pc_tiocmset(tty, file, mstat, ~mstat);	case TIOCSDTR:		spin_lock_irqsave(&epca_lock, flags);		ch->omodem |= ch->m_dtr;		globalwinon(ch);		fepcmd(ch, SETMODEM, ch->m_dtr, 0, 10, 1);		memoff(ch);		spin_unlock_irqrestore(&epca_lock, flags);		break;	case TIOCCDTR:		spin_lock_irqsave(&epca_lock, flags);		ch->omodem &= ~ch->m_dtr;		globalwinon(ch);		fepcmd(ch, SETMODEM, 0, ch->m_dtr, 10, 1);		memoff(ch);		spin_unlock_irqrestore(&epca_lock, flags);		break;	case DIGI_GETA:		if (copy_to_user(argp, &ch->digiext, sizeof(digi_t)))			return -EFAULT;		break;	case DIGI_SETAW:	case DIGI_SETAF:		if (cmd == DIGI_SETAW) {			/* Setup an event to indicate when the transmit buffer empties */			spin_lock_irqsave(&epca_lock, flags);			setup_empty_event(tty,ch);			spin_unlock_irqrestore(&epca_lock, flags);			tty_wait_until_sent(tty, 0);		} else {			/* ldisc lock already held in ioctl */			if (tty->ldisc.flush_buffer)				tty->ldisc.flush_buffer(tty);		}		/* Fall Thru */	case DIGI_SETA:		if (copy_from_user(&ch->digiext, argp, sizeof(digi_t)))			return -EFAULT;		if (ch->digiext.digi_flags & DIGI_ALTPIN)  {			ch->dcd = ch->m_dsr;			ch->dsr = ch->m_dcd;		} else {			ch->dcd = ch->m_dcd;			ch->dsr = ch->m_dsr;			}		spin_lock_irqsave(&epca_lock, flags);		globalwinon(ch);		/*		 * The below routine generally sets up parity, baud, flow		 * control issues, etc.... It effect both control flags and		 * input flags.		 */		epcaparam(tty,ch);		memoff(ch);		spin_unlock_irqrestore(&epca_lock, flags);		break;	case DIGI_GETFLOW:	case DIGI_GETAFLOW:		spin_lock_irqsave(&epca_lock, flags);		globalwinon(ch);		if (cmd == DIGI_GETFLOW) {			dflow.startc = readb(&bc->startc);			dflow.stopc = readb(&bc->stopc);		} else {			dflow.startc = readb(&bc->startca);			dflow.stopc = readb(&bc->stopca);		}		memoff(ch);		spin_unlock_irqrestore(&epca_lock, flags);		if (copy_to_user(argp, &dflow, sizeof(dflow)))			return -EFAULT;		break;	case DIGI_SETAFLOW:	case DIGI_SETFLOW:		if (cmd == DIGI_SETFLOW) {			startc = ch->startc;			stopc = ch->stopc;		} else {			startc = ch->startca;			stopc = ch->stopca;		}		if (copy_from_user(&dflow, argp, sizeof(dflow)))			return -EFAULT;		if (dflow.startc != startc || dflow.stopc != stopc) { /* Begin  if setflow toggled */			spin_lock_irqsave(&epca_lock, flags);			globalwinon(ch);			if (cmd == DI

⌨️ 快捷键说明

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