日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux 串口驱动 atmel_set_mctrl何时调用,linux uart serial使用驱动分析

發布時間:2025/3/20 linux 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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使用驱动分析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 2017天天干| 香蕉视频网站入口 | 亚洲制服一区 | 五月婷婷亚洲综合 | 国产日韩欧美精品一区二区 | 91麻豆国产在线观看 | 青青草一区| 91免费.| 日本九九视频 | 国产精选毛片 | 久久国产色av | free性欧美hd另类 | 亚洲欧美日韩中文在线 | 男人深夜网站 | 国产一道本 | 国产精品亚洲欧美 | 亚洲高清天堂 | 国产一av | 伊人影院在线观看 | 日韩av电影一区 | 成人欧美一区二区 | 黄色特级片 | 光明影院手机版在线观看免费 | 男生插女生视频 | 樱桃国产成人精品视频 | 青青草激情视频 | 风间由美在线视频 | 亚洲av无码成人精品国产 | 老司机午夜剧场 | 黄色片的网站 | 久久国产一区二区 | 国产激情网址 | 国产精品自偷自拍 | 成人污污视频在线观看 | 欧美做受高潮中文字幕 | 亚洲精品亚洲 | 国产午夜精品久久久久 | 中文精品一区二区三区 | 蜜桃成人无码区免费视频网站 | 午夜免费播放观看在线视频 | 操操操插插插 | 国产亚洲欧美一区 | 成人三区| www.97ai.com| 深夜成人福利视频 | 男生插女生视频在线观看 | av黄色av | www.超碰97.com | 日本韩国欧美中文字幕 | 大地资源中文在线观看免费版 | 爆操女秘书 | 91插插插视频 | 久久亚洲欧美 | 亚洲女人av| 天天干导航 | 狠狠看| 丁香六月五月婷婷 | 国产a毛片 | 欧美福利影院 | 久久在线视频精品 | 亚洲中文字幕97久久精品少妇 | 99久久久国产 | 亚洲成年网 | 婷婷色中文网 | 自拍偷拍精品 | 免费看国产曰批40分钟粉红裤头 | 午夜国产一级 | 精品欧美日韩 | 18做爰免费视频网站 | 饥渴少妇伦色诱公 | 黄网站在线观看视频 | 私密spa按摩按到高潮 | 亚洲蜜桃视频 | 操极品女神 | 亚洲男人天堂2017 | 免费黄色的网站 | 久久77| 米奇狠狠干 | 在线免费黄色片 | 久久大尺度 | 少妇野外性xx老女人野外性xx | 亚洲影视一区二区三区 | 沈樵精品国产成av片 | 国产精品无码成人网站视频 | 国产一区二区视频免费观看 | 亚洲网址在线观看 | 日本一区二区三区视频免费看 | 精品福利视频导航 | 久久9966| va视频在线 | 国产一区二区99 | 国产一级一片免费播放放a 丁香六月色 | 亚洲精品男人的天堂 | 久久精品一区二区免费播放 | 懂色av蜜臀av粉嫩av分 | 777在线视频 | 美女午夜视频 | 先锋资源av网 | 成人午夜又粗又硬又大 |