我是靠谱客的博主 愉快小兔子,最近开发中收集的这篇文章主要介绍高通驱动实现 GPIO 中断上报键值,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

高通驱动实现 GPIO 中断上报键值

    • 一、 确认keycode值,同步修改上层键值映射表
      • Tips 1: 选择一个好的键值的好处
      • Tips 2: 如何确认驱动代是否ok
    • 二、 驱动代码编写
    • 三、代码调试
    • 四、 驱动代码另一种实现方法——dts 实现
      • 4.1 DTS 配置
      • 4.2 添加 gpio-keys.c 的log
    • 五、上层代码 加log 分析 keycode 方法
      • 5.1 添加 Activity.java --->dispatchKeyEvent() 函数打印log
      • 5.2 添加 View.java --->dispatchKeyEvent() 函数打印log
      • 5.3 添加 KeyEvent.java --->dispatchKeyEvent() 函数打印log
    • 六、 代码内容
      • 6.1 msm8909-mtp.dtsi 文件内容
      • 6.2 gpio-keys.c 文件内容
      • 6.3 aw9523.c 文件内容


一、 确认keycode值,同步修改上层键值映射表

从Generic.kl文件中,我们可以看到 key 204 (0xCC)是没人使用的,
为避免和默认代码有冲突,我们使用 key 204 (0xCC)来做为按键 keycoe。

@frameworksbasedatakeyboardsGeneric.kl
# key 202 "KEY_PROG3"
# key 203 "KEY_PROG4"
# key 204 (undefined)
key 204 "KEY_SOS"		WAKE
# key 205 "KEY_SUSPEND"
# key 206 "KEY_CLOSE"

@ deviceqcommsm8909gpio-keys.kl
key 102   HOME	          WAKE
key 528   FOCUS
key 766   CAMERA
key 204   "KEY_SOS"			WAKE

以上代码确认生效方法 ----->
(1)编译前,剪切或者删除这个目录到其他地方:outtargetproductmsm8937_32gosystemusrkeylayout
(2)整编译大版本 make all
(3)编成功后 看下 outtargetproductmsm8937_32gosystemusrkeylayoutgpio-keys.kl 和 Generic.kl ,看下有没有自已的修改。
(4)下载版本到手机,adb 下 进入 system/usr/keylayout/*.kl 确认是否生效


Tips 1: 选择一个好的键值的好处

系统默认使用了很多键值,如果使用的键值和系统重复了,可能存在这样一种情况。
在上层我们自已写的代码中,永远都监听不到这个事件,因为这个事件已经被别人处理了!!!
为了避免这个情况的发生,建议选择一个未使用过的键值。

Tips 2: 如何确认驱动代是否ok

把键值 改成 KEY_POWER (0x74), 看下跑到代码中后,是否会亮灭屏。
如果会亮灭屏,恭喜你,驱动代码是OK的,接下来检查键值就好了。



二、 驱动代码编写

//for gpio91 SOS 20190921 +++
#define KEY_SOS 204  	// (0xCC)   key code
static int  gpio_sos_num   91  	// (GPIO 91)
int gpio_sos_num_irq = 0;
static struct work_struct gpio_sos_work;


static void gpio_sos_work_handler(struct work_struct *work)
{
	struct aw9523_kpad_platform_data *pdata;

	pdata = g_aw9523_data;

	input_report_key(pdata->input, KEY_SOS, 1);
	input_sync(pdata->input);
	input_report_key(pdata->input, KEY_SOS, 0);
	input_sync(pdata->input);

	// 为了快速看到现象, 可以上报 KEY_POWER 事件, 看是否会亮灭屏,后续注释该代码  ++++
	input_report_key(pdata->input, KEY_POWER, 1);
	input_sync(pdata->input);
	input_report_key(pdata->input, KEY_POWER, 0);
	input_sync(pdata->input);
	// 为了快速看到现象, 可以上报 KEY_POWER 事件, 看是否会亮灭屏,后续注释该代码  ----
	

	printk("[hyl_sos][%s] input_report_key KEY_SOS=%d(0x%x)", __func__, KEY_SOS);
	
	enable_irq(gpio_sos_irq);
}

static irqreturn_t gpio_sos_irq_handler(int irq, void *handle)
{
	struct i2c_client *client = handle;
	struct aw9523_kpad_platform_data *pdata;
	
	disable_irq_nosync(gpio_sos_irq);
	
	schedule_work(&gpio_sos_work);
	
	return IRQ_HANDLED;
}

//for gpio91 SOS 20190921 ---



static int aw9523_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	pdata->input = input_allocate_device();//为新添加的输入设备分配内存
	if (!pdata->input) {
		error = -ENOMEM;
		goto err_free_mem;
	}
	pdata->client = client;
	pdata->input->name = "aw9523-keys";	
	pdata->input->phys = "aw9523-keys/input0";
	pdata->input->dev.parent = &client->dev;
	input_set_drvdata(pdata->input, pdata);//函数用来设置device的私有数据,保存驱动的私有数据。
	__set_bit(EV_KEY, pdata->input->evbit);//支持的事件,EV_KEY事件,事件类型由input_dev.evbit表示
	
	
	//gpio91 SOS 20190921 +++
	// 为了快速看到现象, 可以上报 KEY_POWER 事件, 看是否会亮灭屏,后续注释该代码  ++++
	__set_bit(KEY_POWER & KEY_MAX, pdata->input->keybit);
	// 为了快速看到现象, 可以上报 KEY_POWER 事件, 看是否会亮灭屏,后续注释该代码  ----
	
	__set_bit(KEY_SOS & KEY_MAX, pdata->input->keybit);
	
	ret = gpio_request(gpio_sos_num, "gpio_sos");
	if (ret) {
		dev_err(&client->dev, "[hyl_sos]unable to request gpio [%d]n", gpio_sos_num);
	}
	
	ret = gpio_direction_input(gpio_sos_num);
	if (ret) {
		dev_err(&client->dev, "[hyl_sos]unable to set direction for gpio [%d]n", gpio_sos_num);
	}

	gpio_sos_num_irq = gpio_to_irq(gpio_sos_num);
	
	ret = devm_request_threaded_irq(&client->dev, gpio_sos_num_irq, NULL,
		gpio_sos_irq_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
		"gpio_sos_irq", client);
	if (ret) {
		dev_err(&client->dev, "[hyl_sos]Failed irq=%d request ret = %dn", gpio_sos_num_irq, ret);
	}
	printk("[hyl_sos][%s] request irq success, gpio_sos_num_irq=%d, KEY_SOS=%d(0x%x) n", 
				__func__, gpio_sos_num_irq , KEY_SOS);
				
	INIT_WORK(&gpio_sos_work, gpio_sos_work_handler);			
	
	//for gpio91 SOS 20190921 ---

	error = input_register_device(pdata->input);//将input_dev(输入设备结构体)注册到输入子系统核心中
}


static int aw9523_remove(struct i2c_client *client)
{
	cancel_work_sync(gpio_sos_work);
}



三、代码调试

getevent -l 看下是否按键是否有键值 上报。
注意: 如果要 getevent -l 看到具体名字话,请参考第一章修改。

如下:

C:UsersAdministrator>adb shell
ASUS_X00P_1:/ # getevent -l
add device 2: /dev/input/event8
  name:     "msm8952-snd-card-mtp Button Jack"
add device 3: /dev/input/event7
  name:     "msm8952-snd-card-mtp Headset Jack"
add device 9: /dev/input/event0
  name:     "qpnp_pon"
could not get driver version for /dev/input/mice, Not a typewriter
add device 10: /dev/input/event5
  name:     "gpio-keys"

/dev/input/event0: EV_KEY       KEY_VOLUMEDOWN       DOWN
/dev/input/event0: EV_SYN       SYN_REPORT           00000000
/dev/input/event0: EV_KEY       KEY_VOLUMEDOWN       UP
/dev/input/event0: EV_SYN       SYN_REPORT           00000000


如果映射不对,可以调用
adb shell dumpsys input
看下调用的是哪个 kl 文件,然后把对应的键值 定义在 kl 文件中,就可以了。
如下:

ASUS_X00P_1:/ # dumpsys  input
INPUT MANAGER (dumpsys input)

Input Manager State:
  Interactive: false
  System UI Visibility: 0x8608
  Pointer Speed: 0
  Pointer Gestures Enabled: true
  Show Touches: false
  Pointer Capture Enabled: false

Event Hub State:
  BuiltInKeyboardId: -2
  Devices:
    -1: Virtual
      Classes: 0x40000023
      Path: <virtual>
      Descriptor: a718a782d34bc767f4689c232d64d527998ea7fd
      Location:
      ControllerNumber: 0
      UniqueId: <virtual>
      Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000
      KeyLayoutFile: /system/usr/keylayout/Generic.kl
      KeyCharacterMapFile: /system/usr/keychars/Virtual.kcm
      ConfigurationFile:
      HaveKeyboardLayoutOverlay: false
    1: uinput-fpsensor
      Classes: 0x80000001
      Path: /dev/input/event9
      Descriptor: 485d69228e24f5e46da1598745890b214130dbc4
      Location:
      ControllerNumber: 0
      UniqueId:
      Identifier: bus=0x0003, vendor=0x0001, product=0x0001, version=0x0001
      KeyLayoutFile: /system/usr/keylayout/Generic.kl
      KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
      ConfigurationFile:
      HaveKeyboardLayoutOverlay: false
    2: msm8952-snd-card-mtp Button Jack
      Classes: 0x00000001
      Path: /dev/input/event8
      Descriptor: ab997034df873c1d418cf7db1475423a61777921
      Location: ALSA
      ControllerNumber: 0
      UniqueId:
      Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000
      KeyLayoutFile: /system/usr/keylayout/Generic.kl
      KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
      ConfigurationFile:
      HaveKeyboardLayoutOverlay: false
    3: msm8952-snd-card-mtp Headset Jack
      Classes: 0x00000080
      Path: /dev/input/event7
      Descriptor: 863975bb9064de0fc8a7277b87ac846c366d49c1
      Location: ALSA
      ControllerNumber: 0
      UniqueId:
      Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000
      KeyLayoutFile:
      KeyCharacterMapFile:
      ConfigurationFile:
      HaveKeyboardLayoutOverlay: false
    4: goodix-ts
      Classes: 0x00000015
      Path: /dev/input/event6
      Descriptor: 9e6143a1bc5dd41251b165ed559e32d49b5aad8f
      Location: input/ts
      ControllerNumber: 0
      UniqueId:
      Identifier: bus=0x0018, vendor=0xdead, product=0xbeef, version=0x28bb
      KeyLayoutFile: /system/usr/keylayout/Generic.kl
      KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
      ConfigurationFile:
      HaveKeyboardLayoutOverlay: false
    5: rf-keys
      Classes: 0x00000001
      Path: /dev/input/event4
      Descriptor: bd7533fcf563b1a6b2c798f0e54e396a7aa1eecf
      Location:
      ControllerNumber: 0
      UniqueId:
      Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000
      KeyLayoutFile: /system/usr/keylayout/Generic.kl
      KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
      ConfigurationFile:
      HaveKeyboardLayoutOverlay: false
    6: hbtp_vm
      Classes: 0x00000008
      Path: /dev/input/event3
      Descriptor: 793e303307c34fc398863d5f83ee4c6ea6a7c5c6
      Location:
      ControllerNumber: 0
      UniqueId:
      Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000
      KeyLayoutFile:
      KeyCharacterMapFile:
      ConfigurationFile:
      HaveKeyboardLayoutOverlay: false
    7: sf-keys
      Classes: 0x00000001
      Path: /dev/input/event2
      Descriptor: 1d04485d5413d18cadf673bafa785070142031c8
      Location:
      ControllerNumber: 0
      UniqueId:
      Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000
      KeyLayoutFile: /system/usr/keylayout/Generic.kl
      KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
      ConfigurationFile:
      HaveKeyboardLayoutOverlay: false
    8: madev
      Classes: 0x00000001
      Path: /dev/input/event1
      Descriptor: 6e687c668d87f7a84f420f31851eb0fabc6ebb08
      Location:
      ControllerNumber: 0
      UniqueId:
      Identifier: bus=0x001c, vendor=0x0000, product=0x0000, version=0x0000
      KeyLayoutFile: /system/usr/keylayout/Generic.kl
      KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
      ConfigurationFile:
      HaveKeyboardLayoutOverlay: false
    9: qpnp_pon
      Classes: 0x00000001
      Path: /dev/input/event0
      Descriptor: fb60d4f4370f5dbe8267b63d38dea852987571ab
      Location: qpnp_pon/input0
      ControllerNumber: 0
      UniqueId:
      Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000
      KeyLayoutFile: /system/usr/keylayout/qpnp_pon.kl
      KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
      ConfigurationFile:
      HaveKeyboardLayoutOverlay: false
    10: gpio-keys
      Classes: 0x00000001
      Path: /dev/input/event5
      Descriptor: d2c52ff0f656fac4cd7b7a118d575e0109a9fe1c
      Location: gpio-keys/input0
      ControllerNumber: 0
      UniqueId:
      Identifier: bus=0x0019, vendor=0x0001, product=0x0001, version=0x0100
      KeyLayoutFile: /system/usr/keylayout/gpio-keys.kl
      KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
      ConfigurationFile:
      HaveKeyboardLayoutOverlay: false



四、 驱动代码另一种实现方法——dts 实现

4.1 DTS 配置

@frameworksbasedatakeyboardsGeneric.kl
# key 204 (undefined)
key 204 "KEY_SOS"
# key 205 "KEY_SUSPEND"

@ deviceqcommsm8909gpio-keys.kl
key 102   HOME	          WAKE
key 204   "KEY_SOS"		WAKE



@ kernelmsm-3.18archarmbootdtsqcommsm8909-mtp.dtsi
	gpio_keys {
		compatible = "gpio-keys";
		input-name = "gpio-keys";
		pinctrl-names = "tlmm_gpio_key_active","tlmm_gpio_key_suspend";
		pinctrl-0 = <&gpio_key_active>;
		pinctrl-1 = <&gpio_key_suspend>;

		gpio_sos{
			label = "gpio_sos";
			gpios = <&msm_gpio 91 0x1>;
			linux,input-type = <1>;
			linux,code = <0xCC>;  // 0x204
			gpio-key,wakeup;
			debounce-interval = <15>;
		};
		
		/*  注释这段代码
		camera_focus {
			label = "camera_focus";
			gpios = <&msm_gpio 91 0x1>;
			linux,input-type = <1>;
			linux,code = <0x210>;
			gpio-key,wakeup;
			debounce-interval = <15>;
		};
		*/

		camera_snapshot {
			label = "camera_snapshot";
			gpios = <&msm_gpio 92 0x1>;
			linux,input-type = <1>;
			linux,code = <0x2fe>;
			gpio-key,wakeup;
			debounce-interval = <15>;
		};

		vol_up {
			label = "volume_up";
			gpios = <&msm_gpio 90 0x1>;
			linux,input-type = <1>;
			linux,code = <115>;
			gpio-key,wakeup;
			debounce-interval = <15>;
		};
	};

