Linux3.0内核sc32440串口驱动分析(二)——打开读写操作分析

三、串口驱动操作函数

[wuyujun@wuyujunlocalhost linux-3.0]$ grep -n tty_fops -r ./

匹配到二进制文件 ./.tmp_vmlinux1

匹配到二进制文件 ./vmlinux

./drivers/tty/tty_io.c:451:static const struct file_operations tty_fops = {

./drivers/tty/tty_io.c:475:static const struct file_operations hung_up_tty_fops = {

......

./drivers/tty/tty_io.c:3312:    cdev_init(&tty_cdev, &tty_fops);

......

匹配到二进制文件 ./drivers/built-in.o

匹配到二进制文件 ./.tmp_vmlinux2

tty_register_driver()cdev_init(&tty_cdev, &tty_fops);将tty_fops文件操作结构体与tty_cdev绑定;tty_fops结构体定义在./drivers/tty/tty_io.c的451行

static const struct file_operations tty_fops = {

    .llseek     = no_llseek,

    .read       = tty_read,

    .write      = tty_write,

    .poll       = tty_poll,

    .unlocked_ioctl = tty_ioctl,

    .compat_ioctl   = tty_compat_ioctl,

    .open       = tty_open,

    .release    = tty_release,

    .fasync     = tty_fasync,

};

主要看看tty_open(),tty_read(),tty_write(),几个文件操作函数,当用户空间调用open()、read()、write()就会调用到这几个函数

1、open()打开操作

Tty_open同样也在./drivers/tty/tty_io.c里

static int tty_open(struct inode *inode, struct file *filp)

{

    struct tty_struct *tty = NULL;

    int noctty, retval;

    struct tty_driver *driver;

    int index;

    dev_t device = inode->i_rdev;

    unsigned saved_flags = filp->f_flags;

 

    nonseekable_open(inode, filp);

.......

    driver = get_tty_driver(device, &index);//在tty_driver链表中查找该device对应的驱动,并返回在该driver中的index

 ......

        tty = tty_init_dev(driver, index, 0); //初始化tty_struct,返回赋值给struct tty_struct *tty,建立tty_struct与tty_driver之间的关联

......

    if (tty->ops->open)

        retval = tty->ops->open(tty, filp);//调用tty_struct.ops.open()

    else

        retval = -ENODEV;

......

    return 0;

}

tty_init_dev()利用tty_driver初始化tty_struct,在初始化的时候tty_driver的所有tty_operations赋值给了tty_struct的tty_operations变量,tty_open -> tty_init_dev -> initialize_tty_struct,initialize_tty_struct()

void initialize_tty_struct(struct tty_struct *tty,

        struct tty_driver *driver, int idx)

{

/* 设置线路规程为 N_TTY */

tty_ldisc_init(tty);//struct tty_ldisc *ld = tty_ldisc_get(N_TTY);tty_ldisc_assign(tty, ld);

...

tty_buffer_init(tty);

 

    ...

    tty->ops = driver->ops;

    ...

}

tty_ldisc_init(tty);tty_buffer_init()里会涉及到tty线路规程,初始化一个延时工作队列,唤醒时调用flush_to_ldisc在 tty_ldisc_setup 函数中调用到线路规程的open函数,对于 N_TTY 来说是 n_tty_open,对线路规程如何“格式化数据”设置 ,open()就不分析线路规程部分,到read()和write()再去说。

到最后tty->ops = driver->ops;因此下面的tty->ops->open(tty, flip)事实上是调用了Core层注册的tty_driver的  int  (*open)(struct tty_struct * tty, struct file * filp)函数,那这个结构体在哪被注册上呢,就要看到之前的uart_register_driver串口注册驱动里了, tty_set_operations(normal, &uart_ops);// struct tty_driver *normal,设置驱动中定义的操作函数,操作函数在uart_ops中定义最终可以知道调用tty_struct.ops.open(),tty_struct.ops经过tty_init_dev()来自于tty_driver.ops,而tty_driver.ops在uart_register_driver()通过tty_set_operations(normal, &uart_ops);设置,所以最后是调用了uart_open()

Uart_open()./drivers/tty/serial/serial_core.c

static int uart_open(struct tty_struct *tty, struct file *filp)

{

    struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state;

    struct uart_state *state;

    struct tty_port *port;

    int retval, line = tty->index;

.....

    state = uart_get(drv, line); 

    if (IS_ERR(state)) {

        retval = PTR_ERR(state);

        goto fail;

    }

    port = &state->port;

tty->driver_data = state;

    state->uart_port->state = state;

    tty->low_latency = (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0;

    tty->alt_speed = 0;

    tty_port_tty_set(port, tty);

......

    retval = uart_startup(tty, state, 0);

......

    if (retval == 0)

        retval = tty_port_block_til_ready(port, tty, filp);

 

fail:

    return retval;

}

看到这里这些变量名就已经很熟悉了uart_state、uart_port有关串口的物理信息的结构体,根据 tty_struct 获取到 uart_driver ,再由 uart_driver 获取到里uart_state->uart_port->ops->startup 调用它。

static int uart_startup(struct tty_struct *tty, struct uart_state *state, int init_hw)

{

    struct uart_port *uport = state->uart_port;

    struct tty_port *port = &state->port;

    unsigned long page;

int retval = 0;

.....

if (port->flags & ASYNC_CTS_FLOW) {

spin_lock_irq(&uport->lock); //自旋锁中断

if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS))

tty->hw_stopped = 1;

spin_unlock_irq(&uport->lock);

        }

set_bit(ASYNCB_INITIALIZED, &port->flags);

clear_bit(TTY_IO_ERROR, &tty->flags);

    }

if (retval && capable(CAP_SYS_ADMIN))

        retval = 0;

return retval;

}

