The Pedigree Project 0.1
InputFile.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF,
17 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
18 * SOFTWARE.
19 */
20
21#include "modules/subsys/posix/InputFile.h"
22#include "pedigree/kernel/LockGuard.h"
23#include "pedigree/kernel/syscallError.h"
24#include "pedigree/kernel/time/Time.h"
25
26#include "modules/subsys/posix/PosixSubsystem.h"
27
28namespace {
29constexpr InputManager::CallbackType InputStreamFilter =
30 InputManager::Mouse | InputManager::AbsoluteMouse | InputManager::RawKey | InputManager::Key;
31constexpr size_t InputBufferRecords = 128;
32
33constexpr size_t LinuxInputBufferRecords = 256;
34constexpr uint16_t LinuxBusVirtual = 0x06;
35constexpr int LinuxEvdevVersion = 0x010001;
36
37size_t ioctlSize(size_t command) {
38 return (command >> 16) & 0x3fff;
39}
40
41uint8_t ioctlNumber(size_t command) {
42 return command & 0xff;
43}
44
45uint8_t ioctlDirection(size_t command) {
46 return command >> 30;
47}
48
49bool isEvdevIoctl(size_t command) {
50 return ((command >> 8) & 0xff) == 'E';
51}
52
53void setBit(uint8_t* bitmap, size_t bytes, size_t bit) {
54 if (bit / 8 < bytes) {
55 bitmap[bit / 8] |= static_cast<uint8_t>(1U << (bit % 8));
56 }
57}
58
59uint16_t linuxKeyCode(uint8_t usage) {
60 static constexpr uint16_t letters[] = {
61 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, 50,
62 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44,
63 };
64 if (usage >= 4 && usage <= 29) {
65 return letters[usage - 4];
66 }
67 if (usage >= 30 && usage <= 38) {
68 return usage - 28;
69 }
70 if (usage == 39) {
71 return 11;
72 }
73 if (usage >= 58 && usage <= 67) {
74 return usage + 1;
75 }
76
77 switch (usage) {
78 case 40:
79 return 28;
80 case 41:
81 return 1;
82 case 42:
83 return 14;
84 case 43:
85 return 15;
86 case 44:
87 return 57;
88 case 45:
89 return 12;
90 case 46:
91 return 13;
92 case 47:
93 return 26;
94 case 48:
95 return 27;
96 case 49:
97 return 43;
98 case 50:
99 return 86;
100 case 51:
101 return 39;
102 case 52:
103 return 40;
104 case 53:
105 return 41;
106 case 54:
107 return 51;
108 case 55:
109 return 52;
110 case 56:
111 return 53;
112 case 57:
113 return 58;
114 case 68:
115 return 87;
116 case 69:
117 return 88;
118 case 70:
119 return 99;
120 case 71:
121 return 70;
122 case 72:
123 return 119;
124 case 73:
125 return 110;
126 case 74:
127 return 102;
128 case 75:
129 return 104;
130 case 76:
131 return 111;
132 case 77:
133 return 107;
134 case 78:
135 return 109;
136 case 79:
137 return 106;
138 case 80:
139 return 105;
140 case 81:
141 return 108;
142 case 82:
143 return 103;
144 case 83:
145 return 69;
146 case 224:
147 return 29;
148 case 225:
149 return 42;
150 case 226:
151 return 56;
152 case 227:
153 return 125;
154 case 228:
155 return 97;
156 case 229:
157 return 54;
158 case 230:
159 return 100;
160 case 231:
161 return 126;
162 default:
163 return 0;
164 }
165}
166
167uint16_t linuxButtonCode(size_t button) {
168 static constexpr uint16_t buttons[] = {
169 LinuxBtnLeft, LinuxBtnMiddle, LinuxBtnRight, LinuxBtnSide, LinuxBtnExtra,
170 };
171 return button < sizeof(buttons) / sizeof(buttons[0]) ? buttons[button] : 0;
172}
173} // namespace
174
175InputFile::InputFile(String name, size_t inode, Filesystem* filesystem, File* parent, bool endpoint)
176 : File(name, 0, 0, 0, inode, filesystem, 0, parent),
177 m_Endpoint(endpoint),
178 m_Lock(),
179 m_Buffer(InputBufferRecords * sizeof(InputManager::InputNotification)) {}
180
181InputFile::~InputFile() {
182 bool registered = false;
183 {
184 LockGuard<Mutex> guard(m_Lock);
185 registered = m_Registered;
186 m_Registered = false;
187 }
188
189 if (registered) {
190 InputManager::instance().removeCallback(subscriber, this);
191 }
192}
193
194bool InputFile::initialise() {
195 if (!m_Endpoint) {
196 setPermissionsOnly(FILE_UR | FILE_GR | FILE_OR);
197 setUidOnly(0);
198 setGidOnly(0);
199 return true;
200 }
201
202 setPermissionsOnly(FILE_UR | FILE_GR | FILE_OR);
203 setUidOnly(0);
204 setGidOnly(0);
205
206 InputManager::instance().installCallback(InputStreamFilter, subscriber, this);
207 {
208 LockGuard<Mutex> guard(m_Lock);
209 m_Registered = true;
210 }
211 return true;
212}
213
215 if (m_Endpoint) {
216 return this;
217 }
218
219 auto* endpoint = new InputFile(getName(), 0, getFilesystem(), getParent(), true);
220 if (!endpoint || !endpoint->initialise()) {
221 delete endpoint;
222 return nullptr;
223 }
224 return endpoint;
225}
226
227uint64_t InputFile::readBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
228 bool bCanBlock) {
229 return m_Buffer.read(reinterpret_cast<uint8_t*>(buffer), size, bCanBlock);
230}
231
232uint64_t InputFile::writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
233 bool bCanBlock) {
234 return 0;
235}
236
237int InputFile::select(bool bWriting, int timeout) {
238 if (bWriting) {
239 return 0;
240 }
241 return m_Buffer.canRead(timeout == 1) ? 1 : 0;
242}
243
245 if (!m_Endpoint) {
247 }
248
249 LockGuard<Mutex> guard(m_Lock);
250 ++m_LifetimePins;
251 return true;
252}
253
255 if (!m_Endpoint) {
257 return;
258 }
259
260 bool registered = false;
261 bool retire = false;
262 {
263 LockGuard<Mutex> guard(m_Lock);
264 if (m_LifetimePins) {
265 --m_LifetimePins;
266 }
267 retire = !m_LifetimePins;
268 if (retire) {
269 registered = m_Registered;
270 m_Registered = false;
271 }
272 }
273
274 if (registered) {
275 InputManager::instance().removeCallback(subscriber, this);
276 }
277 if (retire) {
278 delete this;
279 }
280}
281
282void InputFile::subscriber(InputManager::InputNotification& notification) {
283 if (notification.meta) {
284 reinterpret_cast<InputFile*>(notification.meta)->handleInput(notification);
285 }
286}
287
288void InputFile::handleInput(const InputManager::InputNotification& notification) {
289 InputManager::InputNotification copy = notification;
290 copy.meta = nullptr;
291 if (m_Buffer.tryWrite(reinterpret_cast<const uint8_t*>(&copy), sizeof(copy))) {
292 dataChanged();
293 }
294}
295
296EvdevFile::EvdevFile(String name, size_t inode, Filesystem* filesystem, File* parent,
297 DeviceType type, bool endpoint)
298 : File(name, 0, 0, 0, inode, filesystem, 0, parent),
299 m_Type(type),
300 m_Endpoint(endpoint),
301 m_Lock(),
302 m_Buffer(LinuxInputBufferRecords * sizeof(LinuxInputEvent)) {
303 ByteSet(m_KeyState, 0, sizeof(m_KeyState));
304 ByteSet(m_ButtonState, 0, sizeof(m_ButtonState));
305}
306
307EvdevFile::~EvdevFile() {
308 bool registered = false;
309 {
310 LockGuard<Mutex> guard(m_Lock);
311 registered = m_Registered;
312 m_Registered = false;
313 }
314 if (registered) {
315 InputManager::instance().removeCallback(subscriber, this);
316 }
317}
318
319bool EvdevFile::initialise() {
320 setPermissionsOnly(FILE_UR | FILE_GR | FILE_OR);
321 setUidOnly(0);
322 setGidOnly(0);
323 if (!m_Endpoint) {
324 return true;
325 }
326
327 const InputManager::CallbackType filter =
328 m_Type == Keyboard ? InputManager::RawKey
329 : (m_Type == Pointer ? InputManager::Mouse : InputManager::AbsoluteMouse);
330 InputManager::instance().installCallback(filter, subscriber, this);
331 {
332 LockGuard<Mutex> guard(m_Lock);
333 m_Registered = true;
334 }
335 return true;
336}
337
339 if (m_Endpoint) {
340 return this;
341 }
342 auto* endpoint = new EvdevFile(getName(), 0, getFilesystem(), getParent(), m_Type, true);
343 if (!endpoint || !endpoint->initialise()) {
344 delete endpoint;
345 return nullptr;
346 }
347 return endpoint;
348}
349
350uint64_t EvdevFile::readBytewise(uint64_t, uint64_t size, uintptr_t buffer, bool bCanBlock) {
351 if (size < sizeof(LinuxInputEvent)) {
352 SYSCALL_ERROR(InvalidArgument);
353 return 0;
354 }
355 size -= size % sizeof(LinuxInputEvent);
356 return m_Buffer.read(reinterpret_cast<uint8_t*>(buffer), size, bCanBlock);
357}
358
359uint64_t EvdevFile::writeBytewise(uint64_t, uint64_t, uintptr_t, bool) {
360 SYSCALL_ERROR(PermissionDenied);
361 return 0;
362}
363
364int EvdevFile::select(bool bWriting, int timeout) {
365 if (bWriting) {
366 return 0;
367 }
368 return m_Buffer.canRead(timeout == 1) ? 1 : 0;
369}
370
371bool EvdevFile::supports(size_t command) const {
372 if (!isEvdevIoctl(command)) {
373 return false;
374 }
375 const uint8_t number = ioctlNumber(command);
376 if ((number == 0x40 || number == 0x41) && m_Type == AbsolutePointer) {
377 return true;
378 }
379 return number == 0x01 || number == 0x02 || number == 0x03 || number == 0x06 || number == 0x07 ||
380 number == 0x08 || number == 0x09 || (number >= 0x18 && number <= 0x1b) ||
381 (number >= 0x20 && number <= 0x3f) || number == 0x90;
382}
383
384int EvdevFile::command(size_t command, void* buffer) {
385 const uint8_t number = ioctlNumber(command);
386 const size_t capacity = ioctlSize(command);
387 auto copyOut = [buffer](const void* value, size_t size) {
388 if (!PosixSubsystem::copyToUser(buffer, value, size)) {
389 SYSCALL_ERROR(BadAddress);
390 return false;
391 }
392 return true;
393 };
394 if (number == 0x01) {
395 return copyOut(&LinuxEvdevVersion, sizeof(LinuxEvdevVersion)) ? 0 : -1;
396 }
397 if (number == 0x02) {
398 const LinuxInputId id = {LinuxBusVirtual, 0x1, static_cast<uint16_t>(m_Type + 1), 1};
399 return copyOut(&id, sizeof(id)) ? 0 : -1;
400 }
401 if (number == 0x03) {
402 if (ioctlDirection(command) & 2) {
403 const int repeat[2] = {250, 33};
404 return copyOut(repeat, sizeof(repeat)) ? 0 : -1;
405 }
406 return 0;
407 }
408 if (number == 0x06 || number == 0x07 || number == 0x08) {
409 const char* value =
410 number == 0x06
411 ? (m_Type == Keyboard ? "Pedigree keyboard"
412 : (m_Type == Pointer ? "Pedigree pointer" : "Pedigree tablet"))
413 : (number == 0x07 ? "pedigree/input0" : "");
414 const size_t available = StringLength(value) + 1;
415 const size_t copied = min(capacity, available);
416 if (!copyOut(value, copied)) {
417 return -1;
418 }
419 return static_cast<int>(copied);
420 }
421 if (number == 0x90) {
422 return 0;
423 }
424 if (number == 0x40 || number == 0x41) {
425 if (m_Type != AbsolutePointer || capacity < sizeof(LinuxInputAbsInfo)) {
426 SYSCALL_ERROR(InvalidArgument);
427 return -1;
428 }
429 LinuxInputAbsInfo info = {};
430 {
431 LockGuard<Mutex> guard(m_Lock);
432 info.value = number == 0x40 ? m_AbsoluteX : m_AbsoluteY;
433 }
434 info.maximum = 0x7fff;
435 return copyOut(&info, sizeof(info)) ? 0 : -1;
436 }
437
438 uint8_t result[96] = {};
439 if (number == 0x09) {
440 // No special input properties.
441 } else if (number == 0x18) {
442 if (m_Type == Keyboard) {
443 LockGuard<Mutex> guard(m_Lock);
444 MemoryCopy(result, m_KeyState, sizeof(m_KeyState));
445 } else {
446 LockGuard<Mutex> guard(m_Lock);
447 for (size_t i = 0; i < 5; ++i) {
448 if (m_ButtonState[i]) {
449 setBit(result, sizeof(result), linuxButtonCode(i));
450 }
451 }
452 }
453 } else if (number >= 0x20 && number <= 0x3f) {
454 const uint8_t eventType = number - 0x20;
455 if (eventType == 0) {
456 setBit(result, sizeof(result), LinuxEvSyn);
457 setBit(result, sizeof(result), LinuxEvKey);
458 if (m_Type == Pointer) {
459 setBit(result, sizeof(result), LinuxEvRelative);
460 } else if (m_Type == AbsolutePointer) {
461 setBit(result, sizeof(result), LinuxEvAbsolute);
462 setBit(result, sizeof(result), LinuxEvRelative);
463 }
464 } else if (eventType == LinuxEvKey) {
465 if (m_Type == Keyboard) {
466 for (size_t usage = 0; usage < 256; ++usage) {
467 const uint16_t key = linuxKeyCode(usage);
468 if (key) {
469 setBit(result, sizeof(result), key);
470 }
471 }
472 } else {
473 for (size_t i = 0; i < 5; ++i) {
474 setBit(result, sizeof(result), linuxButtonCode(i));
475 }
476 }
477 } else if (eventType == LinuxEvRelative && m_Type != Keyboard) {
478 if (m_Type == Pointer) {
479 setBit(result, sizeof(result), LinuxRelX);
480 setBit(result, sizeof(result), LinuxRelY);
481 }
482 setBit(result, sizeof(result), LinuxRelWheel);
483 } else if (eventType == LinuxEvAbsolute && m_Type == AbsolutePointer) {
484 setBit(result, sizeof(result), LinuxAbsX);
485 setBit(result, sizeof(result), LinuxAbsY);
486 }
487 }
488
489 const size_t copied = min(capacity, sizeof(result));
490 if (!copyOut(result, copied)) {
491 return -1;
492 }
493 return static_cast<int>(copied);
494}
495
497 if (!m_Endpoint) {
499 }
500 LockGuard<Mutex> guard(m_Lock);
501 ++m_LifetimePins;
502 return true;
503}
504
506 if (!m_Endpoint) {
508 return;
509 }
510
511 bool registered = false;
512 bool retire = false;
513 {
514 LockGuard<Mutex> guard(m_Lock);
515 if (m_LifetimePins) {
516 --m_LifetimePins;
517 }
518 retire = !m_LifetimePins;
519 if (retire) {
520 registered = m_Registered;
521 m_Registered = false;
522 }
523 }
524 if (registered) {
525 InputManager::instance().removeCallback(subscriber, this);
526 }
527 if (retire) {
528 delete this;
529 }
530}
531
532void EvdevFile::subscriber(InputManager::InputNotification& notification) {
533 if (notification.meta) {
534 reinterpret_cast<EvdevFile*>(notification.meta)->handleInput(notification);
535 }
536}
537
538void EvdevFile::emit(const LinuxInputEvent* events, size_t count) {
539 if (m_Buffer.tryWrite(reinterpret_cast<const uint8_t*>(events),
540 count * sizeof(LinuxInputEvent))) {
541 dataChanged();
542 }
543}
544
545void EvdevFile::handleInput(const InputManager::InputNotification& notification) {
546 LinuxInputEvent events[9] = {};
547 size_t count = 0;
548 const Time::Timestamp now = Time::getTimeNanoseconds();
549 auto append = [&](uint16_t type, uint16_t code, int32_t value) {
550 LinuxInputEvent& event = events[count++];
551 event.seconds = now / Time::Multiplier::Second;
552 event.microseconds = (now % Time::Multiplier::Second) / Time::Multiplier::Microsecond;
553 event.type = type;
554 event.code = code;
555 event.value = value;
556 };
557
558 if (m_Type == Keyboard && notification.type == InputManager::RawKey) {
559 const uint16_t key = linuxKeyCode(notification.data.rawkey.scancode);
560 if (!key) {
561 return;
562 }
563 const bool pressed = !notification.data.rawkey.keyUp;
564 {
565 LockGuard<Mutex> guard(m_Lock);
566 if (pressed) {
567 setBit(m_KeyState, sizeof(m_KeyState), key);
568 } else {
569 m_KeyState[key / 8] &= static_cast<uint8_t>(~(1U << (key % 8)));
570 }
571 }
572 append(LinuxEvKey, key, pressed ? 1 : 0);
573 } else if (m_Type == Pointer && notification.type == InputManager::Mouse) {
574 if (notification.data.pointy.relx) {
575 append(LinuxEvRelative, LinuxRelX, notification.data.pointy.relx);
576 }
577 if (notification.data.pointy.rely) {
578 append(LinuxEvRelative, LinuxRelY, -notification.data.pointy.rely);
579 }
580 if (notification.data.pointy.relz) {
581 append(LinuxEvRelative, LinuxRelWheel, notification.data.pointy.relz);
582 }
583 {
584 LockGuard<Mutex> guard(m_Lock);
585 for (size_t i = 0; i < 5; ++i) {
586 const bool pressed = notification.data.pointy.buttons[i];
587 if (pressed != m_ButtonState[i]) {
588 m_ButtonState[i] = pressed;
589 append(LinuxEvKey, linuxButtonCode(i), pressed ? 1 : 0);
590 }
591 }
592 }
593 } else if (m_Type == AbsolutePointer && notification.type == InputManager::AbsoluteMouse) {
594 {
595 LockGuard<Mutex> guard(m_Lock);
596 if (!m_HaveAbsolute || notification.data.absolute.x != m_AbsoluteX) {
597 m_AbsoluteX = notification.data.absolute.x;
598 append(LinuxEvAbsolute, LinuxAbsX, m_AbsoluteX);
599 }
600 if (!m_HaveAbsolute || notification.data.absolute.y != m_AbsoluteY) {
601 m_AbsoluteY = notification.data.absolute.y;
602 append(LinuxEvAbsolute, LinuxAbsY, m_AbsoluteY);
603 }
604 m_HaveAbsolute = true;
605 for (size_t i = 0; i < 5; ++i) {
606 const bool pressed = notification.data.absolute.buttons[i];
607 if (pressed != m_ButtonState[i]) {
608 m_ButtonState[i] = pressed;
609 append(LinuxEvKey, linuxButtonCode(i), pressed ? 1 : 0);
610 }
611 }
612 }
613 if (notification.data.absolute.wheel) {
614 append(LinuxEvRelative, LinuxRelWheel, notification.data.absolute.wheel);
615 }
616 }
617
618 if (count) {
619 append(LinuxEvSyn, 0, 0);
620 emit(events, count);
621 }
622}
bool canRead(bool block)
Definition Buffer.cc:525
bool tryWrite(const T *buffer, size_t count)
Definition Buffer.cc:275
size_t read(T *buffer, size_t count, bool block=true)
Definition Buffer.cc:296
int command(size_t command, void *buffer) override
Definition InputFile.cc:384
File * open() override
Definition InputFile.cc:338
uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true) override
Definition InputFile.cc:350
bool supports(size_t command) const override
Definition InputFile.cc:371
uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true) override
Definition InputFile.cc:359
int select(bool bWriting=false, int timeout=0) override
Definition InputFile.cc:364
bool retainVfsReference() override
Definition InputFile.cc:496
void releaseVfsReference() override
Definition InputFile.cc:505
Definition File.h:74
virtual bool retainVfsReference()
Definition File.cc:842
String getName() const
Definition File.cc:714
uint64_t append(uint64_t size, uintptr_t buffer, uint64_t &location, bool bCanBlock=true)
Definition File.cc:399
virtual void releaseVfsReference()
Definition File.cc:846
void setGidOnly(size_t gid)
Definition File.cc:1360
void setPermissionsOnly(uint32_t perms)
Definition File.cc:1352
void setUidOnly(size_t uid)
Definition File.cc:1356
void dataChanged()
Definition File.cc:1364
void releaseVfsReference() override
Definition InputFile.cc:254
bool retainVfsReference() override
Definition InputFile.cc:244
uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true) override
Definition InputFile.cc:227
int select(bool bWriting=false, int timeout=0) override
Definition InputFile.cc:237
File * open() override
Definition InputFile.cc:214
uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true) override
Definition InputFile.cc:232
static const int Key
static InputManager & instance()
Singleton design.
void installCallback(CallbackType filter, callback_t callback, void *meta=0)
Installs a callback.
void removeCallback(callback_t callback, void *meta=0)
Removes a callback.
static bool copyToUser(void *destination, const void *source, size_t count, size_t elementSize=1)
bool keyUp
Whether this is a keyUp event or not.
uint8_t scancode
Machine-specific scancode for the key.