📄 16550a.c
字号:
/* * Copyright (C) 2005, 2006 Jan Kiszka <jan.kiszka@web.de>. * * Xenomai is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Xenomai is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Xenomai; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#include <linux/module.h>#include <linux/ioport.h>#include <asm/io.h>#include <rtdm/rtserial.h>#include <rtdm/rtdm_driver.h>#define MAX_DEVICES 8#define IN_BUFFER_SIZE 4096#define OUT_BUFFER_SIZE 4096#define DEFAULT_BAUD_BASE 115200#define DEFAULT_TX_FIFO 16#define PARITY_MASK 0x03#define DATA_BITS_MASK 0x03#define STOP_BITS_MASK 0x01#define FIFO_MASK 0xC0#define EVENT_MASK 0x0F#define LCR_DLAB 0x80#define FCR_FIFO 0x01#define FCR_RESET 0x06#define IER_RX 0x01#define IER_TX 0x02#define IER_STAT 0x04#define IER_MODEM 0x08#define IIR_MODEM 0x00#define IIR_PIRQ 0x01#define IIR_TX 0x02#define IIR_RX 0x04#define IIR_STAT 0x06#define IIR_TMO 0x0C#define IIR_MASK 0x0F#define RHR(dev) (ioaddr[dev] + 0) /* Receive Holding Buffer */#define THR(dev) (ioaddr[dev] + 0) /* Transmit Holding Buffer */#define DLL(dev) (ioaddr[dev] + 0) /* Divisor Latch LSB */#define IER(dev) (ioaddr[dev] + 1) /* Interrupt Enable Register */#define DLM(dev) (ioaddr[dev] + 1) /* Divisor Latch MSB */#define IIR(dev) (ioaddr[dev] + 2) /* Interrupt Id Register */#define FCR(dev) (ioaddr[dev] + 2) /* Fifo Control Register */#define LCR(dev) (ioaddr[dev] + 3) /* Line Control Register */#define MCR(dev) (ioaddr[dev] + 4) /* Modem Control Register */#define LSR(dev) (ioaddr[dev] + 5) /* Line Status Register */#define MSR(dev) (ioaddr[dev] + 6) /* Modem Status Register */struct rt_16550_context { struct rtser_config config; rtdm_irq_t irq_handle; rtdm_lock_t lock; int dev_id; int in_head; int in_tail; volatile size_t in_npend; int in_nwait; rtdm_event_t in_event; char in_buf[IN_BUFFER_SIZE]; volatile unsigned long in_lock; uint64_t *in_history; int out_head; int out_tail; size_t out_npend; rtdm_event_t out_event; char out_buf[OUT_BUFFER_SIZE]; rtdm_mutex_t out_lock; uint64_t last_timestamp; volatile int ioc_events; rtdm_event_t ioc_event; volatile unsigned long ioc_event_lock; int ier_status; int mcr_status; int status;};static const struct rtser_config default_config = { 0xFFFF, RTSER_DEF_BAUD, RTSER_DEF_PARITY, RTSER_DEF_BITS, RTSER_DEF_STOPB, RTSER_DEF_HAND, RTSER_DEF_FIFO_DEPTH, RTSER_DEF_TIMEOUT, RTSER_DEF_TIMEOUT, RTSER_DEF_TIMEOUT, RTSER_DEF_TIMESTAMP_HISTORY, RTSER_DEF_EVENT_MASK};static struct rtdm_device *device[MAX_DEVICES];static unsigned long ioaddr[MAX_DEVICES];static unsigned int irq[MAX_DEVICES];static unsigned int baud_base[MAX_DEVICES];static int tx_fifo[MAX_DEVICES];static unsigned int start_index;#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)static int ioaddr_c;static int irq_c;static int baud_base_c;static int tx_fifo_c;module_param_array(ioaddr, ulong, &ioaddr_c, 0400);module_param_array(irq, uint, &irq_c, 0400);module_param_array(baud_base, uint, &baud_base_c, 0400);module_param_array(tx_fifo, int, &tx_fifo_c, 0400);#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) */MODULE_PARM(ioaddr, "1-" __MODULE_STRING(MAX_DEVICES) "i");MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_DEVICES) "i");MODULE_PARM(baud_base, "1-" __MODULE_STRING(MAX_DEVICES) "i");MODULE_PARM(tx_fifo, "1-" __MODULE_STRING(MAX_DEVICES) "i");#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) */MODULE_PARM_DESC(ioaddr, "I/O addresses of the serial devices");MODULE_PARM_DESC(irq, "IRQ numbers of the serial devices");MODULE_PARM_DESC(baud_base, "Maximum baud rate of the serial device (internal clock rate / 16)");MODULE_PARM_DESC(tx_fifo, "Transmitter FIFO size");module_param(start_index, uint, 0400);MODULE_PARM_DESC(start_index, "First device instance number to be used");MODULE_LICENSE("GPL");MODULE_AUTHOR("jan.kiszka@web.de");static inline int rt_16550_rx_interrupt(struct rt_16550_context *ctx, uint64_t *timestamp){ int dev_id = ctx->dev_id; int rbytes = 0; int lsr = 0; int c; do { c = inb(RHR(dev_id)); /* read input character */ ctx->in_buf[ctx->in_tail] = c; if (ctx->in_history) ctx->in_history[ctx->in_tail] = *timestamp; ctx->in_tail = (ctx->in_tail + 1) & (IN_BUFFER_SIZE - 1); if (++ctx->in_npend > IN_BUFFER_SIZE) { /*DBGIF( if (!testbits(ctx->status, RTSER_SOFT_OVERRUN_ERR)) rtdm_printk("%s: software buffer overrun!\n", device[dev_id]->device_name); );*/ lsr |= RTSER_SOFT_OVERRUN_ERR; ctx->in_npend--; } rbytes++; lsr &= ~RTSER_LSR_DATA; lsr |= (inb(LSR(dev_id)) & (RTSER_LSR_DATA | RTSER_LSR_OVERRUN_ERR | RTSER_LSR_PARITY_ERR | RTSER_LSR_FRAMING_ERR | RTSER_LSR_BREAK_IND)); } while (testbits(lsr, RTSER_LSR_DATA)); /* save new errors */ ctx->status |= lsr; /* If we are enforcing the RTSCTS control flow and the input buffer is busy above the specified high watermark, clear RTS. *//* if (uart->i_count >= uart->config.rts_hiwm && (uart->config.handshake & RT_UART_RTSCTS) != 0 && (uart->modem & MCR_RTS) != 0) { uart->modem &= ~MCR_RTS; outb(uart->modem,MCR(uart)); }*/ return rbytes;}static inline void rt_16550_tx_interrupt(struct rt_16550_context *ctx){ int c; int count; int dev_id = ctx->dev_id;/* if (uart->modem & MSR_CTS)*/ { for (count = tx_fifo[dev_id]; (count > 0) && (ctx->out_npend > 0); count--, ctx->out_npend--) { c = ctx->out_buf[ctx->out_head++]; outb(c, THR(dev_id)); ctx->out_head &= (OUT_BUFFER_SIZE - 1); } }}static inline void rt_16550_stat_interrupt(struct rt_16550_context *ctx){ ctx->status |= (inb(LSR(ctx->dev_id)) & (RTSER_LSR_OVERRUN_ERR | RTSER_LSR_PARITY_ERR | RTSER_LSR_FRAMING_ERR |RTSER_LSR_BREAK_IND));}static int rt_16550_interrupt(rtdm_irq_t *irq_context){ struct rt_16550_context *ctx; int dev_id; int iir; uint64_t timestamp = rtdm_clock_read(); int rbytes = 0; int events = 0; int modem; int ret = RTDM_IRQ_NONE; ctx = rtdm_irq_get_arg(irq_context, struct rt_16550_context); dev_id = ctx->dev_id; rtdm_lock_get(&ctx->lock); while (((iir = inb(IIR(dev_id))) & IIR_PIRQ) == 0) { if (testbits(iir, IIR_RX | IIR_TMO)) { rbytes += rt_16550_rx_interrupt(ctx, ×tamp); events |= RTSER_EVENT_RXPEND; } if (testbits(iir, IIR_STAT)) rt_16550_stat_interrupt(ctx); if (testbits(iir, IIR_TX)) rt_16550_tx_interrupt(ctx); if (testbits(iir, IIR_MODEM)) { modem = inb(MSR(dev_id)); if (modem & (modem << 4)) events |= RTSER_EVENT_MODEMHI; if ((modem ^ 0xF0) & (modem << 4)) events |= RTSER_EVENT_MODEMLO; } ret = RTDM_IRQ_HANDLED; } if (ctx->in_nwait > 0) { if ((ctx->in_nwait <= rbytes) || ctx->status) { ctx->in_nwait = 0; rtdm_event_signal(&ctx->in_event); } else ctx->in_nwait -= rbytes; } if (ctx->status) { events |= RTSER_EVENT_ERRPEND; ctx->ier_status &= ~IER_STAT; } if (testbits(events, ctx->config.event_mask)) { int old_events = ctx->ioc_events; ctx->last_timestamp = timestamp; ctx->ioc_events = events; if (!old_events) rtdm_event_signal(&ctx->ioc_event); } if (testbits(ctx->ier_status, IER_TX) && (ctx->out_npend == 0)) { /* mask transmitter empty interrupt */ ctx->ier_status &= ~IER_TX; rtdm_event_signal(&ctx->out_event); } /* update interrupt mask */ outb(ctx->ier_status, IER(dev_id)); rtdm_lock_put(&ctx->lock); return ret;}static int rt_16550_set_config(struct rt_16550_context *ctx, const struct rtser_config *config, uint64_t **in_history_ptr){ rtdm_lockctx_t lock_ctx; int dev_id; int ret = 0; int baud_div = 0; dev_id = ctx->dev_id; /* make line configuration atomic and IRQ-safe */ rtdm_lock_get_irqsave(&ctx->lock, lock_ctx); if (testbits(config->config_mask, RTSER_SET_BAUD)) { ctx->config.baud_rate = config->baud_rate; baud_div = (baud_base[dev_id] + (ctx->config.baud_rate >> 1)) / ctx->config.baud_rate; outb(LCR_DLAB, LCR(dev_id)); outb(baud_div & 0xff, DLL(dev_id)); outb(baud_div >> 8, DLM(dev_id)); } if (testbits(config->config_mask, RTSER_SET_PARITY)) ctx->config.parity = config->parity & PARITY_MASK; if (testbits(config->config_mask, RTSER_SET_DATA_BITS)) ctx->config.data_bits = config->data_bits & DATA_BITS_MASK; if (testbits(config->config_mask, RTSER_SET_STOP_BITS)) ctx->config.stop_bits = config->stop_bits & STOP_BITS_MASK; if (testbits(config->config_mask, RTSER_SET_PARITY | RTSER_SET_DATA_BITS | RTSER_SET_STOP_BITS | RTSER_SET_BAUD)) { outb((ctx->config.parity << 3) | (ctx->config.stop_bits << 2) | ctx->config.data_bits, LCR(dev_id)); ctx->status = 0; ctx->ioc_events &= ~RTSER_EVENT_ERRPEND; } if (testbits(config->config_mask, RTSER_SET_FIFO_DEPTH)) { ctx->config.fifo_depth = config->fifo_depth & FIFO_MASK; outb(FCR_FIFO | FCR_RESET, FCR(dev_id)); outb(FCR_FIFO | ctx->config.fifo_depth, FCR(dev_id)); } rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx); /* Timeout manipulation is not atomic. The user is supposed to take care * not to use and change timeouts at the same time. */ if (testbits(config->config_mask, RTSER_SET_TIMEOUT_RX)) ctx->config.rx_timeout = config->rx_timeout; if (testbits(config->config_mask, RTSER_SET_TIMEOUT_TX)) ctx->config.tx_timeout = config->tx_timeout; if (testbits(config->config_mask, RTSER_SET_TIMEOUT_EVENT)) ctx->config.event_timeout = config->event_timeout; if (testbits(config->config_mask, RTSER_SET_TIMESTAMP_HISTORY)) { /* change timestamp history atomically */ rtdm_lock_get_irqsave(&ctx->lock, lock_ctx); if (testbits(config->timestamp_history, RTSER_RX_TIMESTAMP_HISTORY)) { if (!ctx->in_history) { ctx->in_history = *in_history_ptr; *in_history_ptr = NULL; if (!ctx->in_history) ret = -ENOMEM; } } else { *in_history_ptr = ctx->in_history; ctx->in_history = NULL; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -