summaryrefslogtreecommitdiff
path: root/documentation/6.components/device-driver/input/uapi.md
blob: 4ca1673db3594ba549bb97d3218b02b5aa18bbcf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
@page page_device_input_uapi Input userspace API (UAPI)

# Input event UAPI (`RT_INPUT_UAPI`)

**`input_uapi.c`** exposes each **`rt_input_device`** as a **Linux-compatible** character device for POSIX userspace (Smart / LWP + DFS). IOCTLs and **`struct input_event`** are defined in **`drivers/input_uapi.h`** (aligned with **`include/uapi/linux/input.h`**).

DM overview and driver binding: @ref page_device_input_dm. In-kernel reporters and handlers: @ref page_device_input.

Sources: **`components/drivers/input/input_uapi.c`**, **`components/drivers/include/drivers/input_uapi.h`**.

---

## Kconfig

| Option | Default | Role |
| --- | --- | --- |
| **`RT_INPUT_UAPI`** | **y** if **`RT_USING_SMART`**, else **n** | Build **`input_uapi.c`**, attach **`dfs_file_ops`** in **`input_uapi_init`** |
| **`RT_INPUT_UAPI_EVENT_MAX`** | **128** | Ring buffer slots (**`struct input_event`** per device) |
| **`RT_UAPI_FAKE_BLOCK`** | **y** | **`O_NONBLOCK`** empty **`read`** returns synthetic **`SYN_REPORT`** instead of **`-EAGAIN`** |

**Dependencies**: **`RT_USING_INPUT`**, **`RT_USING_KTIME`**, **`RT_USING_POSIX_DEVIO`**.

---

## Architecture

```
  rt_input_report_* → rt_input_sync
        → rt_input_event (input.c)
              → input_uapi_event (if ref_count > 0)
                    ring buffer + ktime timestamp
                    SYN_REPORT → sync_count++, wake wait_queue
        → read() / poll(POLLIN) on /dev/inputN
```

| Hook | When |
| --- | --- |
| **`input_uapi_init`** | **`rt_input_device_register`** — allocates **`struct input_uapi`**, sets **`idev->parent.fops`** |
| **`input_uapi_event`** | Every **`rt_input_event`** while UAPI enabled |
| **`input_uapi_finit`** | **`rt_input_device_unregister`** — free UAPI state |

Registered device names are **`input0`**, **`input1`**, … (**`rt_dm_ida`** in **`input.c`**). Open via devfs:

```c
int fd = open("/dev/input0", O_RDONLY);
```

**`input_uapi_event`** returns without queuing when **`idev->parent.ref_count == 0`** — userspace must **`open`** the node before events appear.

---

## Event format

| | Kernel handler path | UAPI queue |
| --- | --- | --- |
| Struct | **`struct rt_input_event`** (`drivers/input.h`) | **`struct input_event`** (`input_uapi.h`) |
| Time | **`rt_tick_t tick`** | **`struct timeval`** from **`rt_ktime_boottime_get_us`** |
| Codes | **`dt-bindings/input/event-codes.h`** | Same |

**`struct input_absinfo`** returned by **`EVIOCGABS`** matches **`struct rt_input_absinfo`** (**`RT_ASSERT`** on layout in **`input_uapi_init`**).

---

## Ring buffer and framing

**`struct input_uapi`** holds:

- Atomic **`read_idx`**, **`write_idx`**, **`sync_count`**
- **`events[RT_INPUT_UAPI_EVENT_MAX]`**
- Optional **`grabbed_file`** for **`EVIOCGRAB`**

On each kernel event copy, **`write_idx`** advances. When **`type == EV_SYN`** and **`code == SYN_REPORT`**, **`sync_count`** increments and **`rt_wqueue_wakeup`** runs.

**`read`** drains events until the user buffer is full or a **`SYN_REPORT`** completes one frame; each consumed frame decrements **`sync_count`**.

**Overflow**: if the next write would overwrite unread data, the event is **dropped** (warning log). Raise **`RT_INPUT_UAPI_EVENT_MAX`** for high-rate touch streams.

---

## `read()` / `poll()` / `write()`

| Behavior | Detail |
| --- | --- |
| **Min size** | **`count != 0`** and **`count < sizeof(struct input_event)`** → **`-EINVAL`** |
| **`count == 0`** | Allowed (availability check after wait) |
| **Blocking** | Waits on **`idev->parent.wait_queue`** until **`sync_count > 0`** |
| **`O_NONBLOCK`** | Empty queue → **`-EAGAIN`**, or fake **`SYN_REPORT`** if **`RT_UAPI_FAKE_BLOCK`** |
| **`poll`** | **`POLLIN`** when **`sync_count > 0`** (**`O_RDONLY`** / **`O_RDWR`** only) |
| **`write`** | **`-ENOSYS`** |

**Grab**: if another **`dfs_file`** holds **`EVIOCGRAB`**, **`read`** returns **`-EAGAIN`**.

---

## `ioctl()` (implemented)

| IOCTL | Purpose |
| --- | --- |
| **`EVIOCGVERSION`** | **`EV_VERSION`** (`0x010001`) |
| **`EVIOCGID`** | Virtual ID: bustype **`0x06`**, vendor **`0x5354`**, product **`0x4556`**, version **`RT_VER_NUM >> 16`** |
| **`EVIOCGNAME(len)`** | Device name (**`inputN`**) |
| **`EVIOCGPROP(len)`** | Property bitmap (direct-touch bit when buffer large enough) |
| **`EVIOCGBIT(ev, len)`** | Type bitmap (**`ev==0`**) or **`EV_KEY`/`EV_REL`/`EV_ABS`** maps |
| **`EVIOCGABS(abs)`** | Axis **`struct input_absinfo`** |
| **`EVIOCGRAB`** | Exclusive grab; non-zero **`args`** grabs; second grabber **`-EBUSY`** |

**`EVIOCSABS`**, force-feedback, and other Linux input IOCTLs → **`-EINVAL`**.

---

## Userspace example

```c
#include <fcntl.h>
#include <unistd.h>
#include <poll.h>
#include <drivers/input_uapi.h>

void listen_input0(void)
{
    struct input_event ev[64];
    struct pollfd pfd = { .fd = open("/dev/input0", O_RDONLY), .events = POLLIN };

    for (;;)
    {
        if (poll(&pfd, 1, -1) <= 0)
            continue;

        ssize_t n = read(pfd.fd, ev, sizeof(ev));
        if (n <= 0)
            continue;

        for (int i = 0; i < n / (int)sizeof(struct input_event); ++i)
        {
            /* ev[i].type, ev[i].code, ev[i].value */
        }
    }
}
```

Use **`rt_input_add_handler`** in kernel; use UAPI when a POSIX process (e.g. **`evtest`**) reads the same event stream.

---

## Pitfalls

| Issue | Mitigation |
| --- | --- |
| **No events before `open`** | **`ref_count`** gate in **`input_uapi_event`** |
| **Unregister with open fd** | **`rt_input_device_unregister`** checks **`ref_count`** — close fds first |
| **Grab conflicts** | Release with **`EVIOCGRAB`** and zero argument |
| **Touch bursts** | Increase **`RT_INPUT_UAPI_EVENT_MAX`** or read faster |
| **`RT_UAPI_FAKE_BLOCK`** | Disable for strict **`-EAGAIN`** on empty non-blocking reads |

---

## See also

- @ref page_device_input_dm
- @ref page_device_input
- `components/drivers/include/drivers/input_uapi.h`
- `components/drivers/input/Kconfig`