The Pedigree Project 0.1
Ps2Mouse.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 SPECIAL, 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 USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "Ps2Mouse.h"
21#include "pedigree/kernel/machine/InputManager.h"
22#include "pedigree/kernel/process/Thread.h"
23#include "pedigree/kernel/processor/Processor.h"
24#include "pedigree/kernel/processor/ProcessorInformation.h"
25#include "pedigree/kernel/time/Time.h"
26#include "pedigree/kernel/utilities/new"
27
28#include "system/kernel/machine/mach_pc/Ps2Controller.h"
29
30class Process;
31
32Ps2Mouse::Ps2Mouse(Device* pDev)
33 : m_pController(0), m_PacketDecoder(), m_IrqWait(0), m_ReaderThread(), m_Callbacks() {
34 setSpecificType(String("ps2-mouse"));
35}
36
37Ps2Mouse::~Ps2Mouse() {
38 m_ReaderThread.stop();
39}
40
41bool Ps2Mouse::initialise(Ps2Controller* pController) {
42 if (!pController || !pController->hasSecondPort())
43 return false;
44 m_pController = pController;
45
46 // Command responses use the controller's split-IRQ buffer once its worker
47 // is active, so IRQ12 must be enabled before waiting for either ACK.
48 m_pController->setIrqEnable(true, true);
49
50 // Missing devices and lost command replies must not block module loading.
51 const uint8_t commands[] = {SetDefaults, MouseStream};
52 for (uint8_t command : commands) {
53 bool acknowledged = false;
54 for (size_t retry = 0; retry < 3 && !acknowledged; ++retry) {
55 m_pController->writeSecondPort(command);
56 uint8_t response = 0;
57 bool received = false;
58 for (size_t poll = 0; poll < 250 && !m_pController->readsStopping(); ++poll) {
59 if (m_pController->readSecondPort(response, false)) {
60 received = true;
61 break;
62 }
63 Time::delay(Time::Multiplier::Millisecond);
64 }
65 acknowledged = received && response == MouseAck;
66 if (!received || (response != MouseAck && response != 0xfe))
67 break;
68 }
69 if (!acknowledged) {
70 m_pController->setIrqEnable(true, false);
71 WARNING("PS/2 mouse did not acknowledge command " << Hex << command);
72 return false;
73 }
74 }
75
76 Process* pProcess = Processor::information().getCurrentThread()->getParent();
77 Thread* pThread = new Thread(pProcess, readerThreadTrampoline, this);
78 pThread->setName("PS/2 mouse reader");
79 m_ReaderThread.adopt(pThread);
80
81 return true;
82}
83
84void Ps2Mouse::write(const char* bytes, size_t len) {
85 for (size_t i = 0; i < len; ++i) {
86 m_pController->writeSecondPort(bytes[i]);
87 }
88}
89
90bool Ps2Mouse::subscribe(MouseHandlerFunction handler, void* param, Registration& registration) {
91 return m_Callbacks.subscribe(handler, param, registration);
92}
93
94void Ps2Mouse::updateSubscribers(const void* buffer, size_t len) {
95 m_Callbacks.dispatch(buffer, len);
96}
97
98int Ps2Mouse::readerThreadTrampoline(void* param) {
99 Ps2Mouse* instance = reinterpret_cast<Ps2Mouse*>(param);
100 instance->readerThread();
101 return 0;
102}
103
104void Ps2Mouse::readerThread() {
105 while (true) {
106 uint8_t byte;
107 if (!m_pController->readSecondPort(byte)) {
108 Thread* thread = Processor::information().getCurrentThread();
109 if (m_pController->readsStopping() ||
110 (thread && thread->getUnwindState() != Thread::Continue)) {
111 return;
112 }
113 continue;
114 }
115
116 updateSubscribers(&byte, 1);
117
118 Ps2MousePacket packet = {};
119 if (m_PacketDecoder.feed(byte, packet)) {
120 InputManager::instance().mouseUpdate(packet.relativeX, packet.relativeY, 0, packet.buttons);
121 }
122 }
123}
static InputManager & instance()
Singleton design.
void mouseUpdate(ssize_t relX, ssize_t relY, ssize_t relZ, uint32_t buttonBitmap)
Called whenever mouse input comes in.
Process * getParent()
Definition Process.h:620
static ProcessorInformation & information()
EXPORTED_PUBLIC bool readsStopping() const
Reports that buffered readers must leave their long-lived loops.
EXPORTED_PUBLIC void writeSecondPort(uint8_t byte)
Send a byte to the second port, dropping it on timeout.
EXPORTED_PUBLIC bool hasSecondPort() const
Reports whether this PS/2 controller has two ports.
EXPORTED_PUBLIC void setIrqEnable(bool firstEnabled, bool secondEnabled)
Enables/disables IRQs for the first or second ports.
EXPORTED_PUBLIC bool readSecondPort(uint8_t &byte, bool block=true)
Reads a single byte from the second port.
bool feed(uint8_t byte, Ps2MousePacket &packet)
@ Continue
No unwind necessary, carry on as normal.
Definition Thread.h:517
UnwindType getUnwindState()
Definition Thread.h:535
@ Hex
Definition Log.h:124