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 < 0)

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))//第一次初始化,函数始终返回为即 下面的函数一定会被执行

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  cant get irq\n);

return retval;

}

 

//串口使用状态的初始化

atmel_init_property(atmel_port, pdev);

 

//调用串口接收和发送数据之前的准备工作

//有三种方式: 普通,PDCDMA三种方式发送和接收数据

if(atmel_port->prepare_rx){

retval = atmel_port->prepare_rx(port);

if(retval < 0)

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 uart serial使用驱动分析)