source: mainline/uspace/srv/hw/bus/cuda_adb/cuda_adb.c@ df01d303

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since df01d303 was c170438, checked in by Jakub Jermar <jakub@…>, 9 years ago

Do not create a new fibril for each IRQ notification

In the absence of fibril serialization, manager fibrils can
theoretically block in async IPC or on fibril synchronization
primitives. Consequently, it is safe to execute the IRQ handler directly
from the manager fibril. The manager fibril can block while processing
the notification, but most of the times it will not block and the
handler will execute atomically.

This changeset modifies the current behaviour so that we no longer spawn
a new notification fibril for each IRQ, but rather execute the handler
directly from the manager fibril and test if the execution blocked. If
it blocked, the manager fibril had assumed the role of a notification
fibril and we destroy it afterwards - merely to avoid fibril population
explosion. Otherwise, which is the usual behavior, we keep it so that
it resumes its job of a manager fibril.

  • 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 <stdio.h>
40#include <stdlib.h>
41#include <sys/types.h>
42#include <stdbool.h>
43#include <ddi.h>
44#include <libarch/ddi.h>
45#include <loc.h>
46#include <sysinfo.h>
47#include <errno.h>
48#include <ipc/adb.h>
49#include <assert.h>
50#include "cuda_adb.h"
51
52#define NAME "cuda_adb"
53
54static void cuda_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg);
55static int cuda_init(void);
56static void cuda_irq_handler(ipc_callid_t iid, ipc_call_t *call, void *arg);
57
58static void cuda_irq_listen(void);
59static void cuda_irq_receive(void);
60static void cuda_irq_rcv_end(void *buf, size_t *len);
61static void cuda_irq_send_start(void);
62static void cuda_irq_send(void);
63
64static void cuda_packet_handle(uint8_t *buf, size_t len);
65static void cuda_send_start(void);
66static void cuda_autopoll_set(bool enable);
67
68static void adb_packet_handle(uint8_t *data, size_t size, bool autopoll);
69
70
71/** B register fields */
72enum {
73 TREQ = 0x08,
74 TACK = 0x10,
75 TIP = 0x20
76};
77
78/** IER register fields */
79enum {
80 IER_CLR = 0x00,
81 IER_SET = 0x80,
82
83 SR_INT = 0x04,
84 ALL_INT = 0x7f
85};
86
87/** ACR register fields */
88enum {
89 SR_OUT = 0x10
90};
91
92/** Packet types */
93enum {
94 PT_ADB = 0x00,
95 PT_CUDA = 0x01
96};
97
98/** CUDA packet types */
99enum {
100 CPT_AUTOPOLL = 0x01
101};
102
103enum {
104 ADB_MAX_ADDR = 16
105};
106
107static irq_pio_range_t cuda_ranges[] = {
108 {
109 .base = 0,
110 .size = sizeof(cuda_t)
111 }
112};
113
114static irq_cmd_t cuda_cmds[] = {
115 {
116 .cmd = CMD_PIO_READ_8,
117 .addr = NULL,
118 .dstarg = 1
119 },
120 {
121 .cmd = CMD_AND,
122 .value = SR_INT,
123 .srcarg = 1,
124 .dstarg = 2
125 },
126 {
127 .cmd = CMD_PREDICATE,
128 .value = 1,
129 .srcarg = 2
130 },
131 {
132 .cmd = CMD_ACCEPT
133 }
134};
135
136
137static irq_code_t cuda_irq_code = {
138 sizeof(cuda_ranges) / sizeof(irq_pio_range_t),
139 cuda_ranges,
140 sizeof(cuda_cmds) / sizeof(irq_cmd_t),
141 cuda_cmds
142};
143
144static cuda_instance_t cinst;
145
146static cuda_instance_t *instance = &cinst;
147static cuda_t *dev;
148
149static adb_dev_t adb_dev[ADB_MAX_ADDR];
150
151int main(int argc, char *argv[])
152{
153 service_id_t service_id;
154 int rc;
155 int i;
156
157 printf(NAME ": VIA-CUDA Apple Desktop Bus driver\n");
158
159 for (i = 0; i < ADB_MAX_ADDR; ++i) {
160 adb_dev[i].client_sess = NULL;
161 adb_dev[i].service_id = 0;
162 }
163
164 async_set_fallback_port_handler(cuda_connection, NULL);
165 rc = loc_server_register(NAME);
166 if (rc < 0) {
167 printf(NAME ": Unable to register server.\n");
168 return rc;
169 }
170
171 rc = loc_service_register("adb/kbd", &service_id);
172 if (rc != EOK) {
173 printf(NAME ": Unable to register service %s.\n", "adb/kdb");
174 return rc;
175 }
176
177 adb_dev[2].service_id = service_id;
178 adb_dev[8].service_id = service_id;
179
180 rc = loc_service_register("adb/mouse", &service_id);
181 if (rc != EOK) {
182 printf(NAME ": Unable to register servise %s.\n", "adb/mouse");
183 return rc;
184 }
185
186 adb_dev[9].service_id = service_id;
187
188 if (cuda_init() < 0) {
189 printf("cuda_init() failed\n");
190 return 1;
191 }
192
193 task_retval(0);
194 async_manager();
195
196 return 0;
197}
198
199/** Character device connection handler */
200static void cuda_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
201{
202 ipc_callid_t callid;
203 ipc_call_t call;
204 sysarg_t method;
205 service_id_t dsid;
206 int dev_addr, i;
207
208 /* Get the device handle. */
209 dsid = IPC_GET_ARG2(*icall);
210
211 /* Determine which disk device is the client connecting to. */
212 dev_addr = -1;
213 for (i = 0; i < ADB_MAX_ADDR; i++) {
214 if (adb_dev[i].service_id == dsid)
215 dev_addr = i;
216 }
217
218 if (dev_addr < 0) {
219 async_answer_0(iid, EINVAL);
220 return;
221 }
222
223 /* Answer the IPC_M_CONNECT_ME_TO call. */
224 async_answer_0(iid, EOK);
225
226 while (true) {
227 callid = async_get_call(&call);
228 method = IPC_GET_IMETHOD(call);
229
230 if (!method) {
231 /* The other side has hung up. */
232 async_answer_0(callid, EOK);
233 return;
234 }
235
236 async_sess_t *sess =
237 async_callback_receive_start(EXCHANGE_SERIALIZE, &call);
238 if (sess != NULL) {
239 if (adb_dev[dev_addr].client_sess == NULL) {
240 adb_dev[dev_addr].client_sess = sess;
241
242 /*
243 * A hack so that we send the data to the session
244 * regardless of which address the device is on.
245 */
246 for (i = 0; i < ADB_MAX_ADDR; ++i) {
247 if (adb_dev[i].service_id == dsid)
248 adb_dev[i].client_sess = sess;
249 }
250
251 async_answer_0(callid, EOK);
252 } else
253 async_answer_0(callid, ELIMIT);
254 } else
255 async_answer_0(callid, EINVAL);
256 }
257}
258
259
260static int cuda_init(void)
261{
262 if (sysinfo_get_value("cuda.address.physical", &(instance->cuda_physical)) != EOK)
263 return -1;
264
265 void *vaddr;
266 if (pio_enable((void *) instance->cuda_physical, sizeof(cuda_t), &vaddr) != 0)
267 return -1;
268
269 dev = vaddr;
270
271 instance->cuda = dev;
272 instance->xstate = cx_listen;
273 instance->bidx = 0;
274 instance->snd_bytes = 0;
275
276 fibril_mutex_initialize(&instance->dev_lock);
277
278 /* Disable all interrupts from CUDA. */
279 pio_write_8(&dev->ier, IER_CLR | ALL_INT);
280
281 cuda_irq_code.ranges[0].base = (uintptr_t) instance->cuda_physical;
282 cuda_irq_code.cmds[0].addr = (void *) &((cuda_t *) instance->cuda_physical)->ifr;
283 async_irq_subscribe(10, device_assign_devno(), cuda_irq_handler, NULL,
284 &cuda_irq_code);
285
286 /* Enable SR interrupt. */
287 pio_write_8(&dev->ier, TIP | TREQ);
288 pio_write_8(&dev->ier, IER_SET | SR_INT);
289
290 /* Enable ADB autopolling. */
291 cuda_autopoll_set(true);
292
293 return 0;
294}
295
296static void cuda_irq_handler(ipc_callid_t iid, ipc_call_t *call, void *arg)
297{
298 uint8_t rbuf[CUDA_RCV_BUF_SIZE];
299 size_t len;
300 bool handle;
301
302 handle = false;
303 len = 0;
304
305 fibril_mutex_lock(&instance->dev_lock);
306
307 switch (instance->xstate) {
308 case cx_listen:
309 cuda_irq_listen();
310 break;
311 case cx_receive:
312 cuda_irq_receive();
313 break;
314 case cx_rcv_end:
315 cuda_irq_rcv_end(rbuf, &len);
316 handle = true;
317 break;
318 case cx_send_start:
319 cuda_irq_send_start();
320 break;
321 case cx_send:
322 cuda_irq_send();
323 break;
324 }
325
326 /* Lower IFR.SR_INT so that CUDA can generate next int by raising it. */
327 pio_write_8(&instance->cuda->ifr, SR_INT);
328
329 fibril_mutex_unlock(&instance->dev_lock);
330
331 /* Handle an incoming packet. */
332 if (handle)
333 cuda_packet_handle(rbuf, len);
334}
335
336/** Interrupt in listen state.
337 *
338 * Start packet reception.
339 */
340static void cuda_irq_listen(void)
341{
342 uint8_t b = pio_read_8(&dev->b);
343
344 if ((b & TREQ) != 0) {
345 printf("cuda_irq_listen: no TREQ?!\n");
346 return;
347 }
348
349 pio_write_8(&dev->b, b & ~TIP);
350 instance->xstate = cx_receive;
351}
352
353/** Interrupt in receive state.
354 *
355 * Receive next byte of packet.
356 */
357static void cuda_irq_receive(void)
358{
359 uint8_t data = pio_read_8(&dev->sr);
360 if (instance->bidx < CUDA_RCV_BUF_SIZE)
361 instance->rcv_buf[instance->bidx++] = data;
362
363 uint8_t b = pio_read_8(&dev->b);
364
365 if ((b & TREQ) == 0) {
366 pio_write_8(&dev->b, b ^ TACK);
367 } else {
368 pio_write_8(&dev->b, b | TACK | TIP);
369 instance->xstate = cx_rcv_end;
370 }
371}
372
373/** Interrupt in rcv_end state.
374 *
375 * Terminate packet reception. Either go back to listen state or start
376 * receiving another packet if CUDA has one for us.
377 */
378static void cuda_irq_rcv_end(void *buf, size_t *len)
379{
380 uint8_t b = pio_read_8(&dev->b);
381
382 if ((b & TREQ) == 0) {
383 instance->xstate = cx_receive;
384 pio_write_8(&dev->b, b & ~TIP);
385 } else {
386 instance->xstate = cx_listen;
387 cuda_send_start();
388 }
389
390 memcpy(buf, instance->rcv_buf, instance->bidx);
391 *len = instance->bidx;
392 instance->bidx = 0;
393}
394
395/** Interrupt in send_start state.
396 *
397 * Process result of sending first byte (and send second on success).
398 */
399static void cuda_irq_send_start(void)
400{
401 uint8_t b;
402
403 b = pio_read_8(&dev->b);
404
405 if ((b & TREQ) == 0) {
406 /* Collision */
407 pio_write_8(&dev->acr, pio_read_8(&dev->acr) & ~SR_OUT);
408 pio_read_8(&dev->sr);
409 pio_write_8(&dev->b, pio_read_8(&dev->b) | TIP | TACK);
410 instance->xstate = cx_listen;
411 return;
412 }
413
414 pio_write_8(&dev->sr, instance->snd_buf[1]);
415 pio_write_8(&dev->b, pio_read_8(&dev->b) ^ TACK);
416 instance->bidx = 2;
417
418 instance->xstate = cx_send;
419}
420
421/** Interrupt in send state.
422 *
423 * Send next byte or terminate transmission.
424 */
425static void cuda_irq_send(void)
426{
427 if (instance->bidx < instance->snd_bytes) {
428 /* Send next byte. */
429 pio_write_8(&dev->sr, instance->snd_buf[instance->bidx++]);
430 pio_write_8(&dev->b, pio_read_8(&dev->b) ^ TACK);
431 return;
432 }
433
434 /* End transfer. */
435 instance->snd_bytes = 0;
436 instance->bidx = 0;
437
438 pio_write_8(&dev->acr, pio_read_8(&dev->acr) & ~SR_OUT);
439 pio_read_8(&dev->sr);
440 pio_write_8(&dev->b, pio_read_8(&dev->b) | TACK | TIP);
441
442 instance->xstate = cx_listen;
443 /* TODO: Match reply with request. */
444}
445
446static void cuda_packet_handle(uint8_t *data, size_t len)
447{
448 if (data[0] != PT_ADB)
449 return;
450 if (len < 2)
451 return;
452
453 adb_packet_handle(data + 2, len - 2, (data[1] & 0x40) != 0);
454}
455
456static void adb_packet_handle(uint8_t *data, size_t size, bool autopoll)
457{
458 uint8_t dev_addr;
459 uint8_t reg_no;
460 uint16_t reg_val;
461 unsigned i;
462
463 dev_addr = data[0] >> 4;
464 reg_no = data[0] & 0x03;
465
466 if (size != 3) {
467 printf("unrecognized packet, size=%d\n", size);
468 for (i = 0; i < size; ++i) {
469 printf(" 0x%02x", data[i]);
470 }
471 putchar('\n');
472 return;
473 }
474
475 if (reg_no != 0) {
476 printf("unrecognized packet, size=%d\n", size);
477 for (i = 0; i < size; ++i) {
478 printf(" 0x%02x", data[i]);
479 }
480 putchar('\n');
481 return;
482 }
483
484 reg_val = ((uint16_t) data[1] << 8) | (uint16_t) data[2];
485
486 if (adb_dev[dev_addr].client_sess == NULL)
487 return;
488
489 async_exch_t *exch =
490 async_exchange_begin(adb_dev[dev_addr].client_sess);
491 async_msg_1(exch, ADB_REG_NOTIF, reg_val);
492 async_exchange_end(exch);
493}
494
495static void cuda_autopoll_set(bool enable)
496{
497 instance->snd_buf[0] = PT_CUDA;
498 instance->snd_buf[1] = CPT_AUTOPOLL;
499 instance->snd_buf[2] = enable ? 0x01 : 0x00;
500 instance->snd_bytes = 3;
501 instance->bidx = 0;
502
503 cuda_send_start();
504}
505
506static void cuda_send_start(void)
507{
508 cuda_t *dev = instance->cuda;
509
510 assert(instance->xstate == cx_listen);
511
512 if (instance->snd_bytes == 0)
513 return;
514
515 /* Check for incoming data. */
516 if ((pio_read_8(&dev->b) & TREQ) == 0)
517 return;
518
519 pio_write_8(&dev->acr, pio_read_8(&dev->acr) | SR_OUT);
520 pio_write_8(&dev->sr, instance->snd_buf[0]);
521 pio_write_8(&dev->b, pio_read_8(&dev->b) & ~TIP);
522
523 instance->xstate = cx_send_start;
524}
525
526/** @}
527 */
Note: See TracBrowser for help on using the repository browser.