4.2 添加 gpio-keys.c 的log

@ kernel.....gpio-keys.c

// 添加 DTS 解析的log
static struct gpio_keys_platform_data * gpio_keys_get_devtree_pdata(struct device *dev)
{
		gpio = of_get_gpio_flags(pp, 0, &flags);
		button = &pdata->buttons[i++];

		button->gpio = gpio;
		button->active_low = flags & OF_GPIO_ACTIVE_LOW;

		if (of_property_read_u32(pp, "linux,code", &button->code)) {
			dev_err(dev, "Button without keycode: 0x%xn", button->gpio);
			return ERR_PTR(-EINVAL);
		}

		button->desc = of_get_property(pp, "label", NULL);

		if (of_property_read_u32(pp, "linux,input-type", &button->type))
			button->type = EV_KEY;

		button->wakeup = !!of_get_property(pp, "gpio-key,wakeup", NULL);

		if (of_property_read_u32(pp, "debounce-interval", &button->debounce_interval))
			button->debounce_interval = 5;
			
		dev_err(dev, "[hyl_sos] [gpio_keys_get_devtree_pdata], gpio:%d, keycode,%d(0x%x), lable=%s n",
				button->gpio, button->code, 
				button->code, button->type);   ///+++++++++++++++++++++++++++++++++
}

// 添加 gpio 上报 key event 的log
static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
{
	state = (__gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low;

	if (type == EV_ABS) {
			input_event(input, type, button->code, button->value);
	} else {
		input_event(input, type, button->code, !!state);
	}
	input_sync(input);
	printk("[hyl_sos][gpio_keys_gpio_report_event], gpio:%d, keycode,%d(0x%x), lable=%s have report to inputsystemn",
				button->gpio, button->code, 
				button->code, button->type);  ///+++++++++++++++++++++++++++++++++
}


// 在 按键中断中 上报键值,添加 log 打印 按下按键的log
static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id)
{
		input_event(input, EV_KEY, button->code, 1);
		input_sync(input);
		
		printk("[hyl_sos][gpio_keys_irq_isr], gpio:%d, keycode,%d(0x%x), lable=%s have report 1(pressed) to inputsystemn",
				bdata->button->gpio, bdata->button->code, 
				bdata->button->code, bdata->button->type);  ///+++++++++++++++++++++++++++++++++
}

// 在 定时器中,添加 log 打印 释放按键的log
static void gpio_keys_irq_timer(unsigned long _data)
{
	if (bdata->key_pressed) {
		input_event(input, EV_KEY, bdata->button->code, 0);
		input_sync(input);
		printk("[hyl_sos][gpio_keys_irq_timer], gpio:%d, keycode,%d(0x%x), lable=%s have report 0(released) to inputsystemn",
				bdata->button->gpio, bdata->button->code, 
				bdata->button->code, bdata->button->type);  ///+++++++++++++++++++++++++++++++++
		bdata->key_pressed = false;
	}

}



五、上层代码 加log 分析 keycode 方法

在上层中,最早处理 keyevent 的时候是在
frameworksbasecorejavaandroidappActivity.java 中

可以按如下流程添加log,分析 上层接收到的 keycode 值。

5.1 添加 Activity.java —>dispatchKeyEvent() 函数打印log

@ frameworksbasecorejavaandroidappActivity.java

    public boolean dispatchKeyEvent(KeyEvent event) {
        onUserInteraction();

        // Let action bars open menus in response to the menu key prioritized over
        // the window handling it
        final int keyCode = event.getKeyCode();

		// add log  begin +++++++
		Slog.v(TAG, "[hyl_sos][dispatchKeyEvent]  " + this + ": " + keyCode); ///+++++++++++++++++++++++++++++++++
		// add log  end --------

        if (keyCode == KeyEvent.KEYCODE_MENU &&
                mActionBar != null && mActionBar.onMenuKeyEvent(event)) {
            return true;
        }

        Window win = getWindow();
        if (win.superDispatchKeyEvent(event)) {
            return true;
        }
        View decor = mDecor;
        if (decor == null) decor = win.getDecorView();
        return event.dispatch(this, decor != null
                ? decor.getKeyDispatcherState() : null, this);
    }

public boolean onKeyDown(int keyCode, KeyEvent event)  {
        Slog.v(TAG, "[hyl_sos][onKeyDown]  " + this + ": " + keyCode); ///+++++++++++++++++++++++++++++++++

        if (keyCode == KeyEvent.KEYCODE_BACK) {
            if (getApplicationInfo().targetSdkVersion
                    >= Build.VERSION_CODES.ECLAIR) {
                event.startTracking();
            } else {
                onBackPressed();
            }
            return true;
        }

public boolean onKeyUp(int keyCode, KeyEvent event) {
		Slog.v(TAG, "[hyl_sos][onKeyUp]  " + this + ": " + keyCode);   ///+++++++++++++++++++++++++++++++++
        if (getApplicationInfo().targetSdkVersion
                >= Build.VERSION_CODES.ECLAIR) {
            if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking()
                    && !event.isCanceled()) {
                onBackPressed();
                return true;
            }
        }
        return false;
    }

5.2 添加 View.java —>dispatchKeyEvent() 函数打印log

frameworksbasecorejavaandroidviewView.java
  /**
     * Dispatch a key event to the next view on the focus path. This path runs
     * from the top of the view tree down to the currently focused view. If this
     * view has focus, it will dispatch to itself. Otherwise it will dispatch
     * the next node down the focus path. This method also fires any key
     * listeners.
     *
     * @param event The key event to be dispatched.
     * @return True if the event was handled, false otherwise.
     */
    public boolean dispatchKeyEvent(KeyEvent event) {

        final int keyCode = event.getKeyCode();
        Log.e(VIEW_LOG_TAG, "[hyl_sos][View.java][dispatchKeyEvent]  keyCode = %d n", keyCode); ///+++++++++++++++++++++++++++++++++


        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onKeyEvent(event, 0);
        }

        // Give any attached key listener a first crack at the event.
        //noinspection SimplifiableIfStatement
        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
                && li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) {
            return true;
        }

        if (event.dispatch(this, mAttachInfo != null
                ? mAttachInfo.mKeyDispatchState : null, this)) {
            return true;
        }

        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
        }
        return false;
    }



5.3 添加 KeyEvent.java —>dispatchKeyEvent() 函数打印log

在 所有 KeyEvent的函中,打印 keycode 值:

Log.v(TAG, "[hyl_sos][KeyEvent(4) ] Keycode = " + mKeyCode);

代码如下

frameworksbasecorejavaandroidviewKeyEvent.java

public class KeyEvent extends InputEvent implements Parcelable {

