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 ReadinessGenerations generations;
246 generations.read = m_Buffer.readableGeneration();
247 return generations;
248}
249
251 if (!m_Endpoint) {
253 }
254
255 LockGuard<Mutex> guard(m_Lock);
256 ++m_LifetimePins;
257 return true;
258}
259
261 if (!m_Endpoint) {
263 return;
264 }
265
266 bool registered = false;
267 bool retire = false;
268 {
269 LockGuard<Mutex> guard(m_Lock);
270 if (m_LifetimePins) {
271 --m_LifetimePins;
272 }
273 retire = !m_LifetimePins;
274 if (retire) {
275 registered = m_Registered;
276 m_Registered = false;
277 }
278 }
279
280 if (registered) {
281 InputManager::instance().removeCallback(subscriber, this);
282 }
283 if (retire) {
284 delete this;
285 }
286}
287
288void InputFile::subscriber(InputManager::InputNotification& notification) {
289 if (notification.meta) {
290 reinterpret_cast<InputFile*>(notification.meta)->handleInput(notification);
291 }
292}
293
294void InputFile::handleInput(const InputManager::InputNotification& notification) {
295 InputManager::InputNotification copy = notification;
296 copy.meta = nullptr;
297 if (m_Buffer.tryWrite(reinterpret_cast<const uint8_t*>(&copy), sizeof(copy))) {
298 dataChanged();
299 }
300}
301
302EvdevFile::EvdevFile(String name, size_t inode, Filesystem* filesystem, File* parent,
303 DeviceType type, bool endpoint)
304 : File(name, 0, 0, 0, inode, filesystem, 0, parent),
305 m_Type(type),
306 m_Endpoint(endpoint),
307 m_Lock(),
308 m_Buffer(LinuxInputBufferRecords * sizeof(LinuxInputEvent)) {
309 ByteSet(m_KeyState, 0, sizeof(m_KeyState));
310 ByteSet(m_ButtonState, 0, sizeof(m_ButtonState));
311}
312
313EvdevFile::~EvdevFile() {
314 bool registered = false;
315 {
316 LockGuard<Mutex> guard(m_Lock);
317 registered = m_Registered;
318 m_Registered = false;
319 }
320 if (registered) {
321 InputManager::instance().removeCallback(subscriber, this);
322 }
323}
324
325bool EvdevFile::initialise() {
326 setPermissionsOnly(FILE_UR | FILE_GR | FILE_OR);
327 setUidOnly(0);
328 setGidOnly(0);
329 if (!m_Endpoint) {
330 return true;
331 }
332
333 const InputManager::CallbackType filter =
334 m_Type == Keyboard ? InputManager::RawKey
335 : (m_Type == Pointer ? InputManager::Mouse : InputManager::AbsoluteMouse);
336 InputManager::instance().installCallback(filter, subscriber, this);
337 {
338 LockGuard<Mutex> guard(m_Lock);
339 m_Registered = true;
340 }
341 return true;
342}
343
345 if (m_Endpoint) {
346 return this;
347 }
348 auto* endpoint = new EvdevFile(getName(), 0, getFilesystem(), getParent(), m_Type, true);
349 if (!endpoint || !endpoint->initialise()) {
350 delete endpoint;
351 return nullptr;
352 }
353 return endpoint;
354}
355
356uint64_t EvdevFile::readBytewise(uint64_t, uint64_t size, uintptr_t buffer, bool bCanBlock) {
357 if (size < sizeof(LinuxInputEvent)) {
358 SYSCALL_ERROR(InvalidArgument);
359 return 0;
360 }
361 size -= size % sizeof(LinuxInputEvent);
362 return m_Buffer.read(reinterpret_cast<uint8_t*>(buffer), size, bCanBlock);
363}
364
365uint64_t EvdevFile::writeBytewise(uint64_t, uint64_t, uintptr_t, bool) {
366 SYSCALL_ERROR(PermissionDenied);
367 return 0;
368}
369
370int EvdevFile::select(bool bWriting, int timeout) {
371 if (bWriting) {
372 return 0;
373 }
374 return m_Buffer.canRead(timeout == 1) ? 1 : 0;
375}
376
378 ReadinessGenerations generations;
379 generations.read = m_Buffer.readableGeneration();
380 return generations;
381}
382
383bool EvdevFile::supports(size_t command) const {
384 if (!isEvdevIoctl(command)) {
385 return false;
386 }
387 const uint8_t number = ioctlNumber(command);
388 if ((number == 0x40 || number == 0x41) && m_Type == AbsolutePointer) {
389 return true;
390 }
391 return number == 0x01 || number == 0x02 || number == 0x03 || number == 0x06 || number == 0x07 ||
392 number == 0x08 || number == 0x09 || (number >= 0x18 && number <= 0x1b) ||
393 (number >= 0x20 && number <= 0x3f) || number == 0x90;
394}
395
396int EvdevFile::command(size_t command, void* buffer) {
397 const uint8_t number = ioctlNumber(command);
398 const size_t capacity = ioctlSize(command);
399 auto copyOut = [buffer](const void* value, size_t size) {
400 if (!PosixSubsystem::copyToUser(buffer, value, size)) {
401 SYSCALL_ERROR(BadAddress);
402 return false;
403 }
404 return true;
405 };
406 if (number == 0x01) {
407 return copyOut(&LinuxEvdevVersion, sizeof(LinuxEvdevVersion)) ? 0 : -1;
408 }
409 if (number == 0x02) {
410 const LinuxInputId id = {LinuxBusVirtual, 0x1, static_cast<uint16_t>(m_Type + 1), 1};
411 return copyOut(&id, sizeof(id)) ? 0 : -1;
412 }
413 if (number == 0x03) {
414 if (ioctlDirection(command) & 2) {
415 const int repeat[2] = {250, 33};
416 return copyOut(repeat, sizeof(repeat)) ? 0 : -1;
417 }
418 return 0;
419 }
420 if (number == 0x06 || number == 0x07 || number == 0x08) {
421 const char* value =
422 number == 0x06
423 ? (m_Type == Keyboard ? "Pedigree keyboard"
424 : (m_Type == Pointer ? "Pedigree pointer" : "Pedigree tablet"))
425 : (number == 0x07 ? "pedigree/input0" : "");
426 const size_t available = StringLength(value) + 1;
427 const size_t copied = min(capacity, available);
428 if (!copyOut(value, copied)) {
429 return -1;
430 }
431 return static_cast<int>(copied);
432 }
433 if (number == 0x90) {
434 return 0;
435 }
436 if (number == 0x40 || number == 0x41) {
437 if (m_Type != AbsolutePointer || capacity < sizeof(LinuxInputAbsInfo)) {
438 SYSCALL_ERROR(InvalidArgument);
439 return -1;
440 }
441 LinuxInputAbsInfo info = {};
442 {
443 LockGuard<Mutex> guard(m_Lock);
444 info.value = number == 0x40 ? m_AbsoluteX : m_AbsoluteY;
445 }
446 info.maximum = 0x7fff;
447 return copyOut(&info, sizeof(info)) ? 0 : -1;
448 }
449
450 uint8_t result[96] = {};
451 if (number == 0x09) {
452 // No special input properties.
453 } else if (number == 0x18) {
454 if (m_Type == Keyboard) {
455 LockGuard<Mutex> guard(m_Lock);
456 MemoryCopy(result, m_KeyState, sizeof(m_KeyState));
457 } else {
458 LockGuard<Mutex> guard(m_Lock);
459 for (size_t i = 0; i < 5; ++i) {
460 if (m_ButtonState[i]) {
461 setBit(result, sizeof(result), linuxButtonCode(i));
462 }
463 }
464 }
465 } else if (number >= 0x20 && number <= 0x3f) {
466 const uint8_t eventType = number - 0x20;
467 if (eventType == 0) {
468 setBit(result, sizeof(result), LinuxEvSyn);
469 setBit(result, sizeof(result), LinuxEvKey);
470 if (m_Type == Pointer) {
471 setBit(result, sizeof(result), LinuxEvRelative);
472 } else if (m_Type == AbsolutePointer) {
473 setBit(result, sizeof(result), LinuxEvAbsolute);
474 setBit(result, sizeof(result), LinuxEvRelative);
475 }
476 } else if (eventType == LinuxEvKey) {
477 if (m_Type == Keyboard) {
478 for (size_t usage = 0; usage < 256; ++usage) {
479 const uint16_t key = linuxKeyCode(usage);
480 if (key) {
481 setBit(result, sizeof(result), key);
482 }
483 }
484 } else {
485 for (size_t i = 0; i < 5; ++i) {
486 setBit(result, sizeof(result), linuxButtonCode(i));
487 }
488 }
489 } else if (eventType == LinuxEvRelative && m_Type != Keyboard) {
490 if (m_Type == Pointer) {
491 setBit(result, sizeof(result), LinuxRelX);
492 setBit(result, sizeof(result), LinuxRelY);
493 }
494 setBit(result, sizeof(result), LinuxRelWheel);
495 } else if (eventType == LinuxEvAbsolute && m_Type == AbsolutePointer) {
496 setBit(result, sizeof(result), LinuxAbsX);
497 setBit(result, sizeof(result), LinuxAbsY);
498 }
499 }
500
501 const size_t copied = min(capacity, sizeof(result));
502 if (!copyOut(result, copied)) {
503 return -1;
504 }
505 return static_cast<int>(copied);
506}
507
509 if (!m_Endpoint) {
511 }
512 LockGuard<Mutex> guard(m_Lock);
513 ++m_LifetimePins;
514 return true;
515}
516
518 if (!m_Endpoint) {
520 return;
521 }
522
523 bool registered = false;
524 bool retire = false;
525 {
526 LockGuard<Mutex> guard(m_Lock);
527 if (m_LifetimePins) {
528 --m_LifetimePins;
529 }
530 retire = !m_LifetimePins;
531 if (retire) {
532 registered = m_Registered;
533 m_Registered = false;
534 }
535 }
536 if (registered) {
537 InputManager::instance().removeCallback(subscriber, this);
538 }
539 if (retire) {
540 delete this;
541 }
542}
543
544void EvdevFile::subscriber(InputManager::InputNotification& notification) {
545 if (notification.meta) {
546 reinterpret_cast<EvdevFile*>(notification.meta)->handleInput(notification);
547 }
548}
549
550void EvdevFile::emit(const LinuxInputEvent* events, size_t count) {
551 if (m_Buffer.tryWrite(reinterpret_cast<const uint8_t*>(events),
552 count * sizeof(LinuxInputEvent))) {
553 dataChanged();
554 }
555}
556
557void EvdevFile::handleInput(const InputManager::InputNotification& notification) {
558 LinuxInputEvent events[9] = {};
559 size_t count = 0;
560 const Time::Timestamp now = Time::getTimeNanoseconds();
561 auto append = [&](uint16_t type, uint16_t code, int32_t value) {
562 LinuxInputEvent& event = events[count++];
563 event.seconds = now / Time::Multiplier::Second;
564 event.microseconds = (now % Time::Multiplier::Second) / Time::Multiplier::Microsecond;
565 event.type = type;
566 event.code = code;
567 event.value = value;
568 };
569
570 if (m_Type == Keyboard && notification.type == InputManager::RawKey) {
571 const uint16_t key = linuxKeyCode(notification.data.rawkey.scancode);
572 if (!key) {
573 return;
574 }
575 const bool pressed = !notification.data.rawkey.keyUp;
576 {
577 LockGuard<Mutex> guard(m_Lock);
578 if (pressed) {
579 setBit(m_KeyState, sizeof(m_KeyState), key);
580 } else {
581 m_KeyState[key / 8] &= static_cast<uint8_t>(~(1U << (key % 8)));
582 }
583 }
584 append(LinuxEvKey, key, pressed ? 1 : 0);
585 } else if (m_Type == Pointer && notification.type == InputManager::Mouse) {
586 if (notification.data.pointy.relx) {
587 append(LinuxEvRelative, LinuxRelX, notification.data.pointy.relx);
588 }
589 if (notification.data.pointy.rely) {
590 append(LinuxEvRelative, LinuxRelY, -notification.data.pointy.rely);
591 }
592 if (notification.data.pointy.relz) {
593 append(LinuxEvRelative, LinuxRelWheel, notification.data.pointy.relz);
594 }
595 {
596 LockGuard<Mutex> guard(m_Lock);
597 for (size_t i = 0; i < 5; ++i) {
598 const bool pressed = notification.data.pointy.buttons[i];
599 if (pressed != m_ButtonState[i]) {
600 m_ButtonState[i] = pressed;
601 append(LinuxEvKey, linuxButtonCode(i), pressed ? 1 : 0);
602 }
603 }
604 }
605 } else if (m_Type == AbsolutePointer && notification.type == InputManager::AbsoluteMouse) {
606 {
607 LockGuard<Mutex> guard(m_Lock);
608 if (!m_HaveAbsolute || notification.data.absolute.x != m_AbsoluteX) {
609 m_AbsoluteX = notification.data.absolute.x;
610 append(LinuxEvAbsolute, LinuxAbsX, m_AbsoluteX);
611 }
612 if (!m_HaveAbsolute || notification.data.absolute.y != m_AbsoluteY) {
613 m_AbsoluteY = notification.data.absolute.y;
614 append(LinuxEvAbsolute, LinuxAbsY, m_AbsoluteY);
615 }
616 m_HaveAbsolute = true;
617 for (size_t i = 0; i < 5; ++i) {
618 const bool pressed = notification.data.absolute.buttons[i];
619 if (pressed != m_ButtonState[i]) {
620 m_ButtonState[i] = pressed;
621 append(LinuxEvKey, linuxButtonCode(i), pressed ? 1 : 0);
622 }
623 }
624 }
625 if (notification.data.absolute.wheel) {
626 append(LinuxEvRelative, LinuxRelWheel, notification.data.absolute.wheel);
627 }
628 }
629
630 if (count) {
631 append(LinuxEvSyn, 0, 0);
632 emit(events, count);
633 }
634}
bool canRead(bool block)
Definition Buffer.cc:525
uint64_t readableGeneration() const
Definition Buffer.cc:559
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:396
File * open() override
Definition InputFile.cc:344
uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true) override
Definition InputFile.cc:356
bool supports(size_t command) const override
Definition InputFile.cc:383
uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true) override
Definition InputFile.cc:365
ReadinessGenerations readinessGenerations() override
Definition InputFile.cc:377
int select(bool bWriting=false, int timeout=0) override
Definition InputFile.cc:370
bool retainVfsReference() override
Definition InputFile.cc:508
void releaseVfsReference() override
Definition InputFile.cc:517
Definition File.h:75
virtual bool retainVfsReference()
Definition File.cc:910
String getName() const
Definition File.cc:782
uint64_t append(uint64_t size, uintptr_t buffer, uint64_t &location, bool bCanBlock=true)
Definition File.cc:415
virtual void releaseVfsReference()
Definition File.cc:914
void setGidOnly(size_t gid)
Definition File.cc:1448
void setPermissionsOnly(uint32_t perms)
Definition File.cc:1440
void setUidOnly(size_t uid)
Definition File.cc:1444
void dataChanged()
Definition File.cc:1452
ReadinessGenerations readinessGenerations() override
Definition InputFile.cc:244
void releaseVfsReference() override
Definition InputFile.cc:260
bool retainVfsReference() override
Definition InputFile.cc:250
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.