瀏覽代碼

[dm][input] support input #11031

GUI 2 月之前
父節點
當前提交
3ff3fc3948

+ 1 - 0
components/drivers/Kconfig

@@ -22,6 +22,7 @@ rsource "graphic/Kconfig"
 rsource "hwcrypto/Kconfig"
 rsource "wlan/Kconfig"
 rsource "led/Kconfig"
+rsource "input/Kconfig"
 rsource "mailbox/Kconfig"
 rsource "phye/Kconfig"
 rsource "ata/Kconfig"

+ 360 - 0
components/drivers/include/dt-bindings/input/event-codes.h

@@ -0,0 +1,360 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __DT_BINDINGS_INPUT_EVENT_CODES_H__
+#define __DT_BINDINGS_INPUT_EVENT_CODES_H__
+
+/*
+ * Event types
+ */
+
+#define EV_SYN                  0
+#define EV_KEY                  1
+#define EV_REL                  2
+#define EV_ABS                  3
+#define EV_MSC                  4
+#define EV_SW                   5
+#define EV_LED                  17
+#define EV_SND                  18
+#define EV_REP                  20
+#define EV_FF                   21
+#define EV_PWR                  22
+#define EV_FF_STATUS            23
+#define EV_MAX                  31
+#define EV_CNT                  (EV_MAX + 1)
+
+/*
+ * Synchronization events.
+ */
+
+#define SYN_REPORT              0
+#define SYN_CONFIG              1
+#define SYN_MT_REPORT           2
+#define SYN_DROPPED             3
+#define SYN_MAX                 15
+#define SYN_CNT                 (SYN_MAX + 1)
+
+/*
+ * Keys and buttons
+ */
+
+#define KEY_RESERVED            0   /* Reserved, do not use */
+#define KEY_ESC                 1   /* Escape Key */
+#define KEY_1                   2   /* 1 Key */
+#define KEY_2                   3   /* 2 Key */
+#define KEY_3                   4   /* 3 Key */
+#define KEY_4                   5   /* 4 Key */
+#define KEY_5                   6   /* 5 Key */
+#define KEY_6                   7   /* 6 Key */
+#define KEY_7                   8   /* 7 Key */
+#define KEY_8                   9   /* 8 Key */
+#define KEY_9                   10  /* 9 Key */
+#define KEY_0                   11  /* 0 Key */
+#define KEY_MINUS               12  /* Minus Key */
+#define KEY_EQUAL               13  /* Equal Key */
+#define KEY_BACKSPACE           14  /* Backspace Key */
+#define KEY_TAB                 15  /* Tab Key*/
+#define KEY_Q                   16  /* Q Key */
+#define KEY_W                   17  /* W Key */
+#define KEY_E                   18  /* E Key */
+#define KEY_R                   19  /* R Key */
+#define KEY_T                   20  /* T Key */
+#define KEY_Y                   21  /* Y Key */
+#define KEY_U                   22  /* U Key */
+#define KEY_I                   23  /* I Key */
+#define KEY_O                   24  /* O Key */
+#define KEY_P                   25  /* P Key */
+#define KEY_LEFTBRACE           26  /* Left Brace Key */
+#define KEY_RIGHTBRACE          27  /* Right Brace Key */
+#define KEY_ENTER               28  /* Enter Key */
+#define KEY_LEFTCTRL            29  /* Left Ctrl Key */
+#define KEY_A                   30  /* A Key */
+#define KEY_S                   31  /* S Key */
+#define KEY_D                   32  /* D Key */
+#define KEY_F                   33  /* F Key */
+#define KEY_G                   34  /* G Key */
+#define KEY_H                   35  /* H Key */
+#define KEY_J                   36  /* J Key */
+#define KEY_K                   37  /* K Key */
+#define KEY_L                   38  /* L Key */
+#define KEY_SEMICOLON           39  /* Semicolon Key */
+#define KEY_APOSTROPHE          40  /* Apostrophe Key */
+#define KEY_GRAVE               41  /* Grave (backtick) Key */
+#define KEY_LEFTSHIFT           42  /* Left Shift Key */
+#define KEY_BACKSLASH           43  /* Backslash Key */
+#define KEY_Z                   44  /* Z Key */
+#define KEY_X                   45  /* X Key */
+#define KEY_C                   46  /* C Key */
+#define KEY_V                   47  /* V Key */
+#define KEY_B                   48  /* B Key */
+#define KEY_N                   49  /* N Key */
+#define KEY_M                   50  /* M Key */
+#define KEY_COMMA               51  /* Comma Key */
+#define KEY_DOT                 52  /* Dot Key */
+#define KEY_SLASH               53  /* Slash Key */
+#define KEY_RIGHTSHIFT          54  /* Right Shift Key */
+#define KEY_KPASTERISK          55  /* Keypad Asterisk Key */
+#define KEY_LEFTALT             56  /* Left Alt Key */
+#define KEY_SPACE               57  /* Space Key */
+#define KEY_CAPSLOCK            58  /* Caps Lock Key */
+#define KEY_F1                  59  /* F1 Key */
+#define KEY_F2                  60  /* F2 Key */
+#define KEY_F3                  61  /* F3 Key */
+#define KEY_F4                  62  /* F4 Key */
+#define KEY_F5                  63  /* F5 Key */
+#define KEY_F6                  64  /* F6 Key */
+#define KEY_F7                  65  /* F7 Key */
+#define KEY_F8                  66  /* F8 Key */
+#define KEY_F9                  67  /* F9 Key */
+#define KEY_F10                 68  /* F10 Key */
+#define KEY_NUMLOCK             69  /* Num Lock Key */
+#define KEY_SCROLLLOCK          70  /* Scroll Lock Key */
+#define KEY_KP7                 71  /* Keypad 7 Key */
+#define KEY_KP8                 72  /* Keypad 8 Key */
+#define KEY_KP9                 73  /* Keypad 9 Key */
+#define KEY_KPMINUS             74  /* Keypad Minus Key */
+#define KEY_KP4                 75  /* Keypad 4 Key */
+#define KEY_KP5                 76  /* Keypad 5 Key */
+#define KEY_KP6                 77  /* Keypad 6 Key */
+#define KEY_KPPLUS              78  /* Keypad Plus Key */
+#define KEY_KP1                 79  /* Keypad 1 Key */
+#define KEY_KP2                 80  /* Keypad 2 Key */
+#define KEY_KP3                 81  /* Keypad 3 Key */
+#define KEY_KP0                 82  /* Keypad 0 Key */
+#define KEY_KPDOT               83  /* Keypad Dot Key */
+#define KEY_F11                 87  /* F11 Key */
+#define KEY_F12                 88  /* F12 Key */
+#define KEY_KPENTER             96  /* Keypad Enter Key */
+#define KEY_RIGHTCTRL           97  /* Right Ctrl Key */
+#define KEY_KPSLASH             98  /* Keypad Slash Key */
+#define KEY_SYSRQ               99  /* SysReq Key */
+#define KEY_RIGHTALT            100 /* Right Alt Key */
+#define KEY_HOME                102 /* Home Key */
+#define KEY_UP                  103 /* Up Key */
+#define KEY_PAGEUP              104 /* Page UpKey */
+#define KEY_LEFT                105 /* Left Key */
+#define KEY_RIGHT               106 /* Right Key */
+#define KEY_END                 107 /* End Key */
+#define KEY_DOWN                108 /* Down Key */
+#define KEY_PAGEDOWN            109 /* Page Down Key */
+#define KEY_INSERT              110 /* Insert Key */
+#define KEY_DELETE              111 /* Delete Key */
+#define KEY_MUTE                113 /* Mute Key */
+#define KEY_VOLUMEDOWN          114 /* Volume Down Key */
+#define KEY_VOLUMEUP            115 /* Volume Up Key */
+#define KEY_POWER               116 /* Power Key */
+#define KEY_KPEQUAL             117 /* Keypad Equal Key */
+#define KEY_KPPLUSMINUS         118 /* Keypad Plus Key */
+#define KEY_PAUSE               119 /* Pause Key */
+#define KEY_SCALE               120 /* Scale Key */
+#define KEY_KPCOMMA             121 /* Keypad Comma Key */
+#define KEY_LEFTMETA            125 /* Left Meta Key */
+#define KEY_RIGHTMETA           126 /* Right Meta Key */
+#define KEY_COMPOSE             127 /* Compose Key */
+#define KEY_STOP                128 /* AC Stop */
+#define KEY_MENU                139 /* Menu Key */
+#define KEY_SETUP               141
+#define KEY_SLEEP               142 /* System Sleep Key */
+#define KEY_WAKEUP              143 /* System Wake Up Key */
+#define KEY_COFFEE              152 /* Screen Saver Key */
+#define KEY_BACK                158 /* Back Key */
+#define KEY_FORWARD             159 /* Forward Key */
+#define KEY_PLAYPAUSE           164
+#define KEY_RECORD              167
+#define KEY_REWIND              168
+#define KEY_EXIT                174 /* AC Exit */
+#define KEY_F13                 183 /* F13 Key */
+#define KEY_F14                 184 /* F14 Key */
+#define KEY_F15                 185 /* F15 Key */
+#define KEY_F16                 186 /* F16 Key */
+#define KEY_F17                 187 /* F17 Key */
+#define KEY_F18                 188 /* F18 Key */
+#define KEY_F19                 189 /* F19 Key */
+#define KEY_F20                 190 /* F20 Key */
+#define KEY_F21                 191 /* F21 Key */
+#define KEY_F22                 192 /* F22 Key */
+#define KEY_F23                 193 /* F23 Key */
+#define KEY_F24                 194 /* F24 Key */
+#define KEY_PLAY                207 /* Play Key */
+#define KEY_FASTFORWARD         208 /* Fast Forward Key */
+#define KEY_PRINT               210 /* Print Key */
+#define KEY_CONNECT             218 /* Connect Key */
+#define KEY_CANCEL              223 /* AC Cancel */
+#define KEY_BRIGHTNESSDOWN      224 /* Brightness Down Key */
+#define KEY_BRIGHTNESSUP        225 /* Brightneess Up Key */
+#define KEY_MEDIA               226 /* Media toggle */
+#define KEY_BLUETOOTH           237 /* Bluetooth Key */
+#define KEY_WLAN                238 /* Wireless LAN Key */
+#define KEY_UWB                 239 /* Ultra-Wideband Key */
+#define KEY_SELECT              353
+#define KEY_CLEAR               355
+#define KEY_INFO                358 /* AL OEM Features/Tips/Tutorial */
+#define KEY_PROGRAM             362 /* Media Select Program Guide */
+#define KEY_CALENDAR            397
+#define KEY_RED                 398
+#define KEY_GREEN               399
+#define KEY_YELLOW              400
+#define KEY_BLUE                401
+#define KEY_CHANNELUP           402 /* Channel Increment */
+#define KEY_CHANNELDOWN         403 /* Channel Decrement */
+#define KEY_RESTART             408 /* Restart Key */
+
+#define BTN_MISC                256
+#define BTN_0                   256
+#define BTN_1                   257
+#define BTN_2                   258
+#define BTN_3                   259
+#define BTN_4                   260
+#define BTN_5                   261
+#define BTN_6                   262
+#define BTN_7                   263
+#define BTN_8                   264
+#define BTN_9                   265
+
+#define BTN_MOUSE               272
+#define BTN_LEFT                272
+#define BTN_RIGHT               273
+#define BTN_MIDDLE              274
+#define BTN_SIDE                275
+#define BTN_EXTRA               276
+#define BTN_FORWARD             277
+#define BTN_BACK                278
+#define BTN_TASK                279
+
+#define BTN_JOYSTICK            288
+#define BTN_TRIGGER             288
+#define BTN_THUMB               289
+#define BTN_THUMB2              290
+#define BTN_TOP                 291
+#define BTN_TOP2                292
+#define BTN_PINKIE              293
+#define BTN_BASE                294
+#define BTN_BASE2               295
+#define BTN_BASE3               296
+#define BTN_BASE4               297
+#define BTN_BASE5               298
+#define BTN_BASE6               299
+#define BTN_DEAD                303
+
+#define BTN_GAMEPAD             304
+#define BTN_SOUTH               304
+#define BTN_A                   BTN_SOUTH
+#define BTN_EAST                305
+#define BTN_B                   BTN_EAST
+#define BTN_C                   306
+#define BTN_NORTH               307
+#define BTN_X                   BTN_NORTH
+#define BTN_WEST                308
+#define BTN_Y                   BTN_WEST
+#define BTN_Z                   309
+#define BTN_TL                  310
+#define BTN_TR                  311
+#define BTN_TL2                 312
+#define BTN_TR2                 313
+#define BTN_SELECT              314
+#define BTN_START               315
+#define BTN_MODE                316
+#define BTN_THUMBL              317
+#define BTN_THUMBR              318
+
+#define BTN_DIGI                320
+#define BTN_TOOL_PEN            320
+#define BTN_TOOL_RUBBER         321
+#define BTN_TOOL_BRUSH          322
+#define BTN_TOOL_PENCIL         323
+#define BTN_TOOL_AIRBRUSH       324
+#define BTN_TOOL_FINGER         325
+#define BTN_TOOL_MOUSE          326
+#define BTN_TOOL_LENS           327
+#define BTN_TOOL_QUINTTAP       328 /* Five fingers on trackpad */
+#define BTN_STYLUS3             329
+#define BTN_TOUCH               330
+#define BTN_STYLUS              331
+#define BTN_STYLUS2             332
+#define BTN_TOOL_DOUBLETAP      333
+#define BTN_TOOL_TRIPLETAP      334
+#define BTN_TOOL_QUADTAP        335 /* Four fingers on trackpad */
+
+#define KEY_MAX                 767
+#define KEY_CNT                 (KEY_MAX + 1)
+
+#define BTN_TOUCH               330
+
+/*
+ * Relative axes
+ */
+
+#define REL_X                   0
+#define REL_Y                   1
+#define REL_Z                   2
+#define REL_RX                  3
+#define REL_RY                  4
+#define REL_RZ                  5
+#define REL_HWHEEL              6
+#define REL_DIAL                7
+#define REL_WHEEL               8
+#define REL_MISC                9
+#define REL_RESERVED            10
+#define REL_WHEEL_HI_RES        11
+#define REL_HWHEEL_HI_RES       12
+#define REL_MAX                 15
+#define REL_CNT                 (REL_MAX + 1)
+
+/*
+ * Absolute axes
+ */
+
+#define ABS_X                   0
+#define ABS_Y                   1
+#define ABS_Z                   2
+#define ABS_RX                  3
+#define ABS_RY                  4
+#define ABS_RZ                  5
+#define ABS_THROTTLE            6
+#define ABS_RUDDER              7
+#define ABS_WHEEL               8
+#define ABS_GAS                 9
+#define ABS_BRAKE               10
+#define ABS_HAT0X               16
+#define ABS_HAT0Y               17
+#define ABS_HAT1X               18
+#define ABS_HAT1Y               19
+#define ABS_HAT2X               20
+#define ABS_HAT2Y               21
+#define ABS_HAT3X               22
+#define ABS_HAT3Y               23
+#define ABS_PRESSURE            24
+#define ABS_DISTANCE            25
+#define ABS_TILT_X              26
+#define ABS_TILT_Y              27
+#define ABS_TOOL_WIDTH          28
+
+#define ABS_VOLUME              32
+#define ABS_PROFILE             33
+
+#define ABS_MISC                40
+#define ABS_RESERVED            46
+
+#define ABS_MT_SLOT             47      /* MT slot being modified */
+#define ABS_MT_TOUCH_MAJOR      48      /* Major axis of touching ellipse */
+#define ABS_MT_TOUCH_MINOR      49      /* Minor axis (omit if circular) */
+#define ABS_MT_WIDTH_MAJOR      50      /* Major axis of approaching ellipse */
+#define ABS_MT_WIDTH_MINOR      51      /* Minor axis (omit if circular) */
+#define ABS_MT_ORIENTATION      52      /* Ellipse orientation */
+#define ABS_MT_POSITION_X       53      /* Center X touch position */
+#define ABS_MT_POSITION_Y       54      /* Center Y touch position */
+#define ABS_MT_TOOL_TYPE        55      /* Type of touching device */
+#define ABS_MT_BLOB_ID          56      /* Group a set of packets as a blob */
+#define ABS_MT_TRACKING_ID      57      /* Unique ID of initiated contact */
+#define ABS_MT_PRESSURE         58      /* Pressure on contact area */
+#define ABS_MT_DISTANCE         59      /* Contact hover distance */
+#define ABS_MT_TOOL_X           60      /* Center X tool position */
+#define ABS_MT_TOOL_Y           61      /* Center Y tool position */
+
+#define ABS_MAX                 63
+#define ABS_CNT                 (ABS_MAX + 1)
+
+#endif /* __DT_BINDINGS_INPUT_EVENT_CODES_H__ */

