linux驱动学习笔记(八)misc 与 input

前言

Linux驱动中设备基本都是挂在总线上,但是有一些我们不知道类型的字符设备我们可以挂在misc下,linux还有专门为了输入设备提供了一个input子系统。这些输入设备通常都是通过按压触摸等某种方式产生一个中断,然后cpu通过某种协议去读键值坐标等放到一个缓冲区域并上报给用户。

misc设备

所有的misc设备主设备号都是10,从设备号不同。misc会自动创建cdev 不需要我们手动创建

struct miscdevice {
	int minor; /* 子设备号 */
	const char *name; /* 设备名字 */
	const struct file_operations *fops; /* 设备操作集 */
	struct list_head list;
	struct device *parent;
	struct device *this_device;
	const struct attribute_group **groups;
	const char *nodename;
	umode_t mode;
};
int misc_register(struct miscdevice * misc);//注册misc设备
int misc_deregister(struct miscdevice *misc);//注销misc设备

demo

	misc_led{
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "misc-demo";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_led>;
		led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
		status = "okay";
	};
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>

#define led_NAME		"misc_led"	/* 名字 	*/
#define led_MINOR		222			/* 子设备号 */
/* led设备结构体 */
struct led_dev{
	struct class *class;	/* 类 		*/
	struct device_node	*nd; /* 设备节点 */
	int led_gpio;			/* beep所使用的GPIO编号		*/
	int led_flag;
};

struct led_dev led;		/* beep设备 */

/* 设备操作函数 */
static struct file_operations led_fops = {
	.owner = THIS_MODULE,
};
static ssize_t std_show(struct device *dev, struct device_attribute *attr, char *buf){
	return sprintf(buf, "%d\n",led.led_flag);
}
static ssize_t std_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len){
    sscanf(buf, "%d",&led.led_flag);
	printk("led.led_flag[%d]\n",led.led_flag);
    if(led.led_flag == 0)
    {
        gpio_set_value(led.led_gpio, 0);
    }else{
        gpio_set_value(led.led_gpio, 1);
    }
	
	return len;
}
static DEVICE_ATTR_RW(std);


static struct attribute *demo_class_attrs[] = {
	&dev_attr_std.attr,
	NULL,
};

static const struct attribute_group demo_group = {
	.attrs = demo_class_attrs,
};

static const struct attribute_group *demo_groups[] = {
	&demo_group,
	NULL,
};
/* MISC设备结构体 */
static struct miscdevice led_miscdev = {
	.minor = led_MINOR,
	.name = led_NAME,
	.fops = &led_fops,
    .groups = demo_groups,
};
static int led_probe(struct platform_device *dev){

	int ret = 0;
    printk("match device-tree success!\n");
	led.nd = of_find_node_by_path("/misc_led");
	if(led.nd == NULL) {
		printk("misc_led node not find!\r\n");
		return -EINVAL;
	} 
	led.led_gpio = of_get_named_gpio(led.nd, "led-gpio", 0);
	if(led.led_gpio < 0) {
		printk("can't get led-gpio");
		return -EINVAL;
	}

	ret = gpio_direction_output(led.led_gpio, 1);
	if(ret < 0) {
		printk("can't set gpio!\r\n");
	}
	
	ret = misc_register(&led_miscdev);
	if(ret < 0){
		printk("misc device register failed!\r\n");
		return -EFAULT;
	}

	return 0;
}
static int led_remove(struct platform_device *dev){
	/* 注销设备的时候关闭LED灯 */
	gpio_set_value(led.led_gpio, 1);

	/* 注销misc设备 */
	misc_deregister(&led_miscdev);
	return 0;
}
 /* 匹配列表 */
 static const struct of_device_id misc_of_match[] = {
     { .compatible = "misc-demo" },
     { /* Sentinel */ }
 };
 
 /* platform驱动结构体 */
