linux 串口驱动
代碼走讀記錄
目錄
1.? 核心數據結構 ops
2. ops各個接口功能及在流程中的位置
? ?2.1??cdns_uart_startup 即打開串口
2.2 start tx? 即發送使能
2.3 中斷發送與軟件隊列的同步
3. 中斷處理程序
3.1 RX 中斷處理
xilinx_uartps.c?? ?drivers\tty\serial?? ?49414?? ?2021/10/26?? ?885
?
1.? 核心數據結構 ops
static const struct uart_ops cdns_uart_ops = {.set_mctrl = cdns_uart_set_mctrl,.get_mctrl = cdns_uart_get_mctrl,.start_tx = cdns_uart_start_tx,.stop_tx = cdns_uart_stop_tx,.stop_rx = cdns_uart_stop_rx,.tx_empty = cdns_uart_tx_empty,.break_ctl = cdns_uart_break_ctl,.set_termios = cdns_uart_set_termios,.startup = cdns_uart_startup,.shutdown = cdns_uart_shutdown,.pm = cdns_uart_pm,.type = cdns_uart_type,.verify_port = cdns_uart_verify_port,.request_port = cdns_uart_request_port,.release_port = cdns_uart_release_port,.config_port = cdns_uart_config_port, #ifdef CONFIG_CONSOLE_POLL.poll_get_char = cdns_uart_poll_get_char,.poll_put_char = cdns_uart_poll_put_char, #endif };2. ops各個接口功能及在流程中的位置
? ?2.1??cdns_uart_startup 即打開串口
? ??
static int cdns_uart_startup(struct uart_port *port) {struct cdns_uart *cdns_uart = port->private_data;bool is_brk_support;int ret;unsigned long flags;unsigned int status = 0;is_brk_support = cdns_uart->quirks & CDNS_UART_RXBS_SUPPORT;spin_lock_irqsave(&port->lock, flags); //這里采用鎖,多個進程同時調用支持?為啥禁止中斷/* Disable the TX and RX */writel(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS,port->membase + CDNS_UART_CR); //禁用發送和接收/* Set the Control Register with TX/RX Enable, TX/RX Reset,* no break chars.*/writel(CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST,port->membase + CDNS_UART_CR);while (readl(port->membase + CDNS_UART_CR) &(CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST))cpu_relax();/** Clear the RX disable bit and then set the RX enable bit to enable* the receiver.*/status = readl(port->membase + CDNS_UART_CR);status &= ~CDNS_UART_CR_RX_DIS;status |= CDNS_UART_CR_RX_EN;writel(status, port->membase + CDNS_UART_CR); //使能接收/* Set the Mode Register with normal mode,8 data bits,1 stop bit,* no parity.*/writel(CDNS_UART_MR_CHMODE_NORM | CDNS_UART_MR_STOPMODE_1_BIT| CDNS_UART_MR_PARITY_NONE | CDNS_UART_MR_CHARLEN_8_BIT,port->membase + CDNS_UART_MR);/** Set the RX FIFO Trigger level to use most of the FIFO, but it* can be tuned with a module parameter*/writel(rx_trigger_level, port->membase + CDNS_UART_RXWM); //接收FIFO門限設置。門限的大小和波特率相關,可以控制中斷的數目/** Receive Timeout register is enabled but it* can be tuned with a module parameter*/writel(rx_timeout, port->membase + CDNS_UART_RXTOUT); // 設置接收超時,即某些字節在FIFO中,但軟件一直沒有讀取,超過此時間設定后,硬件產生中斷,通知軟件獲取,實際也和波特率相關。/* Clear out any pending interrupts before enabling them */writel(readl(port->membase + CDNS_UART_ISR),port->membase + CDNS_UART_ISR); // 清除中斷spin_unlock_irqrestore(&port->lock, flags); //釋放鎖ret = request_irq(port->irq, cdns_uart_isr, 0, CDNS_UART_NAME, port);if (ret) {dev_err(port->dev, "request_irq '%d' failed with %d\n",port->irq, ret);return ret;}//使能接收中斷,開始接收數據/* Set the Interrupt Registers with desired interrupts */if (is_brk_support)writel(CDNS_UART_RX_IRQS | CDNS_UART_IXR_BRK,port->membase + CDNS_UART_IER);elsewritel(CDNS_UART_RX_IRQS, port->membase + CDNS_UART_IER);return 0; }綜上,代碼open的主要操作為:
1)?禁用發送和接收
2) 使能接收
3)? 設置串口工作模式
4) 接收FIFO門限設置。門限的大小和波特率相關,可以控制中斷的數目
5)設置接收超時,即某些字節在FIFO中,但軟件一直沒有讀取,超過此時間設定后,硬件產生中斷,通知軟件獲取,實際也和波特率相關。
6)? 清除中斷
7)掛接中斷處理
8) 使能接收中斷,開始接收數據
以上代碼流程中 spin_lock_irqsave 禁止中斷與采取鎖的背景考慮是? 多線程 是lock,那么中斷,是怕在操作的過程中有串口中斷上來,而此時中斷處理程序還沒上來?
2.2 start tx? 即發送使能
? 將數據從軟件緩存隊列里面放到TX FIFO 中,并進行發送及tx fifo 空中斷的使能控制
static void cdns_uart_start_tx(struct uart_port *port) {unsigned int status;if (uart_tx_stopped(port))return;/** Set the TX enable bit and clear the TX disable bit to enable the* transmitter.*/status = readl(port->membase + CDNS_UART_CR);status &= ~CDNS_UART_CR_TX_DIS;status |= CDNS_UART_CR_TX_EN;writel(status, port->membase + CDNS_UART_CR);if (uart_circ_empty(&port->state->xmit)) //如果上層tty給的緩存里面沒有數據,即實際沒有數據需要發送,因而直接返回。return;cdns_uart_handle_tx(port); //如果軟件有數據發送,則在此處填充硬件TX FIFO,此處填充,由于上面已經將TX 發送使能打開了,所以邊往fifo里面寫,串口邊從FIFO讀取并發送出去。writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_ISR);/* Enable the TX Empty interrupt */writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_IER); //使能tx fifo空中斷,這樣 }2.3 中斷發送與軟件隊列的同步
即 硬件將軟件緩存中的數據發送的差不多時,此時再次喚醒軟件往緩存中增加數據.
static void cdns_uart_handle_tx(void *dev_id) {struct uart_port *port = (struct uart_port *)dev_id;unsigned int numbytes;//從軟件緩存里面取數據,然后寫入到TX FIFO中。//以下這個代碼用于喚醒軟件進程繼續往緩存里面寫數據,當緩存中的數據小于 WAKUP_CHARS(此處是256)if (uart_circ_chars_pending(&port->state->xmit) < WAKEUP_CHARS)uart_write_wakeup(port);} }? ? 為什么不是軟件一直往緩存里面寫,而硬件一直從緩存里面讀,而采取這種門限喚醒的機制呢?
serdev-ttyport.c?? ?drivers\tty\serdev?? ?7921?? ?2021/10/26?? ?189
?
3. 中斷處理程序
? ??分為接收和發送中斷。?
? ?
static irqreturn_t cdns_uart_isr(int irq, void *dev_id) {struct uart_port *port = (struct uart_port *)dev_id; //支持多個串口,獲取此中斷對應的串口實例,以便獲取資源信息,避免數據的混亂。unsigned int isrstatus;spin_lock(&port->lock); //端口鎖,無中斷禁止操作/* Read the interrupt status register to determine which* interrupt(s) is/are active and clear them.*/isrstatus = readl(port->membase + CDNS_UART_ISR);writel(isrstatus, port->membase + CDNS_UART_ISR);if (isrstatus & CDNS_UART_IXR_TXEMPTY) {cdns_uart_handle_tx(dev_id); //針對發送處理isrstatus &= ~CDNS_UART_IXR_TXEMPTY;}/** Skip RX processing if RX is disabled as RXEMPTY will never be set* as read bytes will not be removed from the FIFO.*/if (isrstatus & CDNS_UART_IXR_RXMASK &&!(readl(port->membase + CDNS_UART_CR) & CDNS_UART_CR_RX_DIS))cdns_uart_handle_rx(dev_id, isrstatus); //針對接收處理spin_unlock(&port->lock);return IRQ_HANDLED; }3.1 RX 中斷處理
static void cdns_uart_handle_rx(void *dev_id, unsigned int isrstatus) {struct uart_port *port = (struct uart_port *)dev_id;struct cdns_uart *cdns_uart = port->private_data;char status = TTY_NORMAL;bool is_rxbs_support;is_rxbs_support = cdns_uart->quirks & CDNS_UART_RXBS_SUPPORT;// 主要過程循環讀取RX FIFO中的數據,并判斷數據的正確性while ((readl(port->membase + CDNS_UART_SR) &CDNS_UART_SR_RXEMPTY) != CDNS_UART_SR_RXEMPTY) {data = readl(port->membase + CDNS_UART_FIFO);port->icount.rx++;tty_insert_flip_char(&port->state->port, data, status); //寫入到緩存isrstatus = 0;}tty_flip_buffer_push(&port->state->port); // 寫入到緩存。這個接口比較重要 }The Serial Device Bus (linuxfoundation.org)
Serial TTY overview - stm32mpu (stmicroelectronics.cn)
(3條消息) Linux tty驅動學習 - UART驅動的write操作流程_sbctsp的博客-CSDN博客? (此篇文章,主要參考write_wait,我們在中斷處理發送流程中看到對此wakeup的操作)
總結
以上是生活随笔為你收集整理的linux 串口驱动的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 进度条 不更新_java进度条
- 下一篇: 黑客攻防实战入门读书笔记