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))//第一次初始化,函数始终返回为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 < 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;
}