linux输入设备驱动程序

文章详细介绍了Linux内核中输入设备的结构,包括structinput_dev的成员和其在内核中的表示,如设备名称、支持的事件类型、键位等。此外,还阐述了如何分配、注册和注销输入设备,以及如何报告输入事件,如EV_KEY和EV_ABS事件。文章最后提到了用户空间通过/dev/input/event接口与内核交互的方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        输入设备是与系统交互的设备,这些设备包括按钮、键盘、触摸屏、鼠标等。他们的工作方式是发送事件、输入内核在系统上捕获和传播。

一、输入设备结构

        输入设备在内核中表示为struct input_dev{}实例:

struct input_dev {
    const char *name; // 设备的名称
    const char *phys; // 系统层次结构中设备的路径

    ulong evbit[BITS_TO_LONGS(EV_CNT)]; 
    /* evbit是设备支持的位图,有EV_KEY(键盘), EV_REL(鼠标), EV_ABS(操纵杆),
        set_bit(EV_KEY, my_input_dev->evbit);
        set_bit(EV_REL, my_input_dev->evbit);
        set_bit(EV_ABS, my_input_dev->evbit); 
    */
    ulong keybit[BITS_TO_LONGS(KEY_CNT)]; // 支持KEY_0, KEY_A, KEY_B
    ulong relbit[BITS_TO_LONGS(REL_CNT)]; // 支持REL_X, REL_Y, REL_Y,REL_RX等
    ulong absbit[BITS_TO_LONGS(ABS_CNT)]; // 支持ABS_X, ABS_Y等
    ulong mscbit[BITS_TO_LONGS(MSC_CNT)]; // 用于支持EV_MSC类型的设备,支持其他位图

    uint repeat_kesy; // 存储最后一次安静的建码
    int rep[REP_CNT]; // 自动重复参数的当前值
    struct input_absinfo *absinfo;
    /* absinfo中存储关于绝对坐标轴的信息(当前值、最小值、最大值、平坦值、模糊度和分辨率)
        应使用input_set_abs_params()函数设置这些值
        void input_set_abs_params(struct input_dev *dev, uint axis, int min,
                                    int max, int fuzz, int flat);min和max是指下限值
        和上限值,fuzz是指输入设备的指定通道上预期噪声。
        #define ABSMAX_ACC_VAL 0x01ff
        #define ABSMIN_ACC_VAL -(ABSMAX_ACC_VAL)
        [...]
        set_bit(EV_ABS, idev->evbit);
        input_set_abs_param(idev, ABS_X, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
        input_set_abs_param(idev, ABS_y, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
        input_set_abs_param(idev, ABS_z, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
    */
    ulong key[BITS_TO_LONGS(KEY_CNT)]; // 反应设备按键/按钮的状态

    void (*open)(struct input_dev *dev); // 第一个用户调用input_open_device时使用的方法
    void (*close)(struct input_dev *dev);// 最后一个用户调用input_close_device时使用的方法
    uint users; // 存储打开此设备的用户数量
    struct device dev;
    uint num_vals; // 当前帧中的排队的值的数量
    uint max_vals; // 帧中排队的值的最大数量
    struct input_value *vals; // 当前帧中排队的值的数组
    bool devres_managed; // 表示当前设备使用devres框架管理
};

二、分配和注册输入设备

1. input_dev设备的分配和注册

// 分配、注册、注销和释放设备内存的函数
struct input_dev *input_alloc_device(void);
struct input_dev *devm_input_alloc_device(struct device *dev);

void input_free_device(struct input_dev *);

int input_register_device(struct input_dev *dev);
void input_unregister_device(struct input_dev *dev);

// 下面是i2c设备上输入设备的probe函数示例
struct input_dev *idev;
int error;

idev = input_alloc_device();
if (!idev) return -ENOMEM;
idev->name = BMA150_DRIVER;
idev->phys = BMA150_DRIVER "/input0";
idev->id.bustype = BUS_I2C;
idev->dev.parent = &client->dev;

