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

📄 serial_44b0.c

📁 serial port driver in linux
💻 C
📖 第 1 页 / 共 5 页
字号:
/*                        break;                                                             */
/*        }                                                                                  */
/*}                                                                                          */
/*
static void rs_83977_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{

	unsigned int 	tmp;
	int status;
	struct async_struct * info;
	info = IRQ_ports[irq];
	if (!info || !info->tty)
		return;
	tmp=CSR_READ(rEXTINTPND);
	if(tmp&0x01)
	{
		//串口3中断
		printk("串口3中断\n");
		do_com_interrupt(info,&status,regs);
		CSR_WRITE(rEXTINTPND,tmp&(~0x01));
	}
	if(tmp&0x02)
	{
		//串口4中断
		printk("串口4中断\n");
		do_com_interrupt(info,&status,regs);
		CSR_WRITE(rEXTINTPND,tmp&(~0x02));
	}
}
*/
/*
 * -------------------------------------------------------------------
 * 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_)
{
	struct async_struct	*info = (struct async_struct *) 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);
#ifdef SERIAL_HAVE_POLL_WAIT
		wake_up_interruptible(&tty->poll_wait);
#endif
	}
}

/*
 * This subroutine is called when the RS_TIMER goes off.  It is used
 * by the serial driver to handle ports that do not have an interrupt
 * (irq=0).  This doesn't work very well for 16450's, but gives barely
 * passable results for a 16550A.  (Although at the expense of much
 * CPU overhead).
 */
static void rs_timer(unsigned long dummy)
{
#ifndef UARTPOLLING 
	static unsigned long last_strobe;
	struct async_struct *info;
	unsigned int	i;
	unsigned long flags;

	if ((jiffies - last_strobe) >= RS_STROBE_TIME) {
		for (i=0; i < NR_IRQS; i++) {
			info = IRQ_ports[i];
			if (!info)
				continue;
			save_flags(flags); cli();
			rs_interrupt_single(i, NULL, NULL);
			restore_flags(flags);
		}
	}
	last_strobe = jiffies;
	mod_timer(&serial_timer, jiffies + RS_STROBE_TIME);

	if (IRQ_ports[0]) {
		save_flags(flags); cli();
		rs_interrupt_single(0, NULL, NULL);
		restore_flags(flags);

		mod_timer(&serial_timer, jiffies + IRQ_timeout[0]);
	}
#endif
}
/*
 * ---------------------------------------------------------------
 * Low level utility subroutines for the serial driver:  routines to
 * figure out the appropriate timeout for an interrupt chain, routines
 * to initialize and startup a serial port, and routines to shutdown a
 * serial port.  Useful stuff like that.
 * ---------------------------------------------------------------
 */

/*
 * This routine figures out the correct timeout for a particular IRQ.
 * It uses the smallest timeout of all of the serial ports in a
 * particular interrupt chain.  Now only used for IRQ 0....
 */
static void figure_IRQ_timeout(int irq)
{
	struct	async_struct	*info;
	int	timeout = 60*HZ;	// 60 seconds === a long time :-)

	info = IRQ_ports[irq];
	if (!info) {
		IRQ_timeout[irq] = 60*HZ;
		return;
	}
	while (info) {
		if (info->timeout < timeout)
			timeout = info->timeout;
		info = info->next_port;
	}
	if (!irq)
		timeout = timeout / 2;
	IRQ_timeout[irq] = (timeout > 3) ? timeout-2 : 1;
}