uart_startup()完成了硬件初始化工作,并申请了中断处理例程.硬件此时只是打开了接收终端,到这里open()就结束了

2、write()写操作

同样的从tty_write看起,在./drivers/tty/tty_io.c里

static ssize_t tty_write(struct file *file, const char __user *buf,

                        size_t count, loff_t *ppos)

{

    struct inode *inode = file->f_path.dentry->d_inode;

    struct tty_struct *tty = file_tty(file);

    struct tty_ldisc *ld;

    ssize_t ret;

 

    if (tty_paranoia_check(tty, inode, "tty_write"))

        return -EIO;

    if (!tty || !tty->ops->write ||

        (test_bit(TTY_IO_ERROR, &tty->flags)))

            return -EIO;

    /* Short term debug to catch buggy drivers */

    if (tty->ops->write_room == NULL)

        printk(KERN_ERR "tty driver %s lacks a write_room method.\n",

            tty->driver->name);

    ld = tty_ldisc_ref_wait(tty); //获取线路规程

    if (!ld->ops->write)

        ret = -EIO;

    else

        ret = do_tty_write(ld->ops->write, tty, file, buf, count); //ld->ops->write实际上是调用到调用线路规程 n_tty_write 函数

    tty_ldisc_deref(ld);

    return ret;

}

接下来看线路规程的n_tty_write函数

static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,

               const unsigned char *buf, size_t nr)

{

    const unsigned char *b = buf;

    DECLARE_WAITQUEUE(wait, current);

    int c;

    ssize_t retval = 0;

 

    /* Job control check -- must be done at start (POSIX.1 7.1.1.4). */

    if (L_TOSTOP(tty) && file->f_op->write != redirected_tty_write) {

        retval = tty_check_change(tty);

        if (retval)

            return retval;

    }    

 

    /* Write out any echoed characters that are still pending */

    process_echoes(tty);

 

    add_wait_queue(&tty->write_wait, &wait); // 添加到等待队列

    while (1) {

        set_current_state(TASK_INTERRUPTIBLE); //设置成可中断

        if (signal_pending(current)) {

            retval = -ERESTARTSYS;

            break;

        }

        if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) {

            retval = -EIO;

            break;

        }

        if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) {

            while (nr > 0) {

                ssize_t num = process_output_block(tty, b, nr);

                if (num < 0) {

                    if (num == -EAGAIN)

                        break;

                    retval = num;

                    goto break_out;

                }

                b += num;

                nr -= num;

                if (nr == 0)

                    break;

                c = *b;

                if (process_output(c, tty) < 0)

                    break;

                b++; nr--;

            }

            if (tty->ops->flush_chars)

                tty->ops->flush_chars(tty);

        } else {

            while (nr > 0) {

                c = tty->ops->write(tty, b, nr); // tty->ops->write 也就是 uart_write

                if (c < 0) {

                    retval = c;

                    goto break_out;

                }

                if (!c)

                    break;

                b += c;

                nr -= c;

            }

        }

        if (!nr)

            break;

        if (file->f_flags & O_NONBLOCK) {

            retval = -EAGAIN;

            break;

        }

        schedule();

    }

