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 + -
显示快捷键?