source: mainline/uspace/drv/char/ps2mouse/ps2mouse.c@ db9ef0c

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since db9ef0c was db9ef0c, checked in by Jan Vesely <jano.vesely@…>, 14 years ago

ps2mouse: Fix movement parsing.

It is reported as 9bit values.

  • Property mode set to 100644
File size: 7.4 KB
Line 
1/*
2 * Copyright (c) 2011 Jan Vesely
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28/** @addtogroup drvmouse
29 * @{
30 */
31/** @file
32 * @brief ps2 mouse driver.
33 */
34
35#include <bool.h>
36#include <errno.h>
37#include <devman.h>
38#include <device/char_dev.h>
39#include <ddf/log.h>
40#include <io/keycode.h>
41#include <io/console.h>
42#include <ipc/mouseev.h>
43#include <abi/ipc/methods.h>
44
45#include "ps2mouse.h"
46
47#define PS2_MOUSE_OUT_INIT 0xf4
48#define PS2_MOUSE_ACK 0xfa
49
50#define BUFSIZE 3
51
52#define X_SIGN (1 << 4)
53#define Y_SIGN (1 << 5)
54#define X_OVERFLOW (1 << 6)
55#define Y_OVERFLOW (1 << 7)
56
57#define BUTTON_LEFT 0
58#define BUTTON_RIGHT 1
59#define BUTTON_MIDDLE 2
60#define BUTTON_COUNT 3
61
62#define BUTTON_MASK(button) (1 << button)
63
64/*----------------------------------------------------------------------------*/
65static int polling_ps2(void *);
66static void default_connection_handler(ddf_fun_t *, ipc_callid_t, ipc_call_t *);
67/*----------------------------------------------------------------------------*/
68/** ps/2 mouse driver ops. */
69static ddf_dev_ops_t mouse_ops = {
70 .default_handler = default_connection_handler
71};
72/*----------------------------------------------------------------------------*/
73/** Initialize mouse driver structure.
74 * @param kbd Mouse driver structure to initialize.
75 * @param dev DDF device structure.
76 *
77 * Connects to parent, creates keyboard function, starts polling fibril.
78 */
79int ps2_mouse_init(ps2_mouse_t *mouse, ddf_dev_t *dev)
80{
81 assert(mouse);
82 assert(dev);
83 mouse->input_sess = NULL;
84 mouse->parent_sess = devman_parent_device_connect(EXCHANGE_SERIALIZE,
85 dev->handle, IPC_FLAG_BLOCKING);
86 if (!mouse->parent_sess)
87 return ENOMEM;
88
89 mouse->mouse_fun = ddf_fun_create(dev, fun_exposed, "mouse");
90 if (!mouse->mouse_fun) {
91 async_hangup(mouse->parent_sess);
92 return ENOMEM;
93 }
94 mouse->mouse_fun->ops = &mouse_ops;
95 mouse->mouse_fun->driver_data = mouse;
96
97 int ret = ddf_fun_bind(mouse->mouse_fun);
98 if (ret != EOK) {
99 async_hangup(mouse->parent_sess);
100 mouse->mouse_fun->driver_data = NULL;
101 ddf_fun_destroy(mouse->mouse_fun);
102 return ENOMEM;
103 }
104
105 ret = ddf_fun_add_to_category(mouse->mouse_fun, "mouse");
106 if (ret != EOK) {
107 async_hangup(mouse->parent_sess);
108 ddf_fun_unbind(mouse->mouse_fun);
109 mouse->mouse_fun->driver_data = NULL;
110 ddf_fun_destroy(mouse->mouse_fun);
111 return ENOMEM;
112 }
113 /* Enable mouse data reporting. */
114 uint8_t report = PS2_MOUSE_OUT_INIT;
115 ssize_t size = char_dev_write(mouse->parent_sess, &report, 1);
116 if (size != 1) {
117 ddf_msg(LVL_ERROR, "Failed to write INIT.");
118 async_hangup(mouse->parent_sess);
119 ddf_fun_unbind(mouse->mouse_fun);
120 mouse->mouse_fun->driver_data = NULL;
121 ddf_fun_destroy(mouse->mouse_fun);
122 return EIO;
123 }
124
125 size = char_dev_read(mouse->parent_sess, &report, 1);
126 if (size != 1 || report != PS2_MOUSE_ACK) {
127 ddf_msg(LVL_ERROR, "MOUSE FAILED TO ACK %hhx.", report);
128 async_hangup(mouse->parent_sess);
129 ddf_fun_unbind(mouse->mouse_fun);
130 mouse->mouse_fun->driver_data = NULL;
131 ddf_fun_destroy(mouse->mouse_fun);
132 return EIO;
133 }
134
135 mouse->polling_fibril = fibril_create(polling_ps2, mouse);
136 if (!mouse->polling_fibril) {
137 async_hangup(mouse->parent_sess);
138 ddf_fun_unbind(mouse->mouse_fun);
139 mouse->mouse_fun->driver_data = NULL;
140 ddf_fun_destroy(mouse->mouse_fun);
141 return ENOMEM;
142 }
143 fibril_add_ready(mouse->polling_fibril);
144 return EOK;
145}
146/*----------------------------------------------------------------------------*/
147/** Get data and parse ps2 protocol packets.
148 * @param arg Pointer to ps2_mouse_t structure.
149 * @return Never.
150 */
151int polling_ps2(void *arg)
152{
153 assert(arg);
154 const ps2_mouse_t *mouse = arg;
155
156 assert(mouse->parent_sess);
157 bool buttons[BUTTON_COUNT] = {};
158 while (1) {
159
160 uint8_t packet[BUFSIZE] = {};
161 const ssize_t size =
162 char_dev_read(mouse->parent_sess, packet, BUFSIZE);
163
164 if (size != 3) {
165 ddf_msg(LVL_WARN, "Incorrect packet size: %zd.", size);
166 continue;
167 }
168 ddf_msg(LVL_DEBUG2, "Got packet: %hhx:%hhx:%hhx.",
169 packet[0], packet[1], packet[2]);
170
171 async_exch_t *exch =
172 async_exchange_begin(mouse->input_sess);
173 if (!exch) {
174 ddf_msg(LVL_ERROR,
175 "Failed to create input exchange.");
176 continue;
177 }
178
179 /* Buttons */
180 for (unsigned i = 0; i < BUTTON_COUNT; ++i) {
181 if (buttons[i] != (bool)(packet[0] & BUTTON_MASK(i))) {
182 buttons[i] = !buttons[i];
183 async_msg_2(exch, MOUSEEV_BUTTON_EVENT, i + 1,
184 buttons[i]);
185 }
186 }
187
188 /* Movement */
189 const int16_t move_x =
190 ((packet[0] & X_SIGN) ? 0xff00 : 0) | packet[1];
191 const int16_t move_y =
192 (((packet[0] & Y_SIGN) ? 0xff00 : 0) | packet[2]);
193 //TODO: Consider overflow bit
194 if (move_x != 0 || move_y != 0) {
195 async_msg_2(exch, MOUSEEV_MOVE_EVENT, move_x, -move_y);
196 }
197 async_exchange_end(exch);
198 }
199}
200/*----------------------------------------------------------------------------*/
201/** Default handler for IPC methods not handled by DDF.
202 *
203 * @param fun Device function handling the call.
204 * @param icallid Call id.
205 * @param icall Call data.
206 */
207void default_connection_handler(ddf_fun_t *fun,
208 ipc_callid_t icallid, ipc_call_t *icall)
209{
210 if (fun == NULL || fun->driver_data == NULL) {
211 ddf_msg(LVL_ERROR, "%s: Missing parameter.", __FUNCTION__);
212 async_answer_0(icallid, EINVAL);
213 return;
214 }
215
216 const sysarg_t method = IPC_GET_IMETHOD(*icall);
217 ps2_mouse_t *mouse = fun->driver_data;
218
219 switch (method) {
220 /* This might be ugly but async_callback_receive_start makes no
221 * difference for incorrect call and malloc failure. */
222 case IPC_M_CONNECT_TO_ME: {
223 async_sess_t *sess =
224 async_callback_receive_start(EXCHANGE_SERIALIZE, icall);
225 /* Probably ENOMEM error, try again. */
226 if (sess == NULL) {
227 ddf_msg(LVL_WARN,
228 "Failed to create start input session");
229 async_answer_0(icallid, EAGAIN);
230 break;
231 }
232 if (mouse->input_sess == NULL) {
233 mouse->input_sess = sess;
234 ddf_msg(LVL_DEBUG, "Set input session");
235 async_answer_0(icallid, EOK);
236 } else {
237 ddf_msg(LVL_ERROR, "Input session already set");
238 async_answer_0(icallid, ELIMIT);
239 }
240 break;
241 }
242 default:
243 ddf_msg(LVL_ERROR, "Unknown method: %d.", (int)method);
244 async_answer_0(icallid, EINVAL);
245 break;
246 }
247}
Note: See TracBrowser for help on using the repository browser.