break_out:

    __set_current_state(TASK_RUNNING);

    remove_wait_queue(&tty->write_wait, &wait);

    if (b - buf != nr && tty->fasync)

        set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);

    return (b - buf) ? b - buf : retval;

}

n_tty_write 调用 tty->ops->write 也就是 uart_write

static int uart_write(struct tty_struct *tty, const unsigned char *buf, int count)

{

uart_start(tty);

return ret;

}

static void uart_start(struct tty_struct *tty)

{

__uart_start(tty);

}

static void __uart_start(struct tty_struct *tty)

{

struct uart_state *state = tty->driver_data;

struct uart_port *port = state->uart_port;

 

if (!uart_circ_empty(&state->xmit) && state->xmit.buf &&

    !tty->stopped && !tty->hw_stopped)

/* 调用到最底层的 start_tx */

port->ops->start_tx(port);

}

最终会调用到s3c24xx_serial_start_tx函数

static void s3c24xx_serial_start_tx(struct uart_port *port)

{

    struct s3c24xx_uart_port *ourport = to_ourport(port);

    static int a =1;//temp

......

    if (!tx_enabled(port)) {

        if (port->flags & UPF_CONS_FLOW)

            s3c24xx_serial_rx_disable(port);

 

        enable_irq(ourport->tx_irq);

        tx_enabled(port) = 1;

    }

}

使能发送并没有真正的发送过程,而只是使能发送中断,在中断处理里用户从write系统调用传下来的数据就会写入这个UTXH0寄存器。发送完事之后处理器会产生一个内部中断。

下面才是发送中断的ISR(Interrupt Service Routine)中断服务例程。一个irqreturn_t类型的函数。

static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)

{

    struct s3c24xx_uart_port *ourport = id;

    struct uart_port *port = &ourport->port;

    struct circ_buf *xmit = &port->state->xmit;

    int count = 256;

 

    if (port->x_char) { //判断x_char是否为0,如果不为0则发送x_char。

        wr_regb(port, S3C2410_UTXH, port->x_char); //往特定寄存器写的过程

        port->icount.tx++;

        port->x_char = 0;

        goto out;

    }

 

    /* if there isn't anything more to transmit, or the uart is now

     * stopped, disable the uart and exit

    */

 

    if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { //如果循环发送缓冲区为空或者驱动被设置为停止发送,则取消发送

        s3c24xx_serial_stop_tx(port);

        goto out;

    }

 

    /* try and drain the buffer... */

 

    while (!uart_circ_empty(xmit) && count-- > 0) { //循环发送缓冲不为空

        if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull) //如果读寄存器fifo队列满了,退出

            break;

 

        wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]); //写入寄存器

        xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); //更新循环缓冲尾部位置

        port->icount.tx++;

    }

 

    if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) //如果循环缓冲发送剩余量小于WAKEUP_CHARS,则唤醒之前设置的等待队列

        uart_write_wakeup(port); 

 

    if (uart_circ_empty(xmit)) //如果循环缓冲区为空,则停止发送

        s3c24xx_serial_stop_tx(port);

 

 out:

    return IRQ_HANDLED;

}

到这里写操作也分析完了

3、read()读操作

一样的从tty_read函数开始看

static ssize_t tty_read(struct file *file, char __user *buf, size_t count,

            loff_t *ppos)