     */
    public final boolean dispatch(Callback receiver, DispatcherState state,
            Object target) {

        Log.v(TAG, "[hyl_sos][KeyEvent][dispatch]   Keycode = " + mKeyCode); ///+++++++++++++++++++++++++++++++++
        switch (mAction) {
            case ACTION_DOWN: 
                boolean res = receiver.onKeyDown(mKeyCode, this);
            case ACTION_UP:
                return receiver.onKeyUp(mKeyCode, this);
            case ACTION_MULTIPLE:
                receiver.onKeyMultiple(code, count, this))
                if (code != KeyEvent.KEYCODE_UNKNOWN) {
                    mAction = ACTION_DOWN;
                    mRepeatCount = 0;
                    boolean handled = receiver.onKeyDown(code, this);
                    if (handled) {
                        mAction = ACTION_UP;
                        receiver.onKeyUp(code, this);
                }
    }


  public KeyEvent(int action, int code) {
        mAction = action;
        mKeyCode = code;
        mRepeatCount = 0;
        mDeviceId = KeyCharacterMap.VIRTUAL_KEYBOARD;
        Log.v(TAG, "[hyl_sos][KeyEvent(int action, int code)]   Keycode = " + mKeyCode); ///+++++++++++++++++++++++++++++++++
    }
    
public KeyEvent(long downTime, long eventTime, int action,
                    int code, int repeat) {
        mDownTime = downTime;
        mEventTime = eventTime;
        mAction = action;
        mKeyCode = code;
        mRepeatCount = repeat;
        mDeviceId = KeyCharacterMap.VIRTUAL_KEYBOARD;
        Log.v(TAG, "[hyl_sos][KeyEvent(5) ]   Keycode = " + mKeyCode); ///+++++++++++++++++++++++++++++++++
    }
    
public KeyEvent(long downTime, long eventTime, int action,
                    int code, int repeat, int metaState) {
        mDownTime = downTime;
        mEventTime = eventTime;
        mAction = action;
        mKeyCode = code;
        mRepeatCount = repeat;
        mMetaState = metaState;
        mDeviceId = KeyCharacterMap.VIRTUAL_KEYBOARD;
        Log.v(TAG, "[hyl_sos][KeyEvent(6) ]   Keycode = " + mKeyCode); ///+++++++++++++++++++++++++++++++++
    }
 
 public KeyEvent(long downTime, long eventTime, int action,
                    int code, int repeat, int metaState,
                    int deviceId, int scancode) {
        mDownTime = downTime;
        mEventTime = eventTime;
        mAction = action;
        mKeyCode = code;
        mRepeatCount = repeat;
        mMetaState = metaState;
        mDeviceId = deviceId;
        mScanCode = scancode;
        Log.v(TAG, "[hyl_sos][KeyEvent(8) ]   Keycode = " + mKeyCode); ///+++++++++++++++++++++++++++++++++
    }
    
public KeyEvent(long downTime, long eventTime, int action,
                    int code, int repeat, int metaState,
                    int deviceId, int scancode, int flags) {
        mDownTime = downTime;
        mEventTime = eventTime;
        mAction = action;
        mKeyCode = code;
        mRepeatCount = repeat;
        mMetaState = metaState;
        mDeviceId = deviceId;
        mScanCode = scancode;
        mFlags = flags;
        Log.v(TAG, "[hyl_sos][KeyEvent(9) ]   Keycode = " + mKeyCode); ///+++++++++++++++++++++++++++++++++
    }
    
public KeyEvent(long downTime, long eventTime, int action,
                    int code, int repeat, int metaState,
                    int deviceId, int scancode, int flags, int source) {
        mDownTime = downTime;
        mEventTime = eventTime;
        mAction = action;
        mKeyCode = code;
        mRepeatCount = repeat;
        mMetaState = metaState;
        mDeviceId = deviceId;
        mScanCode = scancode;
        mFlags = flags;
        mSource = source;
        Log.v(TAG, "[hyl_sos][KeyEvent(10) ]   Keycode = " + mKeyCode); ///+++++++++++++++++++++++++++++++++
    }
    
public KeyEvent(long time, String characters, int deviceId, int flags) {
        mDownTime = time;
        mEventTime = time;
        mCharacters = characters;
        mAction = ACTION_MULTIPLE;
        mKeyCode = KEYCODE_UNKNOWN;
        mRepeatCount = 0;
        mDeviceId = deviceId;
        mFlags = flags;
        mSource = InputDevice.SOURCE_KEYBOARD;
        Log.v(TAG, "[hyl_sos][KeyEvent(4) ]   Keycode = " + mKeyCode); ///+++++++++++++++++++++++++++++++++
    }
    
public KeyEvent(KeyEvent origEvent) {
        mDownTime = origEvent.mDownTime;
        mEventTime = origEvent.mEventTime;
        mAction = origEvent.mAction;
        mKeyCode = origEvent.mKeyCode;
        mRepeatCount = origEvent.mRepeatCount;
        mMetaState = origEvent.mMetaState;
        mDeviceId = origEvent.mDeviceId;
        mSource = origEvent.mSource;
        mScanCode = origEvent.mScanCode;
        mFlags = origEvent.mFlags;
        mCharacters = origEvent.mCharacters;
        Log.v(TAG, "[hyl_sos][KeyEvent(KeyEvent origEvent) ]   Keycode = " + mKeyCode); ///+++++++++++++++++++++++++++++++++
    }
public KeyEvent(KeyEvent origEvent, long eventTime, int newRepeat) {
        mDownTime = origEvent.mDownTime;
        mEventTime = eventTime;
        mAction = origEvent.mAction;
        mKeyCode = origEvent.mKeyCode;
        mRepeatCount = newRepeat;
        mMetaState = origEvent.mMetaState;
        mDeviceId = origEvent.mDeviceId;
        mSource = origEvent.mSource;
        mScanCode = origEvent.mScanCode;
        mFlags = origEvent.mFlags;
        mCharacters = origEvent.mCharacters;
        Log.v(TAG, "[hyl_sos][KeyEvent(3)]   Keycode = " + mKeyCode); ///+++++++++++++++++++++++++++++++++
    }


private KeyEvent(KeyEvent origEvent, int action) {
        mDownTime = origEvent.mDownTime;
        mEventTime = origEvent.mEventTime;
        mAction = action;
        mKeyCode = origEvent.mKeyCode;
        mRepeatCount = origEvent.mRepeatCount;
        mMetaState = origEvent.mMetaState;
        mDeviceId = origEvent.mDeviceId;
        mSource = origEvent.mSource;
        mScanCode = origEvent.mScanCode;
        mFlags = origEvent.mFlags;
        Log.v(TAG, "[hyl_sos][KeyEvent(KeyEvent origEvent, int action) ]   Keycode = " + mKeyCode);  ///+++++++++++++++++++++++++++++++++
        // Don't copy mCharacters, since one way or the other we'll lose it
        // when changing the action.
    }

private KeyEvent(Parcel in) {
        mDeviceId = in.readInt();
        mSource = in.readInt();
        mAction = in.readInt();
        mKeyCode = in.readInt();
        mRepeatCount = in.readInt();
        mMetaState = in.readInt();
        mScanCode = in.readInt();
        mFlags = in.readInt();
        mDownTime = in.readLong();
        mEventTime = in.readLong();
        Log.v(TAG, "[hyl_sos][KeyEvent(Parcel in)]   Keycode = " + mKeyCode);  ///+++++++++++++++++++++++++++++++++
    }

不知不觉就这么晚,实在扛不住,睡觉了
Date:2019/09/25 - 0:30



六、 代码内容

6.1 msm8909-mtp.dtsi 文件内容

@ kernelmsm-3.18archarmbootdtsqcommsm8909-mtp.dtsi
	gpio_keys {
		compatible = "gpio-keys";
		input-name = "gpio-keys";
		pinctrl-names = "tlmm_gpio_key_active","tlmm_gpio_key_suspend";
		pinctrl-0 = <&gpio_key_active>;
		pinctrl-1 = <&gpio_key_suspend>;

		gpio_sos{
			label = "gpio_sos";
			gpios = <&msm_gpio 91 0x1>;
			linux,input-type = <1>;
			linux,code = <0xCC>;  // 0x204
			gpio-key,wakeup;
			debounce-interval = <15>;
		};
		
		/*  注释这段代码
		camera_focus {
			label = "camera_focus";
			gpios = <&msm_gpio 91 0x1>;
			linux,input-type = <1>;
			linux,code = <0x210>;
			gpio-key,wakeup;
			debounce-interval = <15>;
		};
		*/

		camera_snapshot {
			label = "camera_snapshot";
			gpios = <&msm_gpio 92 0x1>;
			linux,input-type = <1>;
			linux,code = <0x2fe>;
			gpio-key,wakeup;
			debounce-interval = <15>;
		};

		vol_up {
			label = "volume_up";
			gpios = <&msm_gpio 90 0x1>;
			linux,input-type = <1>;
			linux,code = <115>;
			gpio-key,wakeup;
			debounce-interval = <15>;
		};
	};

6.2 gpio-keys.c 文件内容

/*
 * Driver for keys on GPIO lines capable of generating interrupts.
 *
 * Copyright 2005 Phil Blundell
 * Copyright 2010, 2011 David Jander <david@protonic.nl>
 * Copyright (c) 2015, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/module.h>

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/gpio_keys.h>
#include <linux/workqueue.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/spinlock.h>
#include <linux/pinctrl/consumer.h>
#include <linux/syscore_ops.h>

struct gpio_button_data {
	const struct gpio_keys_button *button;
	struct input_dev *input;
	struct timer_list timer;
	struct work_struct work;
	unsigned int timer_debounce;	/* in msecs */
	unsigned int irq;
	spinlock_t lock;
	bool disabled;
	bool key_pressed;
};

struct gpio_keys_drvdata {
	const struct gpio_keys_platform_data *pdata;
	struct pinctrl *key_pinctrl;
	struct input_dev *input;
	struct mutex disable_lock;
	struct gpio_button_data data[0];
};

static struct device *global_dev;
static struct syscore_ops gpio_keys_syscore_pm_ops;

static void gpio_keys_syscore_resume(void);

/*
 * SYSFS interface for enabling/disabling keys and switches:
 *
 * There are 4 attributes under /sys/devices/platform/gpio-keys/
 *	keys [ro]              - bitmap of keys (EV_KEY) which can be
 *	                         disabled
 *	switches [ro]          - bitmap of switches (EV_SW) which can be
 *	                         disabled
 *	disabled_keys [rw]     - bitmap of keys currently disabled
 *	disabled_switches [rw] - bitmap of switches currently disabled
 *
 * Userland can change these values and hence disable event generation
 * for each key (or switch). Disabling a key means its interrupt line
 * is disabled.
 *
 * For example, if we have following switches set up as gpio-keys:
 *	SW_DOCK = 5
 *	SW_CAMERA_LENS_COVER = 9
 *	SW_KEYPAD_SLIDE = 10
 *	SW_FRONT_PROXIMITY = 11
 * This is read from switches:
 *	11-9,5
 * Next we want to disable proximity (11) and dock (5), we write:
 *	11,5
 * to file disabled_switches. Now proximity and dock IRQs are disabled.
 * This can be verified by reading the file disabled_switches:
 *	11,5
 * If we now want to enable proximity (11) switch we write:
 *	5
 * to disabled_switches.
 *
 * We can disable only those keys which don't allow sharing the irq.
 */

/**
 * get_n_events_by_type() - returns maximum number of events per @type
 * @type: type of button (%EV_KEY, %EV_SW)
 *
 * Return value of this function can be used to allocate bitmap
 * large enough to hold all bits for given type.
 */
static inline int get_n_events_by_type(int type)
{
	BUG_ON(type != EV_SW && type != EV_KEY);

	return (type == EV_KEY) ? KEY_CNT : SW_CNT;
}

/**
 * gpio_keys_disable_button() - disables given GPIO button
 * @bdata: button data for button to be disabled
 *
 * Disables button pointed by @bdata. This is done by masking
 * IRQ line. After this function is called, button won't generate
 * input events anymore. Note that one can only disable buttons
 * that don't share IRQs.
 *
 * Make sure that @bdata->disable_lock is locked when entering
 * this function to avoid races when concurrent threads are
 * disabling buttons at the same time.
 */
static void gpio_keys_disable_button(struct gpio_button_data *bdata)
{
	if (!bdata->disabled) {
		/*
		 * Disable IRQ and possible debouncing timer.
		 */
		disable_irq(bdata->irq);
		if (bdata->timer_debounce)
			del_timer_sync(&bdata->timer);

		bdata->disabled = true;
	}
}

/**
 * gpio_keys_enable_button() - enables given GPIO button
 * @bdata: button data for button to be disabled
 *
 * Enables given button pointed by @bdata.
 *
 * Make sure that @bdata->disable_lock is locked when entering
 * this function to avoid races with concurrent threads trying
 * to enable the same button at the same time.
 */
static void gpio_keys_enable_button(struct gpio_button_data *bdata)
{
	if (bdata->disabled) {
		enable_irq(bdata->irq);
		bdata->disabled = false;
	}
}

/**
 * gpio_keys_attr_show_helper() - fill in stringified bitmap of buttons
 * @ddata: pointer to drvdata
 * @buf: buffer where stringified bitmap is written
 * @type: button type (%EV_KEY, %EV_SW)
 * @only_disabled: does caller want only those buttons that are
 *                 currently disabled or all buttons that can be
 *                 disabled
 *
 * This function writes buttons that can be disabled to @buf. If
 * @only_disabled is true, then @buf contains only those buttons
 * that are currently disabled. Returns 0 on success or negative
 * errno on failure.
 */
static ssize_t gpio_keys_attr_show_helper(struct gpio_keys_drvdata *ddata,
					  char *buf, unsigned int type,
					  bool only_disabled)
{
	int n_events = get_n_events_by_type(type);
	unsigned long *bits;
	ssize_t ret;
	int i;

	bits = kcalloc(BITS_TO_LONGS(n_events), sizeof(*bits), GFP_KERNEL);
	if (!bits)
		return -ENOMEM;

	for (i = 0; i < ddata->pdata->nbuttons; i++) {
		struct gpio_button_data *bdata = &ddata->data[i];

		if (bdata->button->type != type)
			continue;

		if (only_disabled && !bdata->disabled)
			continue;

		__set_bit(bdata->button->code, bits);
	}

	ret = bitmap_scnlistprintf(buf, PAGE_SIZE - 2, bits, n_events);
	buf[ret++] = 'n';
	buf[ret] = '';

	kfree(bits);

	return ret;
}

/**
 * gpio_keys_attr_store_helper() - enable/disable buttons based on given bitmap
 * @ddata: pointer to drvdata
 * @buf: buffer from userspace that contains stringified bitmap
 * @type: button type (%EV_KEY, %EV_SW)
 *
 * This function parses stringified bitmap from @buf and disables/enables
 * GPIO buttons accordingly. Returns 0 on success and negative error
 * on failure.
 */
static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata,
					   const char *buf, unsigned int type)
{
	int n_events = get_n_events_by_type(type);
	unsigned long *bits;
	ssize_t error;
	int i;

	bits = kcalloc(BITS_TO_LONGS(n_events), sizeof(*bits), GFP_KERNEL);
	if (!bits)
		return -ENOMEM;

	error = bitmap_parselist(buf, bits, n_events);
	if (error)
		goto out;

	/* First validate */
	for (i = 0; i < ddata->pdata->nbuttons; i++) {
		struct gpio_button_data *bdata = &ddata->data[i];

		if (bdata->button->type != type)
			continue;

		if (test_bit(bdata->button->code, bits) &&
		    !bdata->button->can_disable) {
			error = -EINVAL;
			goto out;
		}
	}

	mutex_lock(&ddata->disable_lock);

	for (i = 0; i < ddata->pdata->nbuttons; i++) {
		struct gpio_button_data *bdata = &ddata->data[i];

		if (bdata->button->type != type)
			continue;

		if (test_bit(bdata->button->code, bits))
			gpio_keys_disable_button(bdata);
		else
			gpio_keys_enable_button(bdata);
	}

	mutex_unlock(&ddata->disable_lock);

out:
	kfree(bits);
	return error;
}

