1/**
2 * \file
3 * \brief Basic PS/2 mouse protocol driver
4 */
5
6/*
7 * Copyright (c) 2011, ETH Zurich.
8 * All rights reserved.
9 *
10 * This file is distributed under the terms in the attached LICENSE file.
11 * If you do not find this file, copies can be found by writing to:
12 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#include <stdio.h>
16#include <barrelfish/barrelfish.h>
17
18#include "lpc_kbd.h"
19
20#if 0
21#define DEBUG(x...) debug_printf(x)
22#else
23#define DEBUG(x...) (void)0
24#endif
25
26// subset of commands
27#define CMD_ACK         0xfa ///< ACK *from the mouse* in response to a command
28#define CMD_RESET       0xff ///< Reset the mouse
29#define CMD_SETDEFAULTS 0xf6 ///< Apply default values
30#define CMD_DISABLE     0xf5 ///< Disable data reporting
31#define CMD_ENABLE      0xf4 ///< Enable data reporting
32#define CMD_GETDEVID    0xf2 ///< Get device ID
33
34// when the mouse is plugged in or resets itself, it sends these in sequence
35#define RESET0          0xaa
36#define RESET1          0x00
37
38// state of data reception path
39static enum {
40    STATE_IDLE,         ///< Nothing doing
41    STATE_PKT1,         ///< We saw the first byte of a 3- or 4-byte packet
42    STATE_PKT2,         ///< We saw the first two bytes of a 3- or 4-byte packet
43    STATE_PKT3,         ///< We saw the first three bytes of a 4-byte packet
44    STATE_SEND_CMD,     ///< We sent a command, and we're waiting for the ack
45} data_state;
46
47// state of initialisation path (next level up)
48static enum {
49    INIT_STATE_STARTUP,         ///< Just started...
50    INIT_STATE_SENT_RESET,      ///< Sent reset command
51    INIT_STATE_SENT_DEFAULTS,   ///< Sent set defaults command
52    INIT_STATE_SENT_ENABLE,     ///< Sent enable commant
53    INIT_STATE_COMPLETE,        ///< Up and running
54    INIT_STATE_SAW_RESET,       ///< Saw a reset packet
55} init_state;
56
57static bool sending_command;
58
59static void init_data(uint8_t val);
60
61static void handle_packet(uint8_t *buf, size_t len)
62{
63    assert(len == 3);
64
65    // unpack packet (TODO: use Mackerel?)
66    bool yoverflow = (buf[0] & 0x80) != 0;
67    bool xoverflow = (buf[0] & 0x40) != 0;
68    bool ysign     = (buf[0] & 0x20) != 0;
69    bool xsign     = (buf[0] & 0x10) != 0;
70    bool always1   = (buf[0] & 0x08) != 0;
71    bool buttonm   = (buf[0] & 0x04) != 0;
72    bool buttonr   = (buf[0] & 0x02) != 0;
73    bool buttonl   = (buf[0] & 0x01) != 0;
74    uint8_t uxdelta = buf[1];
75    uint8_t uydelta = buf[2];
76
77    if (!always1) {
78        DEBUG("invalid packet, discarded\n");
79        return;
80    }
81
82    if (yoverflow || xoverflow) {
83        DEBUG("X or Y overflow set: probably bogus?\n");
84        return;
85    }
86
87    int xdelta, ydelta;
88
89    if (ysign) {
90        ydelta = uydelta | (-1 & ~0xff);
91    } else {
92        ydelta = uydelta;
93    }
94
95    if (xsign) {
96        xdelta = uxdelta | (-1 & ~0xff);
97    } else {
98        xdelta = uxdelta;
99    }
100
101    DEBUG("%dx%d L%u M%u R%u\n", xdelta, ydelta, buttonl, buttonm, buttonr);
102    mouse_event(xdelta, ydelta, buttonl, buttonm, buttonr);
103}
104
105// this is called when we receive a byte from the mouse
106void mouse_data(uint8_t val)
107{
108    static uint8_t buf[3];
109
110    if (sending_command) {
111        DEBUG("ignored 0x%x while sending command\n", val);
112        return;
113    }
114
115    DEBUG("got 0x%x in state %d:%d\n", val, data_state, init_state);
116
117    switch (data_state) {
118    case STATE_IDLE:
119        if (init_state == INIT_STATE_COMPLETE) {
120            buf[0] = val;
121            data_state++;
122        }
123        break;
124
125    case STATE_PKT1:
126        buf[1] = val;
127        if (buf[0] == RESET0 && buf[1] == RESET1) {
128            init_state = INIT_STATE_SAW_RESET;
129            data_state = STATE_IDLE;
130            init_data(0);
131        } else {
132            data_state++;
133        }
134        break;
135
136    case STATE_PKT2:
137        buf[2] = val;
138        handle_packet(buf, sizeof(buf));
139        data_state = STATE_IDLE;
140        break;
141
142    case STATE_SEND_CMD:
143        if (val == CMD_ACK) {
144            data_state = STATE_IDLE;
145            init_data(val);
146        } else {
147            DEBUG("unexpected data %x while waiting for command ACK\n", val);
148        }
149        break;
150
151    default:
152        USER_PANIC("unhandled state %d\n", data_state);
153    }
154}
155
156static void send_cmd(uint8_t cmd)
157{
158    DEBUG("sending 0x%x in state %d:%d\n", cmd, data_state, init_state);
159
160    assert(data_state == STATE_IDLE);
161    data_state = STATE_SEND_CMD;
162    sending_command = true;
163    send_mouse_cmd(cmd);
164    sending_command = false;
165}
166
167static void init_data(uint8_t val)
168{
169    switch (init_state) {
170    case INIT_STATE_STARTUP:
171        send_cmd(CMD_RESET);
172        init_state = INIT_STATE_SENT_RESET;
173        break;
174
175    case INIT_STATE_SENT_RESET:
176        assert(val == CMD_ACK);
177    case INIT_STATE_SAW_RESET:
178        send_cmd(CMD_SETDEFAULTS);
179        init_state = INIT_STATE_SENT_DEFAULTS;
180        break;
181
182    case INIT_STATE_SENT_DEFAULTS:
183        assert(val == CMD_ACK);
184        send_cmd(CMD_ENABLE);
185        init_state = INIT_STATE_SENT_ENABLE;
186        break;
187
188    case INIT_STATE_SENT_ENABLE:
189        assert(val == CMD_ACK);
190        init_state = INIT_STATE_COMPLETE;
191        break;
192
193    case INIT_STATE_COMPLETE:
194        USER_PANIC("unexpected data 0x%x in complete init state\n", val);
195        break;
196    }
197}
198
199// this is called when we initialise
200void mouse_init(void)
201{
202    init_state = INIT_STATE_STARTUP;
203    init_data(0);
204}
205