高通驱动实现 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。

@\frameworks\base\data\keyboards\Generic.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"@ \device\qcom\msm8909\gpio-keys.kl
key 102   HOME            WAKE
key 528   FOCUS
key 766   CAMERA
key 204   "KEY_SOS"           WAKE

以上代码确认生效方法 ----->
(1)编译前,剪切或者删除这个目录到其他地方:\out\target\product\msm8937_32go\system\usr\keylayout
(2)整编译大版本 make all
(3)编成功后 看下 \out\target\product\msm8937_32go\system\usr\keylayout\gpio-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 = %d\n", 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:\Users\Administrator>adb shell
ASUS_X00P_1:/ # getevent -l
add device 2: /dev/input/event8name:     "msm8952-snd-card-mtp Button Jack"
add device 3: /dev/input/event7name:     "msm8952-snd-card-mtp Headset Jack"
add device 9: /dev/input/event0name:     "qpnp_pon"
could not get driver version for /dev/input/mice, Not a typewriter
add device 10: /dev/input/event5name:     "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: falseSystem UI Visibility: 0x8608Pointer Speed: 0Pointer Gestures Enabled: trueShow Touches: falsePointer Capture Enabled: falseEvent Hub State:BuiltInKeyboardId: -2Devices:-1: VirtualClasses: 0x40000023Path: <virtual>Descriptor: a718a782d34bc767f4689c232d64d527998ea7fdLocation:ControllerNumber: 0UniqueId: <virtual>Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000KeyLayoutFile: /system/usr/keylayout/Generic.klKeyCharacterMapFile: /system/usr/keychars/Virtual.kcmConfigurationFile:HaveKeyboardLayoutOverlay: false1: uinput-fpsensorClasses: 0x80000001Path: /dev/input/event9Descriptor: 485d69228e24f5e46da1598745890b214130dbc4Location:ControllerNumber: 0UniqueId:Identifier: bus=0x0003, vendor=0x0001, product=0x0001, version=0x0001KeyLayoutFile: /system/usr/keylayout/Generic.klKeyCharacterMapFile: /system/usr/keychars/Generic.kcmConfigurationFile:HaveKeyboardLayoutOverlay: false2: msm8952-snd-card-mtp Button JackClasses: 0x00000001Path: /dev/input/event8Descriptor: ab997034df873c1d418cf7db1475423a61777921Location: ALSAControllerNumber: 0UniqueId:Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000KeyLayoutFile: /system/usr/keylayout/Generic.klKeyCharacterMapFile: /system/usr/keychars/Generic.kcmConfigurationFile:HaveKeyboardLayoutOverlay: false3: msm8952-snd-card-mtp Headset JackClasses: 0x00000080Path: /dev/input/event7Descriptor: 863975bb9064de0fc8a7277b87ac846c366d49c1Location: ALSAControllerNumber: 0UniqueId:Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000KeyLayoutFile:KeyCharacterMapFile:ConfigurationFile:HaveKeyboardLayoutOverlay: false4: goodix-tsClasses: 0x00000015Path: /dev/input/event6Descriptor: 9e6143a1bc5dd41251b165ed559e32d49b5aad8fLocation: input/tsControllerNumber: 0UniqueId:Identifier: bus=0x0018, vendor=0xdead, product=0xbeef, version=0x28bbKeyLayoutFile: /system/usr/keylayout/Generic.klKeyCharacterMapFile: /system/usr/keychars/Generic.kcmConfigurationFile:HaveKeyboardLayoutOverlay: false5: rf-keysClasses: 0x00000001Path: /dev/input/event4Descriptor: bd7533fcf563b1a6b2c798f0e54e396a7aa1eecfLocation:ControllerNumber: 0UniqueId:Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000KeyLayoutFile: /system/usr/keylayout/Generic.klKeyCharacterMapFile: /system/usr/keychars/Generic.kcmConfigurationFile:HaveKeyboardLayoutOverlay: false6: hbtp_vmClasses: 0x00000008Path: /dev/input/event3Descriptor: 793e303307c34fc398863d5f83ee4c6ea6a7c5c6Location:ControllerNumber: 0UniqueId:Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000KeyLayoutFile:KeyCharacterMapFile:ConfigurationFile:HaveKeyboardLayoutOverlay: false7: sf-keysClasses: 0x00000001Path: /dev/input/event2Descriptor: 1d04485d5413d18cadf673bafa785070142031c8Location:ControllerNumber: 0UniqueId:Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000KeyLayoutFile: /system/usr/keylayout/Generic.klKeyCharacterMapFile: /system/usr/keychars/Generic.kcmConfigurationFile:HaveKeyboardLayoutOverlay: false8: madevClasses: 0x00000001Path: /dev/input/event1Descriptor: 6e687c668d87f7a84f420f31851eb0fabc6ebb08Location:ControllerNumber: 0UniqueId:Identifier: bus=0x001c, vendor=0x0000, product=0x0000, version=0x0000KeyLayoutFile: /system/usr/keylayout/Generic.klKeyCharacterMapFile: /system/usr/keychars/Generic.kcmConfigurationFile:HaveKeyboardLayoutOverlay: false9: qpnp_ponClasses: 0x00000001Path: /dev/input/event0Descriptor: fb60d4f4370f5dbe8267b63d38dea852987571abLocation: qpnp_pon/input0ControllerNumber: 0UniqueId:Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000KeyLayoutFile: /system/usr/keylayout/qpnp_pon.klKeyCharacterMapFile: /system/usr/keychars/Generic.kcmConfigurationFile:HaveKeyboardLayoutOverlay: false10: gpio-keysClasses: 0x00000001Path: /dev/input/event5Descriptor: d2c52ff0f656fac4cd7b7a118d575e0109a9fe1cLocation: gpio-keys/input0ControllerNumber: 0UniqueId:Identifier: bus=0x0019, vendor=0x0001, product=0x0001, version=0x0100KeyLayoutFile: /system/usr/keylayout/gpio-keys.klKeyCharacterMapFile: /system/usr/keychars/Generic.kcmConfigurationFile:HaveKeyboardLayoutOverlay: false

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

