驱动大功课:手机侧键控制闪光灯
驱动大作业:手机侧键控制闪光灯
新近入职,除了日常的培训外,最终还有一个大作业,通过编写驱动实现手机侧键控制闪光灯的亮灭。
入职前,对驱动可以说是没有半点认识,通过几个月的学习,才勉强可以说是了解一些驱动工作的流程,实在是有些惭愧。
下面是大作业的代码:
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口信息进行修改,此处不再列举。