#define ATTR_SHOW_FN(name, type, only_disabled)				
static ssize_t gpio_keys_show_##name(struct device *dev,		
				     struct device_attribute *attr,	
				     char *buf)				
{									
	struct platform_device *pdev = to_platform_device(dev);		
	struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);	
									
	return gpio_keys_attr_show_helper(ddata, buf,			
					  type, only_disabled);		
}

ATTR_SHOW_FN(keys, EV_KEY, false);
ATTR_SHOW_FN(switches, EV_SW, false);
ATTR_SHOW_FN(disabled_keys, EV_KEY, true);
ATTR_SHOW_FN(disabled_switches, EV_SW, true);

/*
 * ATTRIBUTES:
 *
 * /sys/devices/platform/gpio-keys/keys [ro]
 * /sys/devices/platform/gpio-keys/switches [ro]
 */
static DEVICE_ATTR(keys, S_IRUGO, gpio_keys_show_keys, NULL);
static DEVICE_ATTR(switches, S_IRUGO, gpio_keys_show_switches, NULL);

#define ATTR_STORE_FN(name, type)					
static ssize_t gpio_keys_store_##name(struct device *dev,		
				      struct device_attribute *attr,	
				      const char *buf,			
				      size_t count)			
{									
	struct platform_device *pdev = to_platform_device(dev);		
	struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);	
	ssize_t error;							
									
	error = gpio_keys_attr_store_helper(ddata, buf, type);		
	if (error)							
		return error;						
									
	return count;							
}

ATTR_STORE_FN(disabled_keys, EV_KEY);
ATTR_STORE_FN(disabled_switches, EV_SW);

/*
 * ATTRIBUTES:
 *
 * /sys/devices/platform/gpio-keys/disabled_keys [rw]
 * /sys/devices/platform/gpio-keys/disables_switches [rw]
 */
static DEVICE_ATTR(disabled_keys, S_IWUSR | S_IRUGO,
		   gpio_keys_show_disabled_keys,
		   gpio_keys_store_disabled_keys);
static DEVICE_ATTR(disabled_switches, S_IWUSR | S_IRUGO,
		   gpio_keys_show_disabled_switches,
		   gpio_keys_store_disabled_switches);

static struct attribute *gpio_keys_attrs[] = {
	&dev_attr_keys.attr,
	&dev_attr_switches.attr,
	&dev_attr_disabled_keys.attr,
	&dev_attr_disabled_switches.attr,
	NULL,
};

static struct attribute_group gpio_keys_attr_group = {
	.attrs = gpio_keys_attrs,
};

static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
{
	const struct gpio_keys_button *button = bdata->button;
	struct input_dev *input = bdata->input;
	unsigned int type = button->type ?: EV_KEY;
	int state;

	state = (__gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low;

	if (type == EV_ABS) {
		if (state)
			input_event(input, type, button->code, button->value);
	} else {
		input_event(input, type, button->code, !!state);
	}
	input_sync(input);
	printk("[hyl_sos][gpio_keys_gpio_report_event], gpio:%d, keycode,%d(0x%x), lable=%s have report to inputsystemn",
				button->gpio, button->code, button->code, button->type);
}

static void gpio_keys_gpio_work_func(struct work_struct *work)
{
	struct gpio_button_data *bdata =
		container_of(work, struct gpio_button_data, work);

	gpio_keys_gpio_report_event(bdata);

	if (bdata->button->wakeup)
		pm_relax(bdata->input->dev.parent);
}

static void gpio_keys_gpio_timer(unsigned long _data)
{
	struct gpio_button_data *bdata = (struct gpio_button_data *)_data;

	schedule_work(&bdata->work);
}

static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id)
{
	struct gpio_button_data *bdata = dev_id;

	BUG_ON(irq != bdata->irq);

	if (bdata->button->wakeup)
		pm_stay_awake(bdata->input->dev.parent);
	if (bdata->timer_debounce)
		mod_timer(&bdata->timer,
			jiffies + msecs_to_jiffies(bdata->timer_debounce));
	else
		schedule_work(&bdata->work);

	return IRQ_HANDLED;
}

static void gpio_keys_irq_timer(unsigned long _data)
{
	struct gpio_button_data *bdata = (struct gpio_button_data *)_data;
	struct input_dev *input = bdata->input;
	unsigned long flags;

	spin_lock_irqsave(&bdata->lock, flags);
	if (bdata->key_pressed) {
		input_event(input, EV_KEY, bdata->button->code, 0);
		input_sync(input);
		printk("[hyl_sos][gpio_keys_irq_timer], gpio:%d, keycode,%d(0x%x), lable=%s have report 0(released) to inputsystemn",
				bdata->button->gpio, bdata->button->code, bdata->button->code, bdata->button->type);
		bdata->key_pressed = false;
	}
	spin_unlock_irqrestore(&bdata->lock, flags);
}

static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id)
{
	struct gpio_button_data *bdata = dev_id;
	const struct gpio_keys_button *button = bdata->button;
	struct input_dev *input = bdata->input;
	unsigned long flags;

	BUG_ON(irq != bdata->irq);

	spin_lock_irqsave(&bdata->lock, flags);

	if (!bdata->key_pressed) {
		if (bdata->button->wakeup)
			pm_wakeup_event(bdata->input->dev.parent, 0);

		input_event(input, EV_KEY, button->code, 1);
		input_sync(input);
		
		printk("[hyl_sos][gpio_keys_irq_isr], gpio:%d, keycode,%d(0x%x), lable=%s have report 1(pressed) to inputsystemn",
				bdata->button->gpio, bdata->button->code, bdata->button->code, bdata->button->type);

		if (!bdata->timer_debounce) {
			input_event(input, EV_KEY, button->code, 0);
			input_sync(input);
			goto out;
		}

		bdata->key_pressed = true;
	}

	if (bdata->timer_debounce)
		mod_timer(&bdata->timer,
			jiffies + msecs_to_jiffies(bdata->timer_debounce));
out:
	spin_unlock_irqrestore(&bdata->lock, flags);
	return IRQ_HANDLED;
}

static void gpio_keys_quiesce_key(void *data)
{
	struct gpio_button_data *bdata = data;

	if (bdata->timer_debounce)
		del_timer_sync(&bdata->timer);

	cancel_work_sync(&bdata->work);
}

static int gpio_keys_setup_key(struct platform_device *pdev,
				struct input_dev *input,
				struct gpio_button_data *bdata,
				const struct gpio_keys_button *button)
{
	const char *desc = button->desc ? button->desc : "gpio_keys";
	struct device *dev = &pdev->dev;
	irq_handler_t isr;
	unsigned long irqflags;
	int irq;
	int error;

	bdata->input = input;
	bdata->button = button;
	spin_lock_init(&bdata->lock);

	if (gpio_is_valid(button->gpio)) {

		error = devm_gpio_request_one(&pdev->dev, button->gpio,
					      GPIOF_IN, desc);
		if (error < 0) {
			dev_err(dev, "Failed to request GPIO %d, error %dn",
				button->gpio, error);
			return error;
		}

		if (button->debounce_interval) {
			error = gpio_set_debounce(button->gpio,
					button->debounce_interval * 1000);
			/* use timer if gpiolib doesn't provide debounce */
			if (error < 0)
				bdata->timer_debounce =
						button->debounce_interval;
		}

		irq = gpio_to_irq(button->gpio);
		if (irq < 0) {
			error = irq;
			dev_err(dev,
				"Unable to get irq number for GPIO %d, error %dn",
				button->gpio, error);
			return error;
		}
		bdata->irq = irq;

		INIT_WORK(&bdata->work, gpio_keys_gpio_work_func);
		setup_timer(&bdata->timer,
			    gpio_keys_gpio_timer, (unsigned long)bdata);

		isr = gpio_keys_gpio_isr;
		irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;

	} else {
		if (!button->irq) {
			dev_err(dev, "No IRQ specifiedn");
			return -EINVAL;
		}
		bdata->irq = button->irq;

		if (button->type && button->type != EV_KEY) {
			dev_err(dev, "Only EV_KEY allowed for IRQ buttons.n");
			return -EINVAL;
		}

		bdata->timer_debounce = button->debounce_interval;
		setup_timer(&bdata->timer,
			    gpio_keys_irq_timer, (unsigned long)bdata);

		isr = gpio_keys_irq_isr;
		irqflags = 0;
	}

	input_set_capability(input, button->type ?: EV_KEY, button->code);

	/*
	 * Install custom action to cancel debounce timer and
	 * workqueue item.
	 */
	error = devm_add_action(&pdev->dev, gpio_keys_quiesce_key, bdata);
	if (error) {
		dev_err(&pdev->dev,
			"failed to register quiesce action, error: %dn",
			error);
		return error;
	}

	/*
	 * If platform has specified that the button can be disabled,
	 * we don't want it to share the interrupt line.
	 */
	if (!button->can_disable)
		irqflags |= IRQF_SHARED;

	error = devm_request_any_context_irq(&pdev->dev, bdata->irq,
					     isr, irqflags, desc, bdata);
	if (error < 0) {
		dev_err(dev, "Unable to claim irq %d; error %dn",
			bdata->irq, error);
		return error;
	}

	return 0;
}

static void gpio_keys_report_state(struct gpio_keys_drvdata *ddata)
{
	struct input_dev *input = ddata->input;
	int i;

	for (i = 0; i < ddata->pdata->nbuttons; i++) {
		struct gpio_button_data *bdata = &ddata->data[i];
		if (gpio_is_valid(bdata->button->gpio))
			gpio_keys_gpio_report_event(bdata);
	}
	input_sync(input);
}

static int gpio_keys_pinctrl_configure(struct gpio_keys_drvdata *ddata,
							bool active)
{
	struct pinctrl_state *set_state;
	int retval;

	if (active) {
		set_state =
			pinctrl_lookup_state(ddata->key_pinctrl,
						"tlmm_gpio_key_active");
		if (IS_ERR(set_state)) {
			dev_err(&ddata->input->dev,
				"cannot get ts pinctrl active staten");
			return PTR_ERR(set_state);
		}
	} else {
		set_state =
			pinctrl_lookup_state(ddata->key_pinctrl,
						"tlmm_gpio_key_suspend");
		if (IS_ERR(set_state)) {
			dev_err(&ddata->input->dev,
				"cannot get gpiokey pinctrl sleep staten");
			return PTR_ERR(set_state);
		}
	}
	retval = pinctrl_select_state(ddata->key_pinctrl, set_state);
	if (retval) {
		dev_err(&ddata->input->dev,
				"cannot set ts pinctrl active staten");
		return retval;
	}

	return 0;
}

