source: mainline/uspace/drv/char/ps2mouse/ps2mouse.c@ 2ecb5ec

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

ps2mouse: Simple ps/2 protocol mouse driver.

  • Property mode set to 100644
File size: 6.9 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 drvkbd
29 * @{
30 */
31/** @file
32 * @brief XT keyboard 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 *fun,
67 ipc_callid_t icallid, ipc_call_t *icall);
68/*----------------------------------------------------------------------------*/
69static ddf_dev_ops_t mouse_ops = {
70 .default_handler = default_connection_handler
71};
72/*----------------------------------------------------------------------------*/
73int ps2_mouse_init(ps2_mouse_t *mouse, ddf_dev_t *dev)
74{
75 assert(mouse);
76 assert(dev);
77 mouse->input_sess = NULL;
78 mouse->parent_sess = devman_parent_device_connect(EXCHANGE_SERIALIZE,
79 dev->handle, IPC_FLAG_BLOCKING);
80 if (!mouse->parent_sess)
81 return ENOMEM;
82
83 mouse->mouse_fun = ddf_fun_create(dev, fun_exposed, "mouse");
84 if (!mouse->mouse_fun) {
85 async_hangup(mouse->parent_sess);
86 return ENOMEM;
87 }
88 mouse->mouse_fun->ops = &mouse_ops;
89 mouse->mouse_fun->driver_data = mouse;
90
91 int ret = ddf_fun_bind(mouse->mouse_fun);
92 if (ret != EOK) {
93 async_hangup(mouse->parent_sess);
94 mouse->mouse_fun->driver_data = NULL;
95 ddf_fun_destroy(mouse->mouse_fun);
96 return ENOMEM;
97 }
98
99 ret = ddf_fun_add_to_category(mouse->mouse_fun, "mouse");
100 if (ret != EOK) {
101 async_hangup(mouse->parent_sess);
102 ddf_fun_unbind(mouse->mouse_fun);
103 mouse->mouse_fun->driver_data = NULL;
104 ddf_fun_destroy(mouse->mouse_fun);
105 return ENOMEM;
106 }
107 /* Enable mouse data reporting. */
108 uint8_t report = PS2_MOUSE_OUT_INIT;
109 ssize_t size = char_dev_write(mouse->parent_sess, &report, 1);
110 if (size != 1) {
111 ddf_msg(LVL_ERROR, "Failed to write INIT.\n");
112 async_hangup(mouse->parent_sess);
113 ddf_fun_unbind(mouse->mouse_fun);
114 mouse->mouse_fun->driver_data = NULL;
115 ddf_fun_destroy(mouse->mouse_fun);
116 return EIO;
117 }
118#if 0 /* Enable this when we can guarantee that the response won't be lost. */
119 size = char_dev_read(mouse->parent_sess, &report, 1);
120 if (size != 1 || report != PS2_MOUSE_ACK) {
121 ddf_msg(LVL_ERROR, "MOUSE FAILED TO ACK %hhx.\n", report);
122 async_hangup(mouse->parent_sess);
123 ddf_fun_unbind(mouse->mouse_fun);
124 mouse->mouse_fun->driver_data = NULL;
125 ddf_fun_destroy(mouse->mouse_fun);
126 return EIO;
127 }
128#endif
129
130 mouse->polling_fibril = fibril_create(polling_ps2, mouse);
131 if (!mouse->polling_fibril) {
132 async_hangup(mouse->parent_sess);
133 ddf_fun_unbind(mouse->mouse_fun);
134 mouse->mouse_fun->driver_data = NULL;
135 ddf_fun_destroy(mouse->mouse_fun);
136 return ENOMEM;
137 }
138 fibril_add_ready(mouse->polling_fibril);
139 return EOK;
140}
141/*----------------------------------------------------------------------------*/
142int polling_ps2(void *arg)
143{
144 assert(arg);
145 ps2_mouse_t *mouse = arg;
146
147 assert(mouse->parent_sess);
148 bool buttons[BUTTON_COUNT] = {};
149 while (1) {
150
151 uint8_t packet[BUFSIZE] = {};
152 const ssize_t size =
153 char_dev_read(mouse->parent_sess, packet, BUFSIZE);
154
155 if (size != 3) {
156 ddf_msg(LVL_WARN, "Incorrect packet size: %zd.", size);
157 continue;
158 }
159 ddf_msg(LVL_DEBUG2, "Got packet: %hhx:%hhx:%hhx.",
160 packet[0], packet[1], packet[2]);
161
162 async_exch_t *exch =
163 async_exchange_begin(mouse->input_sess);
164 if (!exch) {
165 ddf_msg(LVL_ERROR,
166 "Failed to create input exchange.");
167 continue;
168 }
169 /* Buttons */
170 for (unsigned i = 0; i < BUTTON_COUNT; ++i) {
171 if (buttons[i] != (bool)(packet[0] & BUTTON_MASK(i))) {
172 buttons[i] = !buttons[i];
173 async_msg_2(exch, MOUSEEV_BUTTON_EVENT, i + 1,
174 buttons[i]);
175 }
176 }
177
178 const int move_x = packet[1] * ((packet[0] & X_SIGN) ? -1 : 1);
179 const int move_y = packet[2] * ((packet[0] & Y_SIGN) ? 1 : -1);
180 if (move_x != 0 || move_y != 0) {
181 async_msg_2(exch, MOUSEEV_MOVE_EVENT, move_x, move_y);
182 }
183 async_exchange_end(exch);
184 }
185}
186/*----------------------------------------------------------------------------*/
187void default_connection_handler(ddf_fun_t *fun,
188 ipc_callid_t icallid, ipc_call_t *icall)
189{
190 if (fun == NULL || fun->driver_data == NULL) {
191 ddf_msg(LVL_ERROR, "%s: Missing parameter.\n", __FUNCTION__);
192 async_answer_0(icallid, EINVAL);
193 return;
194 }
195
196 const sysarg_t method = IPC_GET_IMETHOD(*icall);
197 ps2_mouse_t *mouse = fun->driver_data;
198
199 switch (method) {
200 /* This might be ugly but async_callback_receive_start makes no
201 * difference for incorrect call and malloc failure. */
202 case IPC_M_CONNECT_TO_ME: {
203 async_sess_t *sess =
204 async_callback_receive_start(EXCHANGE_SERIALIZE, icall);
205 /* Probably ENOMEM error, try again. */
206 if (sess == NULL) {
207 ddf_msg(LVL_WARN,
208 "Failed to create start input session");
209 async_answer_0(icallid, EAGAIN);
210 break;
211 }
212 if (mouse->input_sess == NULL) {
213 mouse->input_sess = sess;
214 ddf_msg(LVL_DEBUG, "Set input session");
215 async_answer_0(icallid, EOK);
216 } else {
217 ddf_msg(LVL_ERROR, "Input session already set");
218 async_answer_0(icallid, ELIMIT);
219 }
220 break;
221 }
222 default:
223 ddf_msg(LVL_ERROR, "Unknown method: %d.\n", (int)method);
224 async_answer_0(icallid, EINVAL);
225 break;
226 }
227}
Note: See TracBrowser for help on using the repository browser.