修改设备树
在根节点添加以下节点
my_led{
compatible="ss,led";
pinctrl-names = "default";
pinctrl-0 = <&my_pinctrl_leds>;
led-gpios= < &gpio5 3 GPIO_ACTIVE_LOW>;
};
ss_key{
compatible="ss,key";
pinctrl-names = "default";
pinctrl-0 = <&ss_key1_pinctrl &ss_key2_pinctrl>;
key-gpios=<&gpio5 1 GPIO_ACTIVE_HIGH
&gpio4 14 GPIO_ACTIVE_HIGH>;
};
驱动文件keydrv.c
入口函数和praform_driver
static const struct of_device_id ss_key_match[] = {
{ .compatible = "ss,key", },
{}
};
static struct platform_driver ss_key_driver={
.probe=key_probe,
.remove=key_remove,
.driver={
.name="ss,key",
.owner=THIS_MODULE,
.of_match_table=ss_key_match,
}
};
static int __init ss_key_init(void){
return platform_driver_register(&ss_key_driver);
}
static void __exit ss_key_exit(void){
platform_driver_unregister(&ss_key_driver);
}
module_init(ss_key_init);
module_exit(ss_key_exit);
MODULE_LICENSE("GPL");
probe和remove
static int key_probe(struct platform_device *ppdev){
int i;
int error;
count=gpiod_count(&(ppdev->dev),"key");//获取gpio引脚个数
for(i=0;i<count;++i){
//依次获取引脚描述符,并设置为输入模式
key_gpio_desc[i]= gpiod_get_index(&(ppdev->dev),"key",i,GPIOD_IN);
//获取中断号
irqs[i]=gpiod_to_irq(key_gpio_desc[i]);
//申请中断号
error = request_irq(irqs[i], ss_key_irq,
IRQF_TRIGGER_FALLING,
"ss_key", key_gpio_desc[i]);
}
return 0;
}
static int key_remove(struct platform_device *ppdev){
int i;
for(i=0;i<count;++i){
//释放中断
free_irq(irqs[i],key_gpio_desc[i]);
gpiod_put(key_gpio_desc[i]); //释放描述符
}
return 0;
}
中断函数
static irqreturn_t ss_key_irq(int irq, void *dev_id){
struct gpio_desc *p_desc=dev_id;
//获取逻辑电平
int value=gpiod_get_value(p_desc);
printk("key %d is %d\n",irq,value);
switch (irq)
{
case 189:
//该函数在leddrv.c中定义
turn_led(1);
break;
case 208:
turn_led(0);
break;
default:
break;
}
return IRQ_HANDLED;
}
驱动文件leddrv.c
添加接口供keydrv.c调用
void turn_led(int state){
//设置引脚为输出,并初始化为state逻辑电平
gpiod_direction_output(led_gpio_desc,state);
}
EXPORT_SYMBOL_GPL(turn_led);
request_irq
static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
{
return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}
irq是终端号,handler是中断函数,flags是出发方式,有低电平触发,高电平触发,下降沿触发,上升沿触发,name是设置中断的名字,dev是传给中断函数的参数。
#define IRQF_TRIGGER_NONE 0x00000000
#define IRQF_TRIGGER_RISING 0x00000001
#define IRQF_TRIGGER_FALLING 0x00000002
#define IRQF_TRIGGER_HIGH 0x00000004
#define IRQF_TRIGGER_LOW 0x00000008
#define IRQF_TRIGGER_MASK (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
#define IRQF_TRIGGER_PROBE 0x00000010
free_irq
void free_irq(unsigned int irq, void *dev_id)
irq是终端号,dev_id是传给中断函数的参数,与request_irq中的dev一样