iis7.5怎么做网站,刚做网站做多用户还是单用户,关键词整站优化,wordpress 照片博客一. Linux 中断 API 函数
1.中断号 每个中断都有一个中断号#xff0c;通过中断号即可区分不同的中断#xff0c;有的资料也把中断号叫做中 断线。在 Linux 内核中使用一个 int 变量表示中断号。
2.申请中断 - request_irq
函数原型#xff1a;
int request_irq(unsigne…一. Linux 中断 API 函数
1.中断号 每个中断都有一个中断号通过中断号即可区分不同的中断有的资料也把中断号叫做中 断线。在 Linux 内核中使用一个 int 变量表示中断号。
2.申请中断 - request_irq
函数原型
int request_irq(unsigned int irq,irq_handler_t handler,unsigned long flags,const char *name,void *dev)
功能 ①.request_irq 函数用于申请中断此函数可能会导致睡眠因此不能在中断上下文或者其他禁止睡眠的代码段中使用 ②.request_irq 函数会激活使能中断 ③.申请中断成功返回0申请失败返回其他负值如果返回-EBUSY则表示中断已经被申请了。 参数 irq要申请中断的中断号 handler中断处理函数 name中断名字设置后可在 /proc/interrupts 文件中看到对应的中断名字 dev如果将 flags 设置为 IRQF_SHARED 的话dev 用来区分不同的中断一般情况下将 dev 设置为设备结构体dev 会传递给中断处理函数 irq_handler_t 的第二个参数 flags中断标志常用的中断标志有以下 3.释放中断 - free_irq
函数原型
void free_irq(unsigned int irq,void *dev)
功能 释放中断若中断不是共享的那么 free_irq 会删除中断处理函数并且禁止中断若中断是共享的IRQF_SHARED那么共享中断在被释放掉最后的中断处理函数的时候才会被禁止掉。
参数 irq要释放的中断; dev如果中断设置为共享IRQF_SHARED此参数用来区分具体的中断;
4.设置中断处理函数 - irqreturn_t (*irq_handler_t)(int,void*)
函数原型
irqreturn_t (*irq_handler_t)(int,void*)
功能 设置中断处理函数 一般中断处理函数返回值为 return IRQ_RETVAL(IRQ_HANDLED)
参数 int传入中断号 void用于区分共享中断的不同设备也可以指向设备数据结构; 5.中断使能与禁止函数
函数原型
/* 使能中断 */
void enable_irq(unsigned int irq);/* 禁止中断等待当前正在执行的中断函数执行完才返回 */
void disable_irq(unsigned int irq);/* 禁止中断立刻返回不会等待当前中断处理函数执行完毕 */
void disable_irq_nosync(unsigned int irq);
功能 使能和禁止指定的中断 disable_irq 函数要等到当前正在执行的中断处理函数执行完才返回
参数 irq要禁止的中断号 函数原型
/* 使能当前处理器中断系统 */
local_irq_enable();/* 禁止当前处理器中断系统 */
local_disbale();/* 禁止中断并且将中断状态保存在 flags 中 */
local_irq_save(flags);/* 恢复中断将中断恢复到 flags 状态 */
local_irq_restore(flags);二.上半部与下半部
1.上半部与下半部的说明 中断处理函数完成的要尽可能快点执行完毕如果中断处理函数内处理过程比较耗时那么就将这些比较耗时的代码提出来交给下半部去执行将耗时短的代码例如响应中断清除中断标志位可以放在上半部完成。 这样做的目的是为了实现中断处理函数的快进快出那些对时间敏感、执行速度快的操作可以放到中断处理函数中也就是上半部。剩下的所有工作都可以放到下半部去执行比如在上半部将数据拷贝到内存中关于数据的具体处理就可以放到下半部去执行。
建议的参考 ①.如果要处理的内容不希望被其他中断打断那么可以放到上半部。 ②.如果要处理的任务对时间敏感可以放到上半部。 ③.如果要处理的任务与硬件有关可以放到上半部。 ④.除了上述 3 点其他任务优先考虑放到下半部。 2.软中断 可用 软中断 和 tasklet 来实现下半部。 在 Linux 内核中使用结构体 softirq_action 表示软中断
struct softirq_action
{void (*action)(struct softirq_action *);
}; 一共有 10 个软中断如下所示
static struct softirq_action softirq_vec[NR_SOFTIRQS];/* NR_SOFTIRQS 是枚举类型 */
enum
{HI_SOFTIRQ0, /* 高优先级软中断 */TIMER_SOFTIRQ, /* 定时器软中断 */NET_TX_SOFTIRQ, /* 网络数据发送软中断 */NET_RX_SOFTIRQ, /* 网络数据接收软中断 */BLOCK_SOFTIRQ,BLOCK_IOPOLL_SOFTIRQ,TASKLET_SOFTIRQ, /* tasklet 软中断 */SCHED_SOFTIRQ, /* 调度软中断 */HRTIMER_SOFTIRQ, /* 高精度定时器软中断 */RCU_SOFTIRQ, /* RCU 软中断 */NR_SOFTIRQS
};
1.注册软中断处理函数 - open_softirq
函数原型
void open_softirq(int nr,void (*action)(struct softirq_action *);
功能 注册对应的软中断处理函数 软中断必须在编译的时候静态注册Linux 内核使用 softirq_init 函数初始化软中断
参数 nr要开启的软中断在 HI_SOFTIRQ ~ RCU_SOFTIRQ 里面选一个 action软中断对应的处理函数 3.tasklet
1.tasklet 结构体 Linux 内核使用 tasklet_struct 结构体来表示 tasklet
struct tasklet_struct
{struct tasklet_struct *next; /* 下一个 tasklet */unsigned long state; /* tasklet 状态 */atomic_t count; /* 计数器记录对 tasklet 的引用数 */void (*func)(unsigned long); /* tasklet 执行的函数相当于中断处理函数 */unsigned long data; /* 函数 func 的参数 */
};
2.初始化 tasklet - tasklet_init
函数原型
void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long),unsigned long data);
功能 初始化tasklet
参数 t要初始化的tasklet functasklet 的处理函数 data要传递给 func 函数的参数 也可使用宏 DECLARE_TASKLET 来一次性完成 tasklet 的定义和初始化
/*
* param - name :要定义的tasklet名字,即tasklet_struct 变量
* param func :tasklet的处理函数
* param - data :传递给 func 函数的参数
*/
DECLARE_TASKLET(name,func,data); 3.使 tasklet 在合适的时间运行 - tasklet_schedule
函数原型
void tasklet_schedule(struct tasklet_struct *t);
功能 在上半部也就是中断处理函数中调用 tasklet_schedule 函数就能使 tasklet 在合适的时间运行
参数 t要调度的 tasklet 也就是 DECLARE_TAKLET 宏里面的 name
4.tasklet 的使用示例
/* 定义 taselet */
struct tasklet_struct testtasklet;/* tasklet 处理函数 */
void testtasklet_func(unsigned long data)
{/* tasklet 具体处理内容 */
}/* 中断处理函数 */
irqreturn_t test_handler(int irq, void *dev_id)
{....../* 调度 tasklet */tasklet_schedule(testtasklet);......
}/* 驱动入口函数 */
static int __init xxxx_init(void)
{....../* 初始化 tasklet */tasklet_init(testtasklet, testtasklet_func, data);/* 注册中断处理函数 */request_irq(xxx_irq, test_handler, 0, xxx, xxx_dev);......
} 4.工作队列 工作队列是另外一种下半部执行方式工作队列在进程上下文执行工作队列将要推后的 工作交给一个内核线程去执行因为工作队列工作在进程上下文因此工作队列允许睡眠或重 新调度。因此如果你要推后的工作可以睡眠那么就可以选择工作队列否则的话就只能选择软 中断或 tasklet。
1.工作结构体
struct work_struct
{atomic_long_t data;struct list_head entry;work_func_t func; /* 工作队列处理函数 */
};
2.工作队列结构体
struct workqueue_struct
{struct list_head pwqs;struct list_head list;struct mutex mutex;int work_color;int flush_color;atomic_t nr_pwqs_to_flush;struct wq_flusher *first_flusher;struct list_head flusher_queue;struct list_head flusher_overflow;struct list_head maydays;struct worker *rescuer;int nr_drainers;int saved_max_active;struct workqueue_attrs *unbound_attrs;struct pool_workqueue *dfl_pwq;char name[WQ_NAME_LEN];struct rcu_head rcu;unsigned int flags ____cacheline_aligned;struct pool_workqueue __percpu *cpu_pwqs;struct pool_workqueue __rcu *numa_pwq_tbl[];
};
3.工作者线程结构体
struct worker
{union {struct list_head entry;struct hlist_node hentry;};struct work_struct *current_work;work_func_t current_func;struct pool_workqueue *current_pwq;bool desc_valid;struct list_head scheduled;struct task_struct *task;struct worker_pool *pool;struct list_head node;unsigned long last_active;unsigned int flags;int id;char desc[WORKER_DESC_LEN];struct workqueue_struct *rescue_wq;
};
4.初始化工作 - INIT_WORK / DECLARE_WORK
宏原型
/*
* param - _work:要初始化的工作传入 work_struct 结构体
* param _func :工作对应的处理函数
*/
#define INIT_WORK(_work,_func)/*
* param - n :定义的工作work_struct
* param - f :工作对应的处理函数
*/
#define DECLARE_WORK(n,f) 5.工作调度函数 - schedule_work
函数原型
bool schedule_work(struct work_struct *work)
功能 开启工作的调度 成功时返回0失败时返回其他值
参数 work:要调度的工作 6.工作队列的使用示例
/* 定义工作(work) */
struct work_struct testwork;/* work 处理函数 */
void testwork_func_t(struct work_struct *work);
{/* work 具体处理内容 */
}/* 中断处理函数 */
irqreturn_t test_handler(int irq, void *dev_id)
{....../* 调度 work */schedule_work(testwork);......
}/* 驱动入口函数 */
static int __init xxxx_init(void)
{....../* 初始化 work */INIT_WORK(testwork, testwork_func_t);/* 注册中断处理函数 */request_irq(xxx_irq, test_handler, 0, xxx, xxx_dev);......
} 三.获取中断号 编写驱动的时候需要用到中断号中断信息已经写到了设备树里面我们可以通过 irq_of_parse_and_map 函数从 interrupts 属性中提取到对应的设备号。
1. irq_of_parse_and_map
函数原型
unsigned int irq_of_parse_and_map(struct device_node *dev,int index)
功能 获取中断号 返回值为中断号
参数 dev 设备结点 index 索引号interrupts 属性可能包含多条中断信息通过 index 指定要获取的信息 2. gpio_to_irq 若使用 GPIO 的话可以使用 gpio_to_irq 函数来获取 gpio 对应中断号
函数原型
int gpio_to_irq(unsigned int gpio)
功能 获取 gpio 对应的中断号 返回 GPIO 对应的中断号
参数 gpio要获取的 gpio 的编号 四.GPIO按键中断实验代码编写 1.设备树
1.流程图 2.设备树代码 2.驱动代码
1.流程图 2.代码
#include linux/types.h
#include linux/kernel.h
#include linux/delay.h
#include linux/ide.h
#include linux/init.h
#include linux/module.h
#include linux/errno.h
#include linux/gpio.h
#include linux/cdev.h
#include linux/device.h
#include linux/of.h
#include linux/of_address.h
#include linux/of_gpio.h
#include linux/semaphore.h
#include linux/timer.h
#include linux/of_irq.h
#include linux/irq.h
#include asm/mach/map.h
#include asm/uaccess.h
#include asm/io.h#define IMX6UIRQ_CNT 1 /* 设备号个数 */
#define IMX6UIRQ_NAME imx6uirq /* 设备名字 */
#define KEY0VALUE 0X01 /* KEY0按键值 */
#define INVAKEY 0XFF /* 无效的按键值 */
#define KEY_NUM 1 /* 按键数量 *//* 中断 IO 描述结构体 */
struct irq_keydesc
{int gpio; /* gpio */int irqnum; /* 中断号 */unsigned char value; /* 按键对应的键值 */char name[10]; /* 名字 */irqreturn_t (*handler)(int,void *); /* 指向中断服务函数的函数指针 */
};/* imx6uirq 设备结构体 */
struct imx6uirq_dev
{dev_t devid; /* 设备号 */struct cdev cdev; /* cdev */struct class *class; /* 类 */struct device *device; /* 设备 */int major; /* 主设备号 */int minor; /* 次设备号 */struct device_node *nd; /* 设备结点 */atomic_t keyvalue; /* 有效的按键值 */atomic_t releasekey; /* 标记是否完成一次完整的按键动作 */struct timer_list timer; /* 定义一个定时器 */struct irq_keydesc irqkeydesc[KEY_NUM]; /* 按键中断信息描述数组 */unsigned char curkeynum; /* 当前的按键号 */
};/* irq 设备 */
struct imx6uirq_dev imx6uirq;/*** description: KEY0 按键中断服务函数开启定时器延时 10 ms定时器用于按键消抖* param - irq : 中断号* param - dev_id : 设备结构* return : 中断执行结果*/
static irqreturn_t key0_handler(int irq,void *dev_id)
{struct imx6uirq_dev *dev (struct imx6uirq_dev *)dev_id;/* 表示按键中断触发双边沿触发 因为按键平时为上拉状态 */dev-curkeynum 0;dev-timer.data (volatile long)dev_id;/* 2.让定时器回调函数 10 ms 后触发 */mod_timer(dev-timer,jiffies msecs_to_jiffies(10)); return IRQ_RETVAL(IRQ_HANDLED);
}/*** description: 定时器服务函数用于按键消抖定时器到了以后再次读取按键值* 如果按键还是处于按下的状态就表示按键动作有效* param - arg : 设备结构体变量* return : 无*/
void timer_function(unsigned long arg)
{unsigned char value;unsigned char num;struct irq_keydesc *keydesc;struct imx6uirq_dev *dev (struct imx6uirq_dev *)arg;/* 获取上一刻的按键状态 */num dev-curkeynum;keydesc dev-irqkeydesc[num];/* 读取当前 gpio 的状态 */value gpio_get_value(keydesc-gpio);/* 若此刻 gpio 的状态为低电平 则消抖后确认按键确实是按下了 因为这个定时器服务函数是在按键中断之后触发的 */if(0 value){atomic_set(dev-keyvalue,keydesc-value);}/* 若此刻 gpio 的状态为高电平 则消抖后确认按键确实是松开了 因为这个定时器服务函数是在按键中断之后触发的 */else{atomic_set(dev-keyvalue, 0x80 | keydesc-value); //最高为置 1 atomic_set(dev-releasekey, 1); //标记松开按键}
}/*** description: 按键 IO 初始化* param : 无* return : 成功返回0返回其他则为失败*/
static int keyio_init(void)
{unsigned char i 0;int ret 0;/* 1.获取设备结点 */imx6uirq.nd of_find_node_by_path(/key);if(NULL imx6uirq.nd){printk(key node not find!\r\n);return -EINVAL;}/* 2.获取 KEY 的 GPIO 编号 */for(i 0;i KEY_NUM;i){imx6uirq.irqkeydesc[i].gpio of_get_named_gpio(imx6uirq.nd,key-gpio,i);/* 若获取 gpio 编号失败 */if(imx6uirq.irqkeydesc[i].gpio 0){printk(can not get key%d\r\n,i);}}for(i 0;i KEY_NUM;i){memset(imx6uirq.irqkeydesc[i].name,0,sizeof(imx6uirq.irqkeydesc[i].name));sprintf(imx6uirq.irqkeydesc[i].name,KEY%d,i);/* 3.申请 GPIO */gpio_request(imx6uirq.irqkeydesc[i].gpio,imx6uirq.irqkeydesc[i].name);/* 4.设置 IO 为输入 */gpio_direction_input(imx6uirq.irqkeydesc[i].gpio);/* 5.获取 GPIO 的中断号 */imx6uirq.irqkeydesc[i].irqnum irq_of_parse_and_map(imx6uirq.nd,i);#if 0/* 若使用 GPIO 的话可以使用 gpio_to_irq 函数来获取 gpio 对应中断号 */imx6uirq.irqkeydesc[i].irqnum gpio_to_irq(imx6uirq.irqkeydesc[i].gpio);
#endifprintk(key : %d ; gpio %d , irqnum %d\r\n,i,imx6uirq.irqkeydesc[i].gpio,imx6uirq.irqkeydesc[i].irqnum);}/* 6.申请中断 */imx6uirq.irqkeydesc[0].handler key0_handler;imx6uirq.irqkeydesc[0].value KEY0VALUE;for(i 0;i KEY_NUM;i){ret request_irq(imx6uirq.irqkeydesc[i].irqnum,imx6uirq.irqkeydesc[i].handler,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,imx6uirq.irqkeydesc[i].name,imx6uirq);}/* 7.创建定时器 */init_timer(imx6uirq.timer);imx6uirq.timer.function timer_function;return 0;
}/*** description: 打开设备* param - inode : 传递给驱动的 inode* param - filp : 设备文件* return : 0 为成功 其他为失败*/
static int imx6uirq_open(struct inode *inode,struct file *filp)
{/* 设置私有数据 */filp-private_data imx6uirq;return 0;
}/*** description: 从设备读取数据* param - filp : 文件描述符* param - buf : 返回给用户空间的数据缓冲区* param - cnt : 要读取的字节数* param - offt : 相对于文件首地址的偏移量* return : 成功读取的字节数如果为负值则表示失败*/
static ssize_t imx6uirq_read(struct file *filp,char __user *buf,size_t cnt,loff_t *offt)
{int ret 0;unsigned char keyvalue 0;unsigned char releasekey 0;struct imx6uirq_dev *dev (struct imx6uirq_dev *)filp-private_data;/* 读取消抖后的键值 */keyvalue atomic_read(dev-keyvalue);releasekey atomic_read(dev-releasekey);/* 如果有按键按下 */if(releasekey){if(keyvalue 0x80){keyvalue ~0x80; //将 keyvalue 最高为清零ret copy_to_user(buf,keyvalue,sizeof(keyvalue));}else{goto data_error;}/* 清除按下标志 */atomic_set(dev-releasekey,0); }else{ goto data_error;}return 0;data_error:return -EINVAL;
}/* 设备操作函数 */
static struct file_operations imx6uirq_fops
{.owner THIS_MODULE,.open imx6uirq_open,.read imx6uirq_read,
};/*** description: 驱动入口函数* param : 无* return : 无*/
static int __init imx6uirq_init(void)
{/* 1.创建设备号 */if(imx6uirq.major) //若定义了设备号{imx6uirq.devid MKDEV(imx6uirq.major,0);register_chrdev_region(imx6uirq.devid,IMX6UIRQ_CNT,IMX6UIRQ_NAME);}else //若没有定义设备号{alloc_chrdev_region(imx6uirq.devid,0,IMX6UIRQ_CNT,IMX6UIRQ_NAME);imx6uirq.major MAJOR(imx6uirq.devid);imx6uirq.minor MINOR(imx6uirq.devid);}/* 2.初始化cdev */cdev_init(imx6uirq.cdev,imx6uirq_fops);/* 3,添加一个cdev */cdev_add(imx6uirq.cdev,imx6uirq.devid,IMX6UIRQ_CNT);/* 4.创建类 */imx6uirq.class class_create(THIS_MODULE,IMX6UIRQ_NAME);if(IS_ERR(imx6uirq.class)){return PTR_ERR(imx6uirq.class);}/* 5.创建设备 */imx6uirq.device device_create(imx6uirq.class,NULL,imx6uirq.devid,NULL,IMX6UIRQ_NAME);if(IS_ERR(imx6uirq.device)){return PTR_ERR(imx6uirq.device);}/* 6.初始化按键 */atomic_set(imx6uirq.keyvalue,INVAKEY);atomic_set(imx6uirq.releasekey,0);keyio_init();return 0;
}/*** description: 驱动出口函数* param : 无* return : 无*/
static void __exit imx6uirq_exit(void)
{unsigned int i 0;/* 1.删除定时器 */del_timer_sync(imx6uirq.timer);/* 2.释放 中断 与 GPIO */for(i 0;i KEY_NUM;i){free_irq(imx6uirq.irqkeydesc[i].irqnum,imx6uirq);gpio_free(imx6uirq.irqkeydesc[i].gpio);}/* 3. 注销字符设备驱动 */cdev_del(imx6uirq.cdev);unregister_chrdev_region(imx6uirq.devid,IMX6UIRQ_CNT);device_destroy(imx6uirq.class,imx6uirq.devid);class_destroy(imx6uirq.class);
}module_init(imx6uirq_init);
module_exit(imx6uirq_exit);
MODULE_LICENSE(GPL);
MODULE_AUTHOR(kaneki); Makefile:
KERNELDIR : /home/linux/IMX6ULL/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek
CURRENT_PATH :$(shell pwd)
obj-m : imx6uirq.o
build: kernel_modules
kernel_modules:$(MAKE) -C $(KERNELDIR) M$(CURRENT_PATH) modules
clean:$(MAKE) -C $(KERNELDIR) M$(CURRENT_PATH) clean 可在 /proc/interrupts 文件下查看对应的中断有没有被注册