Linux驱动学习-关中断与自旋锁

问题来自于学习韦东山 Linux驱动入门实验班DS18B20温度传感器Linux驱动,因为设备脉冲信号时间很短,很可能错失中断,且需要关中断

在read函数中完成硬件操作:启动温度转换,休眠等待1s,再读取温度。关中断使用spin_lock_irqsave(&ds18b20_spinlock, flags);,恢复中断使用spin_unlock_irqrestore(&ds18b20_spinlock, flags);

/* 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t ds18b20_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	int err;
	unsigned long flags;
	unsigned char kern_buf[9];
	int i;
	int result_buf[2];

	if (size != 8)
	{
		return -EINVAL;
	}

	/* 1.启动温度转换 */

	//	1.1 关中断 
	spin_lock_irqsave(&ds18b20_spinlock, flags);
	//	1.2 发出reset信号并等待回应 
	err = ds18b20_reset_and_wait_ack();
	if (err)
	{
		spin_unlock_irqrestore(&ds18b20_spinlock, flags);
		printk("ds18b20_reset_and_wait_ack first err\n");
		return err;
	}
	//	1.3 发出ROM命令:skip rom,0xcc 
	ds18b20_send_cmd(0xcc);

	//	1.4 发出FUN命令:启动温度转换,0x44 
	ds18b20_send_cmd(0x44);

	//	1.5 恢复中断 
	spin_unlock_irqrestore(&ds18b20_spinlock, flags);


	/* 2.等待温度转换成功: 可能长达1s,需要休眠等待 */
	schedule_timeout_interruptible(msecs_to_jiffies(1000));


	/* 3.读取温度 */

	/* 3.1 关中断 */
	spin_lock_irqsave(&ds18b20_spinlock, flags);
	/* 3.2 发出reset信号并等待回应 */
	err = ds18b20_reset_and_wait_ack();
	if (err)
	{
		spin_unlock_irqrestore(&ds18b20_spinlock, flags);
		printk("ds18b20_reset_and_wait_ack second err\n");
		return err;
	}
	/* 3.3 发出ROM命令:skip rom,0xcc */
	ds18b20_send_cmd(0xcc);
	/* 3.4 发出FUN命令:read scratchpad,0xbe */
	ds18b20_send_cmd(0xbe);
	/* 3.5 读9byte的数据 */
	for ( i = 0; i < 9; i++)
	{
		ds18b20_read_byte_data(&kern_buf[i]);
	}

	/* 3.6 恢复中断 */
	spin_unlock_irqrestore(&ds18b20_spinlock, flags);
	/* 3.7 校验CRC */
	err = ds18b20_verify_crc(kern_buf);
	if (err)
	{
		printk("ds18b20_verify_crc err\n");
		return err;
	}

	/* 4.copy_to_user */
	ds18b20_calc_val(kern_buf, result_buf);
	err = copy_to_user(buf, result_buf, 8);	//在imx6ull上一个int是4字节
	
	return 8;
}


/* 定义自己的file_operations结构体                                              */
static struct file_operations gpio_key_drv = {
	.owner	 = THIS_MODULE,
	.read    = ds18b20_read,
};

关中断与自旋锁等知识见**《Linux设备驱动开发详解 - 宋宝华》7.5自旋锁 7.5.1 自旋锁的使用**。
自旋锁可以保证临界区不受别的CPU和本CPU内的抢占进程打扰,但是得到锁的代码路径在执行临界区的时候,还可能受到中断和底半部(BH,稍后的章节会介绍)的影响。

spin_lock_irq(spinlock_t *lock): spin_lock(lock) + local_irq_disable()
spin_unlock_irq(spinlock_t *lock): spin_unlock(lock) + local_irq_enable()

spin_lock_irqsave(spinlock_t *lock, unsigned long flags)spin_lock(lock) + local_irq_save(flags)	//关中断并保存状态字
spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)spin_unlock(lock) + local_irq_restore(flags)	//开中断并恢复状态字

这两对函数的区别在于 IRQ(中断请求)的状态的保存和恢复方式。

  1. spin_lock_irq()spin_unlock_irq()
    • spin_lock_irq() 函数会先禁用 IRQ,然后获取自旋锁。
    • spin_unlock_irq() 函数会先释放自旋锁,然后启用 IRQ。

这对函数中,IRQ 的状态不会保存和恢复。如果在获取锁期间发生中断,中断处理程序将不会执行,因为 IRQ 被禁用,这可能会导致一些问题。

  1. spin_lock_irqsave()spin_unlock_irqrestore()
    • spin_lock_irqsave() 函数会先保存当前 CPU 的 IRQ 状态,并禁用 IRQ,然后获取自旋锁。
    • spin_unlock_irqrestore() 函数会先释放自旋锁,然后恢复保存的 IRQ 状态。

这对函数会在获取锁之前保存当前 CPU 的 IRQ 状态,并在释放锁后恢复保存的 IRQ 状态。这样,在获取锁期间发生的中断不会被忽略,而是在释放锁后恢复执行中断处理程序。

注意:在使用 spin_lock_irqsave()spin_unlock_irqrestore() 时,必须将 unsigned long 类型的变量用作第二个参数,用于保存和恢复 IRQ 状态。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值