Darn, SVN checked only modified stamps of the files, not the content.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@28427 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
c3a113e4b4
commit
1ecbbd9c27
@ -0,0 +1,518 @@
|
||||
/*
|
||||
* Copyright 2001-2008 Haiku, Inc.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* PS/2 mouse device driver
|
||||
*
|
||||
* Authors (in chronological order):
|
||||
* Elad Lahav (elad@eldarshany.com)
|
||||
* Stefano Ceccherini (burton666@libero.it)
|
||||
* Axel Dörfler, axeld@pinc-software.de
|
||||
* Marcus Overhagen <marcus@overhagen.de>
|
||||
* Clemens Zeidler <czeidler@gmx.de>
|
||||
*/
|
||||
|
||||
/*
|
||||
* A PS/2 mouse is connected to the IBM 8042 controller, and gets its
|
||||
* name from the IBM PS/2 personal computer, which was the first to
|
||||
* use this device. All resources are shared between the keyboard, and
|
||||
* the mouse, referred to as the "Auxiliary Device".
|
||||
*
|
||||
* I/O:
|
||||
* ~~~
|
||||
* The controller has 3 I/O registers:
|
||||
* 1. Status (input), mapped to port 64h
|
||||
* 2. Control (output), mapped to port 64h
|
||||
* 3. Data (input/output), mapped to port 60h
|
||||
*
|
||||
* Data:
|
||||
* ~~~~
|
||||
* A packet read from the mouse data port is composed of
|
||||
* three bytes:
|
||||
* byte 0: status byte, where
|
||||
* - bit 7: Y overflow (1 = true)
|
||||
* - bit 6: X overflow (1 = true)
|
||||
* - bit 5: MSB of Y offset
|
||||
* - bit 4: MSB of X offset
|
||||
* - bit 3: Syncronization bit (always 1)
|
||||
* - bit 2: Middle button (1 = down)
|
||||
* - bit 1: Right button (1 = down)
|
||||
* - bit 0: Left button (1 = down)
|
||||
* byte 1: X position change, since last probed (-127 to +127)
|
||||
* byte 2: Y position change, since last probed (-127 to +127)
|
||||
*
|
||||
* Intellimouse mice send a four byte packet, where the first three
|
||||
* bytes are the same as standard mice, and the last one reports the
|
||||
* Z position, which is, usually, the wheel movement.
|
||||
*
|
||||
* Interrupts:
|
||||
* ~~~~~~~~~~
|
||||
* The PS/2 mouse device is connected to interrupt 12.
|
||||
* The controller uses 3 consecutive interrupts to inform the computer
|
||||
* that it has new data. On the first the data register holds the status
|
||||
* byte, on the second the X offset, and on the 3rd the Y offset.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include "kb_mouse_driver.h"
|
||||
#include "ps2_service.h"
|
||||
#include "ps2_standard_mouse.h"
|
||||
|
||||
|
||||
const char* kStandardMousePath[4] = {
|
||||
"input/mouse/ps2/standard_0",
|
||||
"input/mouse/ps2/standard_1",
|
||||
"input/mouse/ps2/standard_2",
|
||||
"input/mouse/ps2/standard_3"
|
||||
};
|
||||
const char* kIntelliMousePath[4] = {
|
||||
"input/mouse/ps2/intelli_0",
|
||||
"input/mouse/ps2/intelli_1",
|
||||
"input/mouse/ps2/intelli_2",
|
||||
"input/mouse/ps2/intelli_3"
|
||||
};
|
||||
|
||||
/** Set sampling rate of the ps2 port.
|
||||
*/
|
||||
static inline status_t
|
||||
ps2_set_sample_rate(ps2_dev *dev, uint8 rate)
|
||||
{
|
||||
return ps2_dev_command(dev, PS2_CMD_SET_SAMPLE_RATE, &rate, 1, NULL, 0);
|
||||
}
|
||||
|
||||
|
||||
/** Converts a packet received by the mouse to a "movement".
|
||||
*/
|
||||
static void
|
||||
ps2_packet_to_movement(standard_mouse_cookie *cookie, uint8 packet[],
|
||||
mouse_movement *pos)
|
||||
{
|
||||
int buttons = packet[0] & 7;
|
||||
int xDelta = ((packet[0] & 0x10) ? ~0xff : 0) | packet[1];
|
||||
int yDelta = ((packet[0] & 0x20) ? ~0xff : 0) | packet[2];
|
||||
int xDeltaWheel = 0;
|
||||
int yDeltaWheel = 0;
|
||||
bigtime_t currentTime = system_time();
|
||||
|
||||
if (buttons != 0 && cookie->buttons_state == 0) {
|
||||
if (cookie->click_last_time + cookie->click_speed > currentTime)
|
||||
cookie->click_count++;
|
||||
else
|
||||
cookie->click_count = 1;
|
||||
|
||||
cookie->click_last_time = currentTime;
|
||||
}
|
||||
|
||||
cookie->buttons_state = buttons;
|
||||
|
||||
if (cookie->flags & F_pointing_dev_TYPE_INTELLIMOUSE) {
|
||||
yDeltaWheel = packet[3] & 0x07;
|
||||
if (packet[3] & 0x08)
|
||||
yDeltaWheel |= ~0x07;
|
||||
}
|
||||
/*
|
||||
if (cookie->flags & F_standard_mouse_TYPE_2WHEELS) {
|
||||
switch (packet[3] & 0x0F) {
|
||||
case 0x01: yDeltaWheel = +1; break; // wheel 1 down
|
||||
case 0x0F: yDeltaWheel = -1; break; // wheel 1 up
|
||||
case 0x02: xDeltaWheel = +1; break; // wheel 2 down
|
||||
case 0x0E: xDeltaWheel = -1; break; // wheel 2 up
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// TRACE("packet: %02x %02x %02x %02x: xd %d, yd %d, 0x%x (%d), w-xd %d, "
|
||||
// "w-yd %d\n", packet[0], packet[1], packet[2], packet[3],
|
||||
// xDelta, yDelta, buttons, cookie->click_count, xDeltaWheel,
|
||||
// yDeltaWheel);
|
||||
|
||||
if (pos) {
|
||||
pos->xdelta = xDelta;
|
||||
pos->ydelta = yDelta;
|
||||
pos->buttons = buttons;
|
||||
pos->clicks = cookie->click_count;
|
||||
pos->modifiers = 0;
|
||||
pos->timestamp = currentTime;
|
||||
pos->wheel_ydelta = yDeltaWheel;
|
||||
pos->wheel_xdelta = xDeltaWheel;
|
||||
|
||||
TRACE("ps2: ps2_packet_to_movement xdelta: %d, ydelta: %d, buttons %x, "
|
||||
"clicks: %d, timestamp %Ld\n",
|
||||
xDelta, yDelta, buttons, cookie->click_count, currentTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Read a mouse event from the mouse events chain buffer.
|
||||
*/
|
||||
static status_t
|
||||
standard_mouse_read_event(standard_mouse_cookie *cookie,
|
||||
mouse_movement *movement)
|
||||
{
|
||||
uint8 packet[PS2_MAX_PACKET_SIZE];
|
||||
status_t status;
|
||||
|
||||
TRACE("ps2: standard_mouse_read_event\n");
|
||||
|
||||
status = acquire_sem_etc(cookie->standard_mouse_sem, 1, B_CAN_INTERRUPT, 0);
|
||||
TRACE("ps2: standard_mouse_read_event acquired\n");
|
||||
if (status < B_OK)
|
||||
return status;
|
||||
|
||||
if (!cookie->dev->active) {
|
||||
TRACE("ps2: standard_mouse_read_event: Error device no longer "
|
||||
"active\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
if (packet_buffer_read(cookie->standard_mouse_buffer, packet,
|
||||
cookie->dev->packet_size) != cookie->dev->packet_size) {
|
||||
TRACE("ps2: error copying buffer\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
if (!(packet[0] & 8))
|
||||
panic("ps2: got broken data from packet_buffer_read\n");
|
||||
|
||||
ps2_packet_to_movement(cookie, packet, movement);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
standard_mouse_disconnect(ps2_dev *dev)
|
||||
{
|
||||
// the mouse device might not be opened at this point
|
||||
INFO("ps2: ps2_standard_mouse_disconnect %s\n", dev->name);
|
||||
if (dev->flags & PS2_FLAG_OPEN)
|
||||
release_sem(((standard_mouse_cookie *)dev->cookie)->standard_mouse_sem);
|
||||
}
|
||||
|
||||
|
||||
/** Interrupt handler for the mouse device. Called whenever the I/O
|
||||
* controller generates an interrupt for the PS/2 mouse. Reads mouse
|
||||
* information from the data port, and stores it, so it can be accessed
|
||||
* by read() operations. The full data is obtained using 3 consecutive
|
||||
* calls to the handler, each holds a different byte on the data port.
|
||||
*/
|
||||
int32
|
||||
standard_mouse_handle_int(ps2_dev *dev)
|
||||
{
|
||||
standard_mouse_cookie *cookie = (standard_mouse_cookie*)dev->cookie;
|
||||
const uint8 data = dev->history[0].data;
|
||||
|
||||
TRACE("ps2: standard mouse: %1x\t%1x\t%1x\t%1x\t%1x\t%1x\t%1x\t%1x\n",
|
||||
data >> 7 & 1, data >> 6 & 1, data >> 5 & 1,
|
||||
data >> 4 & 1, data >> 3 & 1, data >> 2 & 1,
|
||||
data >> 1 & 1, data >> 0 & 1);
|
||||
|
||||
if (cookie->packet_index == 0 && !(data & 8)) {
|
||||
INFO("ps2: bad mouse data, trying resync\n");
|
||||
return B_HANDLED_INTERRUPT;
|
||||
}
|
||||
|
||||
// Workarounds for active multiplexing keyboard controllers
|
||||
// that lose data or send them to the wrong port.
|
||||
if (cookie->packet_index == 0 && (data & 0xc0)) {
|
||||
INFO("ps2: strange mouse data, x/y overflow, trying resync\n");
|
||||
return B_HANDLED_INTERRUPT;
|
||||
}
|
||||
if (cookie->packet_index == 1) {
|
||||
int xDelta
|
||||
= ((cookie->packet_buffer[0] & 0x10) ? 0xFFFFFF00 : 0) | data;
|
||||
if (xDelta < -100 || xDelta > 100) {
|
||||
INFO("ps2: strange mouse data, x-delta %d, trying resync\n",
|
||||
xDelta);
|
||||
cookie->packet_index = 0;
|
||||
return B_HANDLED_INTERRUPT;
|
||||
}
|
||||
}
|
||||
if (cookie->packet_index == 2) {
|
||||
int yDelta
|
||||
= ((cookie->packet_buffer[0] & 0x20) ? 0xFFFFFF00 : 0) | data;
|
||||
if (yDelta < -100 || yDelta > 100) {
|
||||
INFO("ps2: strange mouse data, y-delta %d, trying resync\n",
|
||||
yDelta);
|
||||
cookie->packet_index = 0;
|
||||
return B_HANDLED_INTERRUPT;
|
||||
}
|
||||
}
|
||||
|
||||
cookie->packet_buffer[cookie->packet_index++] = data;
|
||||
|
||||
if (cookie->packet_index != dev->packet_size) {
|
||||
// packet not yet complete
|
||||
return B_HANDLED_INTERRUPT;
|
||||
}
|
||||
|
||||
// complete packet is assembled
|
||||
|
||||
cookie->packet_index = 0;
|
||||
if (packet_buffer_write(cookie->standard_mouse_buffer,
|
||||
cookie->packet_buffer, dev->packet_size) != dev->packet_size) {
|
||||
// buffer is full, drop new data
|
||||
return B_HANDLED_INTERRUPT;
|
||||
}
|
||||
|
||||
release_sem_etc(cookie->standard_mouse_sem, 1, B_DO_NOT_RESCHEDULE);
|
||||
return B_INVOKE_SCHEDULER;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
status_t
|
||||
probe_standard_mouse(ps2_dev * dev)
|
||||
{
|
||||
status_t status;
|
||||
uint8 deviceId = 0;
|
||||
|
||||
// get device id
|
||||
status = ps2_dev_command(dev, PS2_CMD_GET_DEVICE_ID, NULL, 0, &deviceId, 1);
|
||||
if (status != B_OK) {
|
||||
INFO("ps2: probe_mouse get device id failed\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
TRACE("ps2: probe_mouse device id: %2x\n", deviceId);
|
||||
|
||||
// check for MS Intellimouse
|
||||
if (deviceId == 0) {
|
||||
uint8 alternate_device_id;
|
||||
status = ps2_set_sample_rate(dev, 200);
|
||||
status |= ps2_set_sample_rate(dev, 100);
|
||||
status |= ps2_set_sample_rate(dev, 80);
|
||||
status |= ps2_dev_command(dev, PS2_CMD_GET_DEVICE_ID, NULL, 0,
|
||||
&alternate_device_id, 1);
|
||||
if (status == 0) {
|
||||
TRACE("ps2: probe_mouse alternate device id: %2x\n",
|
||||
alternate_device_id);
|
||||
deviceId = alternate_device_id;
|
||||
}
|
||||
}
|
||||
|
||||
if (deviceId == PS2_DEV_ID_STANDARD) {
|
||||
INFO("ps2: probe_mouse Standard PS/2 mouse found\n");
|
||||
dev->name = kStandardMousePath[dev->idx];
|
||||
dev->packet_size = PS2_PACKET_STANDARD;
|
||||
} else if (deviceId == PS2_DEV_ID_INTELLIMOUSE) {
|
||||
dev->name = kIntelliMousePath[dev->idx];
|
||||
dev->packet_size = PS2_PACKET_INTELLIMOUSE;
|
||||
INFO("ps2: probe_mouse Extended PS/2 mouse found\n");
|
||||
} else {
|
||||
INFO("ps2: probe_mouse Error unknown device id.\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
// Device functions
|
||||
|
||||
|
||||
status_t
|
||||
standard_mouse_open(const char *name, uint32 flags, void **_cookie)
|
||||
{
|
||||
standard_mouse_cookie *cookie;
|
||||
status_t status;
|
||||
ps2_dev *dev = NULL;
|
||||
int i;
|
||||
|
||||
TRACE("ps2: standard_mouse_open %s\n", name);
|
||||
|
||||
for (dev = NULL, i = 0; i < PS2_DEVICE_COUNT; i++) {
|
||||
if (0 == strcmp(ps2_device[i].name, name)) {
|
||||
dev = &ps2_device[i];
|
||||
break;
|
||||
}
|
||||
/*if (0 == strcmp(g_passthrough_dev.name, name)) {
|
||||
dev = &g_passthrough_dev;
|
||||
isSynapticsPTDevice = true;
|
||||
break;
|
||||
}*/
|
||||
}
|
||||
|
||||
if (dev == NULL) {
|
||||
TRACE("ps2: dev = NULL\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
if (atomic_or(&dev->flags, PS2_FLAG_OPEN) & PS2_FLAG_OPEN)
|
||||
return B_BUSY;
|
||||
|
||||
cookie = malloc(sizeof(standard_mouse_cookie));
|
||||
if (cookie == NULL)
|
||||
goto err1;
|
||||
|
||||
*_cookie = cookie;
|
||||
memset(cookie, 0, sizeof(*cookie));
|
||||
|
||||
cookie->dev = dev;
|
||||
dev->cookie = cookie;
|
||||
dev->disconnect = &standard_mouse_disconnect;
|
||||
dev->handle_int = &standard_mouse_handle_int;
|
||||
|
||||
if (strstr(dev->name, "standard_mouse") != NULL)
|
||||
cookie->flags = F_pointing_dev_TYPE_STANDARD;
|
||||
|
||||
if (strstr(dev->name, "intelli_mouse") != NULL)
|
||||
cookie->flags = F_pointing_dev_TYPE_INTELLIMOUSE;
|
||||
|
||||
cookie->standard_mouse_buffer
|
||||
= create_packet_buffer(standard_mouse_HISTORY_SIZE * dev->packet_size);
|
||||
if (cookie->standard_mouse_buffer == NULL) {
|
||||
TRACE("ps2: can't allocate mouse actions buffer\n");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
// create the mouse semaphore, used for synchronization between
|
||||
// the interrupt handler and the read operation
|
||||
cookie->standard_mouse_sem = create_sem(0, "ps2_standard_mouse_sem");
|
||||
if (cookie->standard_mouse_sem < 0) {
|
||||
TRACE("ps2: failed creating PS/2 mouse semaphore!\n");
|
||||
goto err3;
|
||||
}
|
||||
|
||||
status = ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0);
|
||||
if (status < B_OK) {
|
||||
INFO("ps2: cannot enable mouse %s\n", name);
|
||||
goto err4;
|
||||
}
|
||||
|
||||
atomic_or(&dev->flags, PS2_FLAG_ENABLED);
|
||||
|
||||
|
||||
TRACE("ps2: standard_mouse_open %s success\n", name);
|
||||
return B_OK;
|
||||
|
||||
err4:
|
||||
delete_sem(cookie->standard_mouse_sem);
|
||||
err3:
|
||||
delete_packet_buffer(cookie->standard_mouse_buffer);
|
||||
err2:
|
||||
free(cookie);
|
||||
err1:
|
||||
atomic_and(&dev->flags, ~PS2_FLAG_OPEN);
|
||||
|
||||
TRACE("ps2: standard_mouse_open %s failed\n", name);
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
standard_mouse_close(void *_cookie)
|
||||
{
|
||||
standard_mouse_cookie *cookie = (standard_mouse_cookie*)_cookie;
|
||||
|
||||
TRACE("ps2: standard_mouse_close %s enter\n", cookie->dev->name);
|
||||
|
||||
ps2_dev_command_timeout(cookie->dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0,
|
||||
150000);
|
||||
|
||||
delete_packet_buffer(cookie->standard_mouse_buffer);
|
||||
delete_sem(cookie->standard_mouse_sem);
|
||||
|
||||
atomic_and(&cookie->dev->flags, ~PS2_FLAG_OPEN);
|
||||
atomic_and(&cookie->dev->flags, ~PS2_FLAG_ENABLED);
|
||||
|
||||
TRACE("ps2: standard_mouse_close %s done\n", cookie->dev->name);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
standard_mouse_freecookie(void *_cookie)
|
||||
{
|
||||
standard_mouse_cookie *cookie = (standard_mouse_cookie*)_cookie;
|
||||
free(cookie);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
standard_mouse_read(void *cookie, off_t pos, void *buffer, size_t *_length)
|
||||
{
|
||||
*_length = 0;
|
||||
return B_NOT_ALLOWED;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
standard_mouse_write(void *cookie, off_t pos, const void *buffer,
|
||||
size_t *_length)
|
||||
{
|
||||
*_length = 0;
|
||||
return B_NOT_ALLOWED;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
standard_mouse_ioctl(void *_cookie, uint32 op, void *buffer, size_t length)
|
||||
{
|
||||
standard_mouse_cookie *cookie = (standard_mouse_cookie*)_cookie;
|
||||
|
||||
switch (op) {
|
||||
case MS_NUM_EVENTS:
|
||||
{
|
||||
int32 count;
|
||||
TRACE("ps2: ioctl MS_NUM_EVENTS\n");
|
||||
get_sem_count(cookie->standard_mouse_sem, &count);
|
||||
return count;
|
||||
}
|
||||
|
||||
case MS_READ:
|
||||
{
|
||||
mouse_movement movement;
|
||||
status_t status;
|
||||
TRACE("ps2: ioctl MS_READ\n");
|
||||
if ((status = standard_mouse_read_event(cookie, &movement)) < B_OK)
|
||||
return status;
|
||||
// TRACE("%s %d %d %d %d\n", cookie->dev->name,
|
||||
// movement.xdelta, movement.ydelta, movement.buttons,
|
||||
// movement.clicks);
|
||||
return user_memcpy(buffer, &movement, sizeof(movement));
|
||||
}
|
||||
|
||||
case MS_SET_TYPE:
|
||||
TRACE("ps2: ioctl MS_SET_TYPE not implemented\n");
|
||||
return B_BAD_VALUE;
|
||||
|
||||
case MS_SET_MAP:
|
||||
TRACE("ps2: ioctl MS_SET_MAP (set mouse mapping) not "
|
||||
"implemented\n");
|
||||
return B_BAD_VALUE;
|
||||
|
||||
case MS_GET_ACCEL:
|
||||
TRACE("ps2: ioctl MS_GET_ACCEL (get mouse acceleration) not "
|
||||
"implemented\n");
|
||||
return B_BAD_VALUE;
|
||||
|
||||
case MS_SET_ACCEL:
|
||||
TRACE("ps2: ioctl MS_SET_ACCEL (set mouse acceleration) not "
|
||||
"implemented\n");
|
||||
return B_BAD_VALUE;
|
||||
|
||||
case MS_SET_CLICKSPEED:
|
||||
TRACE("ps2: ioctl MS_SETCLICK (set click speed)\n");
|
||||
return user_memcpy(&cookie->click_speed, buffer, sizeof(bigtime_t));
|
||||
|
||||
default:
|
||||
TRACE("ps2: ioctl unknown mouse opcode: %ld\n", op);
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
device_hooks gStandardMouseDeviceHooks = {
|
||||
standard_mouse_open,
|
||||
standard_mouse_close,
|
||||
standard_mouse_freecookie,
|
||||
standard_mouse_ioctl,
|
||||
standard_mouse_read,
|
||||
standard_mouse_write,
|
||||
};
|
Loading…
Reference in New Issue
Block a user