set_bit(EV_ABS, idev->evbit);
input_set_params(idev, ABS_X, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
input_set_params(idev, ABS_Y, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
input_set_params(idev, ABS_Z, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
error = input_register_device(idev);
if (error) goto err_free_mem;
error = request_threaded_irq(&client->irq, NULL, my_irq_thread,
            IRQ_TRIGGER_RISING | IRQF_ONESHOT, NULL);
if (error) toto err_unregister_dev;
[...]
}

2. input_polled_dev设备的分配和注册

        轮询输入设备时特殊类型的输入设备,它依靠轮询来感知设备状态的变化。而一般的输入设备类型则依靠IRQ来检测变化,并将事件发送到输入内核。轮询输入设备在input内核中描述为struct input_pooled_dev,其定义如下:

struct input_polled_dev {
    void *private;

    void (*open)(struct input_polled_dev *dev); // 用于启用轮询设备
    void (*close)(struct input_polled_dev *dev); // 不再轮询时使用,使设备进入低功耗状态
    void (*poll)(struct input_polled_dev *dev); // 每当设备被轮询时调用。
    uint poll_interval; // msec,不设置时其默认值为500ms
    uint poll_interval_min;
    uint poll_interval_max;

    struct input_dev *input;
    bool devres_managed;
};

// 结构体的分配和释放,在分配后主要要设置其input字段
struct input_polled_dev *input_allocate_polled_device(void);
struct input_polled_dev *devm_input_allocate_polled_device(struct device *dev);
void input_free_polled_device(struct input_polled_dev *dev);

// 注册和释放该结构
int input_register_polled_device(struct input_polled_dev *dev);
void input_unregister_polled_device(struct input_polled_dev *dev);

轮询输入设备的分配和使用

struct my_struct {
    struct gpio_desc *reset_btn_desc;
    struct input_polled_dev *poll_dev;
};

static int button_probe(struct platform_device *pdev) {
    struct my_struct *myst;
    struct input_dev *idev;
    int retval;
    myst = dev_kzalloc(&pdev->dev, sizeof(*myst), GFP_KERNEL);
    if (!my_st) return -ENOMEM;
    myst->poll_dev = devm_input_allocate_polled_device(&pdev->dev);
    if (!my->poll_dev) return -ENOMEM;
    myst->reset_btn_desc = gpiod_get(&pdev->dev, "reset", GPIOD_IN);
    
    myst->poll_dev->private = ms;
    myst->poll_dev->poll = my_btn_poll;
    myst->poll_dev->poll_interval = 200;
    myst->poll_dev->open = my_btn_poll;
 
    input_dev = myst->poll_dev->input;
    input_dev->name = "System Reset Btn";
    input_dev->id.bustype = BUS_I2C;
    input_dev->dev.parent = &pdev->dev;

    set_bit(EV_KEY, input_dev->evbit);
    set_bit(BTN_0, input_dev->keybit);
    retval = devm_input_register_polled_device(&pdev->dev, myst->poll_dev);
    return retval;
}

三、产生和报告输入事件

        输入设备根据可支持事件的不同,内核提供相应的API将其报告给input内核。

// 报告事件函数input_report_xxx
void input_report_abs(struct input_dev *dev, uint code, int val);
void input_report_rel(struct input_dev *dev, uint code, int val);
void input_report_key(struct input_dev *dev, uint code, int val);
// 报告完事件后,应调用input_sync()进行同步
void input_sync(struct input_dev *dev);

// 示例,报告EV_KEY事件
input_report_key(poll_dev->input, BTN_0, gpiod_get_Value(ms->rest_btn_desc)&1);

// 示例,报告EV_ABS事件
input_report_abs(nunchunk->input, ABS_X, x_value);
input_report_abs(nunchunk->input, ABS_Y, y_value);
input_report_abs(nunchunk->input, ABS_Z, z_value);

// 示例,报告EV_REL事件
input_report_rel(nunchunk->input, REL_X, (nunchunk->report.joy_x-128)/10);

bma150数字加速度传感器报告事件的驱动程序如下:

static void threaded_report_xyz(struct bma150_data *bma150) {
    u8 data[DATA_SIZE];
    s16 x,y,z;
    s32 ret;
    ret = i2c_smbus_read_i2c_block_data(bma150->client, BMA150_ACC_X_LSB_REG,
            DATA_SIZE, data);
    if (ret!= DATA_SIZE) return;
    x = ((0xc0 & data[0]) >> 6) | (data[1]<<2);
    y = ((0xc0 & data[2]) >> 6) | (data[3]<<2);
    z = ((0xc0 & data[4]) >> 6) | (data[6]<<2);
    x = (s16) (x<<6)>>6;
    y = (s16) (y<<6)>>6;
    z = (s16) (z<<6)>>6;
    input_report_abs(bma150->input, ABS_X, x);
    input_report_abs(bma150->input, ABS_Y, y);
    input_report_abs(bma150->input, ABS_Z, z);
    input_sync(bma150->input);
}

在轮询设备的poll函数中汇报事件的示例如下:

static void my_btn_poll(struct input_polled_dev *poll_dev) {
    struct my_struct myst = poll_dev->private;
    struct i2c_client = myst->client;
    input_report_key(poll_dev->input, BTN_0, gpiod_get_value(
        myst->reset_btn_desc)&1);
    input_sync(poll_dev->input);
}

四、input设备的用户空间接口

        每个注册的输入设备都有/dev/input/event<X>字符设备表示,从中可以读取来自内核的事件,读取该文件的应用程序会得到格式为struct input_event{}的事件record:

struct input_event {
    struct timeval time;
    u16 type; // 事件类型,EV_KEY, EV_ABS, EV_REL
    u16 code; // 事件代码:REL_X, ABS_X, KEY_BACKSPACE, BTN_0等
    s32 value;// EV_REL:其值相对变化;EV_ABS: 其值是新的绝对值;EV_KEY:按键按下为1,按键释放为0,按键自动重复设置为2
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值