mxser.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,381 行 · 第 1/4 页

C
2,381
字号
			continue;		}		hwconf.pdev = NULL;		if (mxser_initbrd(m, &hwconf) < 0)			continue;		mxser_getcfg(m, &hwconf);		m++;	}	/* start finding PCI board here */#ifdef CONFIG_PCI	{		struct pci_dev *pdev = NULL;		int n = (sizeof(mxser_pcibrds) / sizeof(mxser_pcibrds[0])) - 1;		for (b = 0; b < n; b++) {			while ((pdev = pci_find_device(mxser_pcibrds[b].vendor, mxser_pcibrds[b].device, pdev)))			{				if (pci_enable_device(pdev))					continue;				hwconf.pdev = pdev;				printk("Found MOXA %s board(BusNo=%d,DevNo=%d)\n",					mxser_brdname[mxser_pcibrds[b].driver_data],				pdev->bus->number, PCI_SLOT(pdev->devfn));				if (m >= MXSER_BOARDS) {					printk("Too many Smartio family boards found (maximum %d),board not configured\n", MXSER_BOARDS);				} else {					retval = mxser_get_PCI_conf(pdev, mxser_pcibrds[b].driver_data, &hwconf);					if (retval < 0) {						if (retval == MXSER_ERR_IRQ)							printk("Invalid interrupt number,board not configured\n");						else if (retval == MXSER_ERR_IRQ_CONFLIT)							printk("Invalid interrupt number,board not configured\n");							else if (retval == MXSER_ERR_VECTOR)							printk("Invalid interrupt vector,board not configured\n");						else if (retval == MXSER_ERR_IOADDR)							printk("Invalid I/O address,board not configured\n");						continue;					}					if (mxser_initbrd(m, &hwconf) < 0)						continue;					mxser_getcfg(m, &hwconf);					m++;				}			}		}	}#endif	for (i = m; i < MXSER_BOARDS; i++) {		mxsercfg[i].board_type = -1;	}	if (!tty_register_driver(mxvar_sdriver))		return 0;	put_tty_driver(mxvar_sdriver);	printk("Couldn't install MOXA Smartio family driver !\n");	for (i = 0; i < MXSER_BOARDS; i++) {		if (mxsercfg[i].board_type == -1)			continue;		free_irq(mxsercfg[i].irq, &mxvar_table[i * MXSER_PORTS_PER_BOARD]);	}	return -1;}static void mxser_do_softint(void *private_){	struct mxser_struct *info = (struct mxser_struct *) private_;	struct tty_struct *tty;	tty = info->tty;	if (tty) {		if (test_and_clear_bit(MXSER_EVENT_TXLOW, &info->event)) {			tty_wakeup(tty);			wake_up_interruptible(&tty->write_wait);		}		if (test_and_clear_bit(MXSER_EVENT_HANGUP, &info->event)) {			tty_hangup(tty);	/* FIXME: module removal race here - AKPM */		}	}}/* * This routine is called whenever a serial port is opened.  It * enables interrupts for a serial port, linking in its async structure into * the IRQ chain.   It also performs the serial-specific * initialization for the tty structure. */static int mxser_open(struct tty_struct *tty, struct file *filp){	struct mxser_struct *info;	int retval, line;	unsigned long page;	line = PORTNO(tty);	if (line == MXSER_PORTS)		return (0);	if ((line < 0) || (line > MXSER_PORTS))		return (-ENODEV);	info = mxvar_table + line;	if (!info->base)		return (-ENODEV);	info->count++;	tty->driver_data = info;	info->tty = tty;	if (!mxvar_tmp_buf) {		page = get_zeroed_page(GFP_KERNEL);		if (!page)			return (-ENOMEM);		if (mxvar_tmp_buf)			free_page(page);		else			mxvar_tmp_buf = (unsigned char *) page;	}	/*	 * Start up serial port	 */	retval = mxser_startup(info);	if (retval)		return (retval);	return mxser_block_til_ready(tty, filp, info);}/* * This routine is called when the serial port gets closed.  First, we * wait for the last remaining data to be sent.  Then, we unlink its * async structure from the interrupt chain if necessary, and we free * that IRQ if nothing is left in the chain. */static void mxser_close(struct tty_struct *tty, struct file *filp){	struct mxser_struct *info = (struct mxser_struct *) tty->driver_data;	unsigned long flags;	unsigned long timeout;	if (PORTNO(tty) == MXSER_PORTS)		return;	if (!info)		return;	save_flags(flags);	cli();	if (tty_hung_up_p(filp)) {		restore_flags(flags);		return;	}	if ((tty->count == 1) && (info->count != 1)) {		/*		 * Uh, oh.        tty->count is 1, which means that the tty		 * structure will be freed.  Info->count should always		 * be one in these conditions.  If it's greater than		 * one, we've got real problems, since it means the		 * serial port won't be shutdown.		 */		printk("mxser_close: bad serial port count; tty->count is 1, "		       "info->count is %d\n", info->count);		info->count = 1;	}	if (--info->count < 0) {		printk("mxser_close: bad serial port count for ttys%d: %d\n",		       info->port, info->count);		info->count = 0;	}	if (info->count) {		restore_flags(flags);		return;	}	info->flags |= ASYNC_CLOSING;	info->cflag = tty->termios->c_cflag;	/*	 * Now we wait for the transmit buffer to clear; and we notify	 * the line discipline to only process XON/XOFF characters.	 */	tty->closing = 1;	if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)		tty_wait_until_sent(tty, info->closing_wait);	/*	 * At this point we stop accepting input.  To do this, we	 * disable the receive line status interrupts, and tell the	 * interrupt driver to stop checking the data ready bit in the	 * line status register.	 */	info->IER &= ~UART_IER_RLSI;	/* by William	   info->read_status_mask &= ~UART_LSR_DR;	 */	if (info->flags & ASYNC_INITIALIZED) {		outb(info->IER, info->base + UART_IER);		/*		 * Before we drop DTR, make sure the UART transmitter		 * has completely drained; this is especially		 * important if there is a transmit FIFO!		 */		timeout = jiffies + HZ;		while (!(inb(info->base + UART_LSR) & UART_LSR_TEMT)) {			set_current_state(TASK_INTERRUPTIBLE);			schedule_timeout(5);			if (time_after(jiffies, timeout))				break;		}	}	mxser_shutdown(info);	if (tty->driver->flush_buffer)		tty->driver->flush_buffer(tty);	tty_ldisc_flush(tty);	tty->closing = 0;	info->event = 0;	info->tty = NULL;	if (info->blocked_open) {		if (info->close_delay) {			set_current_state(TASK_INTERRUPTIBLE);			schedule_timeout(info->close_delay);		}		wake_up_interruptible(&info->open_wait);	}	info->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING);	wake_up_interruptible(&info->close_wait);	restore_flags(flags);}static int mxser_write(struct tty_struct *tty, int from_user,		       const unsigned char *buf, int count){	int c, total = 0;	struct mxser_struct *info = (struct mxser_struct *) tty->driver_data;	unsigned long flags;	if (!tty || !info->xmit_buf || !mxvar_tmp_buf)		return (0);	save_flags(flags);	if (from_user) {		down(&mxvar_tmp_buf_sem);		while (1) {			c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,					   SERIAL_XMIT_SIZE - info->xmit_head));			if (c <= 0)				break;			c -= copy_from_user(mxvar_tmp_buf, buf, c);			if (!c) {				if (!total)					total = -EFAULT;				break;			}			cli();			c = min_t(int, c, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,				       SERIAL_XMIT_SIZE - info->xmit_head));			memcpy(info->xmit_buf + info->xmit_head, mxvar_tmp_buf, c);			info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE - 1);			info->xmit_cnt += c;			restore_flags(flags);			buf += c;			count -= c;			total += c;		}		up(&mxvar_tmp_buf_sem);	} else {		while (1) {			cli();			c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,					   SERIAL_XMIT_SIZE - info->xmit_head));			if (c <= 0) {				restore_flags(flags);				break;			}			memcpy(info->xmit_buf + info->xmit_head, buf, c);			info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE - 1);			info->xmit_cnt += c;			restore_flags(flags);			buf += c;			count -= c;			total += c;		}	}	cli();	if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped &&	    !(info->IER & UART_IER_THRI)) {		info->IER |= UART_IER_THRI;		outb(info->IER, info->base + UART_IER);	}	restore_flags(flags);	return (total);}static void mxser_put_char(struct tty_struct *tty, unsigned char ch){	struct mxser_struct *info = (struct mxser_struct *) tty->driver_data;	unsigned long flags;	if (!tty || !info->xmit_buf)		return;	save_flags(flags);	cli();	if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {		restore_flags(flags);		return;	}	info->xmit_buf[info->xmit_head++] = ch;	info->xmit_head &= SERIAL_XMIT_SIZE - 1;	info->xmit_cnt++;	/********************************************** why ??? ***********	if ( !tty->stopped && !tty->hw_stopped &&	     !(info->IER & UART_IER_THRI) ) {	    info->IER |= UART_IER_THRI;	    outb(info->IER, info->base + UART_IER);	}	*****************************************************************/	restore_flags(flags);}static void mxser_flush_chars(struct tty_struct *tty){	struct mxser_struct *info = (struct mxser_struct *) tty->driver_data;	unsigned long flags;	if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||	    !info->xmit_buf)		return;	save_flags(flags);	cli();	info->IER |= UART_IER_THRI;	outb(info->IER, info->base + UART_IER);	restore_flags(flags);}static int mxser_write_room(struct tty_struct *tty){	struct mxser_struct *info = (struct mxser_struct *) tty->driver_data;	int ret;	ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;	if (ret < 0)		ret = 0;	return (ret);}static int mxser_chars_in_buffer(struct tty_struct *tty){	struct mxser_struct *info = (struct mxser_struct *) tty->driver_data;	return (info->xmit_cnt);}static void mxser_flush_buffer(struct tty_struct *tty){	struct mxser_struct *info = (struct mxser_struct *) tty->driver_data;	unsigned long flags;	save_flags(flags);	cli();	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;	restore_flags(flags);	wake_up_interruptible(&tty->write_wait);	tty_wakeup(tty);}static int mxser_ioctl(struct tty_struct *tty, struct file *file,		       unsigned int cmd, unsigned long arg){	unsigned long flags;	struct mxser_struct *info = (struct mxser_struct *) tty->driver_data;	int retval;	struct async_icount cprev, cnow;	/* kernel counter temps */	struct serial_icounter_struct __user *p_cuser;	unsigned long templ;	void __user *argp = (void __user *)arg;	if (PORTNO(tty) == MXSER_PORTS)		return (mxser_ioctl_special(cmd, arg));	if ((cmd != TIOCGSERIAL) && (cmd != TIOCMIWAIT) &&	    (cmd != TIOCGICOUNT)) {		if (tty->flags & (1 << TTY_IO_ERROR))			return (-EIO);	}	switch (cmd) {	case TCSBRK:		/* SVID version: non-zero arg --> no break */		retval = tty_check_change(tty);		if (retval)			return (retval);		tty_wait_until_sent(tty, 0);		if (!arg)			mxser_send_break(info, HZ / 4);		/* 1/4 second */		return (0);	case TCSBRKP:		/* support for POSIX tcsendbreak() */		retval = tty_check_change(tty);		if (retval)			return (retval);		tty_wait_until_sent(tty, 0);		mxser_send_break(info, arg ? arg * (HZ / 10) : HZ / 4);		return (0);	case TIOCGSOFTCAR:		return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long __user *)argp);	case TIOCSSOFTCAR:		if(get_user(templ, (unsigned long __user *) arg))			return -EFAULT;		arg = templ;		tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) |					 (arg ? CLOCAL : 0));		return (0);	case TIOCGSERIAL:		return mxser_get_serial_info(info, argp);	case TIOCSSERIAL:		return mxser_set_serial_info(info, argp);	case TIOCSERGETLSR:	/* Get line status register */		return mxser_get_lsr_info(info, argp);		/*		 * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change		 * - mask passed in arg for lines of interest		 *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)		 * Caller should use TIOCGICOUNT to see which one it was		 */	case TIOCMIWAIT:		save_flags(flags);		cli();		cprev = info->icount;	/* note the counters on entry */		restore_flags(flags);		while (1) {			interruptible_sleep_on(&info->delta_msr_wait);			/* see if a signal did it */			if (signal_pending(current))				return (-ERESTARTSYS);			save_flags(flags);			cli();			cnow = info->icount;	/* atomic copy */			restore_flags(flags);			if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&			  cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)				return (-EIO);	/* no change => error */			if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||			((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||			 ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||			((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) {				return (0);			}			cprev = cnow;		}		/* NOTREACHED */		/*		 * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)		 * Return: write counters to the user passed counter struct		 * NB: both 1->0 and 0->1 transitions are counted except for		 *     RI where only 0->1 is counted.		 */	case TIOCGICOUNT:		save_flags(flags);		cli();		cnow = info->icount;		restore_flags(flags);		p_cuser = argp;		if(put_user(cnow.cts, &p_cuser->cts))			return -EFAULT;		if(put_user(cnow.dsr, &p_cuser->dsr))			return -EFAULT;		if(put_user(cnow.rng, &p_cuser->rng))			return -EFAULT;		return put_user(cnow.dcd, &p_cuser->dcd);	case MOXA_HighSpeedOn:		return put_user(info->baud_base != 115200 ? 1 : 0, (int __user *)argp);	default:		return (-ENOIOCTLCMD);	}	return (0);}static int mxser_ioctl_special(unsigned int cmd, unsigned long arg){	int i, result, status;	void __user *argp = (void __user *)arg;	switch (cmd) {	case MOXA_GET_CONF:		if(copy_to_user(argp, mxsercfg,			     sizeof(struct mxser_hwconf) * 4))			     	return -EFAULT;		return 0;	case MOXA_GET_MAJOR:		if(copy_to_user(argp, &ttymajor, sizeof(int)))			return -EFAULT;		return 0;	case MOXA_GET_CUMAJOR:		result = 0;		if(copy_to_user(argp, &result, sizeof(int)))			return -EFAULT;		return 0;	case MOXA_CHKPORTENABLE:		result = 0;		for (i = 0; i < MXSER_PORTS; i++) {			if (mxvar_table[i].base)				result |= (1 << i);		}		return put_user(result, (unsigned long __user *) argp);	case MOXA_GETDATACOUNT:		if (copy_to_user(argp, &mxvar_log, sizeof(mxvar_log)))			return -EFAULT;		return (0);	case MOXA_GETMSTATUS:		for (i = 0; i < MXSER_PORTS; i++) {			GMStatus[i].ri = 0;			if (!mxvar_table[i].base) {				GMStatus[i].dcd = 0;				GMStatus[i].dsr = 0;				GMStatus[i].cts = 0;				continue;			}			if (!mxvar_table[i].tty || !mxvar_table[i].tty->termios)				GMStatus[i].cflag = mxvar_table[i].cflag;			else				GMStatus[i].cflag = mxvar_table[i].tty->termios->c_cflag;			status = inb(mxvar_table[i].base + UART_MSR);			if (status & 0x80 /*UART_MSR_DCD */ )				GMStatus[i].dcd = 1;			else				GMStatus[i].dcd = 0;			if (status & 0x20 /*UART_MSR_DSR */ )				GMStatus[i].dsr = 1;			else				GMStatus[i].dsr = 0;			if (status & 0x10 /*UART_MSR_CTS */ )				GMStatus[i].cts = 1;			else				GMStatus[i].cts = 0;		}		if(copy_to_user(argp, GMStatus,			     sizeof(struct mxser_mstatus) * MXSER_PORTS))			return -EFAULT;		return 0;	default:		return (-ENOIOCTLCMD);	}	return (0);}/* * This routine is called by the upper-layer tty layer to signal that * incoming characters should be throttled. */static void mxser_throttle(struct tty_struct *tty){	struct mxser_struct *info = (struct mxser_struct *) tty->driver_data;	unsigned long flags;	if (I_IXOFF(tty)) {		info->x_char = STOP_CHAR(tty);		save_flags(flags);		cli();		outb(info->IER, 0);		info->IER |= UART_IER_THRI;		outb(info->IER, info->base + UART_IER);		/* force Tx interrupt */		restore_flags(flags);	}	if (info->tty->termios->c_cflag & CRTSCTS) {		info->MCR &= ~UART_MCR_RTS;		save_flags(flags);		cli();		outb(info->MCR, info->base + UART_MCR);		restore_flags(flags);	}}static void mxser_unthrottle(struct tty_struct *tty){	struct mxser_struct *info = (struct mxser_struct *) tty->driver_data;	unsigned long flags;	if (I_IXOFF(tty)) {		if (info->x_char)			info->x_char = 0;

⌨️ 快捷键说明

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