2 * Copyright (c) 2010 Jiri Svoboda
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
29 /** @addtogroup genarch
32 /** @file VIA-CUDA Apple Desktop Bus driver
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.
40 #include <ddf/driver.h>
45 #include <libarch/ddi.h>
55 #define NAME "cuda_adb"
57 static void cuda_dev_connection(ipc_callid_t
, ipc_call_t
*, void *);
58 static int cuda_init(cuda_t
*);
59 static void cuda_irq_handler(ipc_call_t
*, void *);
61 static void cuda_irq_listen(cuda_t
*);
62 static void cuda_irq_receive(cuda_t
*);
63 static void cuda_irq_rcv_end(cuda_t
*, void *, size_t *);
64 static void cuda_irq_send_start(cuda_t
*);
65 static void cuda_irq_send(cuda_t
*);
67 static void cuda_packet_handle(cuda_t
*, uint8_t *, size_t);
68 static void cuda_send_start(cuda_t
*);
69 static void cuda_autopoll_set(cuda_t
*, bool);
71 static void adb_packet_handle(cuda_t
*, uint8_t *, size_t, bool);
73 static irq_pio_range_t cuda_ranges
[] = {
76 .size
= sizeof(cuda_regs_t
)
80 static irq_cmd_t cuda_cmds
[] = {
82 .cmd
= CMD_PIO_READ_8
,
103 static irq_code_t cuda_irq_code
= {
104 sizeof(cuda_ranges
) / sizeof(irq_pio_range_t
),
106 sizeof(cuda_cmds
) / sizeof(irq_cmd_t
),
110 static int cuda_dev_create(cuda_t
*cuda
, const char *name
, const char *id
,
113 adb_dev_t
*dev
= NULL
;
117 fun
= ddf_fun_create(cuda
->dev
, fun_inner
, name
);
119 ddf_msg(LVL_ERROR
, "Failed creating function '%s'.", name
);
124 rc
= ddf_fun_add_match_id(fun
, id
, 10);
126 ddf_msg(LVL_ERROR
, "Failed adding match ID.");
131 dev
= ddf_fun_data_alloc(fun
, sizeof(adb_dev_t
));
133 ddf_msg(LVL_ERROR
, "Failed allocating memory for '%s'.", name
);
139 list_append(&dev
->lcuda
, &cuda
->devs
);
141 ddf_fun_set_conn_handler(fun
, cuda_dev_connection
);
143 rc
= ddf_fun_bind(fun
);
145 ddf_msg(LVL_ERROR
, "Failed binding function '%s'.", name
);
153 ddf_fun_destroy(fun
);
157 int cuda_add(cuda_t
*cuda
, cuda_res_t
*res
)
159 adb_dev_t
*kbd
= NULL
;
160 adb_dev_t
*mouse
= NULL
;
163 cuda
->phys_base
= res
->base
;
165 rc
= cuda_dev_create(cuda
, "kbd", "adb/keyboard", &kbd
);
169 rc
= cuda_dev_create(cuda
, "mouse", "adb/mouse", &mouse
);
173 cuda
->addr_dev
[2] = kbd
;
174 cuda
->addr_dev
[8] = kbd
;
176 cuda
->addr_dev
[9] = mouse
;
178 rc
= cuda_init(cuda
);
180 ddf_msg(LVL_ERROR
, "Failed initializing CUDA hardware.");
189 int cuda_remove(cuda_t
*cuda
)
194 int cuda_gone(cuda_t
*cuda
)
199 /** Device connection handler */
200 static void cuda_dev_connection(ipc_callid_t iid
, ipc_call_t
*icall
, void *arg
)
202 adb_dev_t
*dev
= (adb_dev_t
*) ddf_fun_data_get((ddf_fun_t
*) arg
);
207 /* Answer the IPC_M_CONNECT_ME_TO call. */
208 async_answer_0(iid
, EOK
);
211 callid
= async_get_call(&call
);
212 method
= IPC_GET_IMETHOD(call
);
215 /* The other side has hung up. */
216 async_answer_0(callid
, EOK
);
221 async_callback_receive_start(EXCHANGE_SERIALIZE
, &call
);
223 dev
->client_sess
= sess
;
224 async_answer_0(callid
, EOK
);
226 async_answer_0(callid
, EINVAL
);
231 static int cuda_init(cuda_t
*cuda
)
236 rc
= pio_enable((void *) cuda
->phys_base
, sizeof(cuda_regs_t
),
242 cuda
->xstate
= cx_listen
;
246 fibril_mutex_initialize(&cuda
->dev_lock
);
248 /* Disable all interrupts from CUDA. */
249 pio_write_8(&cuda
->regs
->ier
, IER_CLR
| ALL_INT
);
251 cuda_irq_code
.ranges
[0].base
= (uintptr_t) cuda
->phys_base
;
252 cuda_irq_code
.cmds
[0].addr
= (void *) &((cuda_regs_t
*)
253 cuda
->phys_base
)->ifr
;
254 async_irq_subscribe(10, cuda_irq_handler
, cuda
, &cuda_irq_code
);
256 /* Enable SR interrupt. */
257 pio_write_8(&cuda
->regs
->ier
, TIP
| TREQ
);
258 pio_write_8(&cuda
->regs
->ier
, IER_SET
| SR_INT
);
260 /* Enable ADB autopolling. */
261 cuda_autopoll_set(cuda
, true);
266 static void cuda_irq_handler(ipc_call_t
*call
, void *arg
)
268 uint8_t rbuf
[CUDA_RCV_BUF_SIZE
];
269 cuda_t
*cuda
= (cuda_t
*)arg
;
276 fibril_mutex_lock(&cuda
->dev_lock
);
278 switch (cuda
->xstate
) {
280 cuda_irq_listen(cuda
);
283 cuda_irq_receive(cuda
);
286 cuda_irq_rcv_end(cuda
, rbuf
, &len
);
290 cuda_irq_send_start(cuda
);
297 /* Lower IFR.SR_INT so that CUDA can generate next int by raising it. */
298 pio_write_8(&cuda
->regs
->ifr
, SR_INT
);
300 fibril_mutex_unlock(&cuda
->dev_lock
);
302 /* Handle an incoming packet. */
304 cuda_packet_handle(cuda
, rbuf
, len
);
307 /** Interrupt in listen state.
309 * Start packet reception.
311 * @param cuda CUDA instance
313 static void cuda_irq_listen(cuda_t
*cuda
)
315 uint8_t b
= pio_read_8(&cuda
->regs
->b
);
317 if ((b
& TREQ
) != 0) {
318 ddf_msg(LVL_WARN
, "cuda_irq_listen: no TREQ?!");
322 pio_write_8(&cuda
->regs
->b
, b
& ~TIP
);
323 cuda
->xstate
= cx_receive
;
326 /** Interrupt in receive state.
328 * Receive next byte of packet.
330 * @param cuda CUDA instance
332 static void cuda_irq_receive(cuda_t
*cuda
)
334 uint8_t data
= pio_read_8(&cuda
->regs
->sr
);
335 if (cuda
->bidx
< CUDA_RCV_BUF_SIZE
)
336 cuda
->rcv_buf
[cuda
->bidx
++] = data
;
338 uint8_t b
= pio_read_8(&cuda
->regs
->b
);
340 if ((b
& TREQ
) == 0) {
341 pio_write_8(&cuda
->regs
->b
, b
^ TACK
);
343 pio_write_8(&cuda
->regs
->b
, b
| TACK
| TIP
);
344 cuda
->xstate
= cx_rcv_end
;
348 /** Interrupt in rcv_end state.
350 * Terminate packet reception. Either go back to listen state or start
351 * receiving another packet if CUDA has one for us.
353 * @param cuda CUDA instance
354 * @param buf Buffer for storing received packet
355 * @param len Place to store length of received packet
357 static void cuda_irq_rcv_end(cuda_t
*cuda
, void *buf
, size_t *len
)
359 uint8_t b
= pio_read_8(&cuda
->regs
->b
);
361 if ((b
& TREQ
) == 0) {
362 cuda
->xstate
= cx_receive
;
363 pio_write_8(&cuda
->regs
->b
, b
& ~TIP
);
365 cuda
->xstate
= cx_listen
;
366 cuda_send_start(cuda
);
369 memcpy(buf
, cuda
->rcv_buf
, cuda
->bidx
);
374 /** Interrupt in send_start state.
376 * Process result of sending first byte (and send second on success).
378 * @param cuda CUDA instance
380 static void cuda_irq_send_start(cuda_t
*cuda
)
384 b
= pio_read_8(&cuda
->regs
->b
);
386 if ((b
& TREQ
) == 0) {
388 pio_write_8(&cuda
->regs
->acr
, pio_read_8(&cuda
->regs
->acr
) &
390 pio_read_8(&cuda
->regs
->sr
);
391 pio_write_8(&cuda
->regs
->b
, pio_read_8(&cuda
->regs
->b
) |
393 cuda
->xstate
= cx_listen
;
397 pio_write_8(&cuda
->regs
->sr
, cuda
->snd_buf
[1]);
398 pio_write_8(&cuda
->regs
->b
, pio_read_8(&cuda
->regs
->b
) ^ TACK
);
401 cuda
->xstate
= cx_send
;
404 /** Interrupt in send state.
406 * Send next byte or terminate transmission.
408 * @param cuda CUDA instance
410 static void cuda_irq_send(cuda_t
*cuda
)
412 if (cuda
->bidx
< cuda
->snd_bytes
) {
413 /* Send next byte. */
414 pio_write_8(&cuda
->regs
->sr
,
415 cuda
->snd_buf
[cuda
->bidx
++]);
416 pio_write_8(&cuda
->regs
->b
, pio_read_8(&cuda
->regs
->b
) ^ TACK
);
424 pio_write_8(&cuda
->regs
->acr
, pio_read_8(&cuda
->regs
->acr
) & ~SR_OUT
);
425 pio_read_8(&cuda
->regs
->sr
);
426 pio_write_8(&cuda
->regs
->b
, pio_read_8(&cuda
->regs
->b
) | TACK
| TIP
);
428 cuda
->xstate
= cx_listen
;
429 /* TODO: Match reply with request. */
432 static void cuda_packet_handle(cuda_t
*cuda
, uint8_t *data
, size_t len
)
434 if (data
[0] != PT_ADB
)
439 adb_packet_handle(cuda
, data
+ 2, len
- 2, (data
[1] & 0x40) != 0);
442 static void adb_packet_handle(cuda_t
*cuda
, uint8_t *data
, size_t size
,
451 dev_addr
= data
[0] >> 4;
452 reg_no
= data
[0] & 0x03;
455 ddf_msg(LVL_WARN
, "Unrecognized packet, size=%zu", size
);
456 for (i
= 0; i
< size
; ++i
) {
457 ddf_msg(LVL_WARN
, " 0x%02x", data
[i
]);
463 ddf_msg(LVL_WARN
, "Unrecognized packet, size=%zu", size
);
464 for (i
= 0; i
< size
; ++i
) {
465 ddf_msg(LVL_WARN
, " 0x%02x", data
[i
]);
470 reg_val
= ((uint16_t) data
[1] << 8) | (uint16_t) data
[2];
472 ddf_msg(LVL_DEBUG
, "Received ADB packet for device address %d",
474 dev
= cuda
->addr_dev
[dev_addr
];
478 async_exch_t
*exch
= async_exchange_begin(dev
->client_sess
);
479 async_msg_1(exch
, ADB_REG_NOTIF
, reg_val
);
480 async_exchange_end(exch
);
483 static void cuda_autopoll_set(cuda_t
*cuda
, bool enable
)
485 cuda
->snd_buf
[0] = PT_CUDA
;
486 cuda
->snd_buf
[1] = CPT_AUTOPOLL
;
487 cuda
->snd_buf
[2] = enable
? 0x01 : 0x00;
491 cuda_send_start(cuda
);
494 static void cuda_send_start(cuda_t
*cuda
)
496 assert(cuda
->xstate
== cx_listen
);
498 if (cuda
->snd_bytes
== 0)
501 /* Check for incoming data. */
502 if ((pio_read_8(&cuda
->regs
->b
) & TREQ
) == 0)
505 pio_write_8(&cuda
->regs
->acr
, pio_read_8(&cuda
->regs
->acr
) | SR_OUT
);
506 pio_write_8(&cuda
->regs
->sr
, cuda
->snd_buf
[0]);
507 pio_write_8(&cuda
->regs
->b
, pio_read_8(&cuda
->regs
->b
) & ~TIP
);
509 cuda
->xstate
= cx_send_start
;