+ 7 - 0
components/drivers/include/rtdevice.h

@@ -53,7 +53,14 @@ extern "C" {
 
 #ifdef RT_USING_LED
 #include "drivers/led.h"
+#endif /* RT_USING_LED */
+
+#ifdef RT_USING_INPUT
+#include "drivers/input.h"
+#ifdef RT_INPUT_UAPI
+#include "drivers/input_uapi.h"
 #endif
+#endif /* RT_USING_INPUT */
 
 #ifdef RT_USING_MBOX
 #include "drivers/mailbox.h"

+ 36 - 0
components/drivers/input/Kconfig

@@ -0,0 +1,36 @@
+menuconfig RT_USING_INPUT
+    bool "Using Input device drivers"
+    depends on RT_USING_DM
+    select RT_USING_ADT
+    select RT_USING_ADT_BITMAP
+    default n
+
+config RT_INPUT_POWER
+    bool "Input event power"
+    depends on RT_USING_INPUT
+    default y
+
+config RT_INPUT_UAPI
+    bool "Input event Unix API"
+    depends on RT_USING_INPUT
+    depends on RT_USING_KTIME
+    depends on RT_USING_POSIX_DEVIO
+    default n
+    default y if RT_USING_SMART
+
+config RT_INPUT_UAPI_EVENT_MAX
+    int "Events storage max"
+    depends on RT_INPUT_UAPI
+    default 128
+
+config RT_UAPI_FAKE_BLOCK
+    bool "Events fake when block"
+    depends on RT_INPUT_UAPI
+    default y
+
+if RT_USING_INPUT
+    rsource "joystick/Kconfig"
+    rsource "keyboard/Kconfig"
+    rsource "misc/Kconfig"
+    rsource "touchscreen/Kconfig"
+endif

+ 32 - 0
components/drivers/input/SConscript

@@ -0,0 +1,32 @@
+from building import *
+
+group = []
+objs = []
+
+if not GetDepend(['RT_USING_INPUT']):
+    Return('group')
+
+cwd = GetCurrentDir()
+list = os.listdir(cwd)
+CPPPATH = [cwd + '/../include']
+
+src = ['input.c']
+
+if GetDepend(['RT_INPUT_POWER']):
+    src += ['input_power.c']
+
+if GetDepend(['RT_INPUT_TOUCHSCREEN']):
+    src += ['input_touch.c']
+
+if GetDepend(['RT_INPUT_UAPI']):
+    src += ['input_uapi.c']
+
+group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
+
+for d in list:
+    path = os.path.join(cwd, d)
+    if os.path.isfile(os.path.join(path, 'SConscript')):
+        objs = objs + SConscript(os.path.join(d, 'SConscript'))
+objs = objs + group
+
+Return('objs')

+ 405 - 0
components/drivers/input/input.c

@@ -0,0 +1,405 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2022-3-08      GuEe-GUI     the first version
+ */
+
+#include <rthw.h>
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#define DBG_TAG "rtdm.input"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+static RT_DEFINE_SPINLOCK(input_device_lock);
+static rt_list_t input_device_nodes = RT_LIST_OBJECT_INIT(input_device_nodes);
+
+static struct rt_dm_ida input_ida = RT_DM_IDA_INIT(INPUT);
+
+#ifdef RT_INPUT_TOUCHSCREEN
+extern void input_touch_register(struct rt_input_device *idev);
+extern void input_touch_unregister(struct rt_input_device *idev);
+#endif
+
+#ifdef RT_INPUT_UAPI
+extern void input_uapi_init(struct rt_input_device *idev);
+extern void input_uapi_finit(struct rt_input_device *idev);
+extern void input_uapi_event(struct rt_input_device *idev, struct rt_input_event *event);
+#endif
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops _input_ops =
+{
+};
+#endif
+
+rt_err_t rt_input_device_register(struct rt_input_device *idev)
+{
+    rt_err_t err;
+    int device_id;
+    const char *dev_name;
+
+    if (!idev)
+    {
+        return -RT_EINVAL;
+    }
+
+    if ((device_id = rt_dm_ida_alloc(&input_ida)) < 0)
+    {
+        err = -RT_EFULL;
+        goto _remove_config;
+    }
+
+    rt_dm_dev_set_name(&idev->parent, "input%u", device_id);
+    dev_name = rt_dm_dev_get_name(&idev->parent);
+
+    rt_list_init(&idev->list);
+    rt_list_init(&idev->handler_nodes);
+    rt_spin_lock_init(&idev->lock);
+
+    /* Just make a search interface */
+    idev->parent.type = RT_Device_Class_Char;
+#ifdef RT_USING_DEVICE_OPS
+    idev->parent.ops = idev->parent.ops ? : &_input_ops;
+#endif
+    idev->parent.master_id = input_ida.master_id;
+    idev->parent.device_id = device_id;
+
+    if ((err = rt_device_register(&idev->parent, dev_name, RT_DEVICE_FLAG_DEACTIVATE)))
+    {
+        goto _fail;
+    }
+
+#ifdef RT_INPUT_UAPI
+    input_uapi_init(idev);
+#endif
+
+    rt_spin_lock(&input_device_lock);
+    rt_list_insert_before(&input_device_nodes, &idev->list);
+    rt_spin_unlock(&input_device_lock);
+
+#ifdef RT_INPUT_TOUCHSCREEN
+    /* MUST be registered after the list is inserted */
+    input_touch_register(idev);
+#endif
+
+    if (idev->poller)
+    {
+        rt_timer_start(&idev->poller->timer);
+    }
+
+    return RT_EOK;
+
+_fail:
+    rt_dm_ida_free(&input_ida, device_id);
+
+_remove_config:
+    rt_input_remove_config(idev);
+
+    return err;
+}
+
+rt_err_t rt_input_device_unregister(struct rt_input_device *idev)
+{
+    const char *dev_name;
+
+    if (!idev)
+    {
+        return -RT_EINVAL;
+    }
+
+    dev_name = rt_dm_dev_get_name(&idev->parent);
+
+    if (idev->parent.ref_count)
+    {
+        LOG_E("%s: there is %u user", dev_name, idev->parent.ref_count);
+        return -RT_EINVAL;
+    }
+
+#ifdef RT_INPUT_UAPI
+    input_uapi_finit(idev);
+#endif
+#ifdef RT_INPUT_TOUCHSCREEN
+    input_touch_unregister(idev);
+#endif
+    rt_input_remove_config(idev);
+
+    rt_dm_ida_free(&input_ida, idev->parent.device_id);
+
+    rt_device_unregister(&idev->parent);
+
+    rt_spin_lock(&input_device_lock);
+    rt_list_remove(&idev->list);
+    rt_spin_unlock(&input_device_lock);
+
+    return RT_EOK;
+}
+
+rt_err_t rt_input_set_capability(struct rt_input_device *idev,
+        rt_uint16_t type, rt_uint16_t code)
+{
+    if (!idev)
+    {
+        return -RT_EINVAL;
+    }
+
+    switch (type)
+    {
+    case EV_KEY:
+        rt_bitmap_set_bit(idev->key_map, code);
+        break;
+
+    case EV_REL:
+        rt_bitmap_set_bit(idev->rel_map, code);
+        break;
+
+    case EV_ABS:
+        if (!idev->absinfo)
+        {
+            idev->absinfo = rt_calloc(ABS_CNT, sizeof(*idev->absinfo));
+
+            if (!idev->absinfo)
+            {
+                return -RT_ENOMEM;
+            }
+        }
+        rt_bitmap_set_bit(idev->abs_map, code);
+        break;
+
+    case EV_MSC:
+    case EV_SW:
+    case EV_LED:
+    case EV_SND:
+    case EV_REP:
+    case EV_FF:
+    case EV_PWR:
+    case EV_FF_STATUS:
+        return -RT_ENOSYS;
+
+    default:
+        return -RT_EINVAL;
+    }
+
+    rt_bitmap_set_bit(idev->cap, type);
+
+    return RT_EOK;
+}
+
+rt_err_t rt_input_set_absinfo(struct rt_input_device *idev, rt_uint32_t axis,
+        rt_int32_t min, rt_int32_t max, rt_int32_t fuzz, rt_int32_t flat)
+{
+    struct rt_input_absinfo *absinfo;
+
+    if (!idev || !idev->absinfo)
+    {
+        return -RT_EINVAL;
+    }
+
+    rt_bitmap_set_bit(idev->abs_map, axis);
+
+    absinfo = &idev->absinfo[axis];
+    absinfo->minimum = min;
+    absinfo->maximum = max;
+    absinfo->fuzz = fuzz;
+    absinfo->flat = flat;
+
+    return RT_EOK;
+}
+
+static void input_poll(void *param)
+{
+    struct rt_input_device *idev = param;
+
+    idev->poller->poll(idev);
+}
+
+rt_err_t rt_input_setup_polling(struct rt_input_device *idev,
+        void (*poll)(struct rt_input_device *idev))
+{
+    const char *dev_name;
+
+    if (!idev || !poll)
+    {
+        return -RT_EINVAL;
+    }
+
+    dev_name = rt_dm_dev_get_name(&idev->parent);
+
+    idev->poller = rt_malloc(sizeof(*idev->poller));
+
+    if (!idev->poller)
+    {
+        return -RT_ENOMEM;
+    }
+
+    idev->poller->poll = poll;
+
+    rt_timer_init(&idev->poller->timer, dev_name, input_poll, idev,
+            rt_tick_from_millisecond(RT_INPUT_POLL_INTERVAL_DEFAULT),
+            RT_TIMER_FLAG_PERIODIC);
+
+    return RT_EOK;
+}
+
+void rt_input_remove_config(struct rt_input_device *idev)
+{
+    if (!idev)
+    {
+        return;
+    }
+
+    if (idev->poller)
+    {
+        rt_timer_stop(&idev->poller->timer);
+        rt_timer_detach(&idev->poller->timer);
+
+        rt_free(idev->poller);
+        idev->poller = RT_NULL;
+    }
+
+    if (idev->absinfo)
+    {
+        rt_free(idev->absinfo);
+        idev->absinfo = RT_NULL;
+    }
+
+#ifdef RT_INPUT_TOUCHSCREEN
+    if (idev->touch)
+    {
+        rt_free(idev->touch);
+        idev->touch = RT_NULL;
+    }
+#endif /* RT_INPUT_TOUCHSCREEN */
+}
+
+rt_err_t rt_input_set_poll_interval(struct rt_input_device *idev,
+        rt_uint32_t interval_ms)
+{
+    rt_tick_t tick;
+
+    if (!idev || !idev->poller)
+    {
+        return -RT_EINVAL;
+    }
+
+    tick = rt_tick_from_millisecond(interval_ms);
+
+    return rt_timer_control(&idev->poller->timer, RT_TIMER_CTRL_SET_TIME, &tick);
+}
+
+rt_err_t rt_input_trigger(struct rt_input_device *idev,
+        rt_uint16_t type, rt_uint16_t code, rt_int32_t value)
+{
+    RT_ASSERT(idev != RT_NULL);
+
+    if (idev->trigger)
+    {
+        return idev->trigger(idev, type, code, value);
+    }
+
+    return -RT_ENOSYS;
+}
+
+void rt_input_event(struct rt_input_device *idev,
+        rt_uint16_t type, rt_uint16_t code, rt_int32_t value)
+{
+    struct rt_input_event event;
+    struct rt_input_handler *handler, *handler_next;
+
+    RT_ASSERT(idev != RT_NULL);
+    RT_ASSERT(type < EV_MAX);
+
+    event.tick = rt_tick_get();
+    event.type = type;
+    event.code = code;
+    event.value = value;
+
+    rt_spin_lock(&idev->lock);
+
+#ifdef RT_INPUT_UAPI
+    input_uapi_event(idev, &event);
+#endif
+
+    rt_list_for_each_entry_safe(handler, handler_next, &idev->handler_nodes, list)
+    {
+        if (handler->callback(handler, &event))
+        {
+            break;
+        }
+    }
+
+    rt_spin_unlock(&idev->lock);
+}
+
+rt_err_t rt_input_add_handler(struct rt_input_handler *handler)
+{
+    struct rt_input_device *idev = RT_NULL, *idev_tmp;
+
+    if (!handler || !handler->callback)
+    {
+        return -RT_EINVAL;
+    }
+
+    if (!handler->idev && !handler->identify)
+    {
+        return -RT_EINVAL;
+    }
+
+    rt_spin_lock(&input_device_lock);
+
+    rt_list_for_each_entry(idev_tmp, &input_device_nodes, list)
+    {
+        if (handler->idev)
+        {
+            if (handler->idev == idev_tmp)
+            {
+                idev = idev_tmp;
+                break;
+            }
+        }
+        else if (handler->identify(handler, idev_tmp))
+        {
+            idev = idev_tmp;
+            break;
+        }
+    }
+
+    rt_spin_unlock(&input_device_lock);
+
+    if (!idev)
+    {
+        return -RT_ENOSYS;
+    }
+
+    handler->idev = idev;
+    rt_list_init(&handler->list);
+
+    rt_spin_lock(&idev->lock);
+    rt_list_insert_before(&idev->handler_nodes, &handler->list);
+    rt_spin_unlock(&idev->lock);
+
+    return RT_EOK;
+}
+
+rt_err_t rt_input_del_handler(struct rt_input_handler *handler)
+{
+    struct rt_input_device *idev;
+
+    if (!handler)
+    {
+        return -RT_EINVAL;
+    }
+
+    idev = handler->idev;
+
+    rt_spin_lock(&idev->lock);
+    rt_list_remove(&handler->list);
+    rt_spin_unlock(&idev->lock);
+
+    return RT_EOK;
+}

+ 130 - 0
components/drivers/input/input_power.c

@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2022-3-08      GuEe-GUI     the first version
+ */
+
+#include <rthw.h>
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#define DBG_TAG "input.power"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+struct input_event_cap
+{
+    rt_uint16_t code;
+    rt_int32_t value;
+    rt_int32_t current;
+};
+
+static rt_bool_t power_input_identify(struct rt_input_handler *handler,
+        struct rt_input_device *idev)
+{
+    struct input_event_cap *cap = handler->priv;
+
+    if (rt_bitmap_test_bit(idev->key_map, cap->code))
+    {
+        if (cap->value == cap->current)
+        {
+            return RT_TRUE;
+        }
+
+        ++cap->current;
+    }
+
+    return RT_FALSE;
+}
+
+static rt_bool_t power_input_callback(struct rt_input_handler *handler,
+        struct rt_input_event *ev)
+{
+    if (ev->code == KEY_POWER)
+    {
+        if (ev->value == 0)
+        {
+            rt_hw_cpu_shutdown();
+        }
+
+        return RT_TRUE;
+    }
+    else if (ev->code == KEY_RESTART)
+    {
+        if (ev->value == 0)
+        {
+            rt_hw_cpu_reset();
+        }
+
+        return RT_TRUE;
+    }
+
+    return RT_FALSE;
+}
+
+static rt_bool_t power_test_cap(struct rt_input_handler *handler,
+        struct rt_input_device *idev)
+{
+    struct input_event_cap *cap = handler->priv;
+
+    if (rt_bitmap_test_bit(idev->key_map, cap->code))
+    {
+        ++cap->value;
+    }
+
+    return RT_FALSE;
+}
+
+static void power_handler_install(rt_uint16_t code)
+{
+    rt_uint32_t idev_count;
+    struct input_event_cap cap;
+    struct rt_input_handler tmp_handler, *handlers;
+
+    cap.code = code;
+    cap.value = 0;
+    tmp_handler.idev = RT_NULL;
+    tmp_handler.identify = &power_test_cap;
+    tmp_handler.callback = &power_input_callback;
+    tmp_handler.priv = &cap;
+    rt_input_add_handler(&tmp_handler); /* Just test */
+
+    if (!(idev_count = cap.value))
+    {
+        return;
+    }
+
+    handlers = rt_calloc(idev_count, sizeof(*handlers));
+
+    if (!handlers)
+    {
+        LOG_E("No memory to install power handler");
+        return;
+    }
+
+    for (int i = 0; i < idev_count; ++i, ++handlers)
+    {
+        cap.value = i;
+        cap.current = 0;
+
+        handlers->identify = &power_input_identify;
+        handlers->callback = &power_input_callback;
+        handlers->priv = &cap;
+
+        rt_input_add_handler(handlers);
+        handlers->priv = RT_NULL;
+    }
+}
+
+static int input_event_power_init(void)
+{
+    power_handler_install(KEY_POWER);
+    power_handler_install(KEY_RESTART);
+
+    return 0;
+}
+INIT_ENV_EXPORT(input_event_power_init);

+ 453 - 0
components/drivers/input/input_touch.c

@@ -0,0 +1,453 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2022-3-08      GuEe-GUI     the first version
+ */
+
+#include <rthw.h>
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#define DBG_TAG "input.touch"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+struct input_touch_device
+{
+    struct rt_touch_device parent;
+    struct rt_input_handler handler;
+
+    rt_bool_t enabled;
+    rt_uint16_t slot;
+    rt_uint16_t down;
+    struct rt_touch_data points[];
+};
+
+struct input_touch_properties
+{
+    rt_uint32_t max_x;
+    rt_uint32_t max_y;
+    rt_bool_t invert_x;
+    rt_bool_t invert_y;
+    rt_bool_t swap_x_y;
+
+    rt_uint16_t track_id;
+    rt_uint32_t num_slots;
+    struct input_touch_device *touch_dev;
+};
+
+static rt_size_t input_touch_readpoint(struct rt_touch_device *touch,
+        void *buf, rt_size_t touch_num)
+{
+    struct input_touch_device *touch_dev;
+
+    touch_dev = rt_container_of(touch, struct input_touch_device, parent);
+
+    rt_memcpy(buf, &touch_dev->points[touch_dev->slot], sizeof(struct rt_touch_data));
+
+    return 1;
+}
+
+static rt_err_t input_touch_control(struct rt_touch_device *touch, int cmd, void *arg)
+{
+    rt_err_t err = RT_EOK;
+    struct input_touch_device *touch_dev;
+
+    if (!arg)
+    {
+        return -RT_EINVAL;
+    }
+
+    touch_dev = rt_container_of(touch, struct input_touch_device, parent);
+
+    switch (cmd)
+    {
+    case RT_TOUCH_CTRL_GET_ID:
+        *(int *)arg = 0;
+        break;
+
+    case RT_TOUCH_CTRL_GET_INFO:
+        rt_memcpy(arg, &touch_dev->parent.info, sizeof(touch_dev->parent.info));
+        break;
+
+    case RT_TOUCH_CTRL_SET_X_RANGE:
+        touch_dev->parent.info.range_x = *(rt_uint16_t *)arg;
+        break;
+
+    case RT_TOUCH_CTRL_SET_Y_RANGE:
+        touch_dev->parent.info.range_y = *(rt_uint16_t *)arg;
+        break;
+
+    case RT_TOUCH_CTRL_SET_X_TO_Y:
+        break;
+
+    case RT_TOUCH_CTRL_DISABLE_INT:
+    case RT_TOUCH_CTRL_POWER_OFF:
+        touch_dev->enabled = RT_FALSE;
+        break;
+
+    case RT_TOUCH_CTRL_ENABLE_INT:
+    case RT_TOUCH_CTRL_POWER_ON:
+        touch_dev->enabled = RT_TRUE;
+        break;
+
+    case RT_TOUCH_CTRL_GET_STATUS:
+        *(int *)arg = touch_dev->enabled;
+        break;
+
+    default:
+        err = -RT_ENOSYS;
+        break;
+    }
+
+    return err;
+}
+
+const static struct rt_touch_ops input_touch_ops =
+{
+    .touch_readpoint = input_touch_readpoint,
+    .touch_control = input_touch_control,
+};
+
+static rt_bool_t input_touch_cb(struct rt_input_handler *handler,
+        struct rt_input_event *ev)
+{
+    struct input_touch_device *touch_dev;
+
+    touch_dev = rt_container_of(handler, struct input_touch_device, handler);
+
+    if (touch_dev->enabled)
+    {
+        struct rt_touch_data *point = &touch_dev->points[touch_dev->slot];
+
+        if (ev->type == EV_ABS)
+        {
+            struct rt_input_device *idev = handler->idev;
+            struct rt_input_absinfo *absinfo = &idev->absinfo[touch_dev->slot];
+
+            switch (ev->code)
+            {
+            case ABS_MT_SLOT:
+                touch_dev->slot = ev->value;
+                break;
+
+            case ABS_MT_TRACKING_ID:
+                point->timestamp = ev->tick;
+
+                if (ev->value == (typeof(ev->code))-1)
+                {
+                    point->event = RT_TOUCH_EVENT_UP;
+
+                    touch_dev->down = 0;
+                    rt_hw_touch_isr(&touch_dev->parent);
+
+                    break;
+                }
+
+                point->track_id = ev->value;
+                point->event = touch_dev->down ? RT_TOUCH_EVENT_MOVE : RT_TOUCH_EVENT_DOWN;
+
+                ++touch_dev->down;
+                break;
+
+            case ABS_MT_POSITION_X:
+            case ABS_X:
+                point->x_coordinate = (ev->value * touch_dev->parent.info.range_x) /
+                        (absinfo->maximum - absinfo->minimum);
+                break;
+
+            case ABS_MT_POSITION_Y:
+            case ABS_Y:
+                point->y_coordinate = (ev->value * touch_dev->parent.info.range_y) /
+                        (absinfo->maximum - absinfo->minimum);
+                break;
+            }
+        }
+        else if (ev->type == EV_SYN && ev->code == SYN_REPORT && ev->value == 0)
+        {
+            rt_hw_touch_isr(&touch_dev->parent);
+        }
+    }
+
+    return RT_FALSE;
+}
+
+void input_touch_register(struct rt_input_device *idev)
+{
+    const char *dev_name;
+    struct rt_touch_device *tdev;
+    struct input_touch_device *touch_dev;
+    struct input_touch_properties *prop = idev->touch;
+
+    /* Only register rt_touch_device */
+    if (!prop || !prop->touch_dev)
+    {
+        return;
+    }
+    touch_dev = prop->touch_dev;
+    tdev = &touch_dev->parent;
+
+    tdev->ops = &input_touch_ops;
+
+    rt_dm_dev_set_name_auto(&tdev->parent, "touch");
+    dev_name = rt_dm_dev_get_name(&tdev->parent);
+
+    rt_hw_touch_register(tdev, dev_name, RT_DEVICE_FLAG_INT_RX, prop);
+
+    touch_dev->enabled = RT_TRUE;
+    touch_dev->handler.idev = idev;
+    touch_dev->handler.callback = &input_touch_cb;
+    rt_input_add_handler(&touch_dev->handler);
+}
+
+void input_touch_unregister(struct rt_input_device *idev)
+{
+    struct rt_touch_device *tdev;
+    struct input_touch_device *touch_dev;
+    struct input_touch_properties *prop = idev->touch;
+
+    if (!prop)
+    {
+        return;
+    }
+
+    if (prop->touch_dev)
+    {
+        goto _end;
+    }
+
+    touch_dev = prop->touch_dev;
+    tdev = &touch_dev->parent;
+
+    rt_input_del_handler(&touch_dev->handler);
+    rt_device_unregister(&tdev->parent);
+
+_end:
+    rt_free(idev->touch);
+    idev->touch = RT_NULL;
+}
+
+static void input_touch_parse(struct rt_input_device *idev,
+        rt_bool_t multitouch, struct input_touch_properties *prop)
+{
+    rt_bool_t present = RT_TRUE;
+    rt_uint32_t axis, axis_x, axis_y;
+    rt_uint32_t minimum, maximum, fuzz;
+    struct rt_device *dev = &idev->parent;
+    struct rt_input_absinfo *absinfo = idev->absinfo;
+
+    axis_x = multitouch ? ABS_MT_POSITION_X : ABS_X;
+    axis_y = multitouch ? ABS_MT_POSITION_Y : ABS_Y;
+
+    if ((present = rt_dm_dev_prop_read_u32(dev, "touchscreen-min-x", &minimum)))
+    {
+        minimum = absinfo[axis_x].minimum;
+    }
+
+    if ((present |= rt_dm_dev_prop_read_u32(dev, "touchscreen-size-x", &maximum)))
+    {
+        maximum = absinfo[axis_x].maximum + 1;
+    }
+
+    if ((present |= rt_dm_dev_prop_read_u32(dev, "touchscreen-fuzz-x", &fuzz)))
+    {
+        fuzz = absinfo[axis_x].fuzz;
+    }
+
+    if (present)
+    {
+        rt_input_set_absinfo(idev, axis_x, minimum, maximum - 1, fuzz, 0);
+    }
+
+    if ((present = rt_dm_dev_prop_read_u32(dev, "touchscreen-min-y", &minimum)))
+    {
+        minimum = absinfo[axis_y].minimum;
+    }
+
+    if ((present |= rt_dm_dev_prop_read_u32(dev, "touchscreen-size-y", &maximum)))
+    {
+        maximum = absinfo[axis_y].maximum + 1;
+    }
+
+    if ((present |= rt_dm_dev_prop_read_u32(dev, "touchscreen-fuzz-y", &fuzz)))
+    {
+        fuzz = absinfo[axis_y].fuzz;
+    }
+
+    if (present)
+    {
+        rt_input_set_absinfo(idev, axis_y, minimum, maximum - 1, fuzz, 0);
+    }
+
+    axis = multitouch ? ABS_MT_PRESSURE : ABS_PRESSURE;
+
+    if ((present = rt_dm_dev_prop_read_u32(dev, "touchscreen-max-pressure", &maximum)))
+    {
+        maximum = absinfo[axis].maximum;
+    }
+
+    if ((present |= rt_dm_dev_prop_read_u32(dev, "touchscreen-fuzz-pressure", &fuzz)))
+    {
+        fuzz = absinfo[axis].fuzz;
+    }
+
+    if (present)
+    {
+        rt_input_set_absinfo(idev, axis, 0, maximum, fuzz, 0);
+    }
+
+    prop->max_x = absinfo[axis_x].maximum;
+    prop->max_y = absinfo[axis_y].maximum;
+
+    prop->invert_x = rt_dm_dev_prop_read_bool(dev, "touchscreen-inverted-x");
+
+    if (prop->invert_x)
+    {
+        absinfo[axis_x].maximum -= absinfo[axis_x].minimum;
+        absinfo[axis_x].minimum = 0;
+    }
+
+    prop->invert_y = rt_dm_dev_prop_read_bool(dev, "touchscreen-inverted-y");
+
+    if (prop->invert_y)
+    {
+        absinfo[axis_y].maximum -= absinfo[axis_y].minimum;
+        absinfo[axis_y].minimum = 0;
+    }
+
+    prop->swap_x_y = rt_dm_dev_prop_read_bool(dev, "touchscreen-swapped-x-y");
+
+    if (prop->swap_x_y)
+    {
+        struct rt_input_absinfo swap_absinfo;
+
+        rt_memcpy(&swap_absinfo, &idev->absinfo[axis_x], sizeof(swap_absinfo));
+        rt_memcpy(&idev->absinfo[axis_x], &idev->absinfo[axis_y], sizeof(swap_absinfo));
+        rt_memcpy(&idev->absinfo[axis_y], &swap_absinfo, sizeof(swap_absinfo));
+    }
+}
+
+rt_err_t rt_input_setup_touch(struct rt_input_device *idev,
+        rt_uint32_t num_slots, struct rt_touch_info *info)
+{
+    rt_size_t alloc_size;
+    rt_bool_t multitouch;
+    struct input_touch_device *touch_dev;
+    struct input_touch_properties *prop;
+
+    if (!idev || idev->touch)
+    {
+        return -RT_EINVAL;
+    }
+
+    multitouch = !!num_slots;
+    alloc_size = sizeof(*prop);
+
+    if (info)
+    {
+        alloc_size += sizeof(*touch_dev);
+        alloc_size += sizeof(*touch_dev->points) * rt_max_t(rt_uint32_t, num_slots, 1);
+    }
+
+    if (!(prop = rt_calloc(1, alloc_size)))
+    {
+        return -RT_ENOMEM;
+    }
+    idev->touch = prop;
+
+    if ((prop->num_slots = num_slots))
+    {
+        rt_input_set_absinfo(idev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0);
+        rt_input_set_absinfo(idev, ABS_MT_TRACKING_ID, 0, RT_UINT16_MAX, 0, 0);
+    }
+
+    input_touch_parse(idev, multitouch, prop);
+
+    if (info)
+    {
+        rt_size_t points_nr = rt_max_t(rt_uint32_t, num_slots, 1);
+
+        touch_dev = (void *)prop + sizeof(*prop);
+        prop->touch_dev = touch_dev;
+
+        for (int i = 0; i < points_nr; ++i)
+        {
+            touch_dev->points[i].width = 1;
+        }
+
+        rt_memcpy(&touch_dev->parent.info, info, sizeof(*info));
+
+        info = &touch_dev->parent.info;
+        info->point_num = info->point_num ? : prop->num_slots;
+        info->range_x = info->range_x ? : prop->max_x;
+        info->range_y = info->range_y ? : prop->max_y;
+    }
+
+    return RT_EOK;
+}
+
+rt_err_t rt_input_parse_touch_position(struct rt_input_device *idev,
+        rt_uint32_t *out_x, rt_uint32_t *out_y)
+{
+    struct input_touch_properties *prop;
+
+    RT_ASSERT(idev != RT_NULL);
+    RT_ASSERT(out_x != RT_NULL);
+    RT_ASSERT(out_y != RT_NULL);
+
+    prop = idev->touch;
+
+    if (prop->invert_x)
+    {
+        *out_x = prop->max_x - *out_x;
+    }
+
+    if (prop->invert_y)
+    {
+        *out_y = prop->max_y - *out_y;
+    }
+
+    if (prop->swap_x_y)
+    {
+        *out_x ^= *out_y;
+        *out_y ^= *out_x;
+        *out_x ^= *out_y;
+    }
+
+    return RT_EOK;
+}
+
+rt_bool_t rt_input_report_touch_inactive(struct rt_input_device *idev,
+        rt_bool_t active)
+{
+    struct input_touch_properties *prop;
+
+    RT_ASSERT(idev != RT_NULL);
+
+    prop = idev->touch;
+
+    if (!active)
+    {
+        rt_input_event(idev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+
+        return RT_FALSE;
+    }
+
+    rt_input_event(idev, EV_ABS, ABS_MT_TRACKING_ID, prop->track_id++);
+
+    return RT_TRUE;
+}
+
+void rt_input_report_touch_position(struct rt_input_device *idev,
+        rt_uint32_t x, rt_uint32_t y, rt_bool_t multitouch)
+{
+    RT_ASSERT(idev != RT_NULL);
+
+    rt_input_parse_touch_position(idev, &x, &y);
+    rt_input_report_abs(idev, multitouch ? ABS_MT_POSITION_X : ABS_X, x);
+    rt_input_report_abs(idev, multitouch ? ABS_MT_POSITION_Y : ABS_Y, y);
+}

+ 399 - 0
components/drivers/input/input_uapi.c

@@ -0,0 +1,399 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2022-3-08      GuEe-GUI     the first version
+ */
+
+#include <rthw.h>
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#include <poll.h>
+#include <errno.h>
+#include <ktime.h>
+#include <dfs_file.h>
+
+#define DBG_TAG "input.uapi"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#define _IOC_NRBITS     8
+#define _IOC_TYPEBITS   8
+#ifndef _IOC_SIZEBITS
+#define _IOC_SIZEBITS   14
+#endif
+#ifndef _IOC_DIRBITS
+#define _IOC_DIRBITS    2
+#endif
+
+#define _IOC_SIZEMASK   ((1 << _IOC_SIZEBITS) - 1)
+
+#define _IOC_NRSHIFT    0
+#define _IOC_TYPESHIFT  (_IOC_NRSHIFT + _IOC_NRBITS)
+#define _IOC_SIZESHIFT  (_IOC_TYPESHIFT + _IOC_TYPEBITS)
+#define _IOC_DIRSHIFT   (_IOC_SIZESHIFT + _IOC_SIZEBITS)
+
+#define _IOC_NR(nr)     (((nr) >> _IOC_NRSHIFT) & 0xFF)
+#define _IOC_TYPE(nr)   (((nr) >> _IOC_TYPESHIFT) & 0xFF)
+#define _IOC_SIZE(nr)   (((nr) >> _IOC_SIZESHIFT) & 0x3FFF)
+#define _IOC_DIR(nr)    (((nr) >> _IOC_DIRSHIFT) & 0x3)
+
+#ifndef _IOC_READ
+#define _IOC_READ       2U
+#endif
+#ifndef _IOC
+#define _IOC(dir, type, nr, size)   \
+(                                   \
+    ((dir)  << _IOC_DIRSHIFT) |     \
+    ((type) << _IOC_TYPESHIFT) |    \
+    ((nr)   << _IOC_NRSHIFT) |      \
+    ((size) << _IOC_SIZESHIFT)      \
+)
+#endif
+
+struct input_uapi
+{
+    struct dfs_file *grabbed_file;
+
+    rt_atomic_t write_idx;
+    rt_atomic_t read_idx;
+    rt_atomic_t sync_count;
+    struct input_event events[RT_INPUT_UAPI_EVENT_MAX];
+};
+
+static int input_uapi_fops_open(struct dfs_file *file)
+{
+    struct rt_input_device *idev = file->vnode->data;
+
+    rt_device_open(&idev->parent, RT_DEVICE_OFLAG_RDWR);
+
+    return 0;
+}
+
+static int input_uapi_fops_close(struct dfs_file *file)
+{
+    struct rt_input_device *idev = file->vnode->data;
+    struct input_uapi *uapi = idev->uapi;
+
+    rt_device_close(&idev->parent);
+
+    if (uapi->grabbed_file == file)
+    {
+        rt_spin_lock(&idev->lock);
+        uapi->grabbed_file = RT_NULL;
+        rt_spin_unlock(&idev->lock);
+    }
+
+    return 0;
+}
+
+static int input_uapi_fops_ioctl(struct dfs_file *file, int cmd, void *args)
+{
+    unsigned int size;
+    struct rt_input_device *idev = file->vnode->data;
+    struct input_uapi *uapi = idev->uapi;
+
+    switch (cmd)
+    {
+    case EVIOCGVERSION:
+    {
+        int version = EV_VERSION;
+
+        rt_memcpy(args, &version, sizeof(int));
+        return 0;
+    }
+
+    case EVIOCGID:
+    {
+        static struct input_id virtual_id =
+        {
+            .bustype = 0x06,    /* BUS_VIRTUAL */
+            .vendor = 0x5354,   /* "RT" */
+            .product = 0x4556,  /* "EV" */
+            .version = RT_VER_NUM >> 16,
+        };
+
+        rt_memcpy(args, &virtual_id, sizeof(virtual_id));
+        return 0;
+    }
+
+    case EVIOCGRAB:
+        rt_spin_lock(&idev->lock);
+
+        if (uapi->grabbed_file && uapi->grabbed_file != file)
+        {
+            rt_spin_unlock(&idev->lock);
+            return -EBUSY;
+        }
+
+        uapi->grabbed_file = args ? file : RT_NULL;
+        rt_spin_unlock(&idev->lock);
+
+        return 0;
+    }
+
+    size = _IOC_SIZE(cmd);
+
+    switch (((cmd) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)))
+    {
+    case EVIOCGNAME(0):
+        rt_strncpy(args, idev->parent.parent.name, rt_min_t(unsigned int, size, RT_NAME_MAX));
+        return 0;
+
+    case EVIOCGPROP(0):
+    {
+        rt_bitmap_t *bitmap = args;
+        const int input_prop_direct = 0x1;
+
+        rt_memset(bitmap, 0, size);
+
+        if (size >= sizeof(rt_bitmap_t))
+        {
+            bitmap[RT_BIT_WORD(input_prop_direct)] |= RT_BIT_MASK(input_prop_direct);
+        }
+        return 0;
+    }
+    }
+
+    if (_IOC_TYPE(cmd) != 'E')
+    {
+        return -EINVAL;
+    }
+
+    if (_IOC_DIR(cmd) == _IOC_READ)
+    {
+        if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0)))
+        {
+            rt_size_t bit_len;
+            rt_bitmap_t *bitmap;
+
+            switch (_IOC_NR(cmd) & EV_MAX)
+            {
+            case      0: bitmap = idev->cap;     bit_len = EV_MAX;  break;
+            case EV_KEY: bitmap = idev->key_map; bit_len = KEY_MAX; break;
+            case EV_REL: bitmap = idev->rel_map; bit_len = REL_MAX; break;
+            case EV_ABS: bitmap = idev->abs_map; bit_len = ABS_MAX; break;
+            default:
+                return -EINVAL;
+            }
+
+            size = rt_min_t(rt_size_t, size, ((bit_len + 8) / 8));
+            rt_memcpy(args, bitmap, size);
+
+            return 0;
+        }
+
+        if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0)))
+        {
+            rt_size_t max;
+
+            if (!idev->absinfo)
+            {
+                return -EINVAL;
+            }
+
+            max = _IOC_NR(cmd) & ABS_MAX;
+
+            rt_memcpy(args, &idev->absinfo[max], rt_min_t(rt_size_t, size, sizeof(struct input_absinfo)));
+
+            return 0;
+        }
+    }
+
+    return -EINVAL;
+}
+
+static ssize_t input_uapi_fops_read(struct dfs_file *file, void *buf, size_t count, off_t *pos)
+{
+    int err;
+    size_t read = 0;
+    struct input_event *event = buf;
+    struct rt_input_device *idev = file->vnode->data;
+    struct input_uapi *uapi = idev->uapi;
+
+    rt_spin_lock(&idev->lock);
+
+    if (uapi->grabbed_file && uapi->grabbed_file != file)
+    {
+        rt_spin_unlock(&idev->lock);
+        return -EAGAIN;
+    }
+
+    rt_spin_unlock(&idev->lock);
+
+    if (count != 0 && count < sizeof(struct input_event))
+    {
+        return -EINVAL;
+    }
+
+    for (;;)
+    {
+        if (!rt_atomic_load(&uapi->sync_count) && (file->flags & O_NONBLOCK))
+        {
+        #ifdef RT_UAPI_FAKE_BLOCK
+            static struct input_event fake_event =
+            {
+                .type = EV_SYN,
+                .code = SYN_REPORT,
+            };
+
+            rt_memcpy(event, &fake_event, sizeof(struct input_event));
+            read += sizeof(struct input_event);
+
+            return read;
+        #else
+            return -EAGAIN;
+        #endif
+        }
+
+        /* No IO is done but we check for error conditions */
+        if (count == 0)
+        {
+            break;
+        }
+
+        while (read + sizeof(struct input_event) <= count && rt_atomic_load(&uapi->sync_count))
+        {
+            rt_ubase_t r_idx = rt_atomic_load(&uapi->read_idx);
+
+            rt_memcpy(event, &uapi->events[r_idx], sizeof(struct input_event));
+
+            rt_atomic_store(&uapi->read_idx, (r_idx + 1) % RT_ARRAY_SIZE(uapi->events));
+
+            if (event->type == EV_SYN && event->code == SYN_REPORT)
+            {
+                rt_atomic_sub(&uapi->sync_count, 1);
+            }
+
+            ++event;
+            read += sizeof(struct input_event);
+        }
+
+        if (read)
+        {
+            break;
+        }
+
+        if (!(file->flags & O_NONBLOCK))
+        {
+            err = rt_wqueue_wait_interruptible(&idev->parent.wait_queue, 0, RT_WAITING_FOREVER);
+
+            if (err)
+            {
+                return err;
+            }
+        }
+    }
+
+    return read;
+}
+
+static ssize_t input_uapi_fops_write(struct dfs_file *file, const void *buf, size_t count, off_t *pos)
+{
+    return -ENOSYS;
+}
+
+static int input_uapi_fops_poll(struct dfs_file *file, struct rt_pollreq *req)
+{
+    /* Only support POLLIN */
+    int mask = 0, flags = file->flags & O_ACCMODE;
+    struct rt_input_device *idev = file->vnode->data;
+    struct input_uapi *uapi = idev->uapi;
+
+    if (flags == O_RDONLY || flags == O_RDWR)
+    {
+        rt_poll_add(&idev->parent.wait_queue, req);
+
+        if (rt_atomic_load(&uapi->sync_count))
+        {
+            mask |= POLLIN;
+        }
+    }
+
+    return mask;
+}
+
+static const struct dfs_file_ops input_uapi_fops =
+{
+    .open = input_uapi_fops_open,
+    .close = input_uapi_fops_close,
+    .ioctl = input_uapi_fops_ioctl,
+    .read = input_uapi_fops_read,
+    .write = input_uapi_fops_write,
+    .lseek = generic_dfs_lseek,
+    .poll = input_uapi_fops_poll,
+};
+
+void input_uapi_init(struct rt_input_device *idev)
+{
+    struct input_uapi *uapi = rt_calloc(1, sizeof(struct input_uapi));
+
+    if (!uapi)
+    {
+        LOG_W("%s: No memory to create UAPI", rt_dm_dev_get_name(&idev->parent));
+        return;
+    }
+
+    idev->uapi = uapi;
+    idev->parent.fops = &input_uapi_fops;
+
+    RT_ASSERT(sizeof(struct input_absinfo) == sizeof(struct rt_input_absinfo));
+    RT_ASSERT(rt_offsetof(struct input_absinfo, value)      == rt_offsetof(struct rt_input_absinfo, value));
+    RT_ASSERT(rt_offsetof(struct input_absinfo, minimum)    == rt_offsetof(struct rt_input_absinfo, minimum));
+    RT_ASSERT(rt_offsetof(struct input_absinfo, maximum)    == rt_offsetof(struct rt_input_absinfo, maximum));
+    RT_ASSERT(rt_offsetof(struct input_absinfo, fuzz)       == rt_offsetof(struct rt_input_absinfo, fuzz));
+    RT_ASSERT(rt_offsetof(struct input_absinfo, flat)       == rt_offsetof(struct rt_input_absinfo, flat));
+    RT_ASSERT(rt_offsetof(struct input_absinfo, resolution) == rt_offsetof(struct rt_input_absinfo, resolution));
+}
+
+void input_uapi_finit(struct rt_input_device *idev)
+{
+    if (idev->uapi)
+    {
+        rt_free(idev->uapi);
+        idev->uapi = RT_NULL;
+        idev->parent.fops = RT_NULL;
+    }
+}
+
+void input_uapi_event(struct rt_input_device *idev, struct rt_input_event *event)
+{
+    rt_ubase_t w_idx, next;
+    struct input_event *uapi_event;
+    struct input_uapi *uapi = idev->uapi;
+
+    if (!idev->parent.ref_count)
+    {
+        return;
+    }
+
+    w_idx = rt_atomic_load(&uapi->write_idx);
+    next = (w_idx + 1) % RT_ARRAY_SIZE(uapi->events);
+
+    if (next == rt_atomic_load(&uapi->read_idx))
+    {
+        LOG_W("%s: Event (type: %d code: %d value: %d) dropped",
+                rt_dm_dev_get_name(&idev->parent),
+                event->type, event->code, event->value);
+        return;
+    }
+
+    uapi_event = &uapi->events[w_idx];
+
+    rt_ktime_boottime_get_us(&uapi_event->time);
+    uapi_event->type = event->type;
+    uapi_event->code = event->code;
+    uapi_event->value = event->value;
+
+    rt_atomic_store(&uapi->write_idx, next);
+
+    if (event->type == EV_SYN && event->code == SYN_REPORT)
+    {
+        rt_atomic_add(&uapi->sync_count, 1);
+        rt_wqueue_wakeup(&idev->parent.wait_queue, (void *)(rt_ubase_t)POLLIN);
+    }
+}

