阅读Linux内核驱动代码—adc_key驱动

2024-01-07 23:33:30

1、前言

????????本驱动源码是adc按键驱动源码,主要的框架是输入子系统。源码只对其做了一小部分修改,使得设备的acc引脚、按键部分、dc输入检测引脚、sd卡检测引脚等可以像按键一样通过输入子系统向应用层报告相应的键值。这样应用层可以通过读取相应的输入设备节点,可以知道反馈了哪些键值,从而知道那些设备输入了还是拔出了,这样可以方便设备的管理。

2、设备树相应的节点

注意:设备树节点的compatible 是 "adc-keys";所以下文中驱动源码的name或者compatible必须是"adc-keys";这样才能匹配然后进入probe函数。

        #include <dt-bindings/input/input.h>
        adc-keys {
            compatible = "adc-keys";
            //io-channels = <&sar 0>;
            io-channels = <0>;
            io-channel-names = "buttons";
            poll-interval=<100>;
            keyup-threshold-microvolt = <1024000>;//adc电压模拟值

            button-left {
                label = "LEFT";
                linux,code = <KEY_LEFT>;
                press-threshold-microvolt = <4400000>;
            };
            button-lcd {
                label = "KEY_BRIGHTNESS_TOGGLE";
                linux,code = <KEY_BRIGHTNESS_TOGGLE>;
                press-threshold-microvolt = <4500000>;
            };
			button-Accon {
                label = "Accon";
                linux,code = <KEY_F11>;
                press-threshold-microvolt = <5000000>;
            };
            button-Accoff {
                label = "Accoff";
                linux,code = <KEY_F12>;
                press-threshold-microvolt = <5500000>;
            };
			button-DCIN {
                label = "DCIN";
                linux,code = <KEY_F13>;
                press-threshold-microvolt = <6000000>;
            };
            button-DCOUT {
                label = "DCOUT";
                linux,code = <KEY_F14>;
                press-threshold-microvolt = <7000000>;
            };

            button-power {
                label = "Power";
                linux,code = <KEY_POWER>;
                press-threshold-microvolt = <6000000>;
            };

			 button-menu {
				label = "Menu";
				linux,code = <KEY_MENU>;
				press-threshold-microvolt = <1680000>;
			};
            button-up {
                label = "Up";
                linux,code = <KEY_UP>;
                press-threshold-microvolt = <2860000>;
            };

            button-down {
                label = "Down";
                linux,code = <KEY_DOWN>;
                press-threshold-microvolt = <3150000>;
            };
			
            button-enter {
                label = "Enter";
                linux,code = <KEY_ENTER>;
                press-threshold-microvolt = <3250000>;
            };

            button-SDIN {
                label = "SDIN";
                linux,code = <KEY_CLOSECD>;
                press-threshold-microvolt = <7100000>;
            };
            button-SDOUT {
                label = "SDOUT";
                linux,code = <KEY_EJECTCD>;
                press-threshold-microvolt = <7200000>;
            };
        };
    };
};

3、makefile & Kconfig

在adc_keys驱动源码下的目录下的添加makefile和Kconfig

Kconfig:

config MS_SARKEY
        tristate "sar key driver"
        help
          Say Y here to enable the driver for the sar key.

          If unsure, say Y.

          To compile this driver as a module, choose M here: the
          module will be called mdrv_sar.

makefile:

# general options
EXTRA_CFLAGS += -Idrivers/sstar/include
EXTRA_CFLAGS += -Idrivers/sstar/rtc/reg
obj-$(CONFIG_MS_SARKEY)		+= adc-keys.o

修改上一级目录下单makefile,添加下面这句:

obj-$(CONFIG_MS_SARKEY) += sar_key/

4、驱动源码

adc-keys.c:

/*
* adc-keys.c- Sigmastar
*
* Copyright (c) [2019~2020] SigmaStar Technology.
*
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License version 2 for more details.
*
*/

#include <linux/err.h>
//#include <linux/iio/consumer.h>
//#include <linux/iio/types.h>
#include <linux/input.h>
#include <linux/input-polldev.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/slab.h>

#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/of_platform.h>
#include <linux/delay.h>
#include "reg_rtcpwc.h"
#include "ms_types.h"
#include <linux/reboot.h>

