您的位置:首页 > 其它

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

2016-06-10 16:01 525 查看
最近总结了这个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中的实现

实验验证

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: