1 #include
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #include
11 #include
12 #include
13 #include
14 #include
15 #include
16 #include
17 #include
18 #include
19 #include
20 #include
21/***************************************************************
22 Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
23文件名 : imx6uirq.c
24作者 : 左忠凯
25版本 : V1.0
26描述 : Linux中断驱动实验
27其他 : 无
28论坛 : www.openedv.com
29日志 : 初版V1.0 2019/7/26 左忠凯创建
30 ***************************************************************/
31 #define IMX6UIRQ_CNT 1 /* 设备号个数 */
32 #define IMX6UIRQ_NAME "imx6uirq" /* 名字 */
33 #define KEY0VALUE 0X01 /* KEY0按键值 */
34 #define INVAKEY 0XFF /* 无效的按键值 */
35 #define KEY_NUM 1 /* 按键数量 */
36
37/* 中断IO描述结构体 */
38struct irq_keydesc {
39int gpio; /* gpio */
40int irqnum; /* 中断号 */
41unsignedchar value; /* 按键对应的键值 */
42char name[10]; /* 名字 */
43 irqreturn_t (*handler)(int,void*); /* 中断服务函数 */
44};
45
46/* imx6uirq设备结构体 */
47struct imx6uirq_dev{
48 dev_t devid; /* 设备号 */
49struct cdev cdev; /* cdev */
50struct class *class; /* 类 */
51struct device *device; /* 设备 */
52int major; /* 主设备号 */
53int minor; /* 次设备号 */
54struct device_node *nd; /* 设备节点 */
55 atomic_t keyvalue; /* 有效的按键键值 */
56 atomic_t releasekey; /* 标记是否完成一次完成的按键*/
57struct timer_list timer; /* 定义一个定时器*/
58struct irq_keydesc irqkeydesc[KEY_NUM]; /* 按键描述数组 */
59unsignedchar curkeynum; /* 当前的按键号 */
60};
61
62struct imx6uirq_dev imx6uirq;/* irq设备 */
63
64/* @description : 中断服务函数,开启定时器,延时10ms,
65 * 定时器用于按键消抖。
66 * @param - irq : 中断号
67 * @param - dev_id : 设备结构。
68 * @return : 中断执行结果
69 */
70static irqreturn_t key0_handler(int irq,void*dev_id)
71{
72struct imx6uirq_dev *dev =(struct imx6uirq_dev *)dev_id;
73
74 dev->curkeynum =0;
75 dev->timer.data =(volatilelong)dev_id;
76 mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10));
77return IRQ_RETVAL(IRQ_HANDLED);
78}
79
80/* @description : 定时器服务函数,用于按键消抖,定时器到了以后
81 * 再次读取按键值,如果按键还是处于按下状态就表示按键有效。
82 * @param – arg : 设备结构变量
83 * @return : 无
84 */
85void timer_function(unsignedlong arg)
86{
87unsignedchar value;
88unsignedchar num;
89struct irq_keydesc *keydesc;
90struct imx6uirq_dev *dev =(struct imx6uirq_dev *)arg;
91
92 num = dev->curkeynum;
93 keydesc =&dev->irqkeydesc[num];
94
95 value = gpio_get_value(keydesc->gpio); /* 读取IO值 */
96if(value ==0){ /* 按下按键 */
97 atomic_set(&dev->keyvalue, keydesc->value);
98}
99else{ /* 按键松开 */
100 atomic_set(&dev->keyvalue,0x80| keydesc->value);
101 atomic_set(&dev->releasekey,1); /* 标记松开按键 */
102}
103}
104
105/*
106 * @description : 按键IO初始化
107 * @param : 无
108 * @return : 无
109 */
110staticint keyio_init(void)
111{
112unsignedchar i =0;
113char name[10];
114int ret =0;
115
116 imx6uirq.nd = of_find_node_by_path("/key");
117if(imx6uirq.nd==NULL){
118 printk("key node not find!\r\n");
119return-EINVAL;
120}
121
122/* 提取GPIO */
123for(i =0; i < KEY_NUM; i++){
124 imx6uirq.irqkeydesc.gpio = of_get_named_gpio(imx6uirq.nd,
"key-gpio", i);
125if(imx6uirq.irqkeydesc.gpio <0){
126 printk("can't get key%d\r\n", i);
127}
128}
129
130/* 初始化key所使用的IO,并且设置成中断模式 */
131for(i =0; i < KEY_NUM; i++){
132 memset(imx6uirq.irqkeydesc.name,0,sizeof(name));
133 sprintf(imx6uirq.irqkeydesc.name,"KEY%d", i);
134 gpio_request(imx6uirq.irqkeydesc.gpio, name);
135 gpio_direction_input(imx6uirq.irqkeydesc.gpio);
136 imx6uirq.irqkeydesc.irqnum = irq_of_parse_and_map(
imx6uirq.nd, i);
137 #if0
138 imx6uirq.irqkeydesc.irqnum = gpio_to_irq(
imx6uirq.irqkeydesc.gpio);
139 #endif
140 printk("key%d:gpio=%d, irqnum=%d\r\n",i,
imx6uirq.irqkeydesc.gpio,
141 imx6uirq.irqkeydesc.irqnum);
142}
143/* 申请中断 */
144 imx6uirq.irqkeydesc[0].handler = key0_handler;
145 imx6uirq.irqkeydesc[0].value = KEY0VALUE;
146
147for(i =0; i < KEY_NUM; i++){
148 ret = request_irq(imx6uirq.irqkeydesc.irqnum,
imx6uirq.irqkeydesc.handler,
IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
imx6uirq.irqkeydesc.name,&imx6uirq);
149if(ret <0){
150 printk("irq %d request failed!\r\n",
imx6uirq.irqkeydesc.irqnum);
151return-EFAULT;
152}
153}
154
155/* 创建定时器 */
156 init_timer(&imx6uirq.timer);
157 imx6uirq.timer.function = timer_function;
158return0;
159}
160
161/*
162 * @description : 打开设备
163 * @param – inode : 传递给驱动的inode
164 * @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
165 * 一般在open的时候将private_data指向设备结构体。
166 * @return : 0 成功;其他失败
167 */
168staticint imx6uirq_open(struct inode *inode,struct file *filp)
169{
170 filp->private_data =&imx6uirq;/* 设置私有数据 */
171return0;
172}
173
174/*
175 * @description : 从设备读取数据
176 * @param – filp : 要打开的设备文件(文件描述符)
177 * @param – buf : 返回给用户空间的数据缓冲区
178 * @param - cnt : 要读取的数据长度
179 * @param – offt : 相对于文件首地址的偏移
180 * @return : 读取的字节数,如果为负值,表示读取失败
181 */
182static ssize_t imx6uirq_read(struct file *filp,char __user *buf,
size_t cnt, loff_t *offt)
183{
184int ret =0;
185unsignedchar keyvalue =0;
186unsignedchar releasekey =0;
187struct imx6uirq_dev *dev =(struct imx6uirq_dev *)
filp->private_data;
188
189 keyvalue = atomic_read(&dev->keyvalue);
190 releasekey = atomic_read(&dev->releasekey);
191
192if(releasekey){ /* 有按键按下 */
193if(keyvalue &0x80){
194 keyvalue &=~0x80;
195 ret = copy_to_user(buf,&keyvalue,sizeof(keyvalue));
196}else{
197goto data_error;
198}
199 atomic_set(&dev->releasekey,0); /* 按下标志清零 */
200}else{
201goto data_error;
202}
203return0;
204
205 data_error:
206return-EINVAL;
207}
208
209/* 设备操作函数 */
210staticstruct file_operations imx6uirq_fops ={
211.owner = THIS_MODULE,
212.open = imx6uirq_open,
213.read = imx6uirq_read,
214};
215
216/*
217 * @description : 驱动入口函数
218 * @param : 无
219 * @return : 无
220 */
221staticint __init imx6uirq_init(void)
222{
223/* 1、构建设备号 */
224if(imx6uirq.major){
225 imx6uirq.devid = MKDEV(imx6uirq.major,0);
226 register_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT,
IMX6UIRQ_NAME);
227}else{
228 alloc_chrdev_region(&imx6uirq.devid,0, IMX6UIRQ_CNT,
IMX6UIRQ_NAME);
229 imx6uirq.major = MAJOR(imx6uirq.devid);
230 imx6uirq.minor = MINOR(imx6uirq.devid);
231}
232
233/* 2、注册字符设备 */
234 cdev_init(&imx6uirq.cdev,&imx6uirq_fops);
235 cdev_add(&imx6uirq.cdev, imx6uirq.devid, IMX6UIRQ_CNT);
236
237/* 3、创建类 */
238 imx6uirq.class = class_create(THIS_MODULE, IMX6UIRQ_NAME);
239if(IS_ERR(imx6uirq.class)){
240return PTR_ERR(imx6uirq.class);
241}
242
243/* 4、创建设备 */
244 imx6uirq.device = device_create(imx6uirq.class,NULL,
imx6uirq.devid,NULL, IMX6UIRQ_NAME);
245if(IS_ERR(imx6uirq.device)){
246return PTR_ERR(imx6uirq.device);
247}
248
249/* 5、始化按键 */
250 atomic_set(&imx6uirq.keyvalue, INVAKEY);
251 atomic_set(&imx6uirq.releasekey,0);
252 keyio_init();
253return0;
254}
255
256/*
257 * @description : 驱动出口函数
258 * @param : 无
259 * @return : 无
260 */
261staticvoid __exit imx6uirq_exit(void)
262{
263unsigned i =0;
264/* 删除定时器 */
265 del_timer_sync(&imx6uirq.timer);
266
267/* 释放中断 */
268for(i =0; i < KEY_NUM; i++){
269 free_irq(imx6uirq.irqkeydesc.irqnum,&imx6uirq);
270}
271 cdev_del(&imx6uirq.cdev);
272 unregister_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT);
273 device_destroy(imx6uirq.class, imx6uirq.devid);
274 class_destroy(imx6uirq.class);
275}
276
277 module_init(imx6uirq_init);
278 module_exit(imx6uirq_exit);
279 MODULE_LICENSE("GPL");
280 MODULE_AUTHOR("zuozhongkai");