serial-u16550.c

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

C
1,050
字号
	c = inb(io_base + UART_IER);	/* The top four bits of the IER should always == 0 */	if ((c & 0xf0) != 0)		ok = 0;		/* failed */	outb(0xaa, io_base + UART_SCR);	/* Write arbitrary data into the scratch reg */	c = inb(io_base + UART_SCR);	/* If it comes back, it's OK */	if (c != 0xaa)		ok = 0;		/* failed */	outb(0x55, io_base + UART_SCR);	/* Write arbitrary data into the scratch reg */	c = inb(io_base + UART_SCR);	/* If it comes back, it's OK */	if (c != 0x55)		ok = 0;		/* failed */	return ok;}static void snd_uart16550_do_open(struct snd_uart16550 * uart){	char byte;	/* Initialize basic variables */	uart->buff_in_count = 0;	uart->buff_in = 0;	uart->buff_out = 0;	uart->fifo_limit = 1;	uart->fifo_count = 0;	uart->timer_running = 0;	outb(UART_FCR_ENABLE_FIFO	/* Enable FIFO's (if available) */	     | UART_FCR_CLEAR_RCVR	/* Clear receiver FIFO */	     | UART_FCR_CLEAR_XMIT	/* Clear transmitter FIFO */	     | UART_FCR_TRIGGER_4	/* Set FIFO trigger at 4-bytes */	/* NOTE: interrupt generated after T=(time)4-bytes	 * if less than UART_FCR_TRIGGER bytes received	 */	     ,uart->base + UART_FCR);	/* FIFO Control Register */	if ((inb(uart->base + UART_IIR) & 0xf0) == 0xc0)		uart->fifo_limit = 16;	if (uart->divisor != 0) {		uart->old_line_ctrl_reg = inb(uart->base + UART_LCR);		outb(UART_LCR_DLAB	/* Divisor latch access bit */		     ,uart->base + UART_LCR);	/* Line Control Register */		uart->old_divisor_lsb = inb(uart->base + UART_DLL);		uart->old_divisor_msb = inb(uart->base + UART_DLM);		outb(uart->divisor		     ,uart->base + UART_DLL);	/* Divisor Latch Low */		outb(0		     ,uart->base + UART_DLM);	/* Divisor Latch High */		/* DLAB is reset to 0 in next outb() */	}	/* Set serial parameters (parity off, etc) */	outb(UART_LCR_WLEN8	/* 8 data-bits */	     | 0		/* 1 stop-bit */	     | 0		/* parity off */	     | 0		/* DLAB = 0 */	     ,uart->base + UART_LCR);	/* Line Control Register */	switch (uart->adaptor) {	default:		outb(UART_MCR_RTS	/* Set Request-To-Send line active */		     | UART_MCR_DTR	/* Set Data-Terminal-Ready line active */		     | UART_MCR_OUT2	/* Set OUT2 - not always required, but when					 * it is, it is ESSENTIAL for enabling interrupts				 */		     ,uart->base + UART_MCR);	/* Modem Control Register */		break;	case SNDRV_SERIAL_MS124W_SA:	case SNDRV_SERIAL_MS124W_MB:		/* MS-124W can draw power from RTS and DTR if they		   are in opposite states. */ 		outb(UART_MCR_RTS | (0&UART_MCR_DTR) | UART_MCR_OUT2,		     uart->base + UART_MCR);		break;	case SNDRV_SERIAL_MS124T:		/* MS-124T can draw power from RTS and/or DTR (preferably		   both) if they are both asserted. */		outb(UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2,		     uart->base + UART_MCR);		break;	}	if (uart->irq < 0) {		byte = (0 & UART_IER_RDI)	/* Disable Receiver data interrupt */		    |(0 & UART_IER_THRI)	/* Disable Transmitter holding register empty interrupt */		    ;	} else if (uart->adaptor == SNDRV_SERIAL_MS124W_SA) {		byte = UART_IER_RDI	/* Enable Receiver data interrupt */		    | UART_IER_MSI	/* Enable Modem status interrupt */		    ;	} else if (uart->adaptor == SNDRV_SERIAL_GENERIC) {		byte = UART_IER_RDI	/* Enable Receiver data interrupt */		    | UART_IER_MSI	/* Enable Modem status interrupt */		    | UART_IER_THRI	/* Enable Transmitter holding register empty interrupt */		    ;	} else {		byte = UART_IER_RDI	/* Enable Receiver data interrupt */		    | UART_IER_THRI	/* Enable Transmitter holding register empty interrupt */		    ;	}	outb(byte, uart->base + UART_IER);	/* Interupt enable Register */	inb(uart->base + UART_LSR);	/* Clear any pre-existing overrun indication */	inb(uart->base + UART_IIR);	/* Clear any pre-existing transmit interrupt */	inb(uart->base + UART_RX);	/* Clear any pre-existing receive interrupt */}static void snd_uart16550_do_close(struct snd_uart16550 * uart){	if (uart->irq < 0)		snd_uart16550_del_timer(uart);	/* NOTE: may need to disable interrupts before de-registering out handler.	 * For now, the consequences are harmless.	 */	outb((0 & UART_IER_RDI)		/* Disable Receiver data interrupt */	     |(0 & UART_IER_THRI)	/* Disable Transmitter holding register empty interrupt */	     ,uart->base + UART_IER);	/* Interupt enable Register */	switch (uart->adaptor) {	default:		outb((0 & UART_MCR_RTS)		/* Deactivate Request-To-Send line  */		     |(0 & UART_MCR_DTR)	/* Deactivate Data-Terminal-Ready line */		     |(0 & UART_MCR_OUT2)	/* Deactivate OUT2 */		     ,uart->base + UART_MCR);	/* Modem Control Register */	  break;	case SNDRV_SERIAL_MS124W_SA:	case SNDRV_SERIAL_MS124W_MB:		/* MS-124W can draw power from RTS and DTR if they		   are in opposite states; leave it powered. */ 		outb(UART_MCR_RTS | (0&UART_MCR_DTR) | (0&UART_MCR_OUT2),		     uart->base + UART_MCR);		break;	case SNDRV_SERIAL_MS124T:		/* MS-124T can draw power from RTS and/or DTR (preferably		   both) if they are both asserted; leave it powered. */		outb(UART_MCR_RTS | UART_MCR_DTR | (0&UART_MCR_OUT2),		     uart->base + UART_MCR);		break;	}	inb(uart->base + UART_IIR);	/* Clear any outstanding interrupts */	/* Restore old divisor */	if (uart->divisor != 0) {		outb(UART_LCR_DLAB		/* Divisor latch access bit */		     ,uart->base + UART_LCR);	/* Line Control Register */		outb(uart->old_divisor_lsb		     ,uart->base + UART_DLL);	/* Divisor Latch Low */		outb(uart->old_divisor_msb		     ,uart->base + UART_DLM);	/* Divisor Latch High */		/* Restore old LCR (data bits, stop bits, parity, DLAB) */		outb(uart->old_line_ctrl_reg		     ,uart->base + UART_LCR);	/* Line Control Register */	}}static int snd_uart16550_input_open(struct snd_rawmidi_substream *substream){	unsigned long flags;	struct snd_uart16550 *uart = substream->rmidi->private_data;	spin_lock_irqsave(&uart->open_lock, flags);	if (uart->filemode == SERIAL_MODE_NOT_OPENED)		snd_uart16550_do_open(uart);	uart->filemode |= SERIAL_MODE_INPUT_OPEN;	uart->midi_input[substream->number] = substream;	spin_unlock_irqrestore(&uart->open_lock, flags);	return 0;}static int snd_uart16550_input_close(struct snd_rawmidi_substream *substream){	unsigned long flags;	struct snd_uart16550 *uart = substream->rmidi->private_data;	spin_lock_irqsave(&uart->open_lock, flags);	uart->filemode &= ~SERIAL_MODE_INPUT_OPEN;	uart->midi_input[substream->number] = NULL;	if (uart->filemode == SERIAL_MODE_NOT_OPENED)		snd_uart16550_do_close(uart);	spin_unlock_irqrestore(&uart->open_lock, flags);	return 0;}static void snd_uart16550_input_trigger(struct snd_rawmidi_substream *substream,					int up){	unsigned long flags;	struct snd_uart16550 *uart = substream->rmidi->private_data;	spin_lock_irqsave(&uart->open_lock, flags);	if (up)		uart->filemode |= SERIAL_MODE_INPUT_TRIGGERED;	else		uart->filemode &= ~SERIAL_MODE_INPUT_TRIGGERED;	spin_unlock_irqrestore(&uart->open_lock, flags);}static int snd_uart16550_output_open(struct snd_rawmidi_substream *substream){	unsigned long flags;	struct snd_uart16550 *uart = substream->rmidi->private_data;	spin_lock_irqsave(&uart->open_lock, flags);	if (uart->filemode == SERIAL_MODE_NOT_OPENED)		snd_uart16550_do_open(uart);	uart->filemode |= SERIAL_MODE_OUTPUT_OPEN;	uart->midi_output[substream->number] = substream;	spin_unlock_irqrestore(&uart->open_lock, flags);	return 0;};static int snd_uart16550_output_close(struct snd_rawmidi_substream *substream){	unsigned long flags;	struct snd_uart16550 *uart = substream->rmidi->private_data;	spin_lock_irqsave(&uart->open_lock, flags);	uart->filemode &= ~SERIAL_MODE_OUTPUT_OPEN;	uart->midi_output[substream->number] = NULL;	if (uart->filemode == SERIAL_MODE_NOT_OPENED)		snd_uart16550_do_close(uart);	spin_unlock_irqrestore(&uart->open_lock, flags);	return 0;};static inline int snd_uart16550_buffer_can_write(struct snd_uart16550 *uart,						 int Num){	if (uart->buff_in_count + Num < TX_BUFF_SIZE)		return 1;	else		return 0;}static inline int snd_uart16550_write_buffer(struct snd_uart16550 *uart,					     unsigned char byte){	unsigned short buff_in = uart->buff_in;	if (uart->buff_in_count < TX_BUFF_SIZE) {		uart->tx_buff[buff_in] = byte;		buff_in++;		buff_in &= TX_BUFF_MASK;		uart->buff_in = buff_in;		uart->buff_in_count++;		if (uart->irq < 0) /* polling mode */			snd_uart16550_add_timer(uart);		return 1;	} else		return 0;}static int snd_uart16550_output_byte(struct snd_uart16550 *uart,				     struct snd_rawmidi_substream *substream,				     unsigned char midi_byte){	if (uart->buff_in_count == 0                    /* Buffer empty? */	    && ((uart->adaptor != SNDRV_SERIAL_MS124W_SA &&	    uart->adaptor != SNDRV_SERIAL_GENERIC) ||		(uart->fifo_count == 0                  /* FIFO empty? */		 && (inb(uart->base + UART_MSR) & UART_MSR_CTS)))) { /* CTS? */	        /* Tx Buffer Empty - try to write immediately */		if ((inb(uart->base + UART_LSR) & UART_LSR_THRE) != 0) {		        /* Transmitter holding register (and Tx FIFO) empty */		        uart->fifo_count = 1;			outb(midi_byte, uart->base + UART_TX);		} else {		        if (uart->fifo_count < uart->fifo_limit) {			        uart->fifo_count++;				outb(midi_byte, uart->base + UART_TX);			} else {			        /* Cannot write (buffer empty) -				 * put char in buffer */				snd_uart16550_write_buffer(uart, midi_byte);			}		}	} else {		if (!snd_uart16550_write_buffer(uart, midi_byte)) {			snd_printk("%s: Buffer overrun on device at 0x%lx\n",				   uart->rmidi->name, uart->base);			return 0;		}	}	return 1;}static void snd_uart16550_output_write(struct snd_rawmidi_substream *substream){	unsigned long flags;	unsigned char midi_byte, addr_byte;	struct snd_uart16550 *uart = substream->rmidi->private_data;	char first;	static unsigned long lasttime = 0;		/* Interupts are disabled during the updating of the tx_buff,	 * since it is 'bad' to have two processes updating the same	 * variables (ie buff_in & buff_out)	 */	spin_lock_irqsave(&uart->open_lock, flags);	if (uart->irq < 0)	/* polling */		snd_uart16550_io_loop(uart);	if (uart->adaptor == SNDRV_SERIAL_MS124W_MB) {		while (1) {			/* buffer full? */			/* in this mode we need two bytes of space */			if (uart->buff_in_count > TX_BUFF_SIZE - 2)				break;			if (snd_rawmidi_transmit(substream, &midi_byte, 1) != 1)				break;#ifdef SNDRV_SERIAL_MS124W_MB_NOCOMBO			/* select exactly one of the four ports */			addr_byte = (1 << (substream->number + 4)) | 0x08;#else			/* select any combination of the four ports */			addr_byte = (substream->number << 4) | 0x08;			/* ...except none */			if (addr_byte == 0x08)				addr_byte = 0xf8;#endif			snd_uart16550_output_byte(uart, substream, addr_byte);			/* send midi byte */			snd_uart16550_output_byte(uart, substream, midi_byte);		}	} else {		first = 0;		while (snd_rawmidi_transmit_peek(substream, &midi_byte, 1) == 1) {			/* Also send F5 after 3 seconds with no data			 * to handle device disconnect */			if (first == 0 &&			    (uart->adaptor == SNDRV_SERIAL_SOUNDCANVAS ||			     uart->adaptor == SNDRV_SERIAL_GENERIC) &&			    (uart->prev_out != substream->number ||			     jiffies-lasttime > 3*HZ)) {				if (snd_uart16550_buffer_can_write(uart, 3)) {					/* Roland Soundcanvas part selection */

⌨️ 快捷键说明

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