source: mainline/uspace/drv/bus/adb/cuda_adb/cuda_adb.c@ 984a9ba

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 984a9ba was 984a9ba, checked in by Martin Decky <martin@…>, 7 years ago

do not expose the call capability handler from the async framework

Keep the call capability handler encapsulated within the async framework
and do not expose it explicitly via its API. Use the pointer to
ipc_call_t as the sole object identifying an IPC call in the code that
uses the async framework.

This plugs a major leak in the abstraction and also simplifies both the
async framework (slightly) and all IPC servers.

  • Property mode set to 100644
File size: 11.4 KB
Line 
1/*
2 * Copyright (c) 2010 Jiri Svoboda
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
29/** @addtogroup genarch
30 * @{
31 */
32/** @file VIA-CUDA Apple Desktop Bus driver
33 *
34 * Note: We should really do a full bus scan at the beginning and resolve
35 * address conflicts. Also we should consider the handler ID in r3. Instead
36 * we just assume a keyboard at address 2 or 8 and a mouse at address 9.
37 */
38
39#include <assert.h>
40#include <ddf/driver.h>
41#include <ddf/log.h>
42#include <ddi.h>
43#include <errno.h>
44#include <ipc/adb.h>
45#include <libarch/ddi.h>
46#include <stdbool.h>
47#include <stddef.h>
48#include <stdint.h>
49#include <stdio.h>
50#include <stdlib.h>
51
52#include "cuda_adb.h"
53#include "cuda_hw.h"
54
55#define NAME "cuda_adb"
56
57static void cuda_dev_connection(ipc_call_t *, void *);
58static errno_t cuda_init(cuda_t *);
59static void cuda_irq_handler(ipc_call_t *, void *);
60
61static void cuda_irq_listen(cuda_t *);
62static void cuda_irq_receive(cuda_t *);
63static void cuda_irq_rcv_end(cuda_t *, void *, size_t *);
64static void cuda_irq_send_start(cuda_t *);
65static void cuda_irq_send(cuda_t *);
66
67static void cuda_packet_handle(cuda_t *, uint8_t *, size_t);
68static void cuda_send_start(cuda_t *);
69static void cuda_autopoll_set(cuda_t *, bool);
70
71static void adb_packet_handle(cuda_t *, uint8_t *, size_t, bool);
72
73static irq_pio_range_t cuda_ranges[] = {
74 {
75 .base = 0,
76 .size = sizeof(cuda_regs_t)
77 }
78};
79
80static irq_cmd_t cuda_cmds[] = {
81 {
82 .cmd = CMD_PIO_READ_8,
83 .addr = NULL,
84 .dstarg = 1
85 },
86 {
87 .cmd = CMD_AND,
88 .value = SR_INT,
89 .srcarg = 1,
90 .dstarg = 2
91 },
92 {
93 .cmd = CMD_PREDICATE,
94 .value = 1,
95 .srcarg = 2
96 },
97 {
98 .cmd = CMD_ACCEPT
99 }
100};
101
102
103static irq_code_t cuda_irq_code = {
104 sizeof(cuda_ranges) / sizeof(irq_pio_range_t),
105 cuda_ranges,
106 sizeof(cuda_cmds) / sizeof(irq_cmd_t),
107 cuda_cmds
108};
109
110static errno_t cuda_dev_create(cuda_t *cuda, const char *name, const char *id,
111 adb_dev_t **rdev)
112{
113 adb_dev_t *dev = NULL;
114 ddf_fun_t *fun;
115 errno_t rc;
116
117 fun = ddf_fun_create(cuda->dev, fun_inner, name);
118 if (fun == NULL) {
119 ddf_msg(LVL_ERROR, "Failed creating function '%s'.", name);
120 rc = ENOMEM;
121 goto error;
122 }
123
124 rc = ddf_fun_add_match_id(fun, id, 10);
125 if (rc != EOK) {
126 ddf_msg(LVL_ERROR, "Failed adding match ID.");
127 rc = ENOMEM;
128 goto error;
129 }
130
131 dev = ddf_fun_data_alloc(fun, sizeof(adb_dev_t));
132 if (dev == NULL) {
133 ddf_msg(LVL_ERROR, "Failed allocating memory for '%s'.", name);
134 rc = ENOMEM;
135 goto error;
136 }
137
138 dev->fun = fun;
139 list_append(&dev->lcuda, &cuda->devs);
140
141 ddf_fun_set_conn_handler(fun, cuda_dev_connection);
142
143 rc = ddf_fun_bind(fun);
144 if (rc != EOK) {
145 ddf_msg(LVL_ERROR, "Failed binding function '%s'.", name);
146 goto error;
147 }
148
149 *rdev = dev;
150 return EOK;
151error:
152 if (fun != NULL)
153 ddf_fun_destroy(fun);
154 return rc;
155}
156
157errno_t cuda_add(cuda_t *cuda, cuda_res_t *res)
158{
159 adb_dev_t *kbd = NULL;
160 adb_dev_t *mouse = NULL;
161 errno_t rc;
162
163 cuda->phys_base = res->base;
164
165 rc = cuda_dev_create(cuda, "kbd", "adb/keyboard", &kbd);
166 if (rc != EOK)
167 goto error;
168
169 rc = cuda_dev_create(cuda, "mouse", "adb/mouse", &mouse);
170 if (rc != EOK)
171 goto error;
172
173 cuda->addr_dev[2] = kbd;
174 cuda->addr_dev[8] = kbd;
175
176 cuda->addr_dev[9] = mouse;
177
178 rc = cuda_init(cuda);
179 if (rc != EOK) {
180 ddf_msg(LVL_ERROR, "Failed initializing CUDA hardware.");
181 return rc;
182 }
183
184 return EOK;
185error:
186 return rc;
187}
188
189errno_t cuda_remove(cuda_t *cuda)
190{
191 return ENOTSUP;
192}
193
194errno_t cuda_gone(cuda_t *cuda)
195{
196 return ENOTSUP;
197}
198
199/** Device connection handler */
200static void cuda_dev_connection(ipc_call_t *icall, void *arg)
201{
202 adb_dev_t *dev = (adb_dev_t *) ddf_fun_data_get((ddf_fun_t *) arg);
203 ipc_call_t call;
204 sysarg_t method;
205
206 /* Answer the IPC_M_CONNECT_ME_TO call. */
207 async_answer_0(icall, EOK);
208
209 while (true) {
210 async_get_call(&call);
211 method = IPC_GET_IMETHOD(call);
212
213 if (!method) {
214 /* The other side has hung up. */
215 async_answer_0(&call, EOK);
216 return;
217 }
218
219 async_sess_t *sess =
220 async_callback_receive_start(EXCHANGE_SERIALIZE, &call);
221 if (sess != NULL) {
222 dev->client_sess = sess;
223 async_answer_0(&call, EOK);
224 } else {
225 async_answer_0(&call, EINVAL);
226 }
227 }
228}
229
230static errno_t cuda_init(cuda_t *cuda)
231{
232 errno_t rc;
233
234 void *vaddr;
235 rc = pio_enable((void *) cuda->phys_base, sizeof(cuda_regs_t),
236 &vaddr);
237 if (rc != EOK)
238 return rc;
239
240 cuda->regs = vaddr;
241 cuda->xstate = cx_listen;
242 cuda->bidx = 0;
243 cuda->snd_bytes = 0;
244
245 fibril_mutex_initialize(&cuda->dev_lock);
246
247 /* Disable all interrupts from CUDA. */
248 pio_write_8(&cuda->regs->ier, IER_CLR | ALL_INT);
249
250 cuda_irq_code.ranges[0].base = (uintptr_t) cuda->phys_base;
251 cuda_irq_code.cmds[0].addr = (void *) &((cuda_regs_t *)
252 cuda->phys_base)->ifr;
253 async_irq_subscribe(10, cuda_irq_handler, cuda, &cuda_irq_code, NULL);
254
255 /* Enable SR interrupt. */
256 pio_write_8(&cuda->regs->ier, TIP | TREQ);
257 pio_write_8(&cuda->regs->ier, IER_SET | SR_INT);
258
259 /* Enable ADB autopolling. */
260 cuda_autopoll_set(cuda, true);
261
262 return EOK;
263}
264
265static void cuda_irq_handler(ipc_call_t *call, void *arg)
266{
267 uint8_t rbuf[CUDA_RCV_BUF_SIZE];
268 cuda_t *cuda = (cuda_t *)arg;
269 size_t len;
270 bool handle;
271
272 handle = false;
273 len = 0;
274
275 fibril_mutex_lock(&cuda->dev_lock);
276
277 switch (cuda->xstate) {
278 case cx_listen:
279 cuda_irq_listen(cuda);
280 break;
281 case cx_receive:
282 cuda_irq_receive(cuda);
283 break;
284 case cx_rcv_end:
285 cuda_irq_rcv_end(cuda, rbuf, &len);
286 handle = true;
287 break;
288 case cx_send_start:
289 cuda_irq_send_start(cuda);
290 break;
291 case cx_send:
292 cuda_irq_send(cuda);
293 break;
294 }
295
296 /* Lower IFR.SR_INT so that CUDA can generate next int by raising it. */
297 pio_write_8(&cuda->regs->ifr, SR_INT);
298
299 fibril_mutex_unlock(&cuda->dev_lock);
300
301 /* Handle an incoming packet. */
302 if (handle)
303 cuda_packet_handle(cuda, rbuf, len);
304}
305
306/** Interrupt in listen state.
307 *
308 * Start packet reception.
309 *
310 * @param cuda CUDA instance
311 */
312static void cuda_irq_listen(cuda_t *cuda)
313{
314 uint8_t b = pio_read_8(&cuda->regs->b);
315
316 if ((b & TREQ) != 0) {
317 ddf_msg(LVL_WARN, "cuda_irq_listen: no TREQ?!");
318 return;
319 }
320
321 pio_write_8(&cuda->regs->b, b & ~TIP);
322 cuda->xstate = cx_receive;
323}
324
325/** Interrupt in receive state.
326 *
327 * Receive next byte of packet.
328 *
329 * @param cuda CUDA instance
330 */
331static void cuda_irq_receive(cuda_t *cuda)
332{
333 uint8_t data = pio_read_8(&cuda->regs->sr);
334 if (cuda->bidx < CUDA_RCV_BUF_SIZE)
335 cuda->rcv_buf[cuda->bidx++] = data;
336
337 uint8_t b = pio_read_8(&cuda->regs->b);
338
339 if ((b & TREQ) == 0) {
340 pio_write_8(&cuda->regs->b, b ^ TACK);
341 } else {
342 pio_write_8(&cuda->regs->b, b | TACK | TIP);
343 cuda->xstate = cx_rcv_end;
344 }
345}
346
347/** Interrupt in rcv_end state.
348 *
349 * Terminate packet reception. Either go back to listen state or start
350 * receiving another packet if CUDA has one for us.
351 *
352 * @param cuda CUDA instance
353 * @param buf Buffer for storing received packet
354 * @param len Place to store length of received packet
355 */
356static void cuda_irq_rcv_end(cuda_t *cuda, void *buf, size_t *len)
357{
358 uint8_t b = pio_read_8(&cuda->regs->b);
359
360 if ((b & TREQ) == 0) {
361 cuda->xstate = cx_receive;
362 pio_write_8(&cuda->regs->b, b & ~TIP);
363 } else {
364 cuda->xstate = cx_listen;
365 cuda_send_start(cuda);
366 }
367
368 memcpy(buf, cuda->rcv_buf, cuda->bidx);
369 *len = cuda->bidx;
370 cuda->bidx = 0;
371}
372
373/** Interrupt in send_start state.
374 *
375 * Process result of sending first byte (and send second on success).
376 *
377 * @param cuda CUDA instance
378 */
379static void cuda_irq_send_start(cuda_t *cuda)
380{
381 uint8_t b;
382
383 b = pio_read_8(&cuda->regs->b);
384
385 if ((b & TREQ) == 0) {
386 /* Collision */
387 pio_write_8(&cuda->regs->acr, pio_read_8(&cuda->regs->acr) &
388 ~SR_OUT);
389 pio_read_8(&cuda->regs->sr);
390 pio_write_8(&cuda->regs->b, pio_read_8(&cuda->regs->b) |
391 TIP | TACK);
392 cuda->xstate = cx_listen;
393 return;
394 }
395
396 pio_write_8(&cuda->regs->sr, cuda->snd_buf[1]);
397 pio_write_8(&cuda->regs->b, pio_read_8(&cuda->regs->b) ^ TACK);
398 cuda->bidx = 2;
399
400 cuda->xstate = cx_send;
401}
402
403/** Interrupt in send state.
404 *
405 * Send next byte or terminate transmission.
406 *
407 * @param cuda CUDA instance
408 */
409static void cuda_irq_send(cuda_t *cuda)
410{
411 if (cuda->bidx < cuda->snd_bytes) {
412 /* Send next byte. */
413 pio_write_8(&cuda->regs->sr,
414 cuda->snd_buf[cuda->bidx++]);
415 pio_write_8(&cuda->regs->b, pio_read_8(&cuda->regs->b) ^ TACK);
416 return;
417 }
418
419 /* End transfer. */
420 cuda->snd_bytes = 0;
421 cuda->bidx = 0;
422
423 pio_write_8(&cuda->regs->acr, pio_read_8(&cuda->regs->acr) & ~SR_OUT);
424 pio_read_8(&cuda->regs->sr);
425 pio_write_8(&cuda->regs->b, pio_read_8(&cuda->regs->b) | TACK | TIP);
426
427 cuda->xstate = cx_listen;
428 /* TODO: Match reply with request. */
429}
430
431static void cuda_packet_handle(cuda_t *cuda, uint8_t *data, size_t len)
432{
433 if (data[0] != PT_ADB)
434 return;
435 if (len < 2)
436 return;
437
438 adb_packet_handle(cuda, data + 2, len - 2, (data[1] & 0x40) != 0);
439}
440
441static void adb_packet_handle(cuda_t *cuda, uint8_t *data, size_t size,
442 bool autopoll)
443{
444 uint8_t dev_addr;
445 uint8_t reg_no;
446 uint16_t reg_val;
447 adb_dev_t *dev;
448 unsigned i;
449
450 dev_addr = data[0] >> 4;
451 reg_no = data[0] & 0x03;
452
453 if (size != 3) {
454 ddf_msg(LVL_WARN, "Unrecognized packet, size=%zu", size);
455 for (i = 0; i < size; ++i) {
456 ddf_msg(LVL_WARN, " 0x%02x", data[i]);
457 }
458 return;
459 }
460
461 if (reg_no != 0) {
462 ddf_msg(LVL_WARN, "Unrecognized packet, size=%zu", size);
463 for (i = 0; i < size; ++i) {
464 ddf_msg(LVL_WARN, " 0x%02x", data[i]);
465 }
466 return;
467 }
468
469 reg_val = ((uint16_t) data[1] << 8) | (uint16_t) data[2];
470
471 ddf_msg(LVL_DEBUG, "Received ADB packet for device address %d",
472 dev_addr);
473 dev = cuda->addr_dev[dev_addr];
474 if (dev == NULL)
475 return;
476
477 async_exch_t *exch = async_exchange_begin(dev->client_sess);
478 async_msg_1(exch, ADB_REG_NOTIF, reg_val);
479 async_exchange_end(exch);
480}
481
482static void cuda_autopoll_set(cuda_t *cuda, bool enable)
483{
484 cuda->snd_buf[0] = PT_CUDA;
485 cuda->snd_buf[1] = CPT_AUTOPOLL;
486 cuda->snd_buf[2] = enable ? 0x01 : 0x00;
487 cuda->snd_bytes = 3;
488 cuda->bidx = 0;
489
490 cuda_send_start(cuda);
491}
492
493static void cuda_send_start(cuda_t *cuda)
494{
495 assert(cuda->xstate == cx_listen);
496
497 if (cuda->snd_bytes == 0)
498 return;
499
500 /* Check for incoming data. */
501 if ((pio_read_8(&cuda->regs->b) & TREQ) == 0)
502 return;
503
504 pio_write_8(&cuda->regs->acr, pio_read_8(&cuda->regs->acr) | SR_OUT);
505 pio_write_8(&cuda->regs->sr, cuda->snd_buf[0]);
506 pio_write_8(&cuda->regs->b, pio_read_8(&cuda->regs->b) & ~TIP);
507
508 cuda->xstate = cx_send_start;
509}
510
511/** @}
512 */
Note: See TracBrowser for help on using the repository browser.