#define LONG_PRESS_HOLD_TIMES    (5)

struct adc_keys_button {
    u32 voltage;
    u32 keycode;
};

struct ms_rtc_info {
    struct platform_device *pdev;
    struct rtc_device *rtc_dev;
    void __iomem *rtc_base;
    u32 default_base;
    spinlock_t mutex;
#ifdef CONFIG_PM_SLEEP
    u32 sw0;
#ifdef CONFIG_RTCPWC_INNER_EHHE
    u32 sw1;
#endif
#endif
};
static struct ms_rtc_info *_pinfo = NULL;
struct input_dev *input;
static int _virtual_key = 0;
static int _vkey_status = 1;
int 	sdInseart = 0;
int    sdStatus = 0;
struct adc_keys_state {
    //struct iio_channel *channel;
    int chan;
    u32 num_keys;
    u32 last_key;
    u32 hold_times;
    u32 keyup_voltage;
    void __iomem *reg_base;
    const struct adc_keys_button *map;
};

#define ISO_S0                      0x00
#define ISO_S1                      0x01
#define ISO_S2                      0x03
#define ISO_S3                      0x07
#define ISO_S4                      0x05
#define ISO_S5                      0x01
#define ISO_ACK_RETRY_TIME          20
#define RTC_CHECK_STATUS_DELAY_TIME_US  100

extern int ms_sar_get(int ch);
extern void ms_sar_hw_init(void);