static int gpio_keys_open(struct input_dev *input)
{
	struct gpio_keys_drvdata *ddata = input_get_drvdata(input);
	const struct gpio_keys_platform_data *pdata = ddata->pdata;
	int error;

	if (pdata->enable) {
		error = pdata->enable(input->dev.parent);
		if (error)
			return error;
	}

	/* Report current state of buttons that are connected to GPIOs */
	gpio_keys_report_state(ddata);

	return 0;
}

static void gpio_keys_close(struct input_dev *input)
{
	struct gpio_keys_drvdata *ddata = input_get_drvdata(input);
	const struct gpio_keys_platform_data *pdata = ddata->pdata;

	if (pdata->disable)
		pdata->disable(input->dev.parent);
}

/*
 * Handlers for alternative sources of platform_data
 */

#ifdef CONFIG_OF
/*
 * Translate OpenFirmware node properties into platform_data
 */
static struct gpio_keys_platform_data *
gpio_keys_get_devtree_pdata(struct device *dev)
{
	struct device_node *node, *pp;
	struct gpio_keys_platform_data *pdata;
	struct gpio_keys_button *button;
	int error;
	int nbuttons;
	int i;

	node = dev->of_node;
	if (!node)
		return ERR_PTR(-ENODEV);

	nbuttons = of_get_child_count(node);
	if (nbuttons == 0)
		return ERR_PTR(-ENODEV);

	pdata = devm_kzalloc(dev,
			     sizeof(*pdata) + nbuttons * sizeof(*button),
			     GFP_KERNEL);
	if (!pdata)
		return ERR_PTR(-ENOMEM);

	pdata->buttons = (struct gpio_keys_button *)(pdata + 1);
	pdata->nbuttons = nbuttons;

	pdata->rep = !!of_get_property(node, "autorepeat", NULL);
	pdata->name = of_get_property(node, "input-name", NULL);
	pdata->use_syscore = of_property_read_bool(node, "use-syscore");

	i = 0;
	for_each_child_of_node(node, pp) {
		int gpio;
		enum of_gpio_flags flags;

		if (!of_find_property(pp, "gpios", NULL)) {
			pdata->nbuttons--;
			dev_warn(dev, "Found button without gpiosn");
			continue;
		}

		gpio = of_get_gpio_flags(pp, 0, &flags);
		if (gpio < 0) {
			error = gpio;
			if (error != -EPROBE_DEFER)
				dev_err(dev,
					"Failed to get gpio flags, error: %dn",
					error);
			return ERR_PTR(error);
		}

		button = &pdata->buttons[i++];

		button->gpio = gpio;
		button->active_low = flags & OF_GPIO_ACTIVE_LOW;

		if (of_property_read_u32(pp, "linux,code", &button->code)) {
			dev_err(dev, "Button without keycode: 0x%xn",
				button->gpio);
			return ERR_PTR(-EINVAL);
		}

		button->desc = of_get_property(pp, "label", NULL);

		if (of_property_read_u32(pp, "linux,input-type", &button->type))
			button->type = EV_KEY;

		button->wakeup = !!of_get_property(pp, "gpio-key,wakeup", NULL);

		if (of_property_read_u32(pp, "debounce-interval",
					&button->debounce_interval))
			button->debounce_interval = 5;
			
		dev_err(dev, "[hyl_sos] gpio_keys_get_devtree_pdata, gpio:%d, keycode,%d(0x%x), lable=%s n",
				button->gpio, button->code, button->code, button->type);
	}

	if (pdata->nbuttons == 0)
		return ERR_PTR(-EINVAL);

	return pdata;
}

static const struct of_device_id gpio_keys_of_match[] = {
	{ .compatible = "gpio-keys", },
	{ },
};
MODULE_DEVICE_TABLE(of, gpio_keys_of_match);

#else

static inline struct gpio_keys_platform_data *
gpio_keys_get_devtree_pdata(struct device *dev)
{
	return ERR_PTR(-ENODEV);
}

#endif

static int gpio_keys_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);
	struct gpio_keys_drvdata *ddata;
	struct input_dev *input;
	size_t size;
	int i, error;
	int wakeup = 0;
	struct pinctrl_state *set_state;

	if (!pdata) {
		pdata = gpio_keys_get_devtree_pdata(dev);
		if (IS_ERR(pdata))
			return PTR_ERR(pdata);
	}

	size = sizeof(struct gpio_keys_drvdata) +
			pdata->nbuttons * sizeof(struct gpio_button_data);
	ddata = devm_kzalloc(dev, size, GFP_KERNEL);
	if (!ddata) {
		dev_err(dev, "failed to allocate staten");
		return -ENOMEM;
	}

	input = devm_input_allocate_device(dev);
	if (!input) {
		dev_err(dev, "failed to allocate input devicen");
		return -ENOMEM;
	}

	global_dev = dev;
	ddata->pdata = pdata;
	ddata->input = input;
	mutex_init(&ddata->disable_lock);

	platform_set_drvdata(pdev, ddata);
	input_set_drvdata(input, ddata);

	input->name = GPIO_KEYS_DEV_NAME;
	input->phys = "gpio-keys/input0";
	input->dev.parent = &pdev->dev;
	input->open = gpio_keys_open;
	input->close = gpio_keys_close;

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

	/* Enable auto repeat feature of Linux input subsystem */
	if (pdata->rep)
		__set_bit(EV_REP, input->evbit);

	/* Get pinctrl if target uses pinctrl */
	ddata->key_pinctrl = devm_pinctrl_get(dev);
	if (IS_ERR(ddata->key_pinctrl)) {
		if (PTR_ERR(ddata->key_pinctrl) == -EPROBE_DEFER)
			return -EPROBE_DEFER;

		pr_debug("Target does not use pinctrln");
		ddata->key_pinctrl = NULL;
	}

	if (ddata->key_pinctrl) {
		error = gpio_keys_pinctrl_configure(ddata, true);
		if (error) {
			dev_err(dev, "cannot set ts pinctrl active staten");
			return error;
		}
	}

	for (i = 0; i < pdata->nbuttons; i++) {
		const struct gpio_keys_button *button = &pdata->buttons[i];
		struct gpio_button_data *bdata = &ddata->data[i];

		error = gpio_keys_setup_key(pdev, input, bdata, button);
		if (error)
			goto err_setup_key;

		if (button->wakeup)
			wakeup = 1;
	}

	error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group);
	if (error) {
		dev_err(dev, "Unable to export keys/switches, error: %dn",
			error);
		goto err_create_sysfs;
	}

	error = input_register_device(input);
	if (error) {
		dev_err(dev, "Unable to register input device, error: %dn",
			error);
		goto err_remove_group;
	}

	device_init_wakeup(&pdev->dev, wakeup);

	if (pdata->use_syscore)
		gpio_keys_syscore_pm_ops.resume = gpio_keys_syscore_resume;

	register_syscore_ops(&gpio_keys_syscore_pm_ops);

	return 0;

err_remove_group:
	sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group);
err_create_sysfs:
err_setup_key:
	if (ddata->key_pinctrl) {
		set_state =
		pinctrl_lookup_state(ddata->key_pinctrl,
						"tlmm_gpio_key_suspend");
		if (IS_ERR(set_state))
			dev_err(dev, "cannot get gpiokey pinctrl sleep staten");
		else
			pinctrl_select_state(ddata->key_pinctrl, set_state);
	}

	return error;
}

static int gpio_keys_remove(struct platform_device *pdev)
{
	sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group);
	unregister_syscore_ops(&gpio_keys_syscore_pm_ops);

	device_init_wakeup(&pdev->dev, 0);

	return 0;
}

#ifdef CONFIG_PM_SLEEP
static void gpio_keys_syscore_resume(void)
{
	struct gpio_keys_drvdata *ddata = dev_get_drvdata(global_dev);
	struct input_dev *input = ddata->input;
	struct gpio_button_data *bdata = NULL;
	int error = 0;
	int i;

	if (ddata->key_pinctrl) {
		error = gpio_keys_pinctrl_configure(ddata, true);
		if (error) {
			dev_err(global_dev, "failed to put the pin in resume staten");
			return;
		}
	}

	if (device_may_wakeup(global_dev)) {
		for (i = 0; i < ddata->pdata->nbuttons; i++) {
			bdata = &ddata->data[i];
			if (bdata->button->wakeup)
				disable_irq_wake(bdata->irq);
		}
	} else {
		mutex_lock(&input->mutex);
		if (input->users)
			error = gpio_keys_open(input);
		mutex_unlock(&input->mutex);
	}

	if (error)
		return;

	gpio_keys_report_state(ddata);
}

static int gpio_keys_suspend(struct device *dev)
{
	struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
	struct input_dev *input = ddata->input;
	int i, ret;

	if (ddata->key_pinctrl) {
		ret = gpio_keys_pinctrl_configure(ddata, false);
		if (ret) {
			dev_err(dev, "failed to put the pin in suspend staten");
			return ret;
		}
	}

	if (device_may_wakeup(dev)) {
		for (i = 0; i < ddata->pdata->nbuttons; i++) {
			struct gpio_button_data *bdata = &ddata->data[i];
			if (bdata->button->wakeup)
				enable_irq_wake(bdata->irq);
		}
	} else {
		mutex_lock(&input->mutex);
		if (input->users)
			gpio_keys_close(input);
		mutex_unlock(&input->mutex);
	}

	return 0;
}

static int gpio_keys_resume(struct device *dev)
{
	struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
	struct input_dev *input = ddata->input;
	int error = 0;
	int i;

	if (ddata->pdata->use_syscore == true) {
		dev_dbg(global_dev, "Using syscore resume, no need of this resume.n");
		return 0;
	}

	if (ddata->key_pinctrl) {
		error = gpio_keys_pinctrl_configure(ddata, true);
		if (error) {
			dev_err(dev, "failed to put the pin in resume staten");
			return error;
		}
	}

	if (device_may_wakeup(dev)) {
		for (i = 0; i < ddata->pdata->nbuttons; i++) {
			struct gpio_button_data *bdata = &ddata->data[i];
			if (bdata->button->wakeup)
				disable_irq_wake(bdata->irq);
		}
	} else {
		mutex_lock(&input->mutex);
		if (input->users)
			error = gpio_keys_open(input);
		mutex_unlock(&input->mutex);
	}

	if (error)
		return error;

	gpio_keys_report_state(ddata);
	return 0;
}

#else

static void gpio_keys_syscore_resume(void){}

static int gpio_keys_suspend(struct device *dev)
{
	return 0;
}

static int gpio_keys_resume(struct device *dev)
{
	return 0;
}

#endif

static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume);

static struct platform_driver gpio_keys_device_driver = {
	.probe		= gpio_keys_probe,
	.remove		= gpio_keys_remove,
	.driver		= {
		.name	= "gpio-keys",
		.owner	= THIS_MODULE,
		.pm	= &gpio_keys_pm_ops,
		.of_match_table = of_match_ptr(gpio_keys_of_match),
	}
};

static int __init gpio_keys_init(void)
{
	return platform_driver_register(&gpio_keys_device_driver);
}

static void __exit gpio_keys_exit(void)
{
	platform_driver_unregister(&gpio_keys_device_driver);
}

