/* * Copyright (c) 2015, 2021 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * mouse/ps2.cpp * PS2 Mouse. */ #include #include #include #include #include #include #include #include #include #include #include "ps2.h" namespace Sortix { static const uint8_t DEVICE_RESET_OK = 0xAA; static const uint8_t DEVICE_ECHO = 0xEE; static const uint8_t DEVICE_ACK = 0xFA; static const uint8_t DEVICE_RESEND = 0xFE; static const uint8_t DEVICE_ERROR = 0xFF; static const uint8_t DEVICE_CMD_ENABLE_SCAN = 0xF4; static const uint8_t DEVICE_CMD_DISABLE_SCAN = 0xF5; static const uint8_t DEVICE_CMD_IDENTIFY = 0xF2; static const uint8_t DEVICE_CMD_RESET = 0xFF; static const size_t DEVICE_RETRIES = 5; PS2Mouse::PS2Mouse() { this->queue = NULL; this->queuelength = 0; this->queueoffset = 0; this->queueused = 0; this->owner = NULL; this->ownerptr = NULL; this->mlock = KTHREAD_MUTEX_INITIALIZER; } PS2Mouse::~PS2Mouse() { delete[] queue; } void PS2Mouse::PS2DeviceInitialize(void* send_ctx, bool (*send)(void*, uint8_t), uint8_t* id, size_t id_size) { if ( sizeof(this->id) < id_size ) id_size = sizeof(this->id); this->send_ctx = send_ctx; this->send = send; this->state = STATE_INIT; this->tries = 0; memcpy(this->id, id, id_size); this->id_size = id_size; PS2DeviceOnByte(DEVICE_RESEND); } void PS2Mouse::PS2DeviceOnByte(uint8_t byte) { ScopedLock lock(&mlock); if ( state == STATE_INIT ) { state = STATE_ENABLE_SCAN; tries = DEVICE_RETRIES; byte = DEVICE_RESEND; } if ( state == STATE_ENABLE_SCAN ) { if ( byte == DEVICE_RESEND && tries-- ) { if ( send(send_ctx, DEVICE_CMD_ENABLE_SCAN) ) return; } state = STATE_NORMAL; tries = DEVICE_RETRIES; if ( byte == DEVICE_ACK ) return; byte = DEVICE_RESEND; } if ( state == STATE_NORMAL ) { PushByte(byte); lock.Reset(); NotifyOwner(); return; } } void PS2Mouse::NotifyOwner() { if ( !owner ) return; owner->OnMouseByte(this, ownerptr); } void PS2Mouse::SetOwner(PS2MouseDevice* owner, void* user) { this->owner = owner; this->ownerptr = user; if ( queueused ) NotifyOwner(); } bool PS2Mouse::PushByte(uint8_t byte) { // Check if we need to allocate or resize the circular queue. if ( queueused == queuelength ) { size_t newqueuelength = queuelength ? 2 * queuelength : 32UL; if ( 1024 < newqueuelength ) return false; uint8_t* newqueue = new uint8_t[newqueuelength]; if ( !newqueue ) return false; size_t elemsize = sizeof(*queue); size_t leadingavai = queuelength - queueoffset; size_t leading = leadingavai < queueused ? leadingavai : queueused; size_t trailing = queueused - leading; if ( queue ) { memcpy(newqueue, queue + queueoffset, leading * elemsize); memcpy(newqueue + leading, queue, trailing * elemsize); delete[] queue; } queue = newqueue; queuelength = newqueuelength; queueoffset = 0; } queue[(queueoffset + queueused++) % queuelength] = byte; return true; } uint8_t PS2Mouse::PopByte() { if ( !queueused ) return 0; uint8_t byte = queue[queueoffset]; queueoffset = (queueoffset + 1) % queuelength; queueused--; return byte; } uint8_t PS2Mouse::Read() { ScopedLock lock(&mlock); return PopByte(); } size_t PS2Mouse::GetPending() const { ScopedLock lock(&mlock); return queueused; } bool PS2Mouse::HasPending() const { ScopedLock lock(&mlock); return queueused; } PS2MouseDevice::PS2MouseDevice(dev_t dev, mode_t mode, uid_t owner, gid_t group, PS2Mouse* mouse) { inode_type = INODE_TYPE_TTY; this->dev = dev; this->ino = (ino_t) this; this->type = S_IFCHR; this->stat_mode = (mode & S_SETABLE) | this->type; this->stat_uid = owner; this->stat_gid = group; this->mouse = mouse; this->devlock = KTHREAD_MUTEX_INITIALIZER; this->datacond = KTHREAD_COND_INITIALIZER; mouse->SetOwner(this, NULL); } PS2MouseDevice::~PS2MouseDevice() { delete mouse; } ssize_t PS2MouseDevice::read(ioctx_t* ctx, uint8_t* buf, size_t count) { ScopedLockSignal lock(&devlock); if ( !lock.IsAcquired() ) return errno = EINTR, -1; size_t sofar = 0; while ( sofar < count ) { if ( !mouse->HasPending() ) { if ( sofar != 0 ) break; do { if ( !kthread_cond_wait_signal(&datacond, &devlock) ) return sofar ? sofar : -1; } while ( !mouse->HasPending() ); } uint8_t byte = mouse->Read(); if ( !ctx->copy_to_dest(buf + sofar, &byte, 1) ) return sofar ? sofar : -1; sofar++; } return (ssize_t) sofar; } short PS2MouseDevice::PollEventStatus() { short status = 0; if ( mouse->HasPending() ) status |= POLLIN | POLLRDNORM; return status; } int PS2MouseDevice::poll(ioctx_t* /*ctx*/, PollNode* node) { ScopedLock lock(&devlock); short ret_status = PollEventStatus() & node->events; if ( ret_status ) { node->master->revents |= ret_status; return 0; } poll_channel.Register(node); return errno = EAGAIN, -1; } void PS2MouseDevice::OnMouseByte(PS2Mouse* /*mouse*/, void* /*user*/) { ScopedLock lock(&devlock); poll_channel.Signal(PollEventStatus()); kthread_cond_signal(&datacond); } } // namespace Sortix