static bool ms_RtcPwr_ISOCTL_EX(void)
{
    u8 ubCheck = ISO_ACK_RETRY_TIME;
    u16 reg = 0 ;
    // Input ISO ctrl sequence ,  3'b000(S0) -> 3'b001(S1)  -> 3'b011(S2)  -> 3'b111(S3)  -> 3'b101(S4)  -> 3'b001(S5)  -> 3'b000(S0)
    // Following notes is from MV2
    // The switch of state needs delay, 1ms at least according to designer,
    // but in our test, set to 3ms will still causes incorrect data read.
    // And the sequence should be finished within 1 sec.

    reg = readw(_pinfo->rtc_base + RTCPWC_DIG2RTC_ISO_CTRL);
    writew(reg & ISO_S0, _pinfo->rtc_base + RTCPWC_DIG2RTC_ISO_CTRL);
    reg = readw(_pinfo->rtc_base + RTCPWC_RTC2DIG_ISO_CTRL_ACK);
    reg = reg & RTCPWC_RTC2DIG_ISO_CTRL_ACK_BIT;

    while ((reg ) && (--ubCheck)) {
        // mdelay(RTC_CHECK_STATUS_DELAY_TIME_MS);
        udelay(RTC_CHECK_STATUS_DELAY_TIME_US);
        reg = readw(_pinfo->rtc_base + RTCPWC_RTC2DIG_ISO_CTRL_ACK);
        reg = reg & RTCPWC_RTC2DIG_ISO_CTRL_ACK_BIT;
    }
    if (ubCheck == 0)
        return FALSE;

    ubCheck = ISO_ACK_RETRY_TIME;
    reg = readw(_pinfo->rtc_base + RTCPWC_DIG2RTC_ISO_CTRL);
    writew(reg | ISO_S1, _pinfo->rtc_base + RTCPWC_DIG2RTC_ISO_CTRL);
    reg = readw(_pinfo->rtc_base + RTCPWC_RTC2DIG_ISO_CTRL_ACK);
    reg = reg & RTCPWC_RTC2DIG_ISO_CTRL_ACK_BIT;

    while ((reg != RTCPWC_RTC2DIG_ISO_CTRL_ACK_BIT)&& (--ubCheck)) {
        // mdelay(RTC_CHECK_STATUS_DELAY_TIME_MS);
        udelay(RTC_CHECK_STATUS_DELAY_TIME_US);
        reg = readw(_pinfo->rtc_base + RTCPWC_RTC2DIG_ISO_CTRL_ACK);
        reg = reg & RTCPWC_RTC2DIG_ISO_CTRL_ACK_BIT;
    }
    if (ubCheck == 0)
        return FALSE;


    ubCheck = ISO_ACK_RETRY_TIME;
    reg = readw(_pinfo->rtc_base + RTCPWC_DIG2RTC_ISO_CTRL);
    writew(reg | ISO_S2, _pinfo->rtc_base + RTCPWC_DIG2RTC_ISO_CTRL);
    reg = readw(_pinfo->rtc_base + RTCPWC_RTC2DIG_ISO_CTRL_ACK);
    reg = reg & RTCPWC_RTC2DIG_ISO_CTRL_ACK_BIT;

    while ((reg )&& (--ubCheck)) {
        // mdelay(RTC_CHECK_STATUS_DELAY_TIME_MS);
        udelay(RTC_CHECK_STATUS_DELAY_TIME_US);
        reg = readw(_pinfo->rtc_base + RTCPWC_RTC2DIG_ISO_CTRL_ACK);
        reg = reg & RTCPWC_RTC2DIG_ISO_CTRL_ACK_BIT;
    }
    if (ubCheck == 0)
        return FALSE;

    ubCheck = ISO_ACK_RETRY_TIME;
    reg = readw(_pinfo->rtc_base + RTCPWC_DIG2RTC_ISO_CTRL);
    writew(reg | ISO_S3, _pinfo->rtc_base + RTCPWC_DIG2RTC_ISO_CTRL);
    reg = readw(_pinfo->rtc_base + RTCPWC_RTC2DIG_ISO_CTRL_ACK);
    reg = reg & RTCPWC_RTC2DIG_ISO_CTRL_ACK_BIT;

    while ((reg != RTCPWC_RTC2DIG_ISO_CTRL_ACK_BIT) && (--ubCheck)) {
        // mdelay(RTC_CHECK_STATUS_DELAY_TIME_MS);
        udelay(RTC_CHECK_STATUS_DELAY_TIME_US);
        reg = readw(_pinfo->rtc_base + RTCPWC_RTC2DIG_ISO_CTRL_ACK);
        reg = reg & RTCPWC_RTC2DIG_ISO_CTRL_ACK_BIT;
    }
    if (ubCheck == 0)
        return FALSE;

    ubCheck = ISO_ACK_RETRY_TIME;
    reg = readw(_pinfo->rtc_base + RTCPWC_DIG2RTC_ISO_CTRL);
    writew(reg & ISO_S4, _pinfo->rtc_base + RTCPWC_DIG2RTC_ISO_CTRL);
    reg = readw(_pinfo->rtc_base + RTCPWC_RTC2DIG_ISO_CTRL_ACK);
    reg = reg & RTCPWC_RTC2DIG_ISO_CTRL_ACK_BIT;

    while ((reg )&& (--ubCheck)) {
        // mdelay(RTC_CHECK_STATUS_DELAY_TIME_MS);
        udelay(RTC_CHECK_STATUS_DELAY_TIME_US);
        reg = readw(_pinfo->rtc_base + RTCPWC_RTC2DIG_ISO_CTRL_ACK);
        reg = reg & RTCPWC_RTC2DIG_ISO_CTRL_ACK_BIT;
    }
    if (ubCheck == 0)
        return FALSE;

    ubCheck = ISO_ACK_RETRY_TIME;
    reg = readw(_pinfo->rtc_base + RTCPWC_DIG2RTC_ISO_CTRL);
    writew(reg & ISO_S5, _pinfo->rtc_base + RTCPWC_DIG2RTC_ISO_CTRL);
    reg = readw(_pinfo->rtc_base + RTCPWC_RTC2DIG_ISO_CTRL_ACK);
    reg = reg & RTCPWC_RTC2DIG_ISO_CTRL_ACK_BIT;

    while ((reg != RTCPWC_RTC2DIG_ISO_CTRL_ACK_BIT )&& (--ubCheck)) {
        // mdelay(RTC_CHECK_STATUS_DELAY_TIME_MS);
        udelay(RTC_CHECK_STATUS_DELAY_TIME_US);
        reg = readw(_pinfo->rtc_base + RTCPWC_RTC2DIG_ISO_CTRL_ACK);
        reg = reg & RTCPWC_RTC2DIG_ISO_CTRL_ACK_BIT;
    }
    if (ubCheck == 0)
        return FALSE;

    ubCheck = ISO_ACK_RETRY_TIME;
    reg = readw(_pinfo->rtc_base + RTCPWC_DIG2RTC_ISO_CTRL);
    writew(reg & ISO_S0, _pinfo->rtc_base + RTCPWC_DIG2RTC_ISO_CTRL);

    reg = readw(_pinfo->rtc_base + RTCPWC_RTC2DIG_ISO_CTRL_ACK);
    reg = reg & RTCPWC_RTC2DIG_ISO_CTRL_ACK_BIT;

    while ((reg )&& (--ubCheck)) {
        // mdelay(RTC_CHECK_STATUS_DELAY_TIME_MS);
        udelay(RTC_CHECK_STATUS_DELAY_TIME_US);
        reg = readw(_pinfo->rtc_base + RTCPWC_RTC2DIG_ISO_CTRL_ACK);
        reg = reg & RTCPWC_RTC2DIG_ISO_CTRL_ACK_BIT;
    }
    if (ubCheck == 0)
        return FALSE;

    ubCheck = 22;
    do {
        reg = readw(_pinfo->rtc_base + RTCPWC_DIG2PWC_RTC_TESTBUS);
        if (reg & RTCPWC_ISO_EN) {
            break;
        }
        udelay(100);
        ubCheck--;
    }
    while (ubCheck);

    if (ubCheck == 0)
        return FALSE;

    // [from designer Belon.Chen] wait 2 ms is must since read/write base/counter/SW0/SW1 is valid after iso state complete
    mdelay(2);
    return TRUE;
}