+ 7 - 0
components/drivers/input/joystick/Kconfig

@@ -0,0 +1,7 @@
+menuconfig RT_INPUT_JOYSTICK
+    bool "Joystick"
+    default n
+
+if RT_INPUT_JOYSTICK
+    osource "$(SOC_DM_INPUT_JOYSTICK_DIR)/Kconfig"
+endif

+ 15 - 0
components/drivers/input/joystick/SConscript

@@ -0,0 +1,15 @@
+from building import *
+
+group = []
+
+if not GetDepend(['RT_INPUT_JOYSTICK']):
+    Return('group')
+
+cwd = GetCurrentDir()
+CPPPATH = [cwd + '/../../include']
+
+src = []
+
+group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
+
+Return('group')

+ 13 - 0
components/drivers/input/keyboard/Kconfig

@@ -0,0 +1,13 @@
+menuconfig RT_INPUT_KEYBOARD
+    bool "Keyboards"
+    default n
+
+config RT_INPUT_KEYBOARD_GPIO
+    bool "GPIO"
+    depends on RT_INPUT_KEYBOARD
+    depends on RT_USING_OFW
+    default n
+
+if RT_INPUT_KEYBOARD
+    osource "$(SOC_DM_INPUT_KEYBOARD_DIR)/Kconfig"
+endif

