epca.c

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

C
2,328
字号
		spin_lock_irqsave(&epca_lock, flags);		tty->closing = 0;		ch->event = 0;		ch->tty = NULL;		spin_unlock_irqrestore(&epca_lock, flags);		if (ch->blocked_open) {			if (ch->close_delay)				msleep_interruptible(jiffies_to_msecs(ch->close_delay));			wake_up_interruptible(&ch->open_wait);		}		ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_INITIALIZED |		                      ASYNC_CLOSING);		wake_up_interruptible(&ch->close_wait);	}}static void shutdown(struct channel *ch){	unsigned long flags;	struct tty_struct *tty;	struct board_chan __iomem *bc;	if (!(ch->asyncflags & ASYNC_INITIALIZED))		return;	spin_lock_irqsave(&epca_lock, flags);	globalwinon(ch);	bc = ch->brdchan;	/*	 * In order for an event to be generated on the receipt of data the	 * idata flag must be set. Since we are shutting down, this is not	 * necessary clear this flag.	 */	if (bc)		writeb(0, &bc->idata);	tty = ch->tty;	/* If we're a modem control device and HUPCL is on, drop RTS & DTR. */	if (tty->termios->c_cflag & HUPCL)  {		ch->omodem &= ~(ch->m_rts | ch->m_dtr);		fepcmd(ch, SETMODEM, 0, ch->m_dtr | ch->m_rts, 10, 1);	}	memoff(ch);	/*	 * The channel has officialy been closed. The next time it is opened it	 * will have to reinitialized. Set a flag to indicate this.	 */	/* Prevent future Digi programmed interrupts from coming active */	ch->asyncflags &= ~ASYNC_INITIALIZED;	spin_unlock_irqrestore(&epca_lock, flags);}static void pc_hangup(struct tty_struct *tty){	struct channel *ch;	/*	 * verifyChannel returns the channel from the tty struct if it is	 * valid. This serves as a sanity check.	 */	if ((ch = verifyChannel(tty)) != NULL) {		unsigned long flags;		if (tty->driver->flush_buffer)			tty->driver->flush_buffer(tty);		tty_ldisc_flush(tty);		shutdown(ch);		spin_lock_irqsave(&epca_lock, flags);		ch->tty   = NULL;		ch->event = 0;		ch->count = 0;		ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_INITIALIZED);		spin_unlock_irqrestore(&epca_lock, flags);		wake_up_interruptible(&ch->open_wait);	}}static int pc_write(struct tty_struct *tty,                    const unsigned char *buf, int bytesAvailable){	unsigned int head, tail;	int dataLen;	int size;	int amountCopied;	struct channel *ch;	unsigned long flags;	int remain;	struct board_chan __iomem *bc;	/*	 * pc_write is primarily called directly by the kernel routine	 * tty_write (Though it can also be called by put_char) found in	 * tty_io.c. pc_write is passed a line discipline buffer where the data	 * to be written out is stored. The line discipline implementation	 * itself is done at the kernel level and is not brought into the	 * driver.	 */	/*	 * verifyChannel returns the channel from the tty struct if it is	 * valid. This serves as a sanity check.	 */	if ((ch = verifyChannel(tty)) == NULL)		return 0;	/* Make a pointer to the channel data structure found on the board. */	bc   = ch->brdchan;	size = ch->txbufsize;	amountCopied = 0;	spin_lock_irqsave(&epca_lock, flags);	globalwinon(ch);	head = readw(&bc->tin) & (size - 1);	tail = readw(&bc->tout);	if (tail != readw(&bc->tout))		tail = readw(&bc->tout);	tail &= (size - 1);	if (head >= tail) {		/* head has not wrapped */		/*		 * remain (much like dataLen above) represents the total amount		 * of space available on the card for data. Here dataLen		 * represents the space existing between the head pointer and		 * the end of buffer. This is important because a memcpy cannot		 * be told to automatically wrap around when it hits the buffer		 * end.		 */		dataLen = size - head;		remain = size - (head - tail) - 1;	} else {		/* head has wrapped around */		remain = tail - head - 1;		dataLen = remain;	}	/*	 * Check the space on the card. If we have more data than space; reduce	 * the amount of data to fit the space.	 */	bytesAvailable = min(remain, bytesAvailable);	txwinon(ch);	while (bytesAvailable > 0) {		/* there is data to copy onto card */		/*		 * If head is not wrapped, the below will make sure the first		 * data copy fills to the end of card buffer.		 */		dataLen = min(bytesAvailable, dataLen);		memcpy_toio(ch->txptr + head, buf, dataLen);		buf += dataLen;		head += dataLen;		amountCopied += dataLen;		bytesAvailable -= dataLen;		if (head >= size) {			head = 0;			dataLen = tail;		}	}	ch->statusflags |= TXBUSY;	globalwinon(ch);	writew(head, &bc->tin);	if ((ch->statusflags & LOWWAIT) == 0)  {		ch->statusflags |= LOWWAIT;		writeb(1, &bc->ilow);	}	memoff(ch);	spin_unlock_irqrestore(&epca_lock, flags);	return amountCopied;}static void pc_put_char(struct tty_struct *tty, unsigned char c){	pc_write(tty, &c, 1);}static int pc_write_room(struct tty_struct *tty){	int remain;	struct channel *ch;	unsigned long flags;	unsigned int head, tail;	struct board_chan __iomem *bc;	remain = 0;	/*	 * verifyChannel returns the channel from the tty struct if it is	 * valid. This serves as a sanity check.	 */	if ((ch = verifyChannel(tty)) != NULL)  {		spin_lock_irqsave(&epca_lock, flags);		globalwinon(ch);		bc   = ch->brdchan;		head = readw(&bc->tin) & (ch->txbufsize - 1);		tail = readw(&bc->tout);		if (tail != readw(&bc->tout))			tail = readw(&bc->tout);		/* Wrap tail if necessary */		tail &= (ch->txbufsize - 1);		if ((remain = tail - head - 1) < 0 )			remain += ch->txbufsize;		if (remain && (ch->statusflags & LOWWAIT) == 0) {			ch->statusflags |= LOWWAIT;			writeb(1, &bc->ilow);		}		memoff(ch);		spin_unlock_irqrestore(&epca_lock, flags);	}	/* Return how much room is left on card */	return remain;}static int pc_chars_in_buffer(struct tty_struct *tty){	int chars;	unsigned int ctail, head, tail;	int remain;	unsigned long flags;	struct channel *ch;	struct board_chan __iomem *bc;	/*	 * verifyChannel returns the channel from the tty struct if it is	 * valid. This serves as a sanity check.	 */	if ((ch = verifyChannel(tty)) == NULL)		return 0;	spin_lock_irqsave(&epca_lock, flags);	globalwinon(ch);	bc = ch->brdchan;	tail = readw(&bc->tout);	head = readw(&bc->tin);	ctail = readw(&ch->mailbox->cout);	if (tail == head && readw(&ch->mailbox->cin) == ctail && readb(&bc->tbusy) == 0)		chars = 0;	else  { /* Begin if some space on the card has been used */		head = readw(&bc->tin) & (ch->txbufsize - 1);		tail &= (ch->txbufsize - 1);		/*		 * The logic here is basically opposite of the above		 * pc_write_room here we are finding the amount of bytes in the		 * buffer filled. Not the amount of bytes empty.		 */		if ((remain = tail - head - 1) < 0 )			remain += ch->txbufsize;		chars = (int)(ch->txbufsize - remain);		/*		 * Make it possible to wakeup anything waiting for output in		 * tty_ioctl.c, etc.		 *		 * If not already set. Setup an event to indicate when the		 * transmit buffer empties.		 */		if (!(ch->statusflags & EMPTYWAIT))			setup_empty_event(tty,ch);	} /* End if some space on the card has been used */	memoff(ch);	spin_unlock_irqrestore(&epca_lock, flags);	/* Return number of characters residing on card. */	return chars;}static void pc_flush_buffer(struct tty_struct *tty){	unsigned int tail;	unsigned long flags;	struct channel *ch;	struct board_chan __iomem *bc;	/*	 * verifyChannel returns the channel from the tty struct if it is	 * valid. This serves as a sanity check.	 */	if ((ch = verifyChannel(tty)) == NULL)		return;	spin_lock_irqsave(&epca_lock, flags);	globalwinon(ch);	bc   = ch->brdchan;	tail = readw(&bc->tout);	/* Have FEP move tout pointer; effectively flushing transmit buffer */	fepcmd(ch, STOUT, (unsigned) tail, 0, 0, 0);	memoff(ch);	spin_unlock_irqrestore(&epca_lock, flags);	tty_wakeup(tty);}static void pc_flush_chars(struct tty_struct *tty){	struct channel *ch;	/*	 * verifyChannel returns the channel from the tty struct if it is	 * valid. This serves as a sanity check.	 */	if ((ch = verifyChannel(tty)) != NULL) {		unsigned long flags;		spin_lock_irqsave(&epca_lock, flags);		/*		 * If not already set and the transmitter is busy setup an		 * event to indicate when the transmit empties.		 */		if ((ch->statusflags & TXBUSY) && !(ch->statusflags & EMPTYWAIT))			setup_empty_event(tty,ch);		spin_unlock_irqrestore(&epca_lock, flags);	}}static int block_til_ready(struct tty_struct *tty,                           struct file *filp, struct channel *ch){	DECLARE_WAITQUEUE(wait,current);	int retval, do_clocal = 0;	unsigned long flags;	if (tty_hung_up_p(filp)) {		if (ch->asyncflags & ASYNC_HUP_NOTIFY)			retval = -EAGAIN;		else			retval = -ERESTARTSYS;		return retval;	}	/*	 * If the device is in the middle of being closed, then block until	 * it's done, and then try again.	 */	if (ch->asyncflags & ASYNC_CLOSING) {		interruptible_sleep_on(&ch->close_wait);		if (ch->asyncflags & ASYNC_HUP_NOTIFY)			return -EAGAIN;		else			return -ERESTARTSYS;	}	if (filp->f_flags & O_NONBLOCK)  {		/*		 * If non-blocking mode is set, then make the check up front		 * and then exit.		 */		ch->asyncflags |= ASYNC_NORMAL_ACTIVE;		return 0;	}	if (tty->termios->c_cflag & CLOCAL)		do_clocal = 1;	/* Block waiting for the carrier detect and the line to become free */	retval = 0;	add_wait_queue(&ch->open_wait, &wait);	spin_lock_irqsave(&epca_lock, flags);	/* We dec count so that pc_close will know when to free things */	if (!tty_hung_up_p(filp))		ch->count--;	ch->blocked_open++;	while (1) {		set_current_state(TASK_INTERRUPTIBLE);		if (tty_hung_up_p(filp) ||		    !(ch->asyncflags & ASYNC_INITIALIZED))		{			if (ch->asyncflags & ASYNC_HUP_NOTIFY)				retval = -EAGAIN;			else				retval = -ERESTARTSYS;			break;		}		if (!(ch->asyncflags & ASYNC_CLOSING) &&			  (do_clocal || (ch->imodem & ch->dcd)))			break;		if (signal_pending(current)) {			retval = -ERESTARTSYS;			break;		}		spin_unlock_irqrestore(&epca_lock, flags);		/*		 * Allow someone else to be scheduled. We will occasionally go		 * through this loop until one of the above conditions change.		 * The below schedule call will allow other processes to enter		 * and prevent this loop from hogging the cpu.		 */		schedule();		spin_lock_irqsave(&epca_lock, flags);	}	__set_current_state(TASK_RUNNING);	remove_wait_queue(&ch->open_wait, &wait);	if (!tty_hung_up_p(filp))		ch->count++;	ch->blocked_open--;	spin_unlock_irqrestore(&epca_lock, flags);	if (retval)		return retval;	ch->asyncflags |= ASYNC_NORMAL_ACTIVE;	return 0;}static int pc_open(struct tty_struct *tty, struct file * filp){	struct channel *ch;	unsigned long flags;	int line, retval, boardnum;	struct board_chan __iomem *bc;	unsigned int head;	line = tty->index;	if (line < 0 || line >= nbdevs)		return -ENODEV;	ch = &digi_channels[line];	boardnum = ch->boardnum;	/* Check status of board configured in system.  */	/*	 * I check to see if the epca_setup routine detected an user error. It	 * might be better to put this in pc_init, but for the moment it goes	 * here.	 */	if (invalid_lilo_config) {		if (setup_error_code & INVALID_BOARD_TYPE)			printk(KERN_ERR "epca: pc_open: Invalid board type specified in kernel options.\n");		if (setup_error_code & INVALID_NUM_PORTS)			printk(KERN_ERR "epca: pc_open: Invalid number of ports specified in kernel options.\n");		if (setup_error_code & INVALID_MEM_BASE)			printk(KERN_ERR "epca: pc_open: Invalid board memory address specified in kernel options.\n");		if (setup_error_code & INVALID_PORT_BASE)			printk(KERN_ERR "epca; pc_open: Invalid board port address specified in kernel options.\n");		if (setup_error_code & INVALID_BOARD_STATUS)			printk(KERN_ERR "epca: pc_open: Invalid board status specified in kernel options.\n");		if (setup_error_code & INVALID_ALTPIN)			printk(KERN_ERR "epca: pc_open: Invalid board altpin specified in kernel options;\n");		tty->driver_data = NULL;   /* Mark this device as 'down' */		return -ENODEV;	}	if (boardnum >= num_cards || boards[boardnum].status == DISABLED)  {		tty->driver_data = NULL;   /* Mark this device as 'down' */		return(-ENODEV);	}	if ((bc = ch->brdchan) == 0) {		tty->driver_data = NULL;		return -ENODEV;	}	spin_lock_irqsave(&epca_lock, flags);	/*

⌨️ 快捷键说明

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