#if defined(SYS_POWEROFF_DIRECT_BY_DRIVER)
static void ms_poweroff_Notify(void)
{
    int ret = -1;
    char path[] = "/usr/bin/killall";
    char *argv[] = {path,"cardv",NULL};
    char *envp[] = {"HOME=/", "PATH=/sbin:/bin:/user/bin", NULL};

    printk(KERN_CRIT "-------%s-------\n",__func__);
    ret = call_usermodehelper(path, argv, envp,UMH_WAIT_PROC);
    ssleep(3);
    kernel_power_off();
}
#endif

static void ms_poweroff_enter(void)
{
    if (!IS_ERR(_pinfo->rtc_base)) {
        //Enable WOS,connect to Gsen_POC
        writew(0x03, (_pinfo->rtc_base + (0x21<<2)));//set reg_vh_sel_poc =3'b011 ' VIH: AVDD x 75% (default)
        writew(0x02, (_pinfo->rtc_base + (0x22<<2)));//set reg_wos_clr =1'b0 ' HIZ select, wait sound input 
        
        writew(0x00, (_pinfo->rtc_base + (0x0f<<2)));
        printk(KERN_CRIT "********%s*******\n",__func__);
        ms_RtcPwr_ISOCTL_EX();
    }
}

static ssize_t ms_panic_show(struct device *dev,struct device_attribute *attr, char *buf)
{
    int ret;
    ret = sprintf(buf, "Use to save Segmentation fault to mtd\n");
    return ret;
}

static ssize_t ms_panic_store(struct device *dev,
                    struct device_attribute *attr,
                    const char *buf, size_t count)
{
    int value,ret;
    ret=kstrtoint(buf, 10, &value);
    panic("***********test************");
    return count;   
}

//#define KEY_UP		103
//#define KEY_LEFT		105
//#define KEY_RIGHT		106
//#define KEY_DOWN		108
//#define KEY_ENTER		28
//#define KEY_MENU		139	/* Menu (show menu) */
//CMD Prompt:"echo 139 0 > /sys/devices/soc0/soc/soc:adc-keys/virtual_input"
static ssize_t ms_virtual_key_store(struct device *dev,
                    struct device_attribute *attr,
                    const char *buf, size_t count)
{
    char *opt;
    
    //printk("%s\n", buf);
    _virtual_key = simple_strtoul(buf, &opt, 10);
    if(*opt == ' ')
        _vkey_status = simple_strtoul(opt+1, NULL, 10);
    
    return count;   
}

