linux 串口驱动 atmel_set_mctrl何时调用,linux uart serial使用驱动分析
uart?tty?serial?驅動分析?內核版本3.14.23
以atmel為例:
起點:
static?int?__init?atmel_serial_init(void)
{
int?ret;
//注冊串口驅動?函數原型:int?uart_register_driver(struct?uart_driver?*drv)
ret?=?uart_register_driver(&atmel_uart);
if(ret)
return?ret;
ret?=?platform_driver_register(&atmel_serial_driver);
if(ret)
uart_unregister_driver(&atmel_uart);
return?ret;
}
struct?uart_driver??atmel_uart?=?{
.owner?=?THIS_MODULE,
.driver_name?=“atmel_serial”, ?//驅動名稱
.dev_name???=?ATMEL_DEVICENAME, ?//“ttyS”
.major ?=?SERIAL_ATMEL_MAJOR, //主設備號
.minor ?=?MINOR_START, ? //從設備號
.nr ? =?ATMEL_MAX_UART, ?//支持最多設備個數
.con ? =?ATMEL_CONSOLE_DEVICE //作為console(控制臺)的一些配置
}
ATMEL_CONSOLE_DEVICE:#define?ATMEL_CONSOLE_DEVICE?(&atmel_console)
struct?console?atmel_console?=?{
.name?=?ATMEL_DEVICENAME,
.write?=?atmel_console_write,??//通過控制臺端口發送字符
.device?=?uart_console_device,//返回tty_driver
.setup?=?atmel_console_setup,//控制臺串口設置
flags??=?CON_PRINTBUFFER,//
.index?=?-1,
.data?=?&atmel_uart,??//uart_driver
}
平臺驅動:
static?struct?platform_driver?atmel_serial_dirver?=?{
.probe?=?atmel_serial_probe,
.remove?=?atmel_serial_remove,
.suspend?=?atmel_serial_suspend,
.resume??=?atmel_serial_resume,
.driver???=?{
.name?=“atmel_usart”,
.owner–THIS_MODULE,
.of_match_table?=?of_match_ptr(atmel_serial_dt_ids),
},
}
//與驅動對應的設備發現后調用
static?int?atmel_serial_probe(struct?platform_device?*pdev)
{
sturct?atmel_uart_port?*port;
//獲取設備樹參數
struct?device_node?*np??=?pdev->dev.of_node;
//獲取平臺設備參數
struct?atmel_uart_data?*pdata?=?dev_get_platdata(&pdev->dev);
void?*data;
int?ret?=?-ENODEV;
//以下主要是為了獲取設備號?是第幾個串口設備
//當設備樹參數存在時,?使用設備樹的參數?否則使用平臺設備的參數
if(np)
ret?=?of_alias_get_id(np,“serial”);
else
if(pdata)
ret?=?pdata->num;
if(ret?
ret?=?find_first_zero_bit(atmel_ports_in_use,?ATMEL_MAX_UART);
if(ret?>=?ATMEL_MAX_UART) {
ret?=?-ENODEV;
goto?err;
}
if(test_and_set_bit(ret,?atmel_ports_in_use))?{
ret?=?-EBUSY;
goto?err;
}
//對應的串口結構?取出?賦值。
port?=?&atmel_ports[ret];
port->backup_imr?=?0;
port->uart.line??=?ret;?//設備號
//初始化使用的串口結構體的數據
ret?=?atmel_init_port(port,pdev);
if(ret)
goto?err;
if(!atmel_usr_pdc_rx(&port->uart))?{
ret?=?-ENOMEM;
//申請接收緩沖區
data=kmalloc(sizeof(struct?atmel_uart_char)?*?ATMEL_SERIAL_RINGSIZE,??GFP_KERNEL);
if(!data)
goto?err_alloc_ring;
port->rx_ring.buf?=?data;
}
//將前面注冊串口設備的驅動和串口的設備聯系到一起
//此函數的原型:int?uart_add_one_port(struct?uart_driver?*drv,?struct?uart_port?*uport)
ret?=?uart_add_one_port(&atmel_uart,?&port->uart);
if(ret)
goto?err_add_port;
if(atmel_is_cosole_port(&port->uart)??&&?ATMEL_CONSOLE_DEVICE->flags?&?CON_ENABLE) {
clk_disable_unprepare(port->clk);
}
//使能設備可喚醒??
device_init_wakeup(&pdev->dev,1);
//將使用的串口結構體當做設備的私有數據?進行傳遞。
platform_set_drvdata(pdev,?port);
//初始化一些串口結構體的狀態數據
atmel_get_ip_name(&port->uart);
//出錯處理
err_add_port:
kfree(port->rx_ring.buf);
port->rx_ring.buf?=?NULL;
err_alloc_ring:
if(!atmel_is_console_port(&port->uart))?{
clk_put(port->clk);
port->clk?=?NULL;
}
err:
return?ret;
}
//串口設備卸載
static?int?atmel_serial_remove(struct?platform_device?*pdev)
{
struct?uart_port?*port?=?platform_get_drvdata(pdev);
struct?atmel_uart_port??*atmel_port?=?to_atmel_uart_port(port);
tasklet_kill(&atmel_port->tasklet);
//關閉可喚醒狀態
device_init_wakeup(&pdev->dev,?0);
ret?=?uart_remove_one_port(&atmel_uart,??port);
kfree(atmel_port->rx_ring.buf);
clear_bit(port->line,?atmel_ports_in_use);
clk_put(atmel_port->clk);
return?ret;
}
//填充串口設備的結構struct?uart_port
static?int?atmel_init_port(struct?atmel_uart_port?*atmel_port,?strcut?platform_device?*pdev)
{
int?ret;
struct?uart_port?*port?=?&atmel_port->uart;
struct?atmel_uart_data?*pdata?=?dev_get_platdata(&pdev->dev);
if(!atmel_init_property(atmel_port,?pdev))//第一次初始化,函數始終返回為0即?下面的函數一定會被執行
atmel_set_ops(port);//將串口接收和發送的初始化,執行,釋放的操作賦值
//485的一些初始操作
atmel_init_rs485(atmel_port,?pdev);
port->iotype?=?UPIO_MEM;
port->flags???=?UPF_BOOT_AUTOCONF;
port->ops????=?&atmel_pops;//操作函數結構?const?struct?uart_ops?*ops;
port->fifosize?=??1;//fifo?大小??
port->dev?=?&pdev->dev;
port->mapbase?=?pdev->resource[0].start;
port->irq =?pdev->resource[1].start;
//task初始化
tasklet_int(&atmel_port->tasklet,?atmel_tasklet_func,?(unsigned?long)port);
//環形緩沖區初始化
memset(&atmel_port->rx_ring,?0,?sizeof(atmel_port->rx_ring));
//時鐘的配置
if(!atmel_port->clk){
atmel_port->clk?=?clk_get(&pdev->dev,“usart”);
if(IS_ERR(atmel_port->clk))?{
ret?=?PTR_ERR(atmel_port->clk);
atmel_port->clk?=?NULL;
return?ret;
}
ret?=?clk_prepare_enable(atmel_port->clk)
if(ret){
clk_put(atmel_port->clk);
atmel_port->clk?=?NULL;
return?ret;
}
port->uartclk?=?clk_get_rate(atmel_port->clk);
clk_disable_unprepare(atmel_port->clk);?//只有當使用時才打開串口時鐘
}
if(atmel_port->rs485.flags?&?SER_RS485_ENABLE)
atmel_port->tx_done_mask?=?ATMEL_US_TXEMPTY;
else?if(atmel_use_pdc_tx(port)){
port->fifosize?=?PDC_BUFFER_SIZE;
atmel_port->tx_done_mask?=?ATMEL_US_ENDTX?|?ATMEL_US_TXBUFE;
}else{
atmel_port->tx_done_mask?=?ATMEL_US_TXRDY;
}
}
//串口的操作函數??此函數的介紹可以查看內核目錄下Documentation/serial/driver文檔
static?struct?uart_ops?atmel_pops?=?{
.tx_empty??=?atmel_tx_empty,//發送為空?狀態
.set_mctrl??=?atmel_set_mctrl,//model控制
.get_mctrl??=?atmel_get_mctrl,
.stop_tx =?atmel_stop_tx,??//停止發送
.start_tx ?=?atmel_start_tx,//開始發送數據
.stop_rx ?=?atmel_stop_rx,//停止接收數據
.enable_ms???=?atmel_enable_ms,//使能model控制中斷
.break_ctl =?atmel_break_ctl,?//?start?break?or?stop?break
.startup ?=?atmel_startup,//打開串口?配置串口??將在打開串口時被執行
.shutdown =?atmel_shutdown,//關閉串口?釋放資源的操作
.flush_buffer =?atmel_flush_buffer
.set_termios =?atmel_set_termiso,?,//設置串口?波特率等
.set_ldisc ?=?atmel_set_ldisc,
.type ?=?atmel_type,//返回port的名稱
.release_port =?atmel_release_port,//釋放串口的address?iounmap映射的資源
.request_port =?atmel_request_port,//申請串口address?ioremap
.config_port =?atmel_config_port,
.verify_port =?atmel_verify_port,//檢查串口的配置
.pm ? =?atmel_serial_pm,//串口時鐘管理?關閉或打開
.ioctl ? =?atmel_ioctl,//ioctl?接口
.poll_get_char =?atmel_poll_get_char,
.poll_put_char=?atmel_poll_put_char,
}
//串口打開設置參數解析
static?int?atmel_startup(struct?uart_port?*port)
{
struct?platform_device?*pdev?=?to_platform_device(port->dev);
struct?atmel_uart_port?*atmel_port?=?to_atmel_uart_port(port);
struct?tty_struct?*tty?=?port->state->port.tty;
int?retval;
//屏蔽所有的中斷
UART_PUT_IDR(port,?-1);
//申請中斷處理函數
retval?=?request_irq(port->irq,??atmel_interrupt,??IRQF_SHARED,?tty??tty->name:”atmel_serial”,?port);
if(retval){
printk(“atmel_serial:atmel_startup–can’t?get?irq\n”);
return?retval;
}
//串口使用狀態的初始化
atmel_init_property(atmel_port,?pdev);
//調用串口接收和發送數據之前的準備工作
//有三種方式:?普通,PDC,DMA三種方式發送和接收數據
if(atmel_port->prepare_rx){
retval?=?atmel_port->prepare_rx(port);
if(retval?
atmel_set_ops(port);
}
if(atmel_port->prepare_tx){
retval?=?atmel_port->prepare_tx(port);
if(retval<0)
atmel_set_ops(port);
}
//調用打開時的鉤子函數
if(atmel_open_hook){
retval?=?atmel_open_hook(port);
if(retval){
free_irq(port->irq,?port);
return?retval;
}
}
//中斷寄存器這時的狀態?保存
atmel_port->irq_status_prev?=?UART_GET_CSR(port);
atmel_port->irq_status?=?atmel_port->irq_status_prev;
//使能串口
UART_PUT_CR(port,?ATMEL_US_RSTSTA?|?ATMEL_US_RSTRX);
UART_PUT_CR(port,?ATMEL_US_TXEN|?ATMEL_US_RXEN);
//設置定時器?此定時函數?完成兩件事:1:tasklet_schedule??2:?mod_timer
setup_timer(&atmel_port->uart_timer,?atmel_uart_timer_callback,?(unsigned?long)port);
if(atmel_usr_pdc_rx(port))?{
//如果不是usart同步和異步的區別?下面其他接收方式同理
if(!atmel_port->is_usart){
//設置超時時間
mod_timer(&atmel_port->uart_timer,?jiffies?+?uart_poll_timeout(prot));
}else{
//如果是usart可以直接設定超時值給寄存器,讓硬件判斷
UART_PUT_RTOR(port,?PDC_RX_TIMEOUT);
UART_PUT_CR(port,?ATMEL_US_STTTO);
//使能超時和接收中斷
UART_PUT_IER(port,?ATMEL_US_ENDRX?|?ATMEL_US_TIMEOUT);
}
UART_PUT_PTCR(port,?ATMEL_PDC_RXTEN);
}else?if(atmel_use_dma_rx(port)){
if(!(atmel_port->is_usart))?{
mod_timer(&atmel_port->uart_timer,?jiffies?+?uart_poll_timeout(port));
}else{
//如果是usart可以直接設定超時值給寄存器,讓硬件判斷
UART_PUT_RTOR(port,?PDC_RX_TIMEOUT);
UART_PUT_CR(port,?ATMEL_US_STTTO);
//使能超時中斷
UART_PUT_IER(port,ATMEL_US_TIMEOUT);
}
}else{
//使能接收中斷
UART_PUT_IER(port,?ATMEL_US_RXRDY);
}
return?0;
}
總結
以上是生活随笔為你收集整理的linux 串口驱动 atmel_set_mctrl何时调用,linux uart serial使用驱动分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux下怎样查看环境变量,Linux
- 下一篇: linux服务器重启后阵列卡分区没了,服