互联网一二线大厂名单/进一步优化营商环境
前言:
linux下platform平台驱动是为了方便隔离bsp与driver,利于移植。体现好代码的高内聚,低耦合。Linux设备驱动模型中,关心总线,设备和驱动三个实体。总线将设备和驱动绑定。在系统每注册一个设备的时候,都会寻找与之相匹配的驱动,相反的,每加载一个驱动的时候,也会寻找与之匹配的设备。匹配由总线完成。linux发明了一种虚拟的总线,称之为platform总线,相应的设备称之为platform_device,驱动为platform_driver。
基于这个模型,又根据面向对象的思想,同一类的事物定义为一个基类。因此在驱动中,将同一基类的驱动,再抽象一个核心层。因此又分为了input设备,I2C设备,SPI设备等驱动。我们今天说的标准按键驱动,就是基于input输入设备。
正题:
在内核中,按键的驱动已经完成!!!不需要我们自己写。driver/input/keyboard/gpio_keys.c 就是驱动文件。刚才说了,有驱动,还要有设备啊~只有这两个匹配了,只有这样我们才能在应用层操作这个设备。这个设备我们在哪注册呢?一般的,在板级的初始化c文件里面。比如:board_max6q_sabresd.c。这里面我们怎么搞呢?看下面::::::
1 #if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE)
2 #define GPIO_BUTTON(gpio_num, ev_code, act_low, descr, wake, debounce) 3 {4 .gpio =gpio_num,5 .type =EV_KEY,6 .code =ev_code,7 .active_low =act_low,8 .desc = "btn"descr,9 .wakeup =wake,10 .debounce_interval =debounce,11 }12
13 static struct gpio_keys_button imx6q_buttons[] ={14 GPIO_BUTTON(SABRESD_KEY_USER1, KEY_VOLUMEUP, 1, "user-key-1", 0, 1),15 GPIO_BUTTON(SABRESD_KEY_USER2, KEY_VOLUMEDOWN, 1, "user-key-2", 0, 1),16 GPIO_BUTTON(SABRESD_KEY_WHIBUSB, KEY_F1, 1, "whibusb", 0, 1),17 GPIO_BUTTON(SABRESD_KEY_WHIBUSL, KEY_F2, 1, "whibusl", 0, 1),18 GPIO_BUTTON(SABRESD_KEY_WHIBUSR, KEY_F3, 1, "whibusr", 0, 1),19 };20
21 static struct gpio_keys_platform_data imx6q_button_data ={22 .buttons =imx6q_buttons,23 .nbuttons =ARRAY_SIZE(imx6q_buttons),24 };25
26 static struct platform_device imx6q_button_device ={27 .name = "gpio-keys",28 .id = -1,29 .num_resources = 0,30 .dev ={31 .platform_data = &imx6q_button_data,32 }33 };34
35 static void __init imx6q_add_device_buttons(void)36 {37 platform_device_register(&imx6q_button_device);38 }39 #else
40 static void __init imx6q_add_device_buttons(void) {}41 #endif
上面注册了5个按键设备。然后在board_init()初始化函数里面,添加imx6q_add_device_buttons()。我们就可以通过应用层操作了。比如:按下某个按键的时候,在read()函数中获取哪个键被按下。下面的链接360无死角的gpio按键应用。注意的是/dev/input/eventX不太一样,操作的时候可以cat /proc/bus/input/devices查看一下咱们的按键是哪个event。
看看上面的代码,依葫芦画瓢就可以完成GPIO按键的设备添加。接下来我们分析下驱动,能用了,最好还是明白下原理。
跟网上其他的差不多,我们着重分析两个函数:
1 static int __devinit gpio_keys_probe(struct platform_device *pdev)2 {3 struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; //相关的结构体以及宏定义在本c文件和include/linux/input.h include/linux/gpio_keys.h里面找。4 struct gpio_keys_drvdata *ddata;5 struct device *dev = &pdev->dev;6 struct input_dev *input;7 inti, error;8 int wakeup = 0;9
10 ddata = kzalloc(sizeof(struct gpio_keys_drvdata) + //分配且清空数据空间
11 pdata->nbuttons * sizeof(structgpio_button_data),12 GFP_KERNEL);13 input =input_allocate_device(); //分配一个input设备14 if (!ddata || !input) {15 dev_err(dev, "failed to allocate state\n");16 error = -ENOMEM;17 gotofail1;18 }19 //设置input设备属性
20 ddata->input =input;21 ddata->n_buttons = pdata->nbuttons;22 ddata->enable = pdata->enable;23 ddata->disable = pdata->disable;24 mutex_init(&ddata->disable_lock);25
26 platform_set_drvdata(pdev, ddata);27 input_set_drvdata(input, ddata);28
29 input->name = pdata->name ? : pdev->name;30 input->phys = "gpio-keys/input0";31 input->dev.parent = &pdev->dev;32 input->open =gpio_keys_open;33 input->close =gpio_keys_close;34
35 input->id.bustype =BUS_HOST;36 input->id.vendor = 0x0001;37 input->id.product = 0x0001;38 input->id.version = 0x0100;39
40 /*Enable auto repeat feature of Linux input subsystem*/
41 if (pdata->rep)42 __set_bit(EV_REP, input->evbit);43
44 for (i = 0; i < pdata->nbuttons; i++) { //对注册的每个gpio进行设置45 struct gpio_keys_button *button = &pdata->buttons[i];46 struct gpio_button_data *bdata = &ddata->data[i];47 unsigned int type = button->type ?: EV_KEY;48
49 bdata->input =input;50 bdata->button =button;51
52 error =gpio_keys_setup_key(pdev, bdata, button); //这个是具体实现,下面分析53 if(error)54 gotofail2;55
56 if (button->wakeup)57 wakeup = 1;58 /*设置设备对事件的支持,比如设置对键1和键2的支持*/
59 input_set_capability(input, type, button->code);60 }61 //创建文件系统的节点,可以网上搜搜看,好像我之前的博文也有写到这块
62 error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group);63 if(error) {64 dev_err(dev, "Unable to export keys/switches, error: %d\n",65 error);66 gotofail2;67 }68 //注册一个input设备
69 error =input_register_device(input);70 if(error) {71 dev_err(dev, "Unable to register input device, error: %d\n",72 error);73 gotofail3;74 }75
76 /*get current state of buttons*/
77 for (i = 0; i < pdata->nbuttons; i++)78 gpio_keys_report_event(&ddata->data[i]);79 input_sync(input);80
81 device_init_wakeup(&pdev->dev, wakeup);82
83 return 0;84
85 fail3:86 sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group);87 fail2:88 while (--i >= 0) {89 free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]);90 if (ddata->data[i].timer_debounce)91 del_timer_sync(&ddata->data[i].timer);92 cancel_work_sync(&ddata->data[i].work);93 gpio_free(pdata->buttons[i].gpio);94 }95
96 platform_set_drvdata(pdev, NULL);97 fail1:98 input_free_device(input);99 kfree(ddata);100
101 returnerror;102 }
上面是probe函数的一些简要说明,probe顾名思义,就是探测到设备注册时,驱动完成的工作。
下面的函数是probe里面重要的gpio_keys_setup_key()函数
1 static int __devinit gpio_keys_setup_key(struct platform_device *pdev,2 struct gpio_button_data *bdata,3 struct gpio_keys_button *button)4 {5 const char *desc = button->desc ? button->desc : "gpio_keys";6 struct device *dev = &pdev->dev;7 unsigned longirqflags;8 intirq, error;9
10 setup_timer(&bdata->timer, gpio_keys_timer, (unsigned long)bdata);11 INIT_WORK(&bdata->work, gpio_keys_work_func);12
13 error = gpio_request(button->gpio, desc);14 if (error < 0) {15 dev_err(dev, "failed to request GPIO %d, error %d\n",16 button->gpio, error);17 gotofail2;18 }19
20 error = gpio_direction_input(button->gpio);21 if (error < 0) {22 dev_err(dev, "failed to configure"
23 "direction for GPIO %d, error %d\n",24 button->gpio, error);25 gotofail3;26 }27
28 if (button->debounce_interval) {29 error = gpio_set_debounce(button->gpio,30 button->debounce_interval * 1000);31 /*use timer if gpiolib doesn‘t provide debounce*/
32 if (error < 0)33 bdata->timer_debounce = button->debounce_interval;34 }35
36 irq = gpio_to_irq(button->gpio);37 if (irq < 0) {38 error =irq;39 dev_err(dev, "Unable to get irq number for GPIO %d, error %d\n",40 button->gpio, error);41 gotofail3;42 }43
44 irqflags = IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING;45 /*
46 * If platform has specified that the button can be disabled,47 * we don‘t want it to share the interrupt line.48 */
49 if (!button->can_disable)50 irqflags |=IRQF_SHARED;51 /*
52 * If platform has specified that the button can wake up the system,53 * for example, the power key which usually use to wake up the system54 * from suspend, we add the IRQF_EARLY_RESUME flag to this irq, so55 * that the power key press can be handled and reported as early as56 * possible. Some platform like Android need to get the power key57 * event early to reume some devcies like framebuffer and etc.58 */
59 if (button->wakeup)60 irqflags |=IRQF_EARLY_RESUME;61
62 error =request_any_context_irq(irq, gpio_keys_isr, irqflags, desc, bdata);63 if (error < 0) {64 dev_err(dev, "Unable to claim irq %d; error %d\n",65 irq, error);66 gotofail3;67 }68
69 return 0;70
71 fail3:72 gpio_free(button->gpio);73 fail2:74 returnerror;75 }
主要是gpio的中断,定时器,工作队列等设置。没什么大问题。具体函数不懂,碰到一个查一个。
代分析码相对简单困了回去睡觉了。
参考:
谢谢!
原文:http://www.cnblogs.com/cyc2009/p/4127496.html