static ssize_t ms_virtual_key_show(struct device *dev,struct device_attribute *attr, char *buf)
{
    int ret;
    ret = sprintf(buf, "Prompt:echo 139 0 > /sys/devices/soc0/soc/soc:adc-keys/virtual_input\n 0->rel\n 1->press\n 2->Lpress\n 255->pre&rel\n");
    return ret;
}

//Segmentation fault
static DEVICE_ATTR(save_seg_fault,  0660,  ms_panic_show, ms_panic_store);
static DEVICE_ATTR(virtual_input,  0660,  ms_virtual_key_show, ms_virtual_key_store);
static const struct attribute *uadc_attrs[] = {
    &dev_attr_save_seg_fault.attr,
    &dev_attr_virtual_input.attr,
    NULL
};
static const struct attribute_group adc_attr_grp = {
    .attrs = (struct attribute **)uadc_attrs,
};

static void adc_keys_poll(struct input_polled_dev *dev)
{
    struct adc_keys_state *st = dev->private;
    int i, value;
    u32 diff, closest = 45;//0xffffffff;
    int keycode = 0;

    //ret = iio_read_channel_processed(st->channel, &value);
    value = ms_sar_get(st->chan);
    value = (value * 325) / 100;
    if (unlikely(value < 0)) {
        /* Forcibly release key if any was pressed */
        value = st->keyup_voltage;
    } else {
        for (i = 0; i < st->num_keys; i++) {
            diff = abs(st->map[i].voltage - value);
            if (diff < closest) {
                closest = diff;
                keycode = st->map[i].keycode;
            }
        }
    }

    if (abs(st->keyup_voltage - value) < closest)
        keycode = 0;

    if (keycode == 0 && !IS_ERR(st->reg_base)) {
        u16 reg = 0;
        reg = readw(st->reg_base + 0x48);
        keycode = (reg & 0x04)? KEY_POWER:0;
        if (keycode == KEY_POWER) {
            //printk(KERN_CRIT "keycode = %d  value=%d  \n", keycode,reg);
            #if defined(SYS_POWEROFF_DIRECT_BY_DRIVER)
            ms_poweroff_Notify();
            #endif
        }
    }

    if (st->last_key && st->last_key != keycode) {
        // printk(" free keycode = %d new = %d value=%d\n", st->last_key, keycode, value);
        // input_report_key(dev->input, st->last_key, 0);
        input_event(dev->input, EV_KEY, st->last_key, 0); // release
        st->hold_times = 0;
    }

    if (st->last_key && st->last_key == keycode) {
        st->hold_times ++;
        if (st->hold_times == LONG_PRESS_HOLD_TIMES) {
            // printk(" long press keycode = %d  value=%d\n", keycode, value);
            input_event(dev->input, EV_KEY, keycode, 2); // long press
        } /* else if (st->hold_times < LONG_PRESS_HOLD_TIMES) {
            input_event(dev->input, EV_KEY, keycode, 1);
        } */
    } else if (keycode) {
        // printk(" press keycode = %d  value=%d\n", keycode, value);
        // input_report_key(dev->input, keycode, 1);
        input_event(dev->input, EV_KEY, keycode, 1); // press
        st->hold_times = 0;
    }

    if(_virtual_key){
        if(_vkey_status == 255){
            input_event(dev->input, EV_KEY, _virtual_key, 1);
            input_event(dev->input, EV_KEY, _virtual_key, 0);
        }
        else if(_vkey_status >= 0 && _vkey_status <= 3){
            input_event(dev->input, EV_KEY, _virtual_key, _vkey_status);
        }
        _virtual_key = 0;
        _vkey_status = 1;
    }

    input_sync(dev->input);
    st->last_key = keycode;
}
int Power_Key_Report(void)
{
	//printk(KERN_CRIT "********%s*******\n",__func__);
	input_report_key(input, KEY_POWER, 1);
	input_report_key(input, KEY_POWER, 0);
	input_sync(input);	
	return 0;
}
int Power_Key_Report_Bkl(void)
{
	input_report_key(input, KEY_BRIGHTNESS_TOGGLE, 1);
	input_report_key(input, KEY_BRIGHTNESS_TOGGLE, 0);
	input_sync(input);
	return 0;
}
int Acc_on_Key_Report(int on)
{
	//printk(KERN_CRIT "********%s*******\n",__func__);
	if(on) 
	{
		input_report_key(input, KEY_F11, 1);
		input_report_key(input, KEY_F11, 0);
		input_sync(input);
	}else{
		input_report_key(input, KEY_F12, 1);
		input_report_key(input, KEY_F12, 0);
		input_sync(input);
	}
	return 0;
}
int DC_Key_Report(int in)
{
	if(in) 
	{
		input_report_key(input, KEY_F13, 1);
		input_report_key(input, KEY_F13, 0);
		input_sync(input);
	}else{
		input_report_key(input, KEY_F14, 1);
		input_report_key(input, KEY_F14, 0);
		input_sync(input);
	}
	return 0;
}