static int startup(struct async_struct * info)
{
	unsigned long flags;
	int	retval=0;
	void (*handler)(int, void *, struct pt_regs *);
	struct serial_state *state= info->state;
	unsigned long page;

	page = get_zeroed_page(GFP_KERNEL);
	if (!page)
		return -ENOMEM;

	save_flags(flags); cli();

	if (info->flags & ASYNC_INITIALIZED) {
		free_page(page);
		goto errout;
	}

	if (!CONFIGURED_SERIAL_PORT(state) || !state->type) {
		if (info->tty)
			set_bit(TTY_IO_ERROR, &info->tty->flags);
		free_page(page);
		goto errout;
	}
	if (info->xmit.buf)
		free_page(page);
	else
		info->xmit.buf = (unsigned char *) page;

#ifdef SERIAL_DEBUG_OPEN
	printk("starting up ttys%d (irq %d)...", info->line, state->irq);
#endif
	switch(info->line)
	{
	    case 0:
	    case 1:
		CLEAR_PEND_INT(state->irq);   /* clear serial UART rx interrupt */
		CLEAR_PEND_INT(state->irq-4); /* clear serial UART tx interrupt */
		break;
						      /**/
	    /*case 2:                                   */
	    /*case 3:                                   */
		/*disable_uart_tx_interrupt(info->line);*/
		/*enable_uart_tx_interrupt(info->line); */
		/*disable_uart_rx_interrupt(info->line);*/
		/*enable_uart_rx_interrupt(info->line); */
		/*break;                                */
	    default:
		break;
	}

	//
	// Allocate the IRQ if necessary
	//
	if (state->irq && (!IRQ_ports[state->irq] ||
			  !IRQ_ports[state->irq]->next_port)) {
		if (IRQ_ports[state->irq]) {
			retval = -EBUSY;
			goto errout;
		} else
		{
			if(info->line==0||info->line==1)
			{
			    handler = rs_interrupt_single;
			    // Mac Wang register rx and tx seperatly
			    retval = request_irq(state->irq, handler, SA_SHIRQ,
						 "serial Rx", &IRQ_ports[state->irq]);

			    retval = request_irq(state->irq - 4, handler, SA_SHIRQ,
						 "serial Tx", &IRQ_ports[state->irq-4]);

			}
			/*
			else
			{
			    handler = rs_83977_interrupt;
			    printk("rs_83977_interrupt:%d\n",state->irq);
			    retval = request_irq(state->irq, handler, SA_SHIRQ,
						 "83977 serial Tx && Rx", &IRQ_ports[state->irq]);
			}
			*/
		}


		if (retval) {
			if (capable(CAP_SYS_ADMIN)) {
				if (info->tty)
					set_bit(TTY_IO_ERROR,
						&info->tty->flags);
				retval = 0;
			}
			goto errout;
		}
	}

	//
	// Insert serial port into IRQ chain.
	//
	info->prev_port = 0;
	info->next_port = IRQ_ports[state->irq];
	if (info->next_port)
		info->next_port->prev_port = info;
	if(info->line==0||info->line==1)
	{
	    IRQ_ports[state->irq] = info;
	    figure_IRQ_timeout(state->irq);

	    IRQ_ports[state->irq - 4] = info;
	    figure_IRQ_timeout(state->irq - 4);
	}
	/*
	else
	{
	    IRQ_ports[state->irq] = info;
	    figure_IRQ_timeout(state->irq);
	}
	*/
	//
	// Now, initialize the UART 
	//
/*
	serial_outp(info, UART_LCR, UART_LCR_WLEN8);	// reset DLAB

	info->MCR = 0;
	if (info->tty->termios->c_cflag & CBAUD)
		info->MCR = UART_MCR_DTR | UART_MCR_RTS;
	{
		if (state->irq != 0)
			info->MCR |= UART_MCR_OUT2;
	}
	info->MCR |= ALPHA_KLUDGE_MCR; 		// Don't ask
	serial_outp(info, UART_MCR, info->MCR);
*/
#ifdef CONFIG_SERIAL_SAMSUNG_IRDA
	if (info->line == 1){
		serial_outp(info, UART_LCR, UART_LCR_WLEN8|UART_LCR_IRDA);
	}
	else
		serial_outp(info, UART_LCR, UART_LCR_WLEN8); // UART0 don't support IRDA mode
#else
	if(info->line==0||info->line==1)
		serial_outp(info, UART_LCR, UART_LCR_WLEN8);
	/*else                                                   */
	/*        serial_outp(info, EX_UART_LCR, UART_LCR_WLEN8);*/
	    
#endif

	/* Enable the I/O pins*/
	if (info->line == 1)
	{
		CSR_WRITE(rPCONC,((3 << 20)|(3 << 22)|(3 << 24)| (3 << 26) | (3<<28)|(3 <<30))); // Enable UART1's Pin
		CSR_WRITE(rPUPC,CSR_READ(rPUPC)|0xfc00); // set pull up
#ifdef CONFIG_SERIAL_SAMSUNG_IRDA
		CSR_WRITE(rPCONE,CSR_READ(rPCONE)|1);    // Enable UART0's Pin
		CSR_WRITE(rPDATE,CSR_READ(rPDATE)&0xFE); // select enable IRDA
#endif
	}
	else
	{
		if(info->line==0)
		{
			CSR_WRITE(rPCONE,CSR_READ(rPCONE)|(2 << 2) | (2 << 4));		 // Enable UART0's Pin
		}
	}

//	serial_outp(info, UART_GCR, UART_GCR_RX_INT|UART_GCR_TX_INT|UART_GCR_RX_STAT_INT);
	if(info->line==0||info->line==1)
	{ 
		serial_outp(info, UART_GCR, UART_GCR_RX_INT|UART_GCR_TX_INT);
	}
	/*else                                                                          */
	/*{                                                                             */
		/*serial_outp(info, EX_UART_GCR, EX_UART_GCR_RX_INT|EX_UART_GCR_TX_INT);*/
	/*}                                                                             */

	//
	// Finally, enable interrupts
	//
/*
	info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
	serial_outp(info, UART_IER, info->IER);	// enable interrupts
*/
	enable_uart_rx_interrupt(info->line) ;
	

	//
	// And clear the interrupt registers again for luck.
	//
	CLEAR_PEND_INT(state->irq);   /* clear serial UART rx interrupt */
	if(info->line==0||info->line==1)
	{
	    CLEAR_PEND_INT(state->irq-4); /* clear serial UART tx interrupt */
	}

	if (info->tty)
		clear_bit(TTY_IO_ERROR, &info->tty->flags);
	info->xmit.head = info->xmit.tail = 0;

	//
	// Set up serial timers...
	//
	mod_timer(&serial_timer, jiffies + 2*HZ/100);

 	//
	// Set up the tty->alt_speed kludge
	//
#if (LINUX_VERSION_CODE >= 131394) // Linux 2.1.66
	if (info->tty) {
		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
			info->tty->alt_speed = 57600;
		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
			info->tty->alt_speed = 115200;
		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
			info->tty->alt_speed = 230400;
		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
			info->tty->alt_speed = 460800;
	}
#endif
	
	//
	// and set the speed of the serial port
	//
	change_speed(info, 0);

	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(struct async_struct * info)
{
	unsigned long	flags;
	struct serial_state *state;
	int		retval;

	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

	//
	// clear delta_msr_wait queue to avoid mem leaks: we may free the irq
	// here so the queue might never be waken up
	//
	wake_up_interruptible(&info->delta_msr_wait);
	
	//
	// First unlink the serial port from the IRQ chain...
	//
	if (info->next_port)
		info->next_port->prev_port = info->prev_port;
	if (info->prev_port)
		info->prev_port->next_port = info->next_port;
	else
		IRQ_ports[state->irq] = info->next_port;
	figure_IRQ_timeout(state->irq);
	
	//
	// Free the IRQ, if necessary
	//
	// rx
	if (state->irq && (!IRQ_ports[state->irq] ||
			  !IRQ_ports[state->irq]->next_port)) {
		if (IRQ_ports[state->irq]) {
			free_irq(state->irq, &IRQ_ports[state->irq]);
			retval = request_irq(state->irq, rs_interrupt_single,
					     SA_SHIRQ, "serial",
					     &IRQ_ports[state->irq]);
			
			if (retval)
				printk("serial shutdown: request_irq: error %d"
				       "  Couldn't reacquire IRQ.\n", retval);
		} else
			free_irq(state->irq, &IRQ_ports[state->irq]);
	}
	// tx

	if (state->irq - 4 && (!IRQ_ports[state->irq - 4] ||
			  !IRQ_ports[state->irq - 4]->next_port)) {
		free_irq(state->irq-4, &IRQ_ports[state->irq-4]);
	}

	if (info->xmit.buf) {
		unsigned long pg = (unsigned long) info->xmit.buf;
		info->xmit.buf = 0;
		free_page(pg);
	}

/*

⌨️ 快捷键说明

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