⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 uart.c

📁 优龙2410linux2.6.8内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
	 * the break.  If not, we exit now, losing the break.  FIXME	 */	if ((tty->flip.count + 1) >= TTY_FLIPBUF_SIZE)		return;	*(tty->flip.flag_buf_ptr++) = TTY_BREAK;	*(tty->flip.char_buf_ptr++) = 0;	tty->flip.count++;	queue_task(&tty->flip.tqueue, &tq_timer);}static _INLINE_ void transmit_chars(ser_info_t *info, struct pt_regs *regs){	if ((info->flags & TX_WAKEUP) ||	    (info->tty->flags & (1 << TTY_DO_WRITE_WAKEUP))) {		rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);	}#ifdef SERIAL_DEBUG_INTR	printk("THRE...");#endif}#ifdef notdef	/* I need to do this for the SCCs, so it is left as a reminder.	*/static _INLINE_ void check_modem_status(struct async_struct *info){	int	status;	struct	async_icount *icount;	status = serial_in(info, UART_MSR);	if (status & UART_MSR_ANY_DELTA) {		icount = &info->state->icount;		/* update input line counters */		if (status & UART_MSR_TERI)			icount->rng++;		if (status & UART_MSR_DDSR)			icount->dsr++;		if (status & UART_MSR_DDCD) {			icount->dcd++;#ifdef CONFIG_HARD_PPS			if ((info->flags & ASYNC_HARDPPS_CD) &&			    (status & UART_MSR_DCD))				hardpps();#endif		}		if (status & UART_MSR_DCTS)			icount->cts++;		wake_up_interruptible(&info->delta_msr_wait);	}	if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))		printk("ttys%d CD now %s...", info->line,		       (status & UART_MSR_DCD) ? "on" : "off");#endif		if (status & UART_MSR_DCD)			wake_up_interruptible(&info->open_wait);		else {#ifdef SERIAL_DEBUG_OPEN			printk("scheduling hangup...");#endif			schedule_task(&info->tqueue_hangup);		}	}	if (info->flags & ASYNC_CTS_FLOW) {		if (info->tty->hw_stopped) {			if (status & UART_MSR_CTS) {#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))				printk("CTS tx start...");#endif				info->tty->hw_stopped = 0;				info->IER |= UART_IER_THRI;				serial_out(info, UART_IER, info->IER);				rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);				return;			}		} else {			if (!(status & UART_MSR_CTS)) {#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))				printk("CTS tx stop...");#endif				info->tty->hw_stopped = 1;				info->IER &= ~UART_IER_THRI;				serial_out(info, UART_IER, info->IER);			}		}	}}#endif/* * This is the serial driver's interrupt routine for a single port */static void rs_8xx_interrupt(void *dev_id, struct pt_regs *regs){	u_char	events;	int	idx;	ser_info_t *info;	volatile smc_t	*smcp;	volatile scc_t	*sccp;	info = (ser_info_t *)dev_id;	idx = PORT_NUM(info->state->smc_scc_num);	if (info->state->smc_scc_num & NUM_IS_SCC) {		sccp = &cpmp->cp_scc[idx];		events = sccp->scc_scce;		if (events & SMCM_BRKE)			receive_break(info, regs);		if (events & SCCM_RX)			receive_chars(info, regs);		if (events & SCCM_TX)			transmit_chars(info, regs);		sccp->scc_scce = events;	}	else {		smcp = &cpmp->cp_smc[idx];		events = smcp->smc_smce;		if (events & SMCM_BRKE)			receive_break(info, regs);		if (events & SMCM_RX)			receive_chars(info, regs);		if (events & SMCM_TX)			transmit_chars(info, regs);		smcp->smc_smce = events;	}#ifdef SERIAL_DEBUG_INTR	printk("rs_interrupt_single(%d, %x)...",					info->state->smc_scc_num, events);#endif#ifdef modem_control	check_modem_status(info);#endif	info->last_active = jiffies;#ifdef SERIAL_DEBUG_INTR	printk("end.\n");#endif}/* * ------------------------------------------------------------------- * Here ends the serial interrupt routines. * ------------------------------------------------------------------- *//* * This routine is used to handle the "bottom half" processing for the * serial driver, known also the "software interrupt" processing. * This processing is done at the kernel interrupt level, after the * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This * is where time-consuming activities which can not be done in the * interrupt driver proper are done; the interrupt driver schedules * them using rs_sched_event(), and they get done here. */static void do_serial_bh(void){	run_task_queue(&tq_serial);}static void do_softint(void *private_){	ser_info_t	*info = (ser_info_t *) private_;	struct tty_struct	*tty;	tty = info->tty;	if (!tty)		return;	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&		    tty->ldisc.write_wakeup)			(tty->ldisc.write_wakeup)(tty);		wake_up_interruptible(&tty->write_wait);	}}/* * 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() -> rs_hangup() * */static void do_serial_hangup(void *private_){	struct async_struct	*info = (struct async_struct *) private_;	struct tty_struct	*tty;	tty = info->tty;	if (tty)		tty_hangup(tty);}/*static void rs_8xx_timer(void){	printk("rs_8xx_timer\n");}*/static int startup(ser_info_t *info){	unsigned long flags;	int	retval=0;	int	idx;	struct serial_state *state= info->state;	volatile smc_t		*smcp;	volatile scc_t		*sccp;	volatile smc_uart_t	*up;	volatile scc_uart_t	*scup;	save_flags(flags); cli();	if (info->flags & ASYNC_INITIALIZED) {		goto errout;	}#ifdef maybe	if (!state->port || !state->type) {		if (info->tty)			set_bit(TTY_IO_ERROR, &info->tty->flags);		goto errout;	}#endif#ifdef SERIAL_DEBUG_OPEN	printk("starting up ttys%d (irq %d)...", info->line, state->irq);#endif#ifdef modem_control	info->MCR = 0;	if (info->tty->termios->c_cflag & CBAUD)		info->MCR = UART_MCR_DTR | UART_MCR_RTS;#endif	if (info->tty)		clear_bit(TTY_IO_ERROR, &info->tty->flags);	/*	 * and set the speed of the serial port	 */	change_speed(info);	idx = PORT_NUM(info->state->smc_scc_num);	if (info->state->smc_scc_num & NUM_IS_SCC) {		sccp = &cpmp->cp_scc[idx];		scup = (scc_uart_t *)&cpmp->cp_dparam[state->port];		scup->scc_genscc.scc_mrblr = RX_BUF_SIZE;		scup->scc_maxidl = RX_BUF_SIZE;		sccp->scc_sccm |= (UART_SCCM_TX | UART_SCCM_RX);		sccp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT);	}	else {		smcp = &cpmp->cp_smc[idx];		/* Enable interrupts and I/O.		*/		smcp->smc_smcm |= (SMCM_RX | SMCM_TX);		smcp->smc_smcmr |= (SMCMR_REN | SMCMR_TEN);		/* We can tune the buffer length and idle characters		 * to take advantage of the entire incoming buffer size.		 * If mrblr is something other than 1, maxidl has to be		 * non-zero or we never get an interrupt.  The maxidl		 * is the number of character times we wait after reception		 * of the last character before we decide no more characters		 * are coming.		 */		up = (smc_uart_t *)&cpmp->cp_dparam[state->port];		up->smc_mrblr = RX_BUF_SIZE;		up->smc_maxidl = RX_BUF_SIZE;		up->smc_brkcr = 1;	/* number of break chars */	}	info->flags |= ASYNC_INITIALIZED;	restore_flags(flags);	return 0;errout:	restore_flags(flags);	return retval;}/* * 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(ser_info_t * info){	unsigned long	flags;	struct serial_state *state;	int		idx;	volatile smc_t	*smcp;	volatile scc_t	*sccp;	if (!(info->flags & ASYNC_INITIALIZED))		return;	state = info->state;#ifdef SERIAL_DEBUG_OPEN	printk("Shutting down serial port %d (irq %d)....", info->line,	       state->irq);#endif	save_flags(flags); cli(); /* Disable interrupts */	idx = PORT_NUM(state->smc_scc_num);	if (state->smc_scc_num & NUM_IS_SCC) {		sccp = &cpmp->cp_scc[idx];		sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);#ifdef CONFIG_SERIAL_CONSOLE		/* We can't disable the transmitter if this is the		 * system console.		 */		if ((state - rs_table) != CONFIG_SERIAL_CONSOLE_PORT)#endif			sccp->scc_sccm &= ~(UART_SCCM_TX | UART_SCCM_RX);	}	else {		smcp = &cpmp->cp_smc[idx];		/* Disable interrupts and I/O.		*/		smcp->smc_smcm &= ~(SMCM_RX | SMCM_TX);#ifdef CONFIG_SERIAL_CONSOLE		/* We can't disable the transmitter if this is the		 * system console.		 */		if ((state - rs_table) != CONFIG_SERIAL_CONSOLE_PORT)#endif			smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN);	}	if (info->tty)		set_bit(TTY_IO_ERROR, &info->tty->flags);	info->flags &= ~ASYNC_INITIALIZED;	restore_flags(flags);}/* * This routine is called to set the UART divisor registers to match * the specified baud rate for a serial port. */static void change_speed(ser_info_t *info){	int	baud_rate;	unsigned cflag, cval, scval, prev_mode, new_mode;	int	i, bits, sbits, idx;	unsigned long	flags;	struct serial_state *state;	volatile smc_t	*smcp;	volatile scc_t	*sccp;	if (!info->tty || !info->tty->termios)		return;	cflag = info->tty->termios->c_cflag;	state = info->state;	/* Character length programmed into the mode register is the	 * sum of: 1 start bit, number of data bits, 0 or 1 parity bit,	 * 1 or 2 stop bits, minus 1.	 * The value 'bits' counts this for us.	 */	cval = 0;	scval = 0;	/* byte size and parity */	switch (cflag & CSIZE) {	      case CS5: bits = 5; break;	      case CS6: bits = 6; break;	      case CS7: bits = 7; break;	      case CS8: bits = 8; break;	      /* Never happens, but GCC is too dumb to figure it out */	      default:  bits = 8; break;	}	sbits = bits - 5;	if (cflag & CSTOPB) {		cval |= SMCMR_SL;	/* Two stops */		scval |= SCU_PMSR_SL;		bits++;	}	if (cflag & PARENB) {		cval |= SMCMR_PEN;		scval |= SCU_PMSR_PEN;		bits++;		if (!(cflag & PARODD)) {			cval |= SMCMR_PM_EVEN;			scval |= (SCU_PMSR_REVP | SCU_PMSR_TEVP);		}	}	/* Determine divisor based on baud rate */	i = cflag & CBAUD;	if (i >= (sizeof(baud_table)/sizeof(int)))		baud_rate = 9600;	else		baud_rate = baud_table[i];	info->timeout = (TX_BUF_SIZE*HZ*bits);	info->timeout += HZ/50;		/* Add .02 seconds of slop */#ifdef modem_control	/* CTS flow control flag and modem status interrupts */	info->IER &= ~UART_IER_MSI;	if (info->flags & ASYNC_HARDPPS_CD)		info->IER |= UART_IER_MSI;	if (cflag & CRTSCTS) {		info->flags |= ASYNC_CTS_FLOW;		info->IER |= UART_IER_MSI;	} else		info->flags &= ~ASYNC_CTS_FLOW;	if (cflag & CLOCAL)		info->flags &= ~ASYNC_CHECK_CD;	else {		info->flags |= ASYNC_CHECK_CD;		info->IER |= UART_IER_MSI;	}	serial_out(info, UART_IER, info->IER);#endif	/*	 * Set up parity check flag	 */#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))	info->read_status_mask = (BD_SC_EMPTY | BD_SC_OV);	if (I_INPCK(info->tty))		info->read_status_mask |= BD_SC_FR | BD_SC_PR;	if (I_BRKINT(info->tty) || I_PARMRK(info->tty))		info->read_status_mask |= BD_SC_BR;	/*	 * Characters to ignore	 */	info->ignore_status_mask = 0;	if (I_IGNPAR(info->tty))		info->ignore_status_mask |= BD_SC_PR | BD_SC_FR;	if (I_IGNBRK(info->tty)) {		info->ignore_status_mask |= BD_SC_BR;		/*		 * If we're ignore parity and break indicators, ignore		 * overruns too.  (For real raw support).		 */		if (I_IGNPAR(info->tty))			info->ignore_status_mask |= BD_SC_OV;	}	/*	 * !!! ignore all characters if CREAD is not set	 */	if ((cflag & CREAD) == 0)		info->read_status_mask &= ~BD_SC_EMPTY;	save_flags(flags); cli();	/* Start bit has not been added (so don't, because we would just	 * subtract it later), and we need to add one for the number of	 * stops bits (there is always at least one).	 */	bits++;	idx = PORT_NUM(state->smc_scc_num);	if (state->smc_scc_num & NUM_IS_SCC) {		sccp = &cpmp->cp_scc[idx];		new_mode = (sbits << 12) | scval;		prev_mode = sccp->scc_pmsr;		if (!(prev_mode & SCU_PMSR_PEN))			/* If parity is disabled, mask out even/odd */			prev_mode &= ~(SCU_PMSR_TPM|SCU_PMSR_RPM);		if (prev_mode != new_mode)			sccp->scc_pmsr = new_mode;	}	else {		smcp = &cpmp->cp_smc[idx];		/* Set the mode register.  We want to keep a copy of the		 * enables, because we want to put them back if they were		 * present.		 */		prev_mode = smcp->smc_smcmr;		new_mode = smcr_mk_clen(bits) | cval |  SMCMR_SM_UART;		new_mode |= (prev_mode & (SMCMR_REN | SMCMR_TEN));		if (!(prev_mode & SMCMR_PEN))			/* If parity is disabled, mask out even/odd */			prev_mode &= ~SMCMR_PM_EVEN;		if (prev_mode != new_mode)			smcp->smc_smcmr = new_mode;	}	m8xx_cpm_setbrg((state - rs_table), baud_rate);	restore_flags(flags);}static void rs_8xx_put_char(struct tty_struct *tty, unsigned char ch){	ser_info_t *info = (ser_info_t *)tty->driver_data;	volatile cbd_t	*bdp;	unsigned char *cp;	if (serial_paranoia_check(info, tty->name, "rs_put_char"))		return;	if (!tty)		return;	bdp = info->tx_cur;	while (bdp->cbd_sc & BD_SC_READY);	cp = info->tx_va_base + ((bdp - info->tx_bd_base) * TX_BUF_SIZE);

⌨️ 快捷键说明

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