//-------------------------------------

EXPORT_SYMBOL_GPL(Power_Key_Report);
EXPORT_SYMBOL_GPL(Power_Key_Report_Bkl);
EXPORT_SYMBOL_GPL(Acc_on_Key_Report);
EXPORT_SYMBOL_GPL(DC_Key_Report);
EXPORT_SYMBOL(sdStatus);
EXPORT_SYMBOL(sdInseart);

static int adc_keys_load_keymap(struct device *dev, struct adc_keys_state *st)
{
    struct adc_keys_button *map;
    struct fwnode_handle *child;
    int i;

    st->num_keys = device_get_child_node_count(dev);
    if (st->num_keys == 0) {
        dev_err(dev, "keymap is missing\n");
        return -EINVAL;
    }

    map = devm_kmalloc_array(dev, st->num_keys, sizeof(*map), GFP_KERNEL);
    if (!map)
        return -ENOMEM;

    i = 0;
    device_for_each_child_node(dev, child) {
        if (fwnode_property_read_u32(child, "press-threshold-microvolt",
                         &map[i].voltage)) {
            dev_err(dev, "Key with invalid or missing voltage\n");
            fwnode_handle_put(child);
            return -EINVAL;
        }
        map[i].voltage /= 1000;

        if (fwnode_property_read_u32(child, "linux,code",
                         &map[i].keycode)) {
            dev_err(dev, "Key with invalid or missing linux,code\n");
            fwnode_handle_put(child);
            return -EINVAL;
        }

        i++;
    }
	#if 1  //hw ver.B
		map[7].voltage = 299;
		map[8].voltage = 1209;
		map[9].voltage = 2034;
		map[10].voltage = 2510;
	#else
		map[7].voltage = 1680;
		map[8].voltage = 2860;
		map[9].voltage = 3150;
		map[10].voltage = 3250;
	#endif
    st->map = map;
    return 0;
}

static int adc_keys_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct adc_keys_state *st;
    struct input_polled_dev *poll_dev;
    
    int i, value,chan;
    int error;
    char compatible_name[64];
    struct device_node *dev_node;
    struct platform_device *pRtcdev;

    st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
    if (!st) {
        return -ENOMEM;
    }

    sprintf(compatible_name, "sstar,infinity-rtcpwc");
    dev_node = of_find_compatible_node(NULL, NULL, compatible_name);

    if (!dev_node) {
        dev_err(dev, "[%s]: failed to find of_find_compatible_node\n", __func__);
        //return 0;
    } else {
        pRtcdev = of_find_device_by_node(dev_node);
        _pinfo = platform_get_drvdata(pRtcdev);
        if (!_pinfo) {
            dev_err(dev, "[%s]: failed to get platform_get_drvdata\n", __func__);
            st->reg_base = NULL;
            //return 0;
        } else {
            st->reg_base = _pinfo->rtc_base;
            dev_err(dev, "adc_keys_probe reg_base=0x%p success\n",_pinfo->rtc_base);
        }
    }

    if (!device_property_read_u32(dev, "io-channels", &chan))
        st->chan = chan;
    else {
    //set default
        st->chan = 0;
    }

    if (device_property_read_u32(dev, "keyup-threshold-microvolt",
            &st->keyup_voltage)) {
        dev_err(dev, "Invalid or missing keyup voltage\n");
        return -EINVAL;
    }
    st->keyup_voltage /= 1000;

    error = adc_keys_load_keymap(dev, st);
    if (error)
        return error;

    platform_set_drvdata(pdev, st);

    poll_dev = devm_input_allocate_polled_device(dev);
    if (!poll_dev) {
        dev_err(dev, "failed to allocate input device\n");
        return -ENOMEM;
    }

    if (!device_property_read_u32(dev, "poll-interval", &value))
        poll_dev->poll_interval = value;

    poll_dev->poll = adc_keys_poll;
    poll_dev->private = st;

    input = poll_dev->input;

    input->name = pdev->name;
    input->phys = "adc-keys/input0";

    input->id.bustype = BUS_HOST;
    input->id.vendor = 0x0001;
    input->id.product = 0x0001;
    input->id.version = 0x0100;

    __set_bit(EV_KEY, input->evbit);
    for (i = 0; i < st->num_keys; i++)
        __set_bit(st->map[i].keycode, input->keybit);

    if (device_property_read_bool(dev, "autorepeat"))
        __set_bit(EV_REP, input->evbit);

    error = input_register_polled_device(poll_dev);
    if (error) {
        dev_err(dev, "Unable to register input device: %d\n", error);
        return error;
    }
    ms_sar_hw_init();

    if(0!=sysfs_create_group(&dev->kobj, &adc_attr_grp))
    {
        pr_warn("adc sysfs_create_group failed...\n");
    }


    pm_power_off = ms_poweroff_enter;
    return 0;
}