{

    int i;

    struct inode *inode = file->f_path.dentry->d_inode;

    struct tty_struct *tty = file_tty(file);

    struct tty_ldisc *ld;

 

    if (tty_paranoia_check(tty, inode, "tty_read"))

        return -EIO;

    if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))

        return -EIO;

 

    /* We want to wait for the line discipline to sort out in this

       situation */

    ld = tty_ldisc_ref_wait(tty); //获取线路规程

    if (ld->ops->read)

        i = (ld->ops->read)(tty, file, buf, count); //ld->ops->read实际上是调用到调用线路规程 n_tty_read函数

    else

        i = -EIO;

    tty_ldisc_deref(ld);

    if (i > 0)

        inode->i_atime = current_fs_time(inode->i_sb);

    return i;

}

ld->ops->read实际上是调用到调用线路规程 n_tty_read函数,n_tty_read()在./drivers/tty/n_tty.c里

static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,

             unsigned char __user *buf, size_t nr)

{

    unsigned char __user *b = buf;

    DECLARE_WAITQUEUE(wait, current); //等待队列

    int c;

    int minimum, time;

    ssize_t retval = 0;

    ssize_t size;

    long timeout;

    unsigned long flags;

    int packet;

 

do_it_again:

.......

    add_wait_queue(&tty->read_wait, &wait);//添加到等待队列

        if (((minimum - (b - buf)) < tty->minimum_to_wake) &&

            ((minimum - (b - buf)) >= 1))

            tty->minimum_to_wake = (minimum - (b - buf));

......

        if (!input_available_p(tty, 0)) { //无数据可读

            if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {

                retval = -EIO;

                break;

            }

            if (tty_hung_up_p(file))

                break;

            if (!timeout)

                break;

            if (file->f_flags & O_NONBLOCK) {   //无数据可读且非阻塞模式,直接返回

                retval = -EAGAIN;

                break;

            }

            if (signal_pending(current)) {  //因为信号唤醒,不是因为有数据可读被唤醒也直接返回

                retval = -ERESTARTSYS;

                break;

            }

            /* FIXME: does n_tty_set_room need locking ? */

            n_tty_set_room(tty);

            timeout = schedule_timeout(timeout);

            BUG_ON(!tty->read_buf);

            continue;

        }

        __set_current_state(TASK_RUNNING);

.......

        if (tty->icanon && !L_EXTPROC(tty)) { //标准模式

            /* N.B. avoid overrun if nr == 0 */

            while (nr && tty->read_cnt) {

                int eol;

 

                eol = test_and_clear_bit(tty->read_tail,

                        tty->read_flags);

                c = tty->read_buf[tty->read_tail];

                spin_lock_irqsave(&tty->read_lock, flags);

                tty->read_tail = ((tty->read_tail+1) &

                          (N_TTY_BUF_SIZE-1));

                tty->read_cnt--;

.......

                if (!eol || (c != __DISABLED_CHAR)) {

                    if (tty_put_user(tty, c, b++)) { //把字符拷贝到用户空间

                        retval = -EFAULT;

                        b--;

                        break;

                    }

                    nr--;

                }

                if (eol) {

                    tty_audit_push(tty);

                    break;

                }

            }

            if (retval)

                break;

        } else {

            .....

/* 非标准模式 */

            }

        }

.......

    mutex_unlock(&tty->atomic_read_lock);

    remove_wait_queue(&tty->read_wait, &wait); //删除等待队列

......

    n_tty_set_room(tty);

    return retval;

}

static inline int tty_put_user(struct tty_struct *tty, unsigned char x,

                   unsigned char __user *ptr)

{

    tty_audit_add_data(tty, &x, 1);

    return put_user(x, ptr);   //拷贝字符到用户空间

}

主要用到了等待队列,先查询数据是否可用,如果不可用这让出CPU,等待其他线程来(即中断中间接调用flush_to_ldisc)唤醒。当硬件接收到数据后,会将数据传到ldata.read_buf[]中,然后唤醒改线程,之后该线程就会被唤醒调用tty_put_user完成数据从ldisc(ldata.read_buf[])层到应用缓存区中的拷贝。

具体实现上中断分两步:

(1)通过tty_insert_flip_char(),将RX-FIFO中的数据保存在struct tty_buffer中。

(2)通过tty_flip_buffer_push()来唤醒队列,即flush_to_ldisc()。将数据从struct tty_buffer传到ldata.read_buf[]缓冲区中。

