zoukankan      html  css  js  c++  java
  • 输入设备驱动

    输入设备是典型的字符驱动。

    工作机理

    输入设备驱动工作机理:

    1. 底层在按键发生时,产生一个中断(或者驱动通过timer定时查询), 
    2. 然后CPU通过总线(SPI, I2C)读取键值,并将它们放入到一个缓冲区
    3. 字符设备驱动管理该缓冲区。驱动的read() API让用户可以读取键值

    只有中断,键值是具体设备相关,其他是输入设备通用。

    因此,内核设计了输入子系统


    输入子系统框架

    引用链接中架构图,点击打开链接





    按键驱动分析

    那么以GPIO按键驱动做个分析。

    GPIO driver

    drivers/input/keyboard/gpio_keys.c

    源码链接

    probe()

    对照输入子系统架构图, GPIO按键驱动调用了“Input Core"中提供的通用API, 见下面代码中的

    input = devm_input_allocate_device(dev);
    error = input_register_device(input);

     /dev/input目录下的事件都是在驱动中调用input_register_device(struct input_dev *dev)产生的。


    static int gpio_keys_probe(struct platform_device *pdev)
    {
    	struct device *dev = &pdev->dev;
    	const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);
    	struct fwnode_handle *child = NULL;
    	struct gpio_keys_drvdata *ddata;
    	struct input_dev *input;
    	size_t size;
    	int i, error;
    	int wakeup = 0;
    	if (!pdata) {
    		pdata = gpio_keys_get_devtree_pdata(dev);
    		if (IS_ERR(pdata))
    			return PTR_ERR(pdata);
    	}
    	size = sizeof(struct gpio_keys_drvdata) +
    			pdata->nbuttons * sizeof(struct gpio_button_data);
    	ddata = devm_kzalloc(dev, size, GFP_KERNEL);
    	if (!ddata) {
    		dev_err(dev, "failed to allocate state
    ");
    		return -ENOMEM;
    	}
    	ddata->keymap = devm_kcalloc(dev,
    				     pdata->nbuttons, sizeof(ddata->keymap[0]),
    				     GFP_KERNEL);
    	if (!ddata->keymap)
    		return -ENOMEM;
    	input = devm_input_allocate_device(dev);
    	if (!input) {
    		dev_err(dev, "failed to allocate input device
    ");
    		return -ENOMEM;
    	}
    	ddata->pdata = pdata;
    	ddata->input = input;
    	mutex_init(&ddata->disable_lock);
    	platform_set_drvdata(pdev, ddata);
    	input_set_drvdata(input, ddata);
    	input->name = pdata->name ? : pdev->name;
    	input->phys = "gpio-keys/input0";
    	input->dev.parent = dev;
    	input->open = gpio_keys_open;
    	input->close = gpio_keys_close;
    	input->id.bustype = BUS_HOST;
    	input->id.vendor = 0x0001;
    	input->id.product = 0x0001;
    	input->id.version = 0x0100;
    	input->keycode = ddata->keymap;
    	input->keycodesize = sizeof(ddata->keymap[0]);
    	input->keycodemax = pdata->nbuttons;
    	/* Enable auto repeat feature of Linux input subsystem */
    	if (pdata->rep)
    		__set_bit(EV_REP, input->evbit);
    	for (i = 0; i < pdata->nbuttons; i++) {
    		const struct gpio_keys_button *button = &pdata->buttons[i];
    		if (!dev_get_platdata(dev)) {
    			child = device_get_next_child_node(dev, child);
    			if (!child) {
    				dev_err(dev,
    					"missing child device node for entry %d
    ",
    					i);
    				return -EINVAL;
    			}
    		}
    		error = gpio_keys_setup_key(pdev, input, ddata,
    					    button, i, child);
    		if (error) {
    			fwnode_handle_put(child);
    			return error;
    		}
    		if (button->wakeup)
    			wakeup = 1;
    	}
    	fwnode_handle_put(child);
    	error = devm_device_add_group(dev, &gpio_keys_attr_group);
    	if (error) {
    		dev_err(dev, "Unable to export keys/switches, error: %d
    ",
    			error);
    		return error;
    	}
    	error = input_register_device(input);
    	if (error) {
    		dev_err(dev, "Unable to register input device, error: %d
    ",
    			error);
    		return error;
    	}
    	device_init_wakeup(dev, wakeup);
    	return 0;
    }

    ISR

    GPIO按键通过 input core提供的API, input_event()和input_sync(),来汇报按键事件

    static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id)
    {
    	struct gpio_button_data *bdata = dev_id;
    	struct input_dev *input = bdata->input;
    	unsigned long flags;
    	BUG_ON(irq != bdata->irq);
    	spin_lock_irqsave(&bdata->lock, flags);
    	if (!bdata->key_pressed) {
    		if (bdata->button->wakeup)
    			pm_wakeup_event(bdata->input->dev.parent, 0);
    		input_event(input, EV_KEY, *bdata->code, 1);
    		input_sync(input);
    		if (!bdata->release_delay) {
    			input_event(input, EV_KEY, *bdata->code, 0);
    			input_sync(input);
    			goto out;
    		}
    		bdata->key_pressed = true;
    	}
    	if (bdata->release_delay)
    		mod_timer(&bdata->release_timer,
    			jiffies + msecs_to_jiffies(bdata->release_delay));
    out:
    	spin_unlock_irqrestore(&bdata->lock, flags);
    	return IRQ_HANDLED;
    }


    Input Core

    设备注册,事件,同步等相关API, drivers/input/input.c , 点击打开链接

    linux VFS相关API (file_operations), drivers/input/evdev.c, 点击打开链接


    platform device

    一般的,在板级的初始化c文件里面。比如:archarmmach-mx6oard-mx6q-sabresd.c 

    #if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE)
    #define GPIO_BUTTON(gpio_num, ev_code, act_low, descr, wake, debounce)    
    {                                
        .gpio        = gpio_num,                
        .type        = EV_KEY,                
        .code        = ev_code,                
        .active_low    = act_low,                
        .desc        = "btn " descr,                
        .wakeup        = wake,                    
        .debounce_interval = debounce,                
    }
    
    static struct gpio_keys_button imx6q_buttons[] = {
        GPIO_BUTTON(SABRESD_KEY_USER1, KEY_VOLUMEUP, 1, "user-key-1", 0, 1),
        GPIO_BUTTON(SABRESD_KEY_USER2, KEY_VOLUMEDOWN, 1, "user-key-2", 0, 1),
        GPIO_BUTTON(SABRESD_KEY_WHIBUSB, KEY_F1, 1, "whibusb", 0, 1),
        GPIO_BUTTON(SABRESD_KEY_WHIBUSL, KEY_F2, 1, "whibusl", 0, 1),
        GPIO_BUTTON(SABRESD_KEY_WHIBUSR, KEY_F3, 1, "whibusr", 0, 1),
    };
    
    static struct gpio_keys_platform_data imx6q_button_data = {
        .buttons    = imx6q_buttons,
        .nbuttons    = ARRAY_SIZE(imx6q_buttons),
    };
    
    static struct platform_device imx6q_button_device = {
        .name        = "gpio-keys",
        .id        = -1,
        .num_resources  = 0,
        .dev        = {
            .platform_data = &imx6q_button_data,
        }
    };
    
    static void __init imx6q_add_device_buttons(void)
    {
        platform_device_register(&imx6q_button_device);
    }
    #else
    static void __init imx6q_add_device_buttons(void) {}
    #endif

    上面注册了5个按键设备。然后在board_init()初始化函数里面,添加imx6q_add_device_buttons()。我们就可以通过应用层操作了。比如:按下某个按键的时候,在read()函数中获取哪个键被按下。


    利用proc文件系统。cat /proc/bus/input/devices  查看一下按键是哪个event。

    I: Bus=0011 Vendor=0002 Product=0006 Version=0000
    N: Name="ImExPS/2 Generic Explorer Mouse"
    P: Phys=isa0060/serio1/input0
    S: Sysfs=/devices/platform/i8042/serio1/input/input4
    U: Uniq=
    H: Handlers=mouse0 event3 
    B: PROP=1
    B: EV=7
    B: KEY=1f0000 0 0 0 0 0 0 0 0
    B: REL=143
    


    platform driver

    drivers/input/keyboard/gpio_keys.c

    static struct platform_driver gpio_keys_device_driver = {
    	.probe		= gpio_keys_probe,
    	.driver		= {
    		.name	= "gpio-keys",
    		.pm	= &gpio_keys_pm_ops,
    		.of_match_table = gpio_keys_of_match,
    	}
    };
    

  • 相关阅读:
    Android Gradle Plugin指南(五)——Build Variants(构建变种版本号)
    文件内容操作篇clearerr fclose fdopen feof fflush fgetc fgets fileno fopen fputc fputs fread freopen fseek ftell fwrite getc getchar gets
    文件操作篇 close creat dup dup2 fcntl flock fsync lseek mkstemp open read sync write
    嵌入式linux应用程序调试方法
    version control system:git/hg/subversion/cvs/clearcase/vss。software configruation management。代码集成CI:Cruisecontrol/hudson/buildbot
    最值得你所关注的10个C语言开源项目
    如何记录linux终端下的操作日志
    CentOS 5.5 虚拟机安装 VirtualBox 客户端增强功能
    sizeof, strlen区别
    C/C++嵌入式开发面试题
  • 原文地址:https://www.cnblogs.com/feiwatson/p/9478227.html
Copyright © 2011-2022 走看看