key是最常用的也是最简单的驱动程序,在linux内核当中,应该有现成的配置和驱动,但在龙芯2k0300中没有配置,在此通过自定义的形式,验证此问题
硬件电路
驱动适配
设备树
gpio_key{compatible="lx,gpio-key";label="gpiokey";gpios=<&gpio 83 GPIO_ACTIVE_LOW>;status="ok";};
驱动代码
平台驱动
staticconststructof_device_id inputkey_of_match[] = { {.compatible ="lx,gpio-key"}, { } };staticstructplatform_driver lx_input_key_driver = { .probe = lx_input_key_probe, .remove= lx_input_key_remove, .driver = { .name ="inputkey", .owner = THIS_MODULE, .of_match_table = inputkey_of_match, }, };
probe
主要注册字符设备,创建class/device,并调用key_gpio_init
staticint lx_input_key_probe(structplatform_device*dev) { int ret=0;ret=alloc_chrdev_region(&key_dev.dev_id,0,DEVICE_CNT,DEVICE_NAME);if(ret<0) { pr_err("it cann't alloc chrdev\\\\n"); goto faile_devid; } key_dev.major=MAJOR(key_dev.dev_id); key_dev.minor=MINOR(key_dev.dev_id); pr_info("dev id geted:%d:%d!\\\\n",key_dev.major,key_dev.minor); key_dev.cdev.owner=THIS_MODULE; cdev_init(&key_dev.cdev,&lx_key_fops); ret=cdev_add(&key_dev.cdev,key_dev.dev_id,DEVICE_CNT);if(ret<0){ pr_err("cdev_add err\\\\r\\\\n"); goto fail_cdev; } pr_info("chr dev inited!\\\\r\\\\n"); key_dev.class=class_create(THIS_MODULE,DEVICE_NAME);if(IS_ERR(key_dev.class)){ pr_info("class err!\\\\r\\\\n"); ret=PTR_ERR(key_dev.class); goto fail_class; } pr_info("dev class created\\\\r\\\\n"); key_dev.device=device_create(key_dev.class,NULL,key_dev.dev_id,NULL,DEVICE_NAME);if(IS_ERR(key_dev.device)){ pr_info("device err!\\\\r\\\\n"); ret=PTR_ERR(key_dev.device); goto fail_device; } pr_info("device created!\\\\r\\\\n"); ret=key_gpio_init(&key_dev);if(ret<0){pr_info("key_gpio_init err!\\\\r\\\\n"); goto fail_device; }returnret; fail_device: pr_info("device create err,class destroyed\\\\r\\\\n"); class_destroy(key_dev.class); fail_class: pr_info("class create err,cdev del\\\\r\\\\n"); cdev_del(&key_dev.cdev); fail_cdev: pr_info("cdev init err,chrdev register\\\\r\\\\n"); unregister_chrdev_region(key_dev.dev_id,DEVICE_CNT); faile_devid: pr_info("dev id err\\\\r\\\\n");returnret; }
控制逻辑
key_gpio_init主要完成设备树信息的获取,并注册中断
staticintkey_gpio_init(struct key_dev *dev) {intret =0; dev->dev_nd = of_find_node_by_path("/gpio_input");if(dev->dev_nd ==NULL) { pr_info("can't find device key\\\\n"); ret = -EINVAL;gotofail_nd; } pr_info("find device key\\n"); dev->key_gpio = of_get_named_gpio(dev->dev_nd,"gpios",0);if(dev->key_gpio <0) { ret = -EINVAL;gotofail_gpio; } pr_info("key gpio = %d",dev->key_gpio); ret = gpio_request(dev->key_gpio,"key0");if(ret){ ret = -EBUSY; pr_err("IO %d can't request\\\\r\\\\n",dev->key_gpio);gotofail_request; } pr_info("gpio=%d\\\\r\\\\n",dev->key_gpio); ret = gpio_direction_input(dev->key_gpio);if(ret<0){ ret = -EINVAL;gotofail_input; } key_dev.irq=gpio_to_irq(key_dev.key_gpio); request_irq(key_dev.irq, gpio_key_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,"gpio-key", &key_dev);return0; fail_input: gpio_free(dev->key_gpio); fail_request: fail_gpio: fail_nd:returnret; }
应用测试
通过select机制,监控按键响应
intmain(intargc, char *argv[]) {intfd; fd_set read_fds; char c;intret;if(argc <2) { fprintf(stderr,"usage: %s \\\\n", argv[0]);exit(EXIT_FAILURE); } ret =open(argv[1], O_RDWR);if(ret <0) { perror("open");exit(EXIT_FAILURE); }printf("file %s opened\\\\n", argv[1]); fd = ret;while(1) {/* Set up reading file descriptors */FD_ZERO(&read_fds); FD_SET(STDIN_FILENO, &read_fds); FD_SET(fd, &read_fds);/* Wait for any data fro our device or stdin */ret =select(FD_SETSIZE, &read_fds, NULL, NULL, NULL);if(ret <0) { perror("select");exit(EXIT_FAILURE); }if(FD_ISSET(STDIN_FILENO, &read_fds)) { ret =read(STDIN_FILENO, &c,1);if(ret <0) { perror("read(STDIN, ...)");exit(EXIT_FAILURE); }printf("got '%c' from stdin!\\\\n", c); }if(FD_ISSET(fd, &read_fds)) { ret =read(fd, &c,1);if(ret <0) { perror("read(fd, ...)");exit(EXIT_FAILURE); }printf("got '%c' from device!\\\\n", c);printf("got '%d' from device!\\\\n", c); } }close(fd);return0; }
验证效果
串口终端安装ko文件
运行应用程序
当按下并弹起,内核驱动打印irq:0和irq:1,用户层打印获取得到的值