late_initcall(gpio_keys_init);
module_exit(gpio_keys_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Phil Blundell <pb@handhelds.org>");
MODULE_DESCRIPTION("Keyboard driver for GPIOs");
MODULE_ALIAS("platform:gpio-keys");


6.3 aw9523.c 文件内容

/*
 * Copyright (c) 2017, The Linux Foundation. All rights reserved.
 *
 * Version: v1.1.1
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * 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 for more details.
 *
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/workqueue.h>
#include <linux/errno.h>
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/of_gpio.h>
#include <linux/pinctrl/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/hrtimer.h>  


#include <linux/input/aw9523_cfg.h>
//<!--wuguang add for debug--!>
#include <linux/leds.h>
#undef pr_debug
#define pr_debug(fmt, ...)
			printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
// end -->
//#define AW9523_DEBUG

#define P0_KROW_MASK 0x1f
#define P1_KCOL_MASK 0x1f
#define P0_LED_MASK  0xe0
#define P1_LED_MASK  0xe0

#define KROW_P0_0 0
#define KROW_P0_1 1
#define KROW_P0_2 2
#define KROW_P0_3 3
#define KROW_P0_4 4
#define KROW_P0_5 5
#define KROW_P0_6 6
#define KROW_P0_7 7

#define KROW_P1_0 0
#define KROW_P1_1 1
#define KROW_P1_2 2
#define KROW_P1_3 3
#define KROW_P1_4 4
#define KROW_P1_5 5
#define KROW_P1_6 6
#define KROW_P1_7 7

//reg list
#define P0_INPUT	0x00
#define P1_INPUT 	0x01
#define P0_OUTPUT 	0x02
#define P1_OUTPUT 	0x03
#define P0_CONFIG	0x04
#define P1_CONFIG 	0x05
#define P0_INT		0x06
#define P1_INT		0x07
#define ID_REG		0x10
#define CTL_REG		0x11
#define P0_LED_MODE	0x12
#define P1_LED_MODE	0x13
#define P1_0_DIM0   0x20
#define P1_1_DIM0   0x21
#define P1_2_DIM0   0x22
#define P1_3_DIM0   0x23
#define P0_0_DIM0   0x24
#define P0_1_DIM0   0x25
#define P0_2_DIM0   0x26
#define P0_3_DIM0   0x27
#define P0_4_DIM0   0x28
#define P0_5_DIM0   0x29
#define P0_6_DIM0   0x2A
#define P0_7_DIM0   0x2B
#define P1_4_DIM0   0x2C
#define P1_5_DIM0   0x2D
#define P1_6_DIM0   0x2E
#define P1_7_DIM0   0x2F
#define SW_RSTN		0x7F

#define HRTIMER_FRAME	20

static int bl_num=0;
#define BLLEVEL_DEF 125
#define LEDLEVEL_DEF 100
enum {
         COLOR_R,
         COLOR_G,
         COLOR_B,
         COLOR_RED,
         COLOR_GREEN,
         COLOR_BLUE,
         NO_LEDS
};

#define MAX_RGBLED_VALUE 255

KEY_STATE key_map[]={
//	name		code				val		row		col
	{"SEND",	KEY_SEND,				0,	KROW_P0_0,	KROW_P1_0},  //
	{"BACK",	KEY_BACKSPACE,			0,	KROW_P0_1,	KROW_P1_0},  //
	{"CAMERA",	KEY_CAMERA,				0,	KROW_P0_2,	KROW_P1_0},  //
	{"CANCEL",	KEY_CANCEL,				0,	KROW_P0_3,	KROW_P1_0}, //
	{"LEFT"	,	KEY_LEFT,				0,	KROW_P0_4,	KROW_P1_0}, //
//	{"ALT"	,	KEY_ALTERASE,				0,	KROW_P0_5,	KROW_P1_0}, //
//	{"W"	,	KEY_W,					0,	KROW_P0_6,	KROW_P1_0},  //

	{"1"	,	KEY_1,					0,	KROW_P0_0,	KROW_P1_1},  //
	{"2"	,	KEY_2,					0,	KROW_P0_1,	KROW_P1_1},  //
	{"3"	,	KEY_3,					0,	KROW_P0_2,	KROW_P1_1},  //
	{"DOWN"	,	KEY_DOWN,				0,	KROW_P0_3,	KROW_P1_1},  //
	{"ENTER",	KEY_ENTER,				0,	KROW_P0_4,	KROW_P1_1},  //
//	{"X"	,	KEY_X,					0,	KROW_P0_5,	KROW_P1_1},  //
//	{"S"	,	KEY_S,					0,	KROW_P0_6,	KROW_P1_1},  //

	{"4"	,	KEY_4,					0,	KROW_P0_0,	KROW_P1_2},  //
	{"5"	,	KEY_5,					0,	KROW_P0_1,	KROW_P1_2},   //
	{"6"	,	KEY_6,					0,	KROW_P0_2,	KROW_P1_2},  //
	{"UP"	,	KEY_UP,					0,	KROW_P0_3,	KROW_P1_2},   //
	{"PLAY"	,	KEY_PLAY,				0,	KROW_P0_4,	KROW_P1_2},  //
//	{"V"	,	KEY_V,					0,	KROW_P0_5,	KROW_P1_2},  //
//	{"G"	,	KEY_G,					0,	KROW_P0_6,	KROW_P1_2},  //

	{"7"	,	KEY_7,					0,	KROW_P0_0,	KROW_P1_3},  //
	{"8"	,	KEY_8,					0,	KROW_P0_1,	KROW_P1_3},   //
	{"9"	,	KEY_9,					0,	KROW_P0_2,	KROW_P1_3},  //
	{"RIGHT",	KEY_RIGHT,				0,	KROW_P0_3,	KROW_P1_3},   //
	{"A"	,	KEY_A,					0,	KROW_P0_4,	KROW_P1_3},  //
//	{"B"	,	KEY_B,					0,	KROW_P0_5,	KROW_P1_3},  //
//	{"H"	,	KEY_H,					0,	KROW_P0_6,	KROW_P1_3},  //

	{"*"	,	KEY_NUMERIC_STAR,		0,	KROW_P0_0,	KROW_P1_4},  //
	{"0"	,	KEY_0,					0,	KROW_P0_1,	KROW_P1_4},  //
	{"#"	,	KEY_NUMERIC_POUND,		0,	KROW_P0_2,	KROW_P1_4},  //
//	{"I"	,	KEY_I,					0,	KROW_P0_3,	KROW_P1_4},  //
//	{"K"	,	KEY_K,					0,	KROW_P0_4,	KROW_P1_4},  //
//	{"$"	,	KEY_DOLLAR,				0,	KROW_P0_5,	KROW_P1_4},  //
//	{"L"	,	KEY_L,					0,	KROW_P0_6,	KROW_P1_4},  //
};

#define P0_NUM_MAX		8
#define P1_NUM_MAX		8
#define KEYST_MAX		2
#define KEYST_OLD		0
#define KEYST_NEW		1
static unsigned char keyst_old[P1_NUM_MAX];
static unsigned char keyst_new[P1_NUM_MAX];

static unsigned char keyst_def[KEYST_MAX][P1_NUM_MAX];

struct aw9523_kpad_platform_data *g_aw9523_data;
//EXPORT_SYMBOL_GPL(aw9523_led_store);


//haoyanling@longcheer.com for gpio91 SOS 20190921 +++
#define KEY_SOS 204  	// (0xCC)  key code
static int  gpio_sos_num   91  	// (GPIO 91)
int gpio_sos_num_irq = 0;
static struct work_struct gpio_sos_work;


static void gpio_sos_work_handler(struct work_struct *work)
{
	struct aw9523_kpad_platform_data *pdata;

	pdata = g_aw9523_data;

	input_report_key(pdata->input, KEY_SOS, 1);
	input_sync(pdata->input);
	input_report_key(pdata->input, KEY_SOS, 0);
	input_sync(pdata->input);
	printk("[hyl_sos][%s] input_report_key KEY_SOS=%d(0x%x) n", __func__, KEY_SOS);
	
	// 为了快速看到现象, 可以上报 KEY_POWER 事件, 看是否会亮灭屏,后续注释该代码  ++++
	input_report_key(pdata->input, KEY_POWER, 1);
	input_sync(pdata->input);
	input_report_key(pdata->input, KEY_POWER, 0);
	input_sync(pdata->input);
	// 为了快速看到现象, 可以上报 KEY_POWER 事件, 看是否会亮灭屏,后续注释该代码  ----
	
	
	enable_irq(gpio_sos_irq);
}

static irqreturn_t gpio_sos_irq_handler(int irq, void *handle)
{
	struct i2c_client *client = handle;
	struct aw9523_kpad_platform_data *pdata;
	
	disable_irq_nosync(gpio_sos_irq);
	
	printk("[hyl_sos][%s] schedule_work(&gpio_sos_work) n", __func__);
	schedule_work(&gpio_sos_work);
	
	return IRQ_HANDLED;
}

//haoyanling@longcheer.com for gpio91 SOS 20190921 ---



static int __aw9523_read_reg(struct i2c_client *client, int reg, unsigned char *val)
{
	int ret;

	ret = i2c_smbus_read_byte_data(client, reg);
	if (ret < 0) {
		dev_err(&client->dev, "i2c read fail: can't read from %02x: %dn", reg, ret);
		return ret;
	} else {
		*val = ret;
	}
	return 0;
}

static int __aw9523_write_reg(struct i2c_client *client, int reg, int val)
{
	int ret;

	ret = i2c_smbus_write_byte_data(client, reg, val);
	if (ret < 0) {
		dev_err(&client->dev, "i2c write fail: can't write %02x to %02x: %dn",
			val, reg, ret);
		return ret;
	}
	return 0;
}

static int aw9523_read_reg(struct i2c_client *client, int reg,
						unsigned char *val)
{
	int rc;
	struct aw9523_kpad_platform_data *pdata;

	pdata = i2c_get_clientdata(client);
	mutex_lock(&pdata->read_write_lock);
	rc = __aw9523_read_reg(client, reg, val);
	mutex_unlock(&pdata->read_write_lock);

	return rc;
}

static int aw9523_write_reg(struct i2c_client *client, int reg,
						unsigned char val)
{
	int rc;
	struct aw9523_kpad_platform_data *pdata;

	pdata = i2c_get_clientdata(client);
	mutex_lock(&pdata->read_write_lock);
	rc = __aw9523_write_reg(client, reg, val);
	mutex_unlock(&pdata->read_write_lock);

	return rc;
}

void aw9523_p0_int_restore(struct i2c_client *client)
{
    aw9523_write_reg(client, P0_INT, 0x00);
}

void aw9523_p1_int_restore(struct i2c_client *client, unsigned char val)
{
    aw9523_write_reg(client, P1_INT, val);
}

void aw9523_p0_int_disable(struct i2c_client *client)
{
    aw9523_write_reg(client, P0_INT, 0xff);
}

unsigned char aw9523_get_p1(struct i2c_client *client) //该函数用来读取P1端口上的电平状态
{
	unsigned char val;
	aw9523_read_reg(client, P1_INPUT, &val);
    return val;
}

unsigned char aw9523_get_p0(struct i2c_client *client) //该函数用来读取P1端口上的电平状态
{
	unsigned char val;
	aw9523_read_reg(client, P0_INPUT, &val);
    return val;
}

void aw9523_set_p0(struct i2c_client *client,unsigned char data)
{
    aw9523_write_reg(client, P0_OUTPUT, data);
}

void aw9523_set_p1(struct i2c_client *client,unsigned char data)
{
    aw9523_write_reg(client, P1_OUTPUT, data);
}
/*
void aw9523_RGBled_color(int r, int g, int b)
{
	struct i2c_client *client = g_aw9523_data->client;

	aw9523_write_reg(client, P1_5_DIM0, r); //color_R
	aw9523_write_reg(client, P1_6_DIM0, g); //color_G
	aw9523_write_reg(client, P1_7_DIM0, b); //color_B
}

static ssize_t aw9523_led_store(struct device *dev,
			      struct device_attribute *attr,
			      const char *buf, size_t size)
{
	const char *delim = "255,255,255"; //example: "255,10,5".
	char *token, *cur;
	int color_R;
	int color_G;
	int color_B;
	int i = 0;
	cur = kstrdup(buf, GFP_KERNEL);
		
	for(i=0; i<3; i++)
	{
		token = strsep(&cur, delim);
		if (token == NULL)
			break;

		if(i==0)
		{
			color_R = simple_strtoll(token, NULL, 0);
			continue;
		}
		if(i==1)
		{
			color_G = simple_strtoll(token, NULL, 0);
			continue;
		}
		if(i==2)
		{
			color_B = simple_strtoll(token, NULL, 0);
			continue;
		}
	}
	aw9523_RGBled_color(color_R, color_G, color_B);

    return size;
}

DEVICE_ATTR(aw9523_led, S_IWUSR | S_IRUGO, NULL, aw9523_led_store);

*/

static ssize_t aw9523_status_show(struct device *dev,
				   struct device_attribute *attr, char *buf)
{
	struct i2c_client *client = g_aw9523_data->client;
	unsigned char p0_status, p1_status;
	aw9523_read_reg(client, P0_INPUT, &p0_status);
	aw9523_read_reg(client, P1_INPUT, &p1_status);
    return snprintf(buf, PAGE_SIZE, "P0_STATUS=%x n P1_STATUS=%x n", p0_status, p1_status);
}

static ssize_t aw9523_status_set(struct device *dev,
			      struct device_attribute *attr,
			      const char *buf, size_t size)
{
	struct i2c_client *client = g_aw9523_data->client;
	//unsigned char p0_status, p1_status;
	unsigned char val;

	aw9523_read_reg(client, P1_OUTPUT, &val);
	aw9523_write_reg(client, P1_OUTPUT, val &(~P1_KCOL_MASK)); //p0 key line output 0

	aw9523_read_reg(client, P0_INT, &val);
	aw9523_write_reg(client, P0_INT, val & (~P0_KROW_MASK)); //p1 keyline enable interrupt 

    return size;
}


DEVICE_ATTR(aw9523_status, S_IWUSR | S_IRUGO, aw9523_status_show, aw9523_status_set);

u32 bl_level_bakup = 0;
bool is_timer_onoff = false;

void aw9523_buttonleds_set_timer_on(void)
{

	//cancel timer 
	if (is_timer_onoff == true)
	{
		hrtimer_cancel(&pdata->key_timer_btn_leds);
	}
	else
	{
		// TODO, light up button leds   hyl_leds +++
		aw9523_buttonleds_set(bl_level_bakup); 
	}
	
	// start button leds timer
	hrtimer_start(&pdata->key_timer_btn_leds, ktime_set(0,( 100 *  1000/HRTIMER_FRAME)*1000000), HRTIMER_MODE_REL);

	is_timer_onoff = true;
}


static void aw9523_key_work(struct work_struct *work)
{
	struct aw9523_kpad_platform_data *pdata;
	struct i2c_client *client;  
	KEY_STATE *keymap;
	unsigned char i,j,cnt;
	unsigned char val;
	int keymap_len;

	printk("%s: begin n", __func__);

	pdata = g_aw9523_data;
	client = pdata->client;

	keymap = pdata->keymap;
	keymap_len = pdata->keymap_len;

	aw9523_buttonleds_set_timer_on();

	for (i=0; i<P1_NUM_MAX; i++) {    
		if (P1_KCOL_MASK & (1<<i)) {
			//aw9523_read_reg(client, P1_CONFIG, &val);
			//printk("%s p1_config1 = 0x%02x n", __func__, val);
			//aw9523_write_reg(client,P1_CONFIG, val | P1_KCOL_MASK);	//set p1 port input mode
			aw9523_read_reg(client, P1_CONFIG, &val);
			//printk("%s p1_config = 0x%02x n", __func__, val);
			aw9523_write_reg(client, P1_CONFIG, (P1_KCOL_MASK | val) & (~(1<<i)));	//set p1_x port output mode
			aw9523_read_reg(client, P1_OUTPUT, &val);
			//printk("%s p1_output = 0x%02x n", __func__, val);
			aw9523_write_reg(client, P1_OUTPUT, (P1_KCOL_MASK | val) & (~(1<<i)));

			aw9523_read_reg(client, P0_INPUT, &val);				// read p0 port status
			keyst_new[i] = (val & P0_KROW_MASK);  // 0x04
			//printk("0x%02x, ", keyst_new[i]);                //i=p1 keyst[i]=p0
		}
	}
	printk("n");

	if (memcmp(keyst_old, keyst_new, P1_NUM_MAX)) {	// keyst changed
		for (i=0; i<P1_NUM_MAX; i++) {  //8 循环
			if (keyst_old[i] != keyst_new[i]) 
			{		// keyst of i col changed
				for (j=0; j<P0_NUM_MAX; j++) 
				{
					if (P0_KROW_MASK & (1<<j)) 
					{								// j row gpio used
						if ((keyst_old[i] & (1<<j)) != (keyst_new[i] & (1<<j))) {				// j row & i col changed
							for (cnt=0; cnt<keymap_len; cnt++) {		// find row&col in the keymap
								if ((keymap[cnt].row == j) && 
									(keymap[cnt].col == i)) 
								{
									break;
								}
							}
							if (keyst_new[i] & (1<<j)) {				// release
								keymap[cnt].key_val = 0;
							} else {										// press
								keymap[cnt].key_val = 1;
							}
							input_report_key(pdata->input, keymap[cnt].key_code, keymap[cnt].key_val);
							input_sync(pdata->input);
							printk("%s: key_report: p0-row=%d, p1-col=%d, key_code=%d, key_val=%dn",
									__func__, j, i, keymap[cnt].key_code, keymap[cnt].key_val);
						}
					}
				}
			}
		}
		memcpy(keyst_old, keyst_new, P1_NUM_MAX);
	}

	if(!(memcmp(&keyst_new[0], &keyst_def[KEYST_NEW][0], P1_NUM_MAX))) {			// all key release   
		aw9523_read_reg(client, P1_CONFIG, &val);
		aw9523_write_reg(client,P1_CONFIG, val & (~P1_KCOL_MASK));	//set p1 port output mode

		aw9523_read_reg(client, P1_OUTPUT, &val);
		aw9523_write_reg(client, P1_OUTPUT, val & (~P1_KCOL_MASK));	//p1 port output 0

		aw9523_read_reg(client, P0_INPUT, &val); 					//clear p0 input irq

		aw9523_read_reg(client, P0_INT, &val);
		aw9523_write_reg(client, P0_INT, val & (~P0_KROW_MASK));	//enable p0 port irq

		enable_irq(client->irq);									//enable bb irq 

		return;
	}

	hrtimer_start(&pdata->key_timer, ktime_set(0,(1000/HRTIMER_FRAME)*1000000), HRTIMER_MODE_REL);
	printk("%s: end n", __func__);
}

static enum hrtimer_restart aw9523_key_timer_func(struct hrtimer *timer)
{
	//printk("%s entern", __func__);

	schedule_work(&g_aw9523_data->key_work);

	return HRTIMER_NORESTART; 
}

//hyl_leds +++
static enum hrtimer_restart aw9523_key_leds_delay_timer_func(struct hrtimer *timer)
{
	printk("%s entern", __func__);

	// TODO ,close button leds
	aw9523_buttonleds_set(0); 
	
	is_timer_on = false;  
	
	return HRTIMER_NORESTART; 
}
//hyl_leds ---

/*********************************************************
 *
 * int work
 *
 ********************************************************/
static void aw9523_int_work(struct work_struct *work)
{
	struct aw9523_kpad_platform_data *pdata = container_of(work,struct aw9523_kpad_platform_data, work.work);
	struct i2c_client *client = pdata->client;
	//printk("%s entern", __func__);

	aw9523_write_reg(client, P0_INT, 0xff); 		//disable p0 port irq

	hrtimer_start(&pdata->key_timer, ktime_set(0,(1000/HRTIMER_FRAME)*1000000), HRTIMER_MODE_REL);
}

static irqreturn_t aw9523_irq(int irq, void *handle)
{
	struct i2c_client *client = handle;
	struct aw9523_kpad_platform_data *pdata;

	disable_irq_nosync(client->irq);

	printk("%s entern", __func__);
	pdata = i2c_get_clientdata(client);

	schedule_delayed_work(&pdata->work, msecs_to_jiffies(pdata->delay));

	return IRQ_HANDLED;
}

/*********************************************************
 *
 * aw9523 reg
 *
 ********************************************************/
static ssize_t aw9523_get_reg(struct device* cd,struct device_attribute *attr, char* buf)
{
	unsigned char val;
	unsigned char i;
	ssize_t len = 0;
	struct i2c_client *client = g_aw9523_data->client;

	for(i=0; i<0x30; i++)
	{
		aw9523_read_reg(client, i, &val);
		len += snprintf(buf+len, PAGE_SIZE-len, "reg%2x = 0x%2x, ", i, val);
	}
	len += snprintf(buf+len, PAGE_SIZE-len, "n");

	return len;
}

static ssize_t aw9523_set_reg(struct device* cd, struct device_attribute *attr, const char* buf, size_t len)
{
	unsigned int databuf[2];
	struct i2c_client *client = g_aw9523_data->client;
	if(2 == sscanf(buf,"%x %x",&databuf[0], &databuf[1]))
	{
		aw9523_write_reg(client,databuf[0], databuf[1]);
	}
	return len;
}
static DEVICE_ATTR(reg, 0660, aw9523_get_reg,  aw9523_set_reg);
static int aw9523_create_sysfs(struct i2c_client *client)
{
	int err;
	struct device *dev = &(client->dev);

	err = device_create_file(dev, &dev_attr_reg);

	return err;
}

/*********************************************************
 *
 * aw9523 check chipid
 *
 ********************************************************/
 int aw9523_read_chipid(struct i2c_client *client)
{
	unsigned char val;
	unsigned char cnt;

	for(cnt=5; cnt>0; cnt--) {
		aw9523_read_reg(client, ID_REG, &val);
		if(val == 0x23) {
			printk("%s is okn", __func__);
			return 0;
		}
		msleep(5);
	}
	
	return -EINVAL;
}

/*********************************************************
 *
 * aw9523 init
 *
 ********************************************************/
void aw9523_key_init(struct i2c_client *client)
{
	unsigned char val;

	aw9523_read_reg(client, P0_LED_MODE, &val);
	aw9523_write_reg(client, P0_LED_MODE, val | P0_KROW_MASK); 	//p0 port gpio mode
	aw9523_read_reg(client, P1_LED_MODE, &val);
	aw9523_write_reg(client, P1_LED_MODE, val | P1_KCOL_MASK); 	//p1 port gpio mode

	aw9523_read_reg(client, P0_INT, &val);
	aw9523_write_reg(client, P0_INT, val | P0_KROW_MASK); 		//disable p0 port irq
	aw9523_write_reg(client, P1_INT, 0xff); 		//disable p1 port irq

	aw9523_read_reg(client, P0_CONFIG, &val);
	aw9523_write_reg(client, P0_CONFIG, val | P0_KROW_MASK);	//set p0 port input mode
	aw9523_read_reg(client, P1_CONFIG, &val);
	aw9523_write_reg(client,P1_CONFIG, val & (~P1_KCOL_MASK));	//set p1 port output mode

	aw9523_read_reg(client, P1_OUTPUT, &val);
	aw9523_write_reg(client, P1_OUTPUT, val & (~P1_KCOL_MASK));	//p1 port output 0

	aw9523_read_reg(client, P0_INPUT, &val); 					//clear p0 input irq
	aw9523_read_reg(client, P1_INPUT, &val); 					//clear p1 input irq

	//aw9523_read_reg(client, P0_INT, &val);
	aw9523_write_reg(client, P0_INT, 0xff & (~P0_KROW_MASK));	//enable p0 port irq

	aw9523_write_reg(client, P1_LED_MODE, (~P1_LED_MASK)&0xff);
	aw9523_write_reg(client, P0_LED_MODE, (~P0_LED_MASK)&0xff);
}

void aw9523_backlight_set(enum led_brightness brightness)
{
	struct i2c_client *client = g_aw9523_data->client;

	printk("%s brightness = %dn", __func__, brightness);
	if (brightness >= 255)
		brightness = 255;
	if(brightness <= 0)
		brightness = 0;

	aw9523_write_reg(client, P0_7_DIM0 , brightness);
	aw9523_write_reg(client, P1_5_DIM0 , brightness);
	aw9523_write_reg(client, P1_6_DIM0 , brightness);
	aw9523_write_reg(client, P1_7_DIM0 , brightness);
}
EXPORT_SYMBOL(aw9523_backlight_set);

static void aw9523_button_backlight(struct led_classdev *led_cdev,
			       enum led_brightness brightness)
{
	aw9523_backlight_set(brightness);
}

static struct led_classdev aw9523button_backlight = {
	.name		= "button-backlight",
	.max_brightness = 255,
	.brightness_set = aw9523_button_backlight,
};

void aw9523_buttonleds_set(enum led_brightness value)
{
	struct i2c_client *client = g_aw9523_data->client;

	printk("%s value = %dn", __func__, value);
	if (value >= 150 )
		value = 150;
	if(value < 150 && value > 0)
		value = 100;
	if(value <= 0)
		value = 0;

	aw9523_write_reg(client, P0_5_DIM0 , value);
	aw9523_write_reg(client, P0_6_DIM0 , value);
}
EXPORT_SYMBOL(aw9523_buttonleds_set);

static void aw9523_button_leds(struct led_classdev *led_cdev,
			       enum led_brightness brightness)
{
	aw9523_buttonleds_set(brightness);  // 3
}

static struct led_classdev aw9523button_leds = {
	.name		= "button-leds",  // echo  fwrite
	.brightness = LEDLEVEL_DEF,
	.max_brightness = 255,
	.brightness_set = aw9523_button_leds,  // 2
};


static int aw9523_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	struct aw9523_kpad_platform_data *pdata = client->dev.platform_data;//pdata获取平台数据
	int error;
	int ret = 0;
	int i =0;
	//检测是否适配
	if (!i2c_check_functionality(client->adapter,I2C_FUNC_SMBUS_BYTE_DATA)) {
		dev_err(&client->dev, "SMBUS Byte Data not Supportedn");
		return -EIO;
	}
	//内核内存分配
	pdata = devm_kzalloc(&client->dev,sizeof(struct aw9523_kpad_platform_data), GFP_KERNEL);
	if (!pdata)
	{
		dev_err(&client->dev, "Failed to allocate memoryn");
		return -ENOMEM;
	}

	g_aw9523_data = pdata;

	/*irq pull power supply*/
	pdata->irq_pull = regulator_get(&client->dev, "aw9523_power");//获取电源管理,dev驱动对应的设备指针,vcc
	if (IS_ERR(pdata->irq_pull)) {
		ret = PTR_ERR(pdata->irq_pull);
		dev_err(&client->dev, "Regulator get failed irq_pull ret=%dn", ret);
		return ret;
	}

	if (regulator_count_voltages(pdata->irq_pull) > 0) {
		ret = regulator_set_voltage(pdata->irq_pull, 1800000,1800000);
		if (ret) {
			dev_err(&client->dev, "Regulator set failed irq_pull ret=%dn", ret);
			return ret;
		}
	}
	
	ret = regulator_enable(pdata->irq_pull);
	if (ret) {
		dev_err(&client->dev, "Regulator irq_pull enable failed ret=%dn", ret);
		ret = regulator_disable(pdata->irq_pull);
		return ret;
	}
	else {
		dev_info(&client->dev, "Regulator irq_pull enable success!n");
	}

	/******end*****/
	pdata->dis_gpio = of_get_named_gpio(client->dev.of_node, "qcom,dis-gpio", 0);
		printk("[%s]--aw9523_probe--pdata->dis_gpio=%dn", __func__, pdata->dis_gpio);

	if ((!gpio_is_valid(pdata->dis_gpio))){
			return -EINVAL;
	}

	ret = gpio_request(pdata->dis_gpio, "aw9523-reset-keys");
	if (ret) {
		dev_err(&client->dev, "unable to request gpio [%d]n",
			pdata->dis_gpio);
		goto err_dis_gpio;
	}

	ret = gpio_direction_output(pdata->dis_gpio, 1);
	if (ret) {
		dev_err(&client->dev, "unable to set direction for gpio [%d]n",
			pdata->dis_gpio);
		goto err_dis_gpio;
	}

	gpio_set_value(pdata->dis_gpio, 0); /* ULPM */
	msleep(1);
	gpio_set_value(pdata->dis_gpio, 1); /* HPD */

	INIT_DELAYED_WORK(&pdata->work, aw9523_int_work);

	pdata->input = input_allocate_device();//为新添加的输入设备分配内存
	if (!pdata->input) {
		error = -ENOMEM;
		goto err_free_mem;
	}

	pdata->client = client;
	pdata->input->name = "aw9523-keys";	
	pdata->input->phys = "aw9523-keys/input0";
	pdata->input->dev.parent = &client->dev;
	pdata->keymap_len = sizeof(key_map)/sizeof(KEY_STATE);
	pdata->keymap = (KEY_STATE *)&key_map;

	input_set_drvdata(pdata->input, pdata);//函数用来设置device的私有数据,保存驱动的私有数据。

	__set_bit(EV_KEY, pdata->input->evbit);//支持的事件,EV_KEY事件,事件类型由input_dev.evbit表示
	//__set_bit(EV_SYN, pdata->input->evbit);

	for (i = 0; i < pdata->keymap_len; i++)
		__set_bit(pdata->keymap[i].key_code & KEY_MAX, pdata->input->keybit);//(要设置的那一位,开始的地址)keybit设备按键映射位



	//haoyanling@longcheer.com for gpio91 SOS 20190921 +++
	
	// 为了快速看到现象, 可以上报 KEY_POWER 事件, 看是否会亮灭屏,后续注释该代码  ++++
	__set_bit(KEY_POWER & KEY_MAX, pdata->input->keybit);
	// 为了快速看到现象, 可以上报 KEY_POWER 事件, 看是否会亮灭屏,后续注释该代码  ----
	
	__set_bit(KEY_SOS & KEY_MAX, pdata->input->keybit);
	
	ret = gpio_request(gpio_sos_num, "gpio_sos");
	if (ret) {
		dev_err(&client->dev, "[hyl_sos]unable to request gpio [%d]n", gpio_sos_num);
	}
	
	ret = gpio_direction_input(gpio_sos_num);
	if (ret) {
		dev_err(&client->dev, "[hyl_sos]unable to set direction for gpio [%d]n", gpio_sos_num);
	}

	gpio_sos_num_irq = gpio_to_irq(gpio_sos_num);
	
	ret = devm_request_threaded_irq(&client->dev, gpio_sos_num_irq, NULL,
		gpio_sos_irq_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
		"gpio_sos_irq", client);
	if (ret) {
		dev_err(&client->dev, "[hyl_sos]Failed irq=%d request ret = %dn", gpio_sos_num_irq, ret);
	}
	printk("[hyl_sos][%s] request irq success, gpio_sos_num_irq=%d, KEY_SOS=%d(0x%x) n", 
				__func__, gpio_sos_num_irq , KEY_SOS);
				
	INIT_WORK(&gpio_sos_work, gpio_sos_work_handler);			
	
	//haoyanling@longcheer.com for gpio91 SOS 20190921 ---
	





	error = input_register_device(pdata->input);//将input_dev(输入设备结构体)注册到输入子系统核心中
	if (error) {
		dev_err(&client->dev, "unable to register input devicen");
		goto err_free_mem;
	}

	pdata->delay = 50; //1;

	mutex_init(&pdata->read_write_lock);

	pdata->irq_gpio = of_get_named_gpio(client->dev.of_node, "qcom,irq-gpio", 0);
	printk("[%s]--aw9523_probe--pdata->irq_gpio=%dn", __func__, pdata->irq_gpio);
	if ((!gpio_is_valid(pdata->irq_gpio))){
			return -EINVAL;
	}
	printk("[%s]----pdata->irq_gpio=%dn", __func__, pdata->irq_gpio);
	ret = gpio_request(pdata->irq_gpio, "aw9523-keys");
	if (ret) {
		dev_err(&client->dev, "unable to request gpio [%d]n",
			pdata->irq_gpio);
		goto err_dis_gpio;
	}
	
	ret = gpio_direction_input(pdata->irq_gpio);
	if (ret) {
		dev_err(&client->dev, "unable to set direction for gpio [%d]n",
			pdata->irq_gpio);
		goto err_irq;
	}

	client->irq = gpio_to_irq(pdata->irq_gpio);
	if (client->irq < 0) {
		ret = client->irq;
		goto err_irq;
	}
	

	/*button backlight control file create*/
	if(led_classdev_register(&client->dev, &aw9523button_backlight))
		pr_err("led_classdev_register aw9523button_backlight failedn");

	if(led_classdev_register(&client->dev, &aw9523button_leds))
		pr_err("led_classdev_register aw9523button_leds failedn");

	ret = device_create_file(&client->dev, &dev_attr_aw9523_status);
	if (ret)
		goto err_create_file;

	ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
		aw9523_irq, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
		"aw9523_irq", client);
	if (ret) {
	dev_err(&client->dev, "Failed aw9523 irq=%d request ret = %dn",
		client->irq, ret);
		goto err_irq;
	}
	//enable_irq_wake(client->irq);
	
	
	//device_init_wakeup(&client->dev, 1);
	/* hrtimer */
	INIT_WORK(&pdata->key_work, aw9523_key_work);
	hrtimer_init(&pdata->key_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
	pdata->key_timer.function = aw9523_key_timer_func;

	//hrtimer_init(&pdata->key_timer_btn_leds, CLOCK_MONOTONIC, HRTIMER_MODE_REL); //hyl_leds
	pdata->key_timer.function = aw9523_key_leds_delay_timer_func;
	/* hrtimer end */
	i2c_set_clientdata(client, pdata);

	dev_info(&client->dev, "Rev. keypad, irq %dn",client->irq);

	if(aw9523_read_chipid(client)) {
		dev_err(&client->dev, "%s: read_chipid errorn", __func__);
		goto err_readid;
	}

	aw9523_create_sysfs(client);

	memset(keyst_new, P0_KROW_MASK, sizeof(keyst_new));
	memset(keyst_old, P0_KROW_MASK, sizeof(keyst_old));
	memset(keyst_def, P0_KROW_MASK, sizeof(keyst_def));

	aw9523_key_init(client);

	if(bl_num==0)
	{
		aw9523_backlight_set(BLLEVEL_DEF);
		bl_num=1;
	}

	printk("%s is okn", __func__);
	return 0;

err_readid:
err_irq:
	gpio_free(pdata->irq_gpio);
err_dis_gpio:
	gpio_free(pdata->dis_gpio);
//err_pwm_gpio:
//	gpio_free(pdata->pwm_gpio);
 err_free_mem:
	input_free_device(pdata->input);
	kfree(pdata);
err_create_file:
	device_remove_file(&client->dev, &dev_attr_aw9523_status);

	return error;
}