4.1 DTS 配置

@\frameworks\base\data\keyboards\Generic.kl
# key 204 (undefined)
key 204 "KEY_SOS"
# key 205 "KEY_SUSPEND"@ \device\qcom\msm8909\gpio-keys.kl
key 102   HOME            WAKE
key 204   "KEY_SOS"       WAKE@ \kernel\msm-3.18\arch\arm\boot\dts\qcom\msm8909-mtp.dtsigpio_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>;  // 0x204gpio-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%x\n", 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 inputsystem\n",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 inputsystem\n",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 inputsystem\n",bdata->button->gpio, bdata->button->code, bdata->button->code, bdata->button->type);  ///+++++++++++++++++++++++++++++++++bdata->key_pressed = false;}}

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

在上层中,最早处理 keyevent 的时候是在
\frameworks\base\core\java\android\app\Activity.java 中

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

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

@ \frameworks\base\core\java\android\app\Activity.javapublic boolean dispatchKeyEvent(KeyEvent event) {onUserInteraction();// Let action bars open menus in response to the menu key prioritized over// the window handling itfinal 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

\frameworks\base\core\java\android\view\View.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 SimplifiableIfStatementListenerInfo 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);

代码如下

\frameworks\base\core\java\android\view\KeyEvent.javapublic 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 文件内容

