之前处理中断在中断服务程序里面,就是我们自己注册了一个中断服务程序,一产生中断就自动调动这个程序。
但是中断的处理越快越好
同步调用:就是等调用的函数执行完毕然后再返回原来的位置。
异步调用:就是不等调用的函数执行完毕,回到原来的位置继续向下执行(线程执行)
中断源调用中断程序后,中断程序再去调度中断底半部,不等调度的程序执行完毕,继续执行原来的程序。这是异步调度。
中断的底半部实现的方式有:
1.软中断
2.tasklet(基于软中断实现)
3.workqueue
1.软中断
2.tasklet
属于中断,处理紧急事件,不能做耗时操作
3.workqueue
不属于中断,处理不紧急事件,可以做耗时的操作
中断上下文:中断相关操作,中断服务程序、软中断、tasklet
进程上下文:进程相关操作,系统调用(open,read,write)、workqueue
上下文:一般指的是代码中保护现场和恢复现场的这个位置的东西。
那么来写一下tasklet代码:
初始化:
200是tasklet_func的参数。
调度:
中断调用底半部tasklet,然后就返回,这时候底半部自己运行。
这样就好了
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <asm/string.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#define DEV_NAME "adc1"
#define ADCCON 0x58000000
#define ADCDAT0 0x5800000C
#define CLKCON 0x4C00000C
static volatile unsigned long * adccon;
static volatile unsigned long * adcdat0;
static volatile unsigned long * clkcon;
static wait_queue_head_t wq;
static int condition = 0;
static struct tasklet_struct tsk;
#define MAGIC_NUM 'x'
#define SET_CH 0
#define GET_CH 1
#define SET_CHANNEL _IOW(MAGIC_NUM, SET_CH, unsigned int)
#define GET_CHANNEL _IOR(MAGIC_NUM, GET_CH, unsigned int)
void adc_init(void)
{
*adccon = (1 << 14) | (49 << 6);
}
int adc_set_channel(unsigned char ch)
{
if(ch > 7)
return -EINVAL;
*adccon &= ~(0x7 << 3);
*adccon |= (ch << 3);
return 0;
}
void adc_start(void)
{
*adccon |= (1 << 0);
}
unsigned short adc_read(void)
{
unsigned short data = 0; //读取数据,启动转换
data = *adcdat0 & 0x3ff;
return data;
}
static void tasklet_func(unsigned long arg)
{
condition = 1;
wake_up_interruptible(&wq);
printk("adc tasklet_func arg = %ld\n", arg);
}
static irqreturn_t irq_handler(int irq_num, void * dev_num)
{
tasklet_schedule(&tsk);
printk("adc irq_handler irq_num = %d dev_num = %d\n", irq_num, *(int *)dev_num);
return IRQ_HANDLED;
}
static int open(struct inode * node, struct file * file)
{
adc_init();
printk("adc open...\n");
return 0;
}
static ssize_t read(struct file * file, char __user * buf, size_t len, loff_t * offset)
{
//copy_to_user();
unsigned short value = 0;
adc_start();
condition = 0;
wait_event_interruptible(wq, condition);
value = adc_read();
copy_to_user(buf, &value, sizeof(value));
printk("adc !!! read...\n");
return sizeof(value);
}
static ssize_t write(struct file * file, const char __user * buf, size_t len, loff_t * offset)
{
return 0;
}
static long ioctl(struct file * file, unsigned int cmd, unsigned long arg)
{
int ret = 0;
unsigned int ch = 0;
printk("cmd = %d arg = %ld\n", cmd, arg);
switch(cmd)
{
case SET_CHANNEL:
copy_from_user(&ch, (unsigned int *)arg, sizeof(ch));
printk("adc ch = %d\n", ch);
ret = adc_set_channel(ch);
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int close(struct inode * node, struct file * file)
{
printk("adc close...\n");
return 0;
}
static struct file_operations fops =
{
.owner = THIS_MODULE,
.open = open,
.read = read,
.write = write,
.unlocked_ioctl = ioctl,
.release = close
};
static struct miscdevice misc =
{
// major = 10
.minor = MISC_DYNAMIC_MINOR,
.name = DEV_NAME,
.fops = &fops
};
static int __init adc1_init(void)
{
int ret = 0;
ret = misc_register(&misc);
if(ret < 0)
goto err_misc_register;
ret = request_irq(IRQ_ADC, irq_handler, IRQF_DISABLED, "irq_adc", &misc.minor);
if(ret < 0)
goto err_request_irq;
adccon = ioremap(ADCCON, sizeof(*adccon));
adcdat0 = ioremap(ADCDAT0, sizeof(*adcdat0));
clkcon = ioremap(CLKCON, sizeof(*clkcon));
*clkcon |= (1 << 15);
printk("CLKCON = %lx\n", *clkcon);
init_waitqueue_head(&wq);
tasklet_init(&tsk, tasklet_func, 200);
printk("adc1_init !!! ##########################\n");
return ret;
err_misc_register:
printk("adc misc_register failed ###################\n");
misc_deregister(&misc);
err_request_irq:
printk("adc request_irq faiadc ##########################\n");
disable_irq(IRQ_ADC);
free_irq(IRQ_ADC, &misc.minor);
return ret;
}
static void __exit adc_exit(void)
{
iounmap(adccon);
iounmap(adcdat0);
iounmap(clkcon);
disable_irq(IRQ_ADC);
free_irq(IRQ_ADC, &misc.minor);
misc_deregister(&misc);
printk("adc_exit ##########################\n");
}
module_init(adc1_init);
module_exit(adc_exit);
MODULE_LICENSE("GPL");
运行结果如下:
从打印的这里也可以看出来中断调用底半部中断之后返回,然后才执行底半部中断,没有等底半部中断结束然后返回。是异步调用。
taklet不能耗时,延时俩秒会导致系统奔溃。不能耗时。也不能被打断,就是不能被同类的,或者workqueue打断。可以被硬中断给打断。
workqueue的代码:
调度:
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <asm/string.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#define DEV_NAME "adc1"
#define ADCCON 0x58000000
#define ADCDAT0 0x5800000C
#define CLKCON 0x4C00000C
static volatile unsigned long * adccon;
static volatile unsigned long * adcdat0;
static volatile unsigned long * clkcon;
static wait_queue_head_t wq;
static int condition = 0;
static struct work_struct work;
#define MAGIC_NUM 'x'
#define SET_CH 0
#define GET_CH 1
#define SET_CHANNEL _IOW(MAGIC_NUM, SET_CH, unsigned int)
#define GET_CHANNEL _IOR(MAGIC_NUM, GET_CH, unsigned int)
void adc_init(void)
{
*adccon = (1 << 14) | (49 << 6);
}
int adc_set_channel(unsigned char ch)
{
if(ch > 7)
return -EINVAL;
*adccon &= ~(0x7 << 3);
*adccon |= (ch << 3);
return 0;
}
void adc_start(void)
{
*adccon |= (1 << 0);
}
unsigned short adc_read(void)
{
unsigned short data = 0; //读取数据,启动转换
data = *adcdat0 & 0x3ff;
return data;
}
static void workqueue_func(struct work_struct *work)
{
ssleep(2);
condition = 1;
wake_up_interruptible(&wq);
printk("adc workqueue_func \n");
}
static irqreturn_t irq_handler(int irq_num, void * dev_num)
{
schedule_work(&work);
printk("adc irq_handler irq_num = %d dev_num = %d\n", irq_num, *(int *)dev_num);
return IRQ_HANDLED;
}
static int open(struct inode * node, struct file * file)
{
adc_init();
printk("adc open...\n");
return 0;
}
static ssize_t read(struct file * file, char __user * buf, size_t len, loff_t * offset)
{
//copy_to_user();
unsigned short value = 0;
adc_start();
condition = 0;
wait_event_interruptible(wq, condition);
value = adc_read();
copy_to_user(buf, &value, sizeof(value));
printk("adc !!! read...\n");
return sizeof(value);
}
static ssize_t write(struct file * file, const char __user * buf, size_t len, loff_t * offset)
{
return 0;
}
static long ioctl(struct file * file, unsigned int cmd, unsigned long arg)
{
int ret = 0;
unsigned int ch = 0;
printk("cmd = %d arg = %ld\n", cmd, arg);
switch(cmd)
{
case SET_CHANNEL:
copy_from_user(&ch, (unsigned int *)arg, sizeof(ch));
printk("adc ch = %d\n", ch);
ret = adc_set_channel(ch);
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int close(struct inode * node, struct file * file)
{
printk("adc close...\n");
return 0;
}
static struct file_operations fops =
{
.owner = THIS_MODULE,
.open = open,
.read = read,
.write = write,
.unlocked_ioctl = ioctl,
.release = close
};
static struct miscdevice misc =
{
// major = 10
.minor = MISC_DYNAMIC_MINOR,
.name = DEV_NAME,
.fops = &fops
};
static int __init adc1_init(void)
{
int ret = 0;
ret = misc_register(&misc);
if(ret < 0)
goto err_misc_register;
ret = request_irq(IRQ_ADC, irq_handler, IRQF_DISABLED, "irq_adc", &misc.minor);
if(ret < 0)
goto err_request_irq;
adccon = ioremap(ADCCON, sizeof(*adccon));
adcdat0 = ioremap(ADCDAT0, sizeof(*adcdat0));
clkcon = ioremap(CLKCON, sizeof(*clkcon));
*clkcon |= (1 << 15);
printk("CLKCON = %lx\n", *clkcon);
init_waitqueue_head(&wq);
INIT_WORK(&work, workqueue_func);
printk("adc1_init !!! ##########################\n");
return ret;
err_misc_register:
printk("adc misc_register failed ###################\n");
misc_deregister(&misc);
err_request_irq:
printk("adc request_irq faiadc ##########################\n");
disable_irq(IRQ_ADC);
free_irq(IRQ_ADC, &misc.minor);
return ret;
}
static void __exit adc_exit(void)
{
iounmap(adccon);
iounmap(adcdat0);
iounmap(clkcon);
disable_irq(IRQ_ADC);
free_irq(IRQ_ADC, &misc.minor);
misc_deregister(&misc);
printk("adc_exit ##########################\n");
}
module_init(adc1_init);
module_exit(adc_exit);
MODULE_LICENSE("GPL");
这里面加入延时俩秒不受影响,因为这是进程。