static struct platform_driver led_driver = {
     .driver     = {
         .name   = "misc-leds",         /* 驱动名字,用于和设备匹配 */
         .of_match_table = misc_of_match, /* 设备树匹配表          */
     },
     .probe      = led_probe,
     .remove     = led_remove,
};

static int __init led_init(void){
	return platform_driver_register(&led_driver);
}


static void __exit led_exit(void){
	platform_driver_unregister(&led_driver);
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("bin");

INPUT子系统

input 作为linux内核为管理输入设备所提供的一种手段,如按键,触摸屏,某些传感器等,按键和键盘代表按键信息,触摸屏代表坐标信息,因此用户处理就不同,驱动只需要上报事件,键值坐标等。
input子系统作为linux一个大类,所以所有的input设备主设备号都为13
input子系统分为三层关系

  1. 驱动层,设备的驱动程序,用来向核心层上报输入信息。
  2. 核心层,为驱动层提供接口,告诉事件层输入信息正在处理。
  3. 事件层,主要用来与用户进行交互。
struct input_dev {
	const char *name;//设备名
	const char *phys;//设备在系统中的路径
	const char *uniq;//设备id
	struct input_id id;//input设备id

	unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];

	unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; /* 事件类型的位图 */
	unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; /* 按键值的位图 */
	unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; /* 相对坐标的位图 */
	unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; /* 绝对坐标的位图 */
	unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)]; /* 杂项事件的位图 */
	unsigned long ledbit[BITS_TO_LONGS(LED_CNT)]; /*LED 相关的位图 */
	unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];/* sound 有关的位图 */
	unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; /* 压力反馈的位图 */
	unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; /*开关状态的位图 */

	unsigned int hint_events_per_packet;

	unsigned int keycodemax;//支持最大按键数
	unsigned int keycodesize;//键值字节数
	void *keycode;//存储键值的数组的首地址

	int (*setkeycode)(struct input_dev *dev,
			  const struct input_keymap_entry *ke,
			  unsigned int *old_keycode);
	int (*getkeycode)(struct input_dev *dev,
			  struct input_keymap_entry *ke);

	struct ff_device *ff;
	/*
		连击事件
	*/
	unsigned int repeat_key;
	struct timer_list timer;

	int rep[REP_CNT];
	
	struct input_mt *mt;//多点触控区域

	struct input_absinfo *absinfo;

	unsigned long key[BITS_TO_LONGS(KEY_CNT)];//设备当前按键状态
	unsigned long led[BITS_TO_LONGS(LED_CNT)];//设备当前灯状态
	unsigned long snd[BITS_TO_LONGS(SND_CNT)];//设备当前声音状态
	unsigned long sw[BITS_TO_LONGS(SW_CNT)];//设备当前开关状态

	int (*open)(struct input_dev *dev);
	void (*close)(struct input_dev *dev);
	int (*flush)(struct input_dev *dev, struct file *file);//用于处理传递设备的事件
	int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);//事件处理函数,主要是接收用户下发的命令

	struct input_handle __rcu *grab;

	spinlock_t event_lock;
	struct mutex mutex;

	unsigned int users;//打开该设备的用户数量
	bool going_away;

	struct device dev;
	/*
	
	*/
	struct list_head	h_list;
	struct list_head	node;

	unsigned int num_vals;
	unsigned int max_vals;
	struct input_value *vals;

	bool devres_managed;
};

struct input_event {                                                            
    struct timeval time;   /* 事件发生的时间  */                                
    __u16 type;             /* 事件类型 */                                      
    __u16 code;             /* 事件码 */                                        
    __s32 value;            /* 事件值 */                                        
};   

evbit 可选事件类型

