mcfserial.c

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

C
1,892
字号
	/* Re-arm timer */	mcfrs_timer_struct.expires = jiffies + HZ/25;	add_timer(&mcfrs_timer_struct);}#endif	/* MCFPP_DCD0 *//* * This routine is called from the scheduler tqueue when the interrupt * routine has signalled that a hangup has occurred. The path of * hangup processing is: * * 	serial interrupt routine -> (scheduler tqueue) -> * 	do_serial_hangup() -> tty->hangup() -> mcfrs_hangup() *  */static void do_serial_hangup(void *private){	struct mcf_serial	*info = (struct mcf_serial *) private;	struct tty_struct	*tty;		tty = info->tty;	if (!tty)		return;	tty_hangup(tty);}static int startup(struct mcf_serial * info){	volatile unsigned char	*uartp;	unsigned long		flags;		if (info->flags & ASYNC_INITIALIZED)		return 0;	if (!info->xmit_buf) {		info->xmit_buf = (unsigned char *) __get_free_page(GFP_KERNEL);		if (!info->xmit_buf)			return -ENOMEM;	}	local_irq_save(flags);#ifdef SERIAL_DEBUG_OPEN	printk("starting up ttyS%d (irq %d)...\n", info->line, info->irq);#endif	/*	 *	Reset UART, get it into known state...	 */	uartp = info->addr;	uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETRX;  /* reset RX */	uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETTX;  /* reset TX */	mcfrs_setsignals(info, 1, 1);	if (info->tty)		clear_bit(TTY_IO_ERROR, &info->tty->flags);	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;	/*	 * and set the speed of the serial port	 */	mcfrs_change_speed(info);	/*	 * Lastly enable the UART transmitter and receiver, and	 * interrupt enables.	 */	info->imr = MCFUART_UIR_RXREADY;	uartp[MCFUART_UCR] = MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE;	uartp[MCFUART_UIMR] = info->imr;	info->flags |= ASYNC_INITIALIZED;	local_irq_restore(flags);	return 0;}/* * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. */static void shutdown(struct mcf_serial * info){	volatile unsigned char	*uartp;	unsigned long		flags;	if (!(info->flags & ASYNC_INITIALIZED))		return;#ifdef SERIAL_DEBUG_OPEN	printk("Shutting down serial port %d (irq %d)....\n", info->line,	       info->irq);#endif		local_irq_save(flags);	uartp = info->addr;	uartp[MCFUART_UIMR] = 0;  /* mask all interrupts */	uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETRX;  /* reset RX */	uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETTX;  /* reset TX */	if (!info->tty || (info->tty->termios->c_cflag & HUPCL))		mcfrs_setsignals(info, 0, 0);	if (info->xmit_buf) {		free_page((unsigned long) info->xmit_buf);		info->xmit_buf = 0;	}	if (info->tty)		set_bit(TTY_IO_ERROR, &info->tty->flags);		info->flags &= ~ASYNC_INITIALIZED;	local_irq_restore(flags);}/* * This routine is called to set the UART divisor registers to match * the specified baud rate for a serial port. */static void mcfrs_change_speed(struct mcf_serial *info){	volatile unsigned char	*uartp;	unsigned int		baudclk, cflag;	unsigned long		flags;	unsigned char		mr1, mr2;	int			i;#ifdef	CONFIG_M5272	unsigned int		fraction;#endif	if (!info->tty || !info->tty->termios)		return;	cflag = info->tty->termios->c_cflag;	if (info->addr == 0)		return;#if 0	printk("%s(%d): mcfrs_change_speed()\n", __FILE__, __LINE__);#endif	i = cflag & CBAUD;	if (i & CBAUDEX) {		i &= ~CBAUDEX;		if (i < 1 || i > 4)			info->tty->termios->c_cflag &= ~CBAUDEX;		else			i += 15;	}	if (i == 0) {		mcfrs_setsignals(info, 0, -1);		return;	}	/* compute the baudrate clock */#ifdef	CONFIG_M5272	/*	 * For the MCF5272, also compute the baudrate fraction.	 */	baudclk = (MCF_BUSCLK / mcfrs_baud_table[i]) / 32;	fraction = MCF_BUSCLK - (baudclk * 32 * mcfrs_baud_table[i]);	fraction *= 16;	fraction /= (32 * mcfrs_baud_table[i]);#else	baudclk = ((MCF_BUSCLK / mcfrs_baud_table[i]) + 16) / 32;#endif	info->baud = mcfrs_baud_table[i];	mr1 = MCFUART_MR1_RXIRQRDY | MCFUART_MR1_RXERRCHAR;	mr2 = 0;	switch (cflag & CSIZE) {	case CS5:	mr1 |= MCFUART_MR1_CS5; break;	case CS6:	mr1 |= MCFUART_MR1_CS6; break;	case CS7:	mr1 |= MCFUART_MR1_CS7; break;	case CS8:	default:	mr1 |= MCFUART_MR1_CS8; break;	}	if (cflag & PARENB) {		if (cflag & CMSPAR) {			if (cflag & PARODD)				mr1 |= MCFUART_MR1_PARITYMARK;			else				mr1 |= MCFUART_MR1_PARITYSPACE;		} else {			if (cflag & PARODD)				mr1 |= MCFUART_MR1_PARITYODD;			else				mr1 |= MCFUART_MR1_PARITYEVEN;		}	} else {		mr1 |= MCFUART_MR1_PARITYNONE;	}	if (cflag & CSTOPB)		mr2 |= MCFUART_MR2_STOP2;	else		mr2 |= MCFUART_MR2_STOP1;	if (cflag & CRTSCTS) {		mr1 |= MCFUART_MR1_RXRTS;		mr2 |= MCFUART_MR2_TXCTS;	}	if (cflag & CLOCAL)		info->flags &= ~ASYNC_CHECK_CD;	else		info->flags |= ASYNC_CHECK_CD;	uartp = info->addr;	local_irq_save(flags);#if 0	printk("%s(%d): mr1=%x mr2=%x baudclk=%x\n", __FILE__, __LINE__,		mr1, mr2, baudclk);#endif	/*	  Note: pg 12-16 of MCF5206e User's Manual states that a	  software reset should be performed prior to changing	  UMR1,2, UCSR, UACR, bit 7	*/	uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETRX;    /* reset RX */	uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETTX;    /* reset TX */	uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETMRPTR;	/* reset MR pointer */	uartp[MCFUART_UMR] = mr1;	uartp[MCFUART_UMR] = mr2;	uartp[MCFUART_UBG1] = (baudclk & 0xff00) >> 8;	/* set msb byte */	uartp[MCFUART_UBG2] = (baudclk & 0xff);		/* set lsb byte */#ifdef	CONFIG_M5272	uartp[MCFUART_UFPD] = (fraction & 0xf);		/* set fraction */#endif	uartp[MCFUART_UCSR] = MCFUART_UCSR_RXCLKTIMER | MCFUART_UCSR_TXCLKTIMER;	uartp[MCFUART_UCR] = MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE;	mcfrs_setsignals(info, 1, -1);	local_irq_restore(flags);	return;}static void mcfrs_flush_chars(struct tty_struct *tty){	volatile unsigned char	*uartp;	struct mcf_serial	*info = (struct mcf_serial *)tty->driver_data;	unsigned long		flags;	if (serial_paranoia_check(info, tty->name, "mcfrs_flush_chars"))		return;	if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||	    !info->xmit_buf)		return;	/* Enable transmitter */	local_irq_save(flags);	uartp = info->addr;	info->imr |= MCFUART_UIR_TXREADY;	uartp[MCFUART_UIMR] = info->imr;	local_irq_restore(flags);}static int mcfrs_write(struct tty_struct * tty, int from_user,		    const unsigned char *buf, int count){	volatile unsigned char	*uartp;	struct mcf_serial	*info = (struct mcf_serial *)tty->driver_data;	unsigned long		flags;	int			c, total = 0;#if 0	printk("%s(%d): mcfrs_write(tty=%x,from_user=%d,buf=%x,count=%d)\n",		__FILE__, __LINE__, (int)tty, from_user, (int)buf, count);#endif	if (serial_paranoia_check(info, tty->name, "mcfrs_write"))		return 0;	if (!tty || !info->xmit_buf)		return 0;		local_save_flags(flags);	while (1) {		local_irq_disable();				c = min(count, (int) min(((int)SERIAL_XMIT_SIZE) - info->xmit_cnt - 1,			((int)SERIAL_XMIT_SIZE) - info->xmit_head));		local_irq_restore(flags);		if (c <= 0)			break;		if (from_user) {			down(&mcfrs_tmp_buf_sem);			if (copy_from_user(mcfrs_tmp_buf, buf, c))				return -EFAULT;			local_irq_disable();			c = min(c, (int) min(((int)SERIAL_XMIT_SIZE) - info->xmit_cnt - 1,				       ((int)SERIAL_XMIT_SIZE) - info->xmit_head));			local_irq_restore(flags);			memcpy(info->xmit_buf + info->xmit_head, mcfrs_tmp_buf, c);			up(&mcfrs_tmp_buf_sem);		} else			memcpy(info->xmit_buf + info->xmit_head, buf, c);		local_irq_disable();		info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);		info->xmit_cnt += c;		local_irq_restore(flags);		buf += c;		count -= c;		total += c;	}	local_irq_disable();	uartp = info->addr;	info->imr |= MCFUART_UIR_TXREADY;	uartp[MCFUART_UIMR] = info->imr;	local_irq_restore(flags);	return total;}static int mcfrs_write_room(struct tty_struct *tty){	struct mcf_serial *info = (struct mcf_serial *)tty->driver_data;	int	ret;	if (serial_paranoia_check(info, tty->name, "mcfrs_write_room"))		return 0;	ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;	if (ret < 0)		ret = 0;	return ret;}static int mcfrs_chars_in_buffer(struct tty_struct *tty){	struct mcf_serial *info = (struct mcf_serial *)tty->driver_data;	if (serial_paranoia_check(info, tty->name, "mcfrs_chars_in_buffer"))		return 0;	return info->xmit_cnt;}static void mcfrs_flush_buffer(struct tty_struct *tty){	struct mcf_serial	*info = (struct mcf_serial *)tty->driver_data;	unsigned long		flags;	if (serial_paranoia_check(info, tty->name, "mcfrs_flush_buffer"))		return;	local_irq_save(flags);	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;	local_irq_restore(flags);	tty_wakeup(tty);}/* * ------------------------------------------------------------ * mcfrs_throttle() *  * This routine is called by the upper-layer tty layer to signal that * incoming characters should be throttled. * ------------------------------------------------------------ */static void mcfrs_throttle(struct tty_struct * tty){	struct mcf_serial *info = (struct mcf_serial *)tty->driver_data;#ifdef SERIAL_DEBUG_THROTTLE	char	buf[64];		printk("throttle %s: %d....\n", _tty_name(tty, buf),	       tty->ldisc.chars_in_buffer(tty));#endif	if (serial_paranoia_check(info, tty->name, "mcfrs_throttle"))		return;		if (I_IXOFF(tty))		info->x_char = STOP_CHAR(tty);	/* Turn off RTS line (do this atomic) */}static void mcfrs_unthrottle(struct tty_struct * tty){	struct mcf_serial *info = (struct mcf_serial *)tty->driver_data;#ifdef SERIAL_DEBUG_THROTTLE	char	buf[64];		printk("unthrottle %s: %d....\n", _tty_name(tty, buf),	       tty->ldisc.chars_in_buffer(tty));#endif	if (serial_paranoia_check(info, tty->name, "mcfrs_unthrottle"))		return;		if (I_IXOFF(tty)) {		if (info->x_char)			info->x_char = 0;		else			info->x_char = START_CHAR(tty);	}	/* Assert RTS line (do this atomic) */}/* * ------------------------------------------------------------ * mcfrs_ioctl() and friends * ------------------------------------------------------------ */static int get_serial_info(struct mcf_serial * info,			   struct serial_struct * retinfo){	struct serial_struct tmp;  	if (!retinfo)		return -EFAULT;	memset(&tmp, 0, sizeof(tmp));	tmp.type = info->type;	tmp.line = info->line;	tmp.port = (unsigned int) info->addr;	tmp.irq = info->irq;	tmp.flags = info->flags;	tmp.baud_base = info->baud_base;	tmp.close_delay = info->close_delay;	tmp.closing_wait = info->closing_wait;	tmp.custom_divisor = info->custom_divisor;	return copy_to_user(retinfo,&tmp,sizeof(*retinfo)) ? -EFAULT : 0;}static int set_serial_info(struct mcf_serial * info,			   struct serial_struct * new_info){	struct serial_struct new_serial;	struct mcf_serial old_info;	int 	retval = 0;	if (!new_info)		return -EFAULT;	if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))		return -EFAULT;	old_info = *info;	if (!capable(CAP_SYS_ADMIN)) {		if ((new_serial.baud_base != info->baud_base) ||		    (new_serial.type != info->type) ||		    (new_serial.close_delay != info->close_delay) ||		    ((new_serial.flags & ~ASYNC_USR_MASK) !=		     (info->flags & ~ASYNC_USR_MASK)))			return -EPERM;		info->flags = ((info->flags & ~ASYNC_USR_MASK) |			       (new_serial.flags & ASYNC_USR_MASK));		info->custom_divisor = new_serial.custom_divisor;		goto check_and_exit;	}	if (info->count > 1)		return -EBUSY;	/*	 * OK, past this point, all the error checking has been done.	 * At this point, we start making changes.....	 */

⌨️ 快捷键说明

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