@ \kernel\msm-3.18\arch\arm\boot\dts\qcom\msm8909-mtp.dtsigpio_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>;  // 0x204gpio-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] = '\0';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);elsegpio_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 inputsystem\n",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));elseschedule_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 inputsystem\n",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 inputsystem\n",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 %d\n",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 %d\n",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 specified\n");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: %d\n",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 %d\n",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 state\n");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 state\n");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 state\n");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 gpios\n");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: %d\n",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%x\n",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);#elsestatic inline struct gpio_keys_platform_data *
gpio_keys_get_devtree_pdata(struct device *dev)
{return ERR_PTR(-ENODEV);
}#endifstatic 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 state\n");return -ENOMEM;}input = devm_input_allocate_device(dev);if (!input) {dev_err(dev, "failed to allocate input device\n");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 pinctrl\n");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 state\n");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: %d\n",error);goto err_create_sysfs;}error = input_register_device(input);if (error) {dev_err(dev, "Unable to register input device, error: %d\n",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 state\n");elsepinctrl_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 state\n");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 state\n");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 state\n");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;
}#elsestatic 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;
}#endifstatic 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   20static 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 255KEY_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: %d\n", 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: %d\n",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_Raw9523_write_reg(client, P1_6_DIM0, g); //color_Gaw9523_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 0aw9523_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 timerhrtimer_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 modeaw9523_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 modeaw9523_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 statuskeyst_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 changedfor (i=0; i<P1_NUM_MAX; i++) {  //8 循环if (keyst_old[i] != keyst_new[i]) {        // keyst of i col changedfor (j=0; j<P0_NUM_MAX; j++) {if (P0_KROW_MASK & (1<<j)) {                             // j row gpio usedif ((keyst_old[i] & (1<<j)) != (keyst_new[i] & (1<<j))) {                // j row & i col changedfor (cnt=0; cnt<keymap_len; cnt++) {      // find row&col in the keymapif ((keymap[cnt].row == j) && (keymap[cnt].col == i)) {break;}}if (keyst_new[i] & (1<<j)) {              // releasekeymap[cnt].key_val = 0;} else {                                     // presskeymap[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=%d\n",__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 modeaw9523_read_reg(client, P1_OUTPUT, &val);aw9523_write_reg(client, P1_OUTPUT, val & (~P1_KCOL_MASK));   //p1 port output 0aw9523_read_reg(client, P0_INPUT, &val);                  //clear p0 input irqaw9523_read_reg(client, P0_INT, &val);aw9523_write_reg(client, P0_INT, val & (~P0_KROW_MASK));  //enable p0 port irqenable_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 enter\n", __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 enter\n", __func__);// TODO ,close button ledsaw9523_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 enter\n", __func__);aw9523_write_reg(client, P0_INT, 0xff);         //disable p0 port irqhrtimer_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 enter\n", __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 ok\n", __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 modeaw9523_read_reg(client, P1_LED_MODE, &val);aw9523_write_reg(client, P1_LED_MODE, val | P1_KCOL_MASK);    //p1 port gpio modeaw9523_read_reg(client, P0_INT, &val);aw9523_write_reg(client, P0_INT, val | P0_KROW_MASK);      //disable p0 port irqaw9523_write_reg(client, P1_INT, 0xff);        //disable p1 port irqaw9523_read_reg(client, P0_CONFIG, &val);aw9523_write_reg(client, P0_CONFIG, val | P0_KROW_MASK);  //set p0 port input modeaw9523_read_reg(client, P1_CONFIG, &val);aw9523_write_reg(client,P1_CONFIG, val & (~P1_KCOL_MASK)); //set p1 port output modeaw9523_read_reg(client, P1_OUTPUT, &val);aw9523_write_reg(client, P1_OUTPUT, val & (~P1_KCOL_MASK));   //p1 port output 0aw9523_read_reg(client, P0_INPUT, &val);                  //clear p0 input irqaw9523_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 irqaw9523_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 = %d\n", __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 = %d\n", __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 Supported\n");return -EIO;}//内核内存分配pdata = devm_kzalloc(&client->dev,sizeof(struct aw9523_kpad_platform_data), GFP_KERNEL);if (!pdata){dev_err(&client->dev, "Failed to allocate memory\n");return -ENOMEM;}g_aw9523_data = pdata;/*irq pull power supply*/pdata->irq_pull = regulator_get(&client->dev, "aw9523_power");//获取电源管理,dev驱动对应的设备指针,vccif (IS_ERR(pdata->irq_pull)) {ret = PTR_ERR(pdata->irq_pull);dev_err(&client->dev, "Regulator get failed irq_pull ret=%d\n", 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=%d\n", ret);return ret;}}ret = regulator_enable(pdata->irq_pull);if (ret) {dev_err(&client->dev, "Regulator irq_pull enable failed ret=%d\n", 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=%d\n", __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 = %d\n", 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 device\n");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=%d\n", __func__, pdata->irq_gpio);if ((!gpio_is_valid(pdata->irq_gpio))){return -EINVAL;}printk("[%s]----pdata->irq_gpio=%d\n", __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 failed\n");if(led_classdev_register(&client->dev, &aw9523button_leds))pr_err("led_classdev_register aw9523button_leds failed\n");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 = %d\n",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_ledspdata->key_timer.function = aw9523_key_leds_delay_timer_func;/* hrtimer end */i2c_set_clientdata(client, pdata);dev_info(&client->dev, "Rev. keypad, irq %d\n",client->irq);if(aw9523_read_chipid(client)) {dev_err(&client->dev, "%s: read_chipid error\n", __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 ok\n", __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 中断上报键值相关推荐

  1. 高通驱动树中的GPIO详解

    高通驱动树中的GPIO详解 reference:https://blog.csdn.net/baidu_37503452/article/details/80257441 Drive Strength ...

  2. 360手机驱动 360手机高通驱动 360手机驱动报错 360手机USB驱动

    360手机驱动 360手机高通驱动 360手机驱动报错 360手机USB驱动   [工具下载] 参考:360手机-360刷机360刷机包twrp.root 刷机包+工具下载地址:https://360 ...

  3. Linux input子系统上报键值失败问题

    昨天在做有关Linux input子系统实验的时候,被一个问题困扰了很久,到第二天才发现原因,最后的问题是一个小细节导致实验的失败. 当时的实验代码如下: static int key_probe(s ...

  4. 高通驱动开发参考(二)

    第1章 Driver相关模块介绍 1.1 REX简介 虽说目前QSC60x5平台上采用L4操作系统,REX只是L4上面的一个Task.但高通为了开发的兼容性,提供的API仍然采用老的一套接口(可能内部 ...

  5. 高通 msm平台GPIO相关的device tree设置

    转载自:http://blog.csdn.net/hongzg1982/article/details/47784627 GPIO相关的dvice tree设置和interrupt设置 gpoi号以及 ...

  6. 高通驱动9008安装_小米10/Redmi K30 Pro系列已支持GPU驱动独立更新,还能双版本切换...

    高通在去年末发布的那颗骁龙865处理器,一大功能升级就是支持GPU驱动独立更新.手机厂商可以通过Google Play或者自己的应用商店向用户推送新版本的GPU驱动更新,大大地简化了这类系统关键组件的 ...

  7. 高通驱动9008安装_高通snapdragon888的性能有多强大

    按照年底发布的传统,高通公司于2020年12月1日晚上正式发布了新一代旗舰5G手机平台小龙888.关于这一命名,高通公司总裁安孟说:" 8'代表Snapdragon系列的顶级平台,代表了旗舰 ...

  8. 编译问题追踪 :高通驱动移植 <utils/Log.h>问题

    问题点 找不到 <utils/Log.h> [ 0% 28/15711] target thumb C: libmmcamera_gc5035_otp_eeprom_32 <= ve ...

  9. 23.mtk6737上报键值更改

    1.dws修改按键上报 2.找底层上报的对应键值标(实体按键) \alps_o1_mp1\update\alps\kst\drv\dct\dct\old_dct\Keypad_YuSu.cmp 3.映 ...

最新文章

  1. 从MS .NET CF版访问电话API(完整版) (转载)
  2. 荒径 弗罗斯特_弗罗斯特庞克,颠覆性城市建设者
  3. 《ASP.NET Core 微服务实战》-- 读书笔记(第1章 、第2章)
  4. 结巴分词关键词相似度_jieba+gensim 实现相似度
  5. css分类及其它技巧
  6. Centos7中完美搭建ftp服务器
  7. mysql初始化登录报错解决-1
  8. php+log+iis,利用nxlog以syslog方式发送iis日志
  9. response下载zip文件
  10. 肠道细菌产生的神经递质调节宿主的感觉行为
  11. UEBA——通过用户画像识别安全威胁
  12. Execl操作基础——自动填充空白单元格
  13. 基于CUDA的GPU计算PI值
  14. utest:检验U型和倒U形关系
  15. 【实用教程】网页视频下载
  16. 攻防世界 favorite_number mfw、[BJDCTF2020]ZJCTF,不过如此
  17. 客服ai虚拟数字人技术方案及制作流程
  18. 网龙2018年财报看点:教育业务全球开花+变现加速 游戏业务逆势增长
  19. 在…视域下是什么意思_视域是什么意思?
  20. cap与一致性(强一致性、弱一致性、最终一致性)

热门文章

  1. C语言/C++常见习题问答集锦(六十五) 之彩票幸运星
  2. c语言用循环语句画红旗,C语言 飘动的红旗(要有旗杆)
  3. 百度动物园六一访问量超300万
  4. linux下复制光盘映像
  5. 苹果iphone手机翻页小技巧
  6. QT中添加图片资源的方法
  7. 图片转成二维码怎么弄?图片二维码的制作方法
  8. 专访首汇农业总经理王守玉:回归本真 顺势而为
  9. linux下安装mysql8及使用
  10. 记录-安装cuda与cudnn 及对应版本的tensorflow|pytorch