static int adc_keys_remove(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    sysfs_remove_group(&dev->kobj, &adc_attr_grp);
    printk("TODO:%s",__func__);
    return 0;
}

#ifdef CONFIG_OF
static const struct of_device_id adc_keys_of_match[] = {
    { .compatible = "adc-keys", },
    { }
};
MODULE_DEVICE_TABLE(of, adc_keys_of_match);
#endif

static struct platform_driver __refdata adc_keys_driver = {
    .driver = {
        .name = "adc_keys",
        .of_match_table = of_match_ptr(adc_keys_of_match),
    },
    .probe = adc_keys_probe,
    .remove	= adc_keys_remove,
};
module_platform_driver(adc_keys_driver);

MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>");
MODULE_DESCRIPTION("Input driver for resistor ladder connected on ADC");
MODULE_LICENSE("GPL v2");

5、应用层code示例

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>

static int s_kbd = -1;
fd_set fdset;
struct timeval timeOut;

#define KEY_ACC_ON				KEY_F11
#define KEY_ACC_OFF				KEY_F12
#define KEY_DC_IN				KEY_F13
#define KEY_DC_OUT				KEY_F14
#define KEY_TOGGLE		        KEY_BRIGHTNESS_TOGGLE
#define KEY_POWER_OFF	        KEY_POWER

int main(int argc, char *argv[])
{
    s_kbd = open("/dev/input/event0", O_RDONLY);
	if(s_kbd < 0) {
		printf("open %s fail!\n", "/dev/input/event0");
		return -1;
	}
    FD_ZERO(&fdset);

    while (1)
	{
        timeOut.tv_sec = 0;
		timeOut.tv_usec = 500000;
        FD_SET(s_kbd, &fdset);
		/select返回值:
		如果没有文件描述符就绪就返回0;
		如果调用失败返回-1;
		如果timeout中中readfds中有事件发生,则返回timeout剩下的时间。
		*/
        ret = select(FD_SETSIZE, &fdset, NULL, NULL, &timeOut);
		if(ret == 0) {
        }
        else if(ret > 0) {
            if (FD_ISSET(s_kbd, &fdset)) {
				FD_CLR(s_kbd, &fdset);
				if((read(s_kbd, &event, sizeof(event)) == sizeof(event)) && (event.type == EV_KEY)) {
					if (event.code == KEY_POWER_OFF) {
					} else if (event.code == KEY_TOGGLE) {
					} else if (event.code == KEY_ACC_OFF) {
					} else if (event.code == KEY_ACC_ON) {
					} else if (event.code == KEY_DC_OUT) {
					} else if (event.code == KEY_DC_IN) {
					} else if (event.code == KEY_DC_IN) {
					}	
			}
        }

    close(s_kbd);
	return 0;
}

文章来源:https://blog.csdn.net/suifen_/article/details/135445836
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。