"阻塞--中断"驱动模型在i2c在子系统、uart驱动、spi子系统中的实现

最近总结了这个kernel中的这个模型,下面我们 开始欣赏吧。

先看i2c中的实现

我们需要明白的是 :

     1. 进程为什么要阻塞
     2.  阻塞后又是 在什么时候被唤醒的?
     3. 从阻塞到唤醒 这中间的过程 是怎么样的 ? 

先上一符图
这里写图片描述

阻塞
这里写图片描述

唤醒
这里写图片描述

阻塞的过程
/**
    这个函数,核心就是调用了s3c24xx_i2c_doxfer来将数据i2c_msg传递给iic从设备
*/
static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, int num)
{
    struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
    /**
        传输错误的重发次数。
    */
    int retry; 
    int ret;
    /**
        这里的 adap->retries 被设置为 2.
        在哪里设置的?
        static int __init i2c_adap_s3c_init(void)
        {
            return platform_driver_register(&s3c24xx_i2c_driver);
        }

        static struct platform_driver s3c24xx_i2c_driver = {
            .probe      = s3c24xx_i2c_probe,
            .remove     = s3c24xx_i2c_remove,
            .id_table   = s3c24xx_driver_ids,
            .driver     = {
                .owner  = THIS_MODULE,
                .name   = "s3c-i2c",
                .pm = S3C24XX_DEV_PM_OPS,
                .of_match_table = s3c24xx_i2c_match,
            },
        };

        导致probe被调用

        static int s3c24xx_i2c_probe(struct platform_device *pdev)
        {
            strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
            i2c->adap.owner   = THIS_MODULE;
            i2c->adap.algo    = &s3c24xx_i2c_algorithm; //iic协议的实现。
            i2c->adap.retries = 2;                      //传递数据到从设备时,发生错误重传的次数
            i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;
            i2c->tx_setup     = 50;                     //往寄存器写入数据的延时时间
        }
    */                  
    for (retry = 0; retry < adap->retries; retry++) {

        /**
            msgs : i2c_msg 组成的数组。
            num  : 数组中i2c_msg的个数
            下面看这个函数。
        */
        ret = s3c24xx_i2c_doxfer(i2c, msgs, num);  //传输函数,传输到iic设备的具体函数。

        if (ret != -EAGAIN) {   //传输成功,返回
            clk_disable(i2c->clk);
            pm_runtime_put_sync(&adap->dev);
            return ret;
            }                   
            /*    下面是传输失败执行的代码
                  延时100微秒后重新调用s3c24xx_i2c_doxfer()发送
            */
        dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);

        udelay(100); //延时 100微秒。
    }

    clk_disable(i2c->clk); 
    pm_runtime_put_sync(&adap->dev);
    return -EREMOTEIO;     //没有成功传输数据.I/O错误
}

static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,struct i2c_msg *msgs, int num)
{
        ...
        /**
            你要往总线上放数据,总得看看总线是不是处于忙的状态把。
            会尝试400 次 检查 iicstat寄存器bit[5],
        */
        ret = s3c24xx_i2c_set_master(i2c);
        if (ret != 0) 
        {                   
            ...
        }
        /**
            这里上锁了。
            那也就是要说明 : 一次只允许一个进程进行数据的传输。
        */
        spin_lock_irq(&i2c->lock);
        /**
            填充iic_msg 消息 结构体

            详细请见我另外一片博文:
            http://blog.csdn.net/leesagacious/article/details/50488949
        */
        i2c->msg     = msgs;     
        i2c->msg_num = num;
        i2c->msg_ptr = 0;
        i2c->msg_idx = 0;
        i2c->state   = STATE_START; //总线状态 : 总线开始状态
        /**
            使能中断。为什么要使能中断?   明白这个很重要。

            iic设备是一个慢速设备,再读写过程中,进程休眠。
            当数据发送完成后,在中断处理函数中会唤醒该休眠的进程。
        */
        s3c24xx_i2c_enable_irq(i2c); 
        /**
            主角终于闪亮登场了....
            这个函数并没有进行数据的传输,数据的传输是放到了中断处理函数中的。
            它只做了两件事情
                        1  : 写从设备地址到寄存器
                        2  : 发送Start信号
            下面详细说。
        */
        s3c24xx_i2c_message_start(i2c, msgs);   
        spin_unlock_irq(&i2c->lock);
        /**
            看,进程休眠了吧..............。
            它在等待数据发送完成。

            具体的是再 s3c24xx_i2c_stop()函数中会调用wake_up()来唤醒该等待队列上的进程
        */
        timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
        ....
        ....    
    }   
}
唤醒的过程

先看是在什么时候注册的中断处理函数

static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
        ....
        ....
        i2c->irq = ret = platform_get_irq(pdev, 0);
        ...
        /**
            看注册了中断处理函数
        */
        ret = request_irq(i2c->irq, s3c24xx_i2c_irq, 0,  dev_name(&pdev->dev), i2c);
        ....
}

好,那么我们来看看这个中断处理函数是 怎么来 设计的
代码分析 请见 我以前的博文
http://blog.csdn.net/leesagacious/article/details/50488949

这里主要说 是怎么来实现的

static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)
{

}
实验验证
UART中的实现
实验验证
spi中的实现
实验验证
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值