+ 18 - 0
components/drivers/input/keyboard/SConscript

@@ -0,0 +1,18 @@
+from building import *
+
+group = []
+
+if not GetDepend(['RT_INPUT_KEYBOARD']):
+    Return('group')
+
+cwd = GetCurrentDir()
+CPPPATH = [cwd + '/../../include']
+
+src = []
+
+if GetDepend(['RT_INPUT_KEYBOARD_GPIO']):
+    src += ['keys-gpio.c']
+
+group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
+
+Return('group')

+ 163 - 0
components/drivers/input/keyboard/keys-gpio.c

@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2022-3-08      GuEe-GUI     the first version
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#define DBG_TAG "input.keyboard.gpio"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+struct gpio_key
+{
+    struct rt_input_device parent;
+
+    rt_base_t pin;
+    rt_uint8_t mode;
+    rt_uint32_t code;
+};
+
+static void gpio_key_event(void *data)
+{
+    struct gpio_key *gkey = data;
+
+    rt_input_report_key(&gkey->parent, gkey->code, 1);
+    rt_input_sync(&gkey->parent);
+
+    rt_input_report_key(&gkey->parent, gkey->code, 0);
+    rt_input_sync(&gkey->parent);
+}
+
+static rt_err_t ofw_append_gpio_key(struct rt_ofw_node *np)
+{
+    rt_err_t err = RT_EOK;
+    rt_uint32_t debounce;
+    const char *propname;
+    struct gpio_key *gkey = rt_calloc(1, sizeof(*gkey));
+
+    if (!gkey)
+    {
+        return -RT_ENOMEM;
+    }
+
+    gkey->pin = rt_ofw_get_named_pin(np, RT_NULL, 0, &gkey->mode, RT_NULL);
+
+    if (gkey->pin < 0)
+    {
+        err = gkey->pin;
+
+        goto _fail;
+    }
+
+    if ((propname = rt_ofw_get_prop_fuzzy_name(np, ",code$")) &&
+        !rt_ofw_prop_read_u32(np, propname, &gkey->code))
+    {
+        rt_input_set_capability(&gkey->parent, EV_KEY, gkey->code);
+
+        if (!(err = rt_input_device_register(&gkey->parent)))
+        {
+            err = rt_pin_attach_irq(gkey->pin, gkey->mode, gpio_key_event, gkey);
+
+            if (err)
+            {
+                rt_input_device_unregister(&gkey->parent);
+                goto _fail;
+            }
+
+            rt_pin_irq_enable(gkey->pin, RT_TRUE);
+        }
+    }
+
+    if (err)
+    {
+        goto _fail;
+    }
+
+    if (!rt_ofw_prop_read_u32(np, "debounce-interval", &debounce))
+    {
+        rt_pin_debounce(gkey->pin, debounce);
+    }
+
+    rt_ofw_data(np) = gkey;
+
+    return RT_EOK;
+
+_fail:
+    rt_free(gkey);
+
+    return err;
+}
+
+static rt_err_t gpio_key_probe(struct rt_platform_device *pdev)
+{
+    rt_err_t err = RT_EOK;
+    struct rt_ofw_node *key_np, *np = pdev->parent.ofw_node;
+
+    rt_ofw_foreach_available_child_node(np, key_np)
+    {
+        err = ofw_append_gpio_key(key_np);
+
+        if (err == -RT_ENOMEM)
+        {
+            rt_ofw_node_put(key_np);
+
+            return err;
+        }
+        else if (err)
+        {
+            LOG_E("%s: create KEY fail", rt_ofw_node_full_name(key_np));
+            continue;
+        }
+    }
+
+    return err;
+}
+
+static rt_err_t gpio_key_remove(struct rt_platform_device *pdev)
+{
+    struct rt_ofw_node *key_np, *np = pdev->parent.ofw_node;
+
+    rt_ofw_foreach_available_child_node(np, key_np)
+    {
+        struct gpio_key *gkey = rt_ofw_data(key_np);
+
+        if (!gkey)
+        {
+            continue;
+        }
+
+        rt_ofw_data(key_np) = RT_NULL;
+
+        rt_pin_irq_enable(gkey->pin, RT_FALSE);
+        rt_pin_detach_irq(gkey->pin);
+
+        rt_input_device_unregister(&gkey->parent);
+
+        rt_free(gkey);
+    }
+
+    return RT_EOK;
+}
+
+static const struct rt_ofw_node_id gpio_key_ofw_ids[] =
+{
+    { .compatible = "gpio-keys" },
+    { /* sentinel */ }
+};
+
+static struct rt_platform_driver gpio_key_driver =
+{
+    .name = "gpio-keys",
+    .ids = gpio_key_ofw_ids,
+
+    .probe = gpio_key_probe,
+    .remove = gpio_key_remove,
+};
+RT_PLATFORM_DRIVER_EXPORT(gpio_key_driver);