#define EV_SYN 0x00 /* 同步事件 */
#define EV_KEY 0x01 /* 按键事件 */
#define EV_REL 0x02 /* 相对坐标事件 */
#define EV_ABS 0x03 /* 绝对坐标事件 */
#define EV_MSC 0x04 /* 杂项(其他)事件 */
#define EV_SW 0x05 /* 开关事件 */
#define EV_LED 0x11 /* LED */
#define EV_SND 0x12 /* 声音 */
#define EV_REP 0x14 /* 重复事件 */
#define EV_FF 0x15 /* 压力事件 */
#define EV_PWR 0x16 /* 电源事件 */
#define EV_FF_STATUS 0x17 /* 压力状态事件 */

操作函数

/*
	申请input_dev
*/
struct input_dev *input_allocate_device(void)
struct input_dev *devm_input_allocate_device(struct device *dev)
void input_free_device(struct input_dev *dev)//释放input_dev
/*
	注册与销毁
*/
int input_register_device(struct input_dev *dev)
void input_unregister_device(struct input_dev *dev)

/*
 * @description		: 上报事件
 * @param - dev 	: input设备结构
 * @param - type 	: 向中断处理函数传参
 * @param - code 	: 事件码,也就是我们注册的按键值
 * @param - value 	: 事件值
 */
void input_event(struct input_dev *dev,unsigned int type,unsigned int code,int value)

void input_report_key(struct input_dev *dev,unsigned int code, int value)//上报按键类型
void input_report_rel(struct input_dev *dev, unsigned int code, int value)//上报相对坐标
void input_report_abs(struct input_dev *dev, unsigned int code, int value)//上报绝对坐标
void input_report_switch(struct input_dev *dev, unsigned int code, int value)//上报开关状态
void input_mt_sync(struct input_dev *dev)//上报同步事件 ,在上报完事件后一定要同步。

模板

struct input_dev *input; /* input 结构体变量 */
/* 驱动入口函数 */
static int __init xxx_init(void)
{

	input= input_allocate_device(); /* 申请 input_dev */
	input->name = "input-key"; /* 设置 input_dev 名字 */
	//第一种方法
	__set_bit(EV_KEY, input->evbit); /* 设置产生按键事件 */
	__set_bit(EV_REP, input->evbit); /* 重复事件 */
	__set_bit(KEY_0, input->keybit); /*设置产生哪些按键值 */
	//第二种方法
	input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
	input->keybit[BIT_WORD(KEY_0)] |= BIT_MASK(KEY_0);
	//第三种方法
	input->evbit[0] = BIT_MASK(EV_KEY) |BIT_MASK(EV_REP);
	input_set_capability(input, EV_KEY, KEY_0);

	/* 注册 input_dev */
	input_register_device(input);

	return 0;
}
/* 驱动出口函数 */
static void __exit xxx_exit(void)
{
	input_unregister_device(input); /* 注销 input_dev */
	input_free_device(input); /* 删除 input_dev */
}

demo

dts

input_key {
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "input-key";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_key>;
		key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>; /* KEY0 */
		interrupt-parent = <&gpio1>;
		interrupts = <18 IRQ_TYPE_EDGE_BOTH>; /* FALLING RISING */
		status = "okay";
	};
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <linux/timer.h>
#include <linux/input.h>

#define DEMO_NAME			"key_input"	/* 名字 */


struct key_dev{
    int gpio;
    unsigned char key_value;

    int irqnumber;
    char irq_name[20];
    irqreturn_t (*handler)(int, void *);
};


struct demo_dev{

