#include <string.h>
#include <SerialPort.h>
#include "SerialMouse.h"
#ifdef DEBUG_SERIAL_MOUSE
#include <stdio.h>
#define LOG(x) printf x // TODO: log to "MouseInputDevice::sLogFile"
#else
#define LOG(x)
#endif
const static bigtime_t kSerialTimeOut = 200000;
const static uint8 kMaxBytesToRead = 255;
the sync[] is a protocol-identification/sync thingy:
if ((read_byte[0] & sync[0]) == sync[1]) then we are at the beggining of the
a packet. Next data bytes are OK... if ((read_byte[i] & sync[2]) == 0))
*/
struct mouse_protocol {
const char* name;
uint8 num_bytes;
uint8 sync[3];
};
const static
struct mouse_protocol mp[] = {
{ "UNKNOWN", 0, { 0x00, 0x00, 0x00 } },
{ "Microsoft", 3, { 0x40, 0x40, 0x40,} },
{ "Logitech", 3, { 0x40, 0x40, 0x40 } },
{ "MouseSystems", 5, { 0xF8, 0x80, 0x00 } },
{ "IntelliMouse", 4, { 0x40, 0x40, 0x00 } },
};
SerialMouse::SerialMouse()
: fSerialPort(NULL),
fPortsCount(0),
fPortNumber(0),
fMouseID(kNotSet),
fButtonsState(0)
{
fSerialPort = new BSerialPort();
fPortsCount = fSerialPort->CountDevices() - 1;
}
SerialMouse::~SerialMouse()
{
if (fSerialPort != NULL) {
fSerialPort->SetRTS(false);
fSerialPort->Close();
delete fSerialPort;
}
}
const char *
SerialMouse::MouseDescription()
{
return mp[fMouseID].name;
}
status_t
SerialMouse::IsMousePresent()
{
for (uint8 i = 1; i <= fPortsCount; i++) {
char dev_name[B_PATH_NAME_LENGTH];
fSerialPort->GetDeviceName(i, dev_name);
if (strncmp(dev_name, "serial", 6) != 0
&& strncmp(dev_name, "pc_serial", 9) != 0)
continue;
if (fSerialPort->Open(dev_name) <= 0) {
LOG(("SerialMouse: Failed to open %s.\n", dev_name));
continue;
}
LOG(("SerialMouse : Opened %s.\n", dev_name));
uint8 retries = 4;
do {
fMouseID = DetectMouse();
if (fMouseID > kNotSet) {
LOG(("SerialMouse: Found a %s Mouse.\n", MouseDescription()));
fSerialPort->SetBlocking(true);
fPortNumber = i;
return fPortNumber;
}
} while ((fMouseID == kUnknown) && (retries--));
LOG(("SerialMouse: Mouse not detected.\n"));
fSerialPort->Close();
}
return B_NO_INIT;
}
mouse_id
SerialMouse::DetectMouse()
{
int bytes_read = 0;
char c;
char id_buffer[20];
char buffer[kMaxBytesToRead];
uint8 id_length = 0;
fSerialPort->SetBlocking(false);
fSerialPort->SetTimeout(kSerialTimeOut);
SetPortOptions();
snooze(10000);
fSerialPort->SetRTS(false);
snooze(120000);
fSerialPort->ClearInput();
fSerialPort->SetRTS(true);
if (fSerialPort->WaitForInput() == 0)
return kNoDevice;
while ((fSerialPort->Read(&c, 1) == 1) && (bytes_read < kMaxBytesToRead)) {
LOG(("read = %c (%d d - %x h)\n", c, c, c));
if (c == 'M' || c == 'H' || c == '3' || c == 'Z' || c == '@') {
if (id_length < 4) {
id_buffer[id_length] = c;
id_length++;
}
} else
buffer[bytes_read] = c;
bytes_read++;
if (fSerialPort->WaitForInput() == 0)
break;
}
if (bytes_read == 0)
return kNoDevice;
fSerialPort->ClearInput();
if (id_length) {
fMouseID = ParseID(id_buffer, id_length);
SetPortOptions();
return fMouseID;
}
if (bytes_read < 3)
return kUnknown;
if (bytes_read == 5) {
fMouseID = kMouseSystems;
SetPortOptions();
return fMouseID;
}
return kUnknown;
}
mouse_id
SerialMouse::ParseID(char buffer[], uint8 length)
{
LOG(("data length = %d\n", (int)length));
if ((length == 1) && (buffer[0] == 'M'))
return kMicrosoft;
if (length == 2) {
if (buffer[0] == 'M' && buffer[1] == '3')
return kLogitech;
else if (buffer[0] == 'H' && buffer[1] == 'H')
return kMouseSystems;
}
if ((length == 4) &&
(buffer[0] == 'M' && buffer[1] == 'Z') && (buffer[2] == '@'))
return kIntelliMouse;
return kUnknown;
}
status_t
SerialMouse::SetPortOptions()
{
switch (fMouseID) {
case kLogitech:
fSerialPort->SetDataRate(B_1200_BPS);
fSerialPort->Write("*q", 2);
fSerialPort->SetDataRate(B_9600_BPS);
fSerialPort->SetDataBits(B_DATA_BITS_7);
break;
case kMouseSystems:
fSerialPort->SetDataRate(B_1200_BPS);
fSerialPort->SetDataBits(B_DATA_BITS_8);
break;
case kNotSet:
default:
fSerialPort->SetDataRate(B_1200_BPS);
fSerialPort->SetDataBits(B_DATA_BITS_7);
break;
}
return B_OK;
}
status_t
SerialMouse::GetMouseEvent(mouse_movement* mm)
{
char data[5];
if (fMouseID <= kNotSet)
return B_ERROR;
if (GetPacket(data) != B_OK)
return B_ERROR;
if (PacketToMM(data, mm) != B_OK)
return B_ERROR;
#ifdef DEBUG_SERIAL_MOUSE
DumpData(mm);
#endif
return B_OK;
}
status_t
SerialMouse::GetPacket(char data[])
{
status_t result = B_ERROR;
uchar c = 0;
uint8 bytes_read;
size_t mpsize = mp[fMouseID].num_bytes;
for (bytes_read = 0; bytes_read < mpsize; bytes_read++) {
if (fSerialPort->Read(&c, 1) != 1) {
snooze(5000);
break;
}
if (bytes_read == 0) {
if ((c & mp[fMouseID].sync[0]) != mp[fMouseID].sync[1]) {
LOG(("Out of sync: skipping byte = %x\n", c));
continue;
}
}
data[bytes_read] = c;
}
if (bytes_read == mpsize) {
result = B_OK;
for (uint8 i = 1; i <= mpsize; i++) {
if ((data[i] & mp[fMouseID].sync[2]) != 0) {
LOG(("Out of sync: wrong data byte = %x\n", data[i]));
result = B_ERROR;
break;
}
}
}
return result;
}
status_t
SerialMouse::PacketToMM(char data[], mouse_movement* mm)
{
const uint8 kPrimaryButton = 1;
const uint8 kSecondaryButton = 2;
const uint8 kTertiaryButton = 4;
static uint8 previous_buttons = 0;
mm->timestamp = system_time();
switch (fMouseID) {
case kMicrosoft:
mm->buttons = ((data[0] & 0x20) ? kPrimaryButton : 0) +
((data[0] & 0x10) ? kSecondaryButton : 0);
mm->xdelta = (int8) (((data[0] & 0x03) << 6) + (data[1] & 0x3F));
mm->ydelta = - (int8) (((data[0] & 0x0C) << 4) + (data[2] & 0x3F));
if ((mm->xdelta == 0) && (mm->ydelta == 0) &&
((uint8) mm->buttons == (previous_buttons & ~kTertiaryButton))) {
mm->buttons = previous_buttons ^ kTertiaryButton;
} else {
mm->buttons |= previous_buttons & kTertiaryButton;
}
previous_buttons = mm->buttons;
break;
case kIntelliMouse:
mm->buttons = ((data[0] & 0x20) ? kPrimaryButton : 0) +
((data[0] & 0x10) ? kSecondaryButton : 0) +
((data[3] & 0x10) ? kTertiaryButton : 0);
mm->xdelta = ((int8) ((data[0] & 0x03) << 6) + (int8) (data[1] & 0x3F));
mm->ydelta = - ((int8) ((data[0] & 0x0C) << 4) + (int8) (data[2] & 0x3F));
switch (data[3] & 0x0F) {
case 0x1: mm->wheel_ydelta = +1; break;
case 0xF: mm->wheel_ydelta = -1; break;
case 0x2: mm->wheel_xdelta = +1; break;
case 0xE: mm->wheel_xdelta = -1; break;
}
break;
case kMouseSystems:
{
uint8 tmp = (~data[0] & 0x07);
mm->buttons = ((tmp & 0x4) ? kPrimaryButton : 0) +
((tmp & 0x1) ? kSecondaryButton : 0) +
((tmp & 0x2) ? kTertiaryButton : 0);
mm->xdelta = ((int8) data[1] + (int8) data[3]);
mm->ydelta = ((int8) data[2] + (int8) data[4]);
break;
}
case kLogitech:
{
mm->buttons = ((data[0] & 0x20) ? kPrimaryButton : 0) +
((data[0] & 0x10) ? kSecondaryButton : 0);
mm->xdelta = (int8) (((data[0] & 0x03) << 6) + (data[1] & 0x3F));
mm->ydelta = - (int8) (((data[0] & 0x0C) << 4) + (data[2] & 0x3F));
break;
}
default:
LOG(("Unhandled protocol. Should not happen.\n"));
return B_ERROR;
}
return B_OK;
}
void
SerialMouse::DumpData(mouse_movement* mm)
{
#ifdef DEBUG_SERIAL_MOUSE
if (mm->buttons ^ fButtonsState)
LOG(("Buttons = %ld\n", mm->buttons));
if (mm->xdelta || mm->ydelta)
LOG(("xdelta = %ld; ydelta = %ld\n", mm->xdelta, mm->ydelta));
if (fMouseID == kIntelliMouse && (mm->wheel_xdelta || mm->wheel_xdelta)) {
LOG(("wheel_xdelta = %ld; wheel_ydelta = %ld\n", mm->wheel_xdelta,
mm->wheel_ydelta));
}
#endif
}