tty_insert_flip_char()./include/linux/tty_flip.h

static inline int tty_insert_flip_char(struct tty_struct *tty,

                    unsigned char ch, char flag)

{

    struct tty_buffer *tb = tty->buf.tail;

    if (tb && tb->used < tb->size) {    //当空间充足时,将数据拷贝到tty_buffer中去

        tb->flag_buf_ptr[tb->used] = flag;   

        tb->char_buf_ptr[tb->used++] = ch;

        return 1;

    }   

    return tty_insert_flip_string_flags(tty, &ch, &flag, 1);   //空间不足时

}

可以在linux-3.0/include/linux/tty.h找到有关这些结构体的定义

/* Each of a tty's open files has private_data pointing to tty_file_private */

struct tty_file_private {

    struct tty_struct *tty;

    struct file *file;

    struct list_head list;

};

struct tty_struct {

    int magic;

    struct kref kref;

    struct device *dev;

    struct tty_driver *driver;

    const struct tty_operations *ops;

    int index;

    /* Protects ldisc changes: Lock tty not pty */

    struct mutex ldisc_mutex;

struct tty_ldisc *ldisc;

......

struct tty_bufhead buf;     /* Locked internally */

int alt_speed;      /* For magic substitution of 38400 bps */

wait_queue_head_t write_wait;

....

struct tty_port *port;

};

再看里面的struct tty_bufhead这个作为临时数据缓冲区的结构体

struct tty_bufhead {

    struct work_struct work;

    spinlock_t lock;

    struct tty_buffer *head;    /* Queue head */

    struct tty_buffer *tail;    /* Active buffer */

    struct tty_buffer *free;    /* Free queue head */

    int memory_used;        /* Buffer space used excluding

                                free queue */

};

 

struct tty_buffer {

    struct tty_buffer *next;

    char *char_buf_ptr;

    unsigned char *flag_buf_ptr;

    int used;

    int size;

    int commit;

    int read;

    /* Data points here */

    unsigned long data[0];

};

struct tty_bufheadstruct tty_buffer共同承担数据临时保存的任务

tty_flip_buffer_push()它在linux-3.0/drivers/tty/tty_buffer.c

void tty_flip_buffer_push(struct tty_struct *tty)

{

    unsigned long flags;

    spin_lock_irqsave(&tty->buf.lock, flags);

    if (tty->buf.tail != NULL)

        tty->buf.tail->commit = tty->buf.tail->used;

    spin_unlock_irqrestore(&tty->buf.lock, flags);

 

    if (tty->low_latency)

        flush_to_ldisc(&tty->buf.work); //调用flush_to_ldisc来唤醒队列将数据从struct tty_buffer传到ldata.read_buf[]缓冲区中。

 

    else

        schedule_work(&tty->buf.work); //调度执行工作队列

}

最后看flush_to_ldisc();在linux-3.0/drivers/tty/tty_buffer.c

static void flush_to_ldisc(struct work_struct *work)

{

    struct tty_struct *tty =

        container_of(work, struct tty_struct, buf.work);

    unsigned long   flags;

    struct tty_ldisc *disc;

 

......

    if (test_bit(TTY_FLUSHPENDING, &tty->flags)) {

        __tty_buffer_flush(tty);

        clear_bit(TTY_FLUSHPENDING, &tty->flags);

        wake_up(&tty->read_wait); //唤醒读操作的等待队列

    }

    spin_unlock_irqrestore(&tty->buf.lock, flags);

    tty_ldisc_deref(disc);

}

在由用户空间发起读操作的时,线程可能会阻塞在add_wait_queue()-->wait_woken()-->TASK_INTERRUPTIBLE()中,等待数据区可读;另一边当中断接收到数据后,先保存到tty_buffer数据结构中,然后通过工作队列tty_bufhead.work(对应flush_to_ldisc),将数据传到ldisc层(ldata.read_buf[]),同时唤醒线程来读取数据。摘至:http://www.wowotech.net/tty_framework/435.html

 

串口驱动太复杂了...分析完也只懂个大概的流程,有机会再深入研究深刻理解

参考:https://blog.csdn.net/lizuobin2/article/details/51773305

http://www.wowotech.net/tty_framework/435.html

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值