    struct device_node	*node; /* 设备节点 */
    struct timer_list timer;/* 定义一个定时器*/
    struct key_dev keys;
    struct input_dev *input;		/* input结构体 */
};
struct demo_dev demo;	/* demo设备 */
static irqreturn_t key0_handler(int irq, void *dev_id)
{

	struct demo_dev *dev = (struct demo_dev *)dev_id;

	dev->timer.data = (volatile long)dev_id;
	mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10));	/* 10ms定时 */
	return IRQ_RETVAL(IRQ_HANDLED);
}
void timer_function(unsigned long arg)
{

	unsigned char value;
	struct demo_dev *dev = (struct demo_dev *)arg;
	value = gpio_get_value(dev->keys.gpio); 	/* 读取IO值 */
	if(value == 0){ 						
		/*按下按键*/
		printk(" the key down ! [%d]\n",dev->keys.key_value);
		input_report_key(dev->input, dev->keys.key_value, 1);/* 最后一个参数表示按下还是松开,1为按下,0为松开 */
	}else{
		/*按键释放*/
		printk(" the key up ! [%d]\n",dev->keys.key_value);
		input_report_key(dev->input, dev->keys.key_value, 0);
    }
    input_sync(dev->input);
}

static int demo_probe(struct platform_device *dev)
{
    int ret = 0;
	int input_init_func_flag = 0;
	/*
		io初始化部分
	*/
	demo.node = of_find_node_by_path("/key");
    if (demo.node== NULL){
		printk("key node not find!\r\n");
		return -EINVAL;
	} 
    demo.keys.gpio = of_get_named_gpio(demo.node ,"key-gpio",0);
    if (demo.keys.gpio< 0) {
        printk("can't get key-gpio\n");
        return -EINVAL;
    }

    memset(demo.keys.irq_name, 0, sizeof(demo.keys.irq_name));	/* 缓冲区清零 */
    sprintf(demo.keys.irq_name, "KEY%d", 0);		/* 组合名字 */
    gpio_request(demo.keys.gpio, demo.keys.irq_name);
    gpio_direction_input(demo.keys.gpio);	
    demo.keys.irqnumber = irq_of_parse_and_map(demo.node, 0);

    demo.keys.handler = key0_handler;
	demo.keys.key_value = KEY_0;


    ret = request_irq(  demo.keys.irqnumber, 
                        demo.keys.handler, 
                        IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, 
                        demo.keys.irq_name, 
                        &demo);
    if(ret < 0){
        printk("irq %d request failed!\r\n", demo.keys.irqnumber);
        return -EFAULT;
    }
    init_timer(&demo.timer);
	demo.timer.function = timer_function;
    /*
		input 初始化部分
	*/
	demo.input = input_allocate_device();
	demo.input->name = DEMO_NAME;

	if(input_init_func_flag == 0)
	{
		__set_bit(EV_KEY, demo.input->evbit); /* 设置产生按键事件 */
		__set_bit(EV_REP, demo.input->evbit); /* 重复事件 */
		__set_bit(KEY_0 , demo.input->keybit); /*设置产生哪些按键值 */
	}else if(input_init_func_flag == 1)
	{
		demo.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);//设置按键事件和重复事件
		input_set_capability(demo.input, EV_KEY, KEY_0);
	}
	/* 注册输入设备 */
	ret = input_register_device(demo.input);
	if (ret) {
		printk("register input device failed!\r\n");
		return ret;
	}
	return 0;
}
static int demo_remove(struct platform_device *dev)
{
	del_timer_sync(&demo.timer);	
    free_irq(demo.keys.irqnumber, &demo);
    /*释放input_dev*/
	input_unregister_device(demo.input);
	input_free_device(demo.input);
    return 0;
}


/* 设备树匹配列表 */
static const struct of_device_id demo_of_match[] = {
	{ .compatible = "input-key" },
	{ /* Sentinel */ }
};

/* platform驱动结构体 */
static struct platform_driver demo_driver= {
	.driver		= {
		.name	= "input-keys",			/* 驱动名字,用于和设备匹配 */
        .of_match_table	= demo_of_match, /* 设备树匹配表,用于和设备树匹配 */
	},
	.probe		= demo_probe,
	.remove		= demo_remove,
};
		

static int __init demo_init(void)
{
	return platform_driver_register(&demo_driver);
}

static void __exit demo_exit(void)
{
	platform_driver_unregister(&demo_driver);
}
module_init(demo_init);
module_exit(demo_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("bin");
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值