驱动大功课:手机侧键控制闪光灯

驱动大作业:手机侧键控制闪光灯

新近入职,除了日常的培训外,最终还有一个大作业,通过编写驱动实现手机侧键控制闪光灯的亮灭。

入职前,对驱动可以说是没有半点认识,通过几个月的学习,才勉强可以说是了解一些驱动工作的流程,实在是有些惭愧。

下面是大作业的代码:

  1 #include <linux/init.h>
  2 #include <linux/module.h>
  3 #include <linux/of.h>
  4 #include <linux/of_address.h>
  5 #include <linux/of_irq.h>
  6 #include <linux/clk.h>
  7 #include <linux/device.h>
  8 #include <linux/miscdevice.h>
  9 #include <linux/platform_device.h>
 10 #include <linux/input.h>
 11 #include <linux/gpio.h>
 12 #include <linux/interrupt.h>
 13 #include <linux/errno.h>
 14 #include <asm/io.h>
 15 #include <linux/cdev.h>
 16 #include <asm/uaccess.h>//各种驱动可能会用到的头文件,基本都包含在内。
 17 
 18 MODULE_LICENSE("Dual BSD/GPL");
 19 
 20 #define KPD_FDEVICE "kpd_flash" //定义设备名
 21 #define KPD_SAY        "kpd: " //定义一些打印函数,事实证明不会用(参考了mtk_kpd.c)
 22 #define kpd_print(fmt, arg...)    pr_err(KPD_SAY fmt, ##arg)
 23 #define kpd_info(fmt, arg...)    pr_warn(KPD_SAY fmt, ##arg)
 24 
 25 static unsigned int kpdf_irqnr; //定义中断号
 26 
 27 static int kpd_fpdrv_probe(struct platform_device *fpdev); //声明设备探测函数
 28 static int kpd_fpdrv_remove(struct platform_device *fpdev); //声明设备移除函数
 29 
 30 static int flash_flag = 0; //定义一个全局变量,作为闪光灯亮灭的flag
 31 
 32 static void kpdf_flash_handler(unsigned long data); //声明中断处理句柄
 33 static DECLARE_TASKLET(kpdf_flash_tasklet, kpdf_flash_handler, 0); //采用底半部延迟中断的方法(具体的还有些没懂)
 34 
 35 struct pinctrl *pinctrl_fkpd; //设备的GPIO口信息
 36 struct pinctrl_state *pins_def; //设备的GPIO口状态
 37 struct pinctrl_state *pins_kpd_kpdf_high, *pins_kpd_kpdf_low,
 38                      *pins_kpd_flashtorch_high, *pins_kpd_flashtorch_low,
 39                      *pins_kpd_flash_eint_as_int;
 40 
 41 struct of_device_id kpdf_of_match[] = {//GPIO口查找表(Ali37平台,对应为ali6737(t/m)_35g_m0.dts)
 42     { .compatible = "mediatek,kpd_falsh", },
 43     {},
 44 };
 45 
 46 static int kpdf_get_gpio_info(struct platform_device *fpdev)//通过GPIO查找表,查询ali6737(t/m)_35g_m0.dts对应的GPIO信息和状态
 47 {
 48     int ret;
 49     pinctrl_fkpd = devm_pinctrl_get(&fpdev->dev);//获取对应设备的GPIO口信息
 50     if (IS_ERR(pinctrl_fkpd)) {
 51         ret = PTR_ERR(pinctrl_fkpd);
 52         dev_err(&fpdev->dev, "fwq Cannot find Keyboard pinctrl_fkpd!\n");
 53         return ret;
 54     }
 55     pins_def = pinctrl_lookup_state(pinctrl_fkpd, "default");//获取默认的GPIO状态
 56     if (IS_ERR(pins_def)) {
 57         ret = PTR_ERR(pins_def);
 58         dev_err(&fpdev->dev, "fwq Cannot find Keyboard pinctrl default %d!\n", ret);
 59     }
 60     pins_kpd_flash_eint_as_int = pinctrl_lookup_state(pinctrl_fkpd, "state_flash_eint_as_int");//获取按键的中断口信息
 61     if (IS_ERR(pins_kpd_flash_eint_as_int)) {
 62         ret = PTR_ERR(pins_kpd_flash_eint_as_int);
 63         dev_err(&fpdev->dev, "fwq Cannot find Keyboard pinctrl state_flash_eint_as_int!\n");
 64         return ret;
 65     }
 66     pins_kpd_kpdf_high = pinctrl_lookup_state(pinctrl_fkpd, "state_flashl_high");//获取闪光灯的两个GPIO口信息
 67     if (IS_ERR(pins_kpd_kpdf_high)) {
 68         ret = PTR_ERR(pins_kpd_kpdf_high);
 69         dev_err(&fpdev->dev, "fwq Cannot find Keyboard pinctrl state_flashl_high!\n");
 70         return ret;
 71     }
 72     pins_kpd_kpdf_low = pinctrl_lookup_state(pinctrl_fkpd, "state_flashl_low");
 73     if (IS_ERR(pins_kpd_kpdf_low)) {
 74         ret = PTR_ERR(pins_kpd_kpdf_low);
 75         dev_err(&fpdev->dev, "fwq Cannot find Keyboard pinctrl state_flashl_low!\n");
 76         return ret;
 77     }
 78     pins_kpd_flashtorch_high = pinctrl_lookup_state(pinctrl_fkpd, "state_flasht_high");
 79     if (IS_ERR(pins_kpd_flashtorch_high)) {
 80         ret = PTR_ERR(pins_kpd_flashtorch_high);
 81         dev_err(&fpdev->dev, "fwq Cannot find Keyboard pinctrl state_flasht_high!\n");
 82         return ret;
 83     }
 84     pins_kpd_flashtorch_low = pinctrl_lookup_state(pinctrl_fkpd, "state_flasht_low");
 85     if (IS_ERR(pins_kpd_flashtorch_low)) {
 86         ret = PTR_ERR(pins_kpd_flashtorch_low);
 87         dev_err(&fpdev->dev, "fwq Cannot find Keyboard pinctrl state_flasht_low!\n");
 88         return ret;
 89     }
 90     return 0;
 91 }
 92 
 93 static struct platform_driver kpd_fpdrv =//定义平台驱动结构
 94 {
 95     .probe  = kpd_fpdrv_probe,
 96     .remove = kpd_fpdrv_remove,
 97     .driver = {
 98         .name           = KPD_FDEVICE,
 99         .owner          = THIS_MODULE,
100         .of_match_table = kpdf_of_match,
101     },
102 };
103 
104 static struct platform_device kpd_fpdev =//定义平台设备结构
105 {
106     .name = KPD_FDEVICE,
107     .id = -1,
108     .dev={
109 
110     },
111 };
112 
113 
114 static void kpdf_flash_handler(unsigned long data)//中断处理函数
115 {
116     if(!flash_flag)  
117     {  
118         pinctrl_select_state(pinctrl_fkpd, pins_kpd_flashtorch_high);//设置闪光灯的gpio口的状态为high(亮)
119         printk("flash is on\n");
120     }  
121     else
122     {  
123         pinctrl_select_state(pinctrl_fkpd, pins_kpd_flashtorch_low);//设置闪光灯的gpio口的状态为low(灭)
124         printk("flash is off\n");
125     }
126     flash_flag = ~flash_flag;//按键中断检测值
127     printk("flash_flag's value = %d", flash_flag);
128     enable_irq(kpdf_irqnr);//中断使能
129 }
130 
131 static irqreturn_t kpdf_irq_handler(int irq, void *dev_id)//中断处理句柄
132 {
133     kpd_info("kpdf_flash request_irq is successed!!!\n");
134     disable_irq_nosync(kpdf_irqnr);//禁止中断,防止其余中断干扰CPU处理中断,产生竞态
135     tasklet_schedule(&kpdf_flash_tasklet);//底半部方法调用中断处理函数
136     return IRQ_HANDLED;
137 }
138 
139 static int kpd_fpdrv_probe(struct platform_device *fpdev)//设备探测函数
140 {
141     int r;
142     struct device_node *node = NULL;//定义中断节点
143 
144     printk(KERN_ERR "The Keypad kpdf probe is start!!!\n");
145     //创建类和设备
146 
147     kpdf_get_gpio_info(fpdev);//获取设备的GPIO信息
148 
149     //注册中断和中断节点
150     //匹配中断节点
151     node = of_find_matching_node(node, kpdf_of_match);//获取中断节点信息
152     if(node){
153         kpdf_irqnr = irq_of_parse_and_map(node, 0);//获取中断号
154         r = request_irq(kpdf_irqnr, (irq_handler_t)kpdf_irq_handler, IRQF_TRIGGER_NONE, "KPDF_FLASH-eint", NULL);//注册中断设备及中断句柄,中断号为kpdf_irqnr,触发方式为IRQF_TRIGGER_NONE(无触发中断),中断区别参数为NULL
155         //printk(KERN_ERR "The keypad irq handle is started.\n");
156         if (r) {//中断注册失败
157             kpd_info("register IRQ failed (%d)\n", r);
158             return r;
159         }
160     }
161     
162     enable_irq(kpdf_irqnr);//中断使能
163 
164     return 0;
165 }
166 
167 static int kpd_fpdrv_remove(struct platform_device *fpdev)//设备移除函数,因为手机无需移除设备,所以直接return 0
168 {
169     return 0;
170 }
171 
172 static int kpd_flash_init(void)//驱动初始化函数
173 {
174     int ret_drv;int ret_dev;
175     ret_dev = platform_device_register(&kpd_fpdev);//注册平台设备,平台设备结构在上文声明
176     if(ret_dev)
177     {
178         printk(KERN_ERR "register deivce failed (%d)\n", ret_dev);
179         return ret_dev;
180     }
181     ret_drv = platform_driver_register(&kpd_fpdrv);//注册平台驱动,平台驱动结构在上文声明,根据平台驱动的结构调用相应函数,并将其注册到内核
182     if(ret_drv)
183     {
184         printk(KERN_ERR "register deivce failed (%d)\n", ret_drv);
185         return ret_drv;
186     }
187     return 0;
188 }
189 
190 static void kpd_flash_exit(void)//驱动移除函数
191 {
192     printk(KERN_ERR "Destroy the driver.\n");
193 
194 }
195 
196 module_init(kpd_flash_init);//初始化驱动程序
197 module_exit(kpd_flash_exit);//移除驱动程序

以上就是按键控制闪光灯亮灭的驱动源程序,涉及到阿里37平台的dts文件修改,可参考其他设备的GPIO口信息进行修改,此处不再列举。