+ 12 - 0
components/drivers/input/misc/Kconfig

@@ -0,0 +1,12 @@
+menuconfig RT_INPUT_MISC
+    bool "Misc"
+    default n
+
+config RT_INPUT_MISC_BUTTON_E3X0
+    bool "NI Ettus Research USRP E3xx Button support"
+    depends on RT_INPUT_MISC
+    default n
+
+if RT_INPUT_MISC
+    osource "$(SOC_DM_INPUT_MISC_DIR)/Kconfig"
+endif

+ 18 - 0
components/drivers/input/misc/SConscript

@@ -0,0 +1,18 @@
+from building import *
+
+group = []
+
+if not GetDepend(['RT_INPUT_MISC']):
+    Return('group')
+
+cwd = GetCurrentDir()
+CPPPATH = [cwd + '/../../include']
+
+src = []
+
+if GetDepend(['RT_INPUT_MISC_BUTTON_E3X0']):
+    src += ['button-e3x0.c']
+
+group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
+
+Return('group')

+ 115 - 0
components/drivers/input/misc/button-e3x0.c

@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2022-3-08      GuEe-GUI     the first version
+ */
+
+#include <rthw.h>
+#include <rtthread.h>
+#include <rtdevice.h>
+
+struct e3x0_button
+{
+    struct rt_input_device parent;
+
+    int press_irq, release_irq;
+};
+
+static void e3x0_button_press_isr(int irqno, void *param)
+{
+    struct e3x0_button *btn = param;
+
+    rt_input_report_key(&btn->parent, KEY_POWER, 1);
+    rt_input_sync(&btn->parent);
+}
+
+static void e3x0_button_release_isr(int irqno, void *param)
+{
+    struct e3x0_button *btn = param;
+
+    rt_input_report_key(&btn->parent, KEY_POWER, 0);
+    rt_input_sync(&btn->parent);
+}
+
+static rt_err_t e3x0_button_probe(struct rt_platform_device *pdev)
+{
+    rt_err_t err;
+    struct rt_device *dev = &pdev->parent;
+    struct e3x0_button *btn = rt_calloc(1, sizeof(*btn));
+
+    if (!btn)
+    {
+        return -RT_ENOMEM;
+    }
+
+    if ((btn->press_irq = rt_dm_dev_get_irq_by_name(dev, "press")) < 0)
+    {
+        err = btn->press_irq;
+        goto _fail;
+    }
+
+    if ((btn->release_irq = rt_dm_dev_get_irq_by_name(dev, "release")) < 0)
+    {
+        err = btn->release_irq;
+        goto _fail;
+    }
+
+    rt_input_set_capability(&btn->parent, EV_KEY, KEY_POWER);
+
+    if ((err = rt_input_device_register(&btn->parent)))
+    {
+        goto _fail;
+    }
+
+    dev->user_data = btn;
+
+    rt_hw_interrupt_install(btn->press_irq, e3x0_button_press_isr, btn, "button-e3x0-press");
+    rt_hw_interrupt_umask(btn->press_irq);
+
+    rt_hw_interrupt_install(btn->release_irq, e3x0_button_release_isr, btn, "button-e3x0-release");
+    rt_hw_interrupt_umask(btn->release_irq);
+
+    return RT_EOK;
+
+_fail:
+    rt_free(btn);
+
+    return err;
+}
+
+static rt_err_t e3x0_button_remove(struct rt_platform_device *pdev)
+{
+    struct e3x0_button *btn = pdev->parent.user_data;
+
+    rt_hw_interrupt_mask(btn->press_irq);
+    rt_pic_detach_irq(btn->press_irq, btn);
+
+    rt_hw_interrupt_mask(btn->release_irq);
+    rt_pic_detach_irq(btn->release_irq, btn);
+
+    rt_input_device_unregister(&btn->parent);
+
+    rt_free(btn);
+
+    return RT_EOK;
+}
+
+static const struct rt_ofw_node_id e3x0_button_ofw_ids[] =
+{
+    { .compatible = "ettus,e3x0-button" },
+    { /* sentinel */ }
+};
+
+static struct rt_platform_driver e3x0_button_driver =
+{
+    .name = "e3x0-button",
+    .ids = e3x0_button_ofw_ids,
+
+    .probe = e3x0_button_probe,
+    .remove = e3x0_button_remove,
+};
+RT_PLATFORM_DRIVER_EXPORT(e3x0_button_driver);

+ 8 - 0
components/drivers/input/touchscreen/Kconfig

@@ -0,0 +1,8 @@
+menuconfig RT_INPUT_TOUCHSCREEN
+    bool "Touchscreens"
+    select RT_USING_TOUCH
+    default n
+
+if RT_INPUT_TOUCHSCREEN
+    osource "$(SOC_DM_INPUT_TOUCHSCREEN_DIR)/Kconfig"
+endif

+ 15 - 0
components/drivers/input/touchscreen/SConscript

@@ -0,0 +1,15 @@
+from building import *
+
+group = []
+
+if not GetDepend(['RT_INPUT_TOUCHSCREEN']):
+    Return('group')
+
+cwd = GetCurrentDir()
+CPPPATH = [cwd + '/../../include']
+
+src = []
+
+group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
+
+Return('group')