key是最常用的也是最简单的驱动程序,在linux内核当中,应该有现成的配置和驱动,但在龙芯2k0300中没有配置,在此通过自定义的形式,验证此问题
硬件威廉希尔官方网站
驱动适配
设备树
gpio_key{
compatible = "lx,gpio-key";
label = "gpiokey";
gpios = <&gpio 83 GPIO_ACTIVE_LOW>;
status = "ok";
};
驱动代码
平台驱动
static const struct of_device_id inputkey_of_match[] = {
{.compatible = "lx,gpio-key"},
{ }
};
static struct platform_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
static int lx_input_key_probe(struct platform_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;
}
return ret;
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");
return ret;
}
控制逻辑
key_gpio_init主要完成设备树信息的获取,并注册中断
static int key_gpio_init(struct key_dev *dev)
{
int ret = 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;
goto fail_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;
goto fail_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);
goto fail_request;
}
pr_info("gpio=%d\\\\r\\\\n",dev->key_gpio);
ret = gpio_direction_input(dev->key_gpio);
if(ret<0){
ret = -EINVAL;
goto fail_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);
return 0;
fail_input:
gpio_free(dev->key_gpio);
fail_request:
fail_gpio:
fail_nd:
return ret;
}
应用测试
通过select机制,监控按键响应
int main(int argc, char *argv[])
{
int fd;
fd_set read_fds;
char c;
int ret;
if (argc < 2) {
fprintf(stderr, "usage: %s <dev>\\\\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);
return 0;
}
验证效果
串口终端安装ko文件
运行应用程序
当按下并弹起,内核驱动打印irq:0和irq:1,用户层打印获取得到的值