static int aw9523_remove(struct i2c_client *client)
{
	struct aw9523_kpad_platform_data *pdata = i2c_get_clientdata(client);

	aw9523_write_reg(client, 0x00, 0);
	free_irq(client->irq, pdata);
	cancel_delayed_work_sync(&pdata->work);
	cancel_work_sync(&pdata->key_work);
	
	//haoyanling@longcheer.com for gpio91 SOS 20190921 +++
	cancel_work_sync(gpio_sos_work);
	//haoyanling@longcheer.com for gpio91 SOS 20190921 ---
	
	input_unregister_device(pdata->input);
	kfree(pdata);
	return 0;
}

static const struct of_device_id aw9523_keypad_of_match[] = {
	{ .compatible = "qcom,aw9523-keys",},
	{},
};

static const struct i2c_device_id aw9523_id[] = {
	{"aw9523-keys", 0},
	{},
};
MODULE_DEVICE_TABLE(i2c, aw9523_id);

static struct i2c_driver aw9523_driver = {
	.driver = {
		.name = "aw9523-keys",
		.owner		= THIS_MODULE,
		.of_match_table = aw9523_keypad_of_match,
	},
	.probe    = aw9523_probe,
	.remove   = aw9523_remove,
	.id_table = aw9523_id,
};

module_i2c_driver(aw9523_driver);

MODULE_AUTHOR("liweilei@awinic.com.cn");
MODULE_DESCRIPTION("AW9523B Keypad driver");
MODULE_LICENSE("GPL V2");

最后

以上就是愉快小兔子为你收集整理的高通驱动实现 GPIO 中断上报键值的全部内容,希望文章能够帮你解决高通驱动实现 GPIO 中断上报键值所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(56)

评论列表共有 0 条评论

立即
投稿
返回
顶部