source: mainline/uspace/lib/graph/graph.c@ 921f393

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

libgraph: Fix mutex unlock in error path

jan vesely: Fix indentation of the surrounding code

  • Property mode set to 100644
File size: 14.9 KB
Line 
1/*
2 * Copyright (c) 2011 Petr Koupy
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 graph
30 * @{
31 */
32/**
33 * @file
34 */
35
36#include <assert.h>
37#include <errno.h>
38#include <inttypes.h>
39#include <stdio.h>
40#include <malloc.h>
41#include <as.h>
42#include "graph.h"
43
44#define NAMESPACE "graphemu"
45#define VISUALIZER_NAME "vsl"
46#define RENDERER_NAME "rnd"
47
48static sysarg_t namespace_idx = 0;
49static sysarg_t visualizer_idx = 0;
50static sysarg_t renderer_idx = 0;
51
52static LIST_INITIALIZE(visualizer_list);
53static LIST_INITIALIZE(renderer_list);
54
55static FIBRIL_MUTEX_INITIALIZE(visualizer_list_mtx);
56static FIBRIL_MUTEX_INITIALIZE(renderer_list_mtx);
57
58visualizer_t *graph_alloc_visualizer(void)
59{
60 visualizer_t *vs = (visualizer_t *) malloc(sizeof(visualizer_t));
61 if (vs == NULL) {
62 return NULL;
63 }
64
65 return vs;
66}
67
68renderer_t *graph_alloc_renderer(void)
69{
70 // TODO
71 renderer_t *rnd = (renderer_t *) malloc(sizeof(renderer_t));
72 if (rnd == NULL) {
73 return NULL;
74 }
75
76 return rnd;
77}
78
79void graph_init_visualizer(visualizer_t *vs)
80{
81 link_initialize(&vs->link);
82 atomic_set(&vs->ref_cnt, 0);
83 vs->notif_sess = NULL;
84 fibril_mutex_initialize(&vs->mode_mtx);
85 list_initialize(&vs->modes);
86 vs->mode_set = false;
87 vs->cells.data = NULL;
88 vs->dev_ctx = NULL;
89}
90
91void graph_init_renderer(renderer_t *rnd)
92{
93 // TODO
94 link_initialize(&rnd->link);
95 atomic_set(&rnd->ref_cnt, 0);
96}
97
98int graph_register_visualizer(visualizer_t *vs)
99{
100 int rc = EOK;
101
102 char node[LOC_NAME_MAXLEN + 1];
103 snprintf(node, LOC_NAME_MAXLEN, "%s%zu/%s%zu", NAMESPACE,
104 namespace_idx, VISUALIZER_NAME, visualizer_idx++);
105
106 category_id_t cat;
107 rc = loc_category_get_id("visualizer", &cat, 0);
108 if (rc != EOK) {
109 return rc;
110 }
111
112 rc = loc_service_register(node, &vs->reg_svc_handle);
113 if (rc != EOK) {
114 return rc;
115 }
116
117 rc = loc_service_add_to_cat(vs->reg_svc_handle, cat);
118 if (rc != EOK) {
119 loc_service_unregister(vs->reg_svc_handle);
120 return rc;
121 }
122
123 fibril_mutex_lock(&visualizer_list_mtx);
124 list_append(&vs->link, &visualizer_list);
125 fibril_mutex_unlock(&visualizer_list_mtx);
126
127 return rc;
128}
129
130int graph_register_renderer(renderer_t *rnd)
131{
132 int rc = EOK;
133
134 char node[LOC_NAME_MAXLEN + 1];
135 snprintf(node, LOC_NAME_MAXLEN, "%s%zu/%s%zu", NAMESPACE,
136 namespace_idx, RENDERER_NAME, renderer_idx++);
137
138 category_id_t cat;
139 rc = loc_category_get_id("renderer", &cat, 0);
140 if (rc != EOK) {
141 return rc;
142 }
143
144 rc = loc_service_register(node, &rnd->reg_svc_handle);
145 if (rc != EOK) {
146 return rc;
147 }
148
149 rc = loc_service_add_to_cat(rnd->reg_svc_handle, cat);
150 if (rc != EOK) {
151 loc_service_unregister(rnd->reg_svc_handle);
152 return rc;
153 }
154
155 fibril_mutex_lock(&renderer_list_mtx);
156 list_append(&rnd->link, &renderer_list);
157 fibril_mutex_unlock(&renderer_list_mtx);
158
159 return rc;
160}
161
162visualizer_t *graph_get_visualizer(sysarg_t handle)
163{
164 visualizer_t *vs = NULL;
165
166 fibril_mutex_lock(&visualizer_list_mtx);
167 list_foreach(visualizer_list, link, visualizer_t, vcur) {
168 if (vcur->reg_svc_handle == handle) {
169 vs = vcur;
170 break;
171 }
172 }
173 fibril_mutex_unlock(&visualizer_list_mtx);
174
175 return vs;
176}
177
178renderer_t *graph_get_renderer(sysarg_t handle)
179{
180 renderer_t *rnd = NULL;
181
182 fibril_mutex_lock(&renderer_list_mtx);
183 list_foreach(renderer_list, link, renderer_t, rcur) {
184 if (rcur->reg_svc_handle == handle) {
185 rnd = rcur;
186 break;
187 }
188 }
189 fibril_mutex_unlock(&renderer_list_mtx);
190
191 return rnd;
192}
193
194int graph_unregister_visualizer(visualizer_t *vs)
195{
196 int rc = EOK;
197
198 fibril_mutex_lock(&visualizer_list_mtx);
199 rc = loc_service_unregister(vs->reg_svc_handle);
200 list_remove(&vs->link);
201 fibril_mutex_unlock(&visualizer_list_mtx);
202
203 return rc;
204}
205
206int graph_unregister_renderer(renderer_t *rnd)
207{
208 int rc = EOK;
209
210 fibril_mutex_lock(&renderer_list_mtx);
211 rc = loc_service_unregister(rnd->reg_svc_handle);
212 list_remove(&rnd->link);
213 fibril_mutex_unlock(&renderer_list_mtx);
214
215 return rc;
216}
217
218void graph_destroy_visualizer(visualizer_t *vs)
219{
220 assert(atomic_get(&vs->ref_cnt) == 0);
221 assert(vs->notif_sess == NULL);
222 assert(!fibril_mutex_is_locked(&vs->mode_mtx));
223 assert(list_empty(&vs->modes));
224 assert(vs->mode_set == false);
225 assert(vs->cells.data == NULL);
226 assert(vs->dev_ctx == NULL);
227
228 free(vs);
229}
230
231void graph_destroy_renderer(renderer_t *rnd)
232{
233 // TODO
234 assert(atomic_get(&rnd->ref_cnt) == 0);
235
236 free(rnd);
237}
238
239int graph_notify_mode_change(async_sess_t *sess, sysarg_t handle, sysarg_t mode_idx)
240{
241 async_exch_t *exch = async_exchange_begin(sess);
242 int ret = async_req_2_0(exch, VISUALIZER_MODE_CHANGE, handle, mode_idx);
243 async_exchange_end(exch);
244
245 return ret;
246}
247
248int graph_notify_disconnect(async_sess_t *sess, sysarg_t handle)
249{
250 async_exch_t *exch = async_exchange_begin(sess);
251 int ret = async_req_1_0(exch, VISUALIZER_DISCONNECT, handle);
252 async_exchange_end(exch);
253
254 async_hangup(sess);
255
256 return ret;
257}
258
259static void vs_claim(visualizer_t *vs, ipc_callid_t iid, ipc_call_t *icall)
260{
261 vs->client_side_handle = IPC_GET_ARG1(*icall);
262 int rc = vs->ops.claim(vs);
263 async_answer_0(iid, rc);
264}
265
266static void vs_yield(visualizer_t *vs, ipc_callid_t iid, ipc_call_t *icall)
267{
268 /* Deallocate resources for the current mode. */
269 if (vs->mode_set) {
270 if (vs->cells.data != NULL) {
271 as_area_destroy((void *) vs->cells.data);
272 vs->cells.data = NULL;
273 }
274 }
275
276 /* Driver might also deallocate resources for the current mode. */
277 int rc = vs->ops.yield(vs);
278
279 /* Now that the driver was given a chance to deallocate resources,
280 * current mode can be unset. */
281 if (vs->mode_set) {
282 vs->mode_set = false;
283 }
284
285 async_answer_0(iid, rc);
286}
287
288static void vs_enumerate_modes(visualizer_t *vs, ipc_callid_t iid, ipc_call_t *icall)
289{
290 fibril_mutex_lock(&vs->mode_mtx);
291 link_t *link = list_nth(&vs->modes, IPC_GET_ARG1(*icall));
292
293 if (link != NULL) {
294 vslmode_list_element_t *mode_elem =
295 list_get_instance(link, vslmode_list_element_t, link);
296 vslmode_t mode = mode_elem->mode;
297 fibril_mutex_unlock(&vs->mode_mtx);
298
299 ipc_callid_t callid;
300 size_t len;
301
302 if (!async_data_read_receive(&callid, &len)) {
303 async_answer_0(iid, EINVAL);
304 return;
305 }
306 int rc = async_data_read_finalize(callid, &mode, len);
307 if (rc != EOK) {
308 async_answer_0(iid, ENOMEM);
309 return;
310 }
311
312 async_answer_0(iid, EOK);
313 } else {
314 fibril_mutex_unlock(&vs->mode_mtx);
315 async_answer_0(iid, ENOENT);
316 }
317}
318
319static void vs_get_default_mode(visualizer_t *vs, ipc_callid_t iid, ipc_call_t *icall)
320{
321 fibril_mutex_lock(&vs->mode_mtx);
322 vslmode_list_element_t *mode_elem = NULL;
323 list_foreach(vs->modes, link, vslmode_list_element_t, cur) {
324 if (cur->mode.index == vs->def_mode_idx) {
325 mode_elem = cur;
326 break;
327 }
328 }
329
330 vslmode_t mode;
331 if (mode_elem != NULL) {
332 mode = mode_elem->mode;
333 fibril_mutex_unlock(&vs->mode_mtx);
334
335 ipc_callid_t callid;
336 size_t len;
337
338 if (!async_data_read_receive(&callid, &len)) {
339 async_answer_0(iid, EINVAL);
340 return;
341 }
342 int rc = async_data_read_finalize(callid, &mode, len);
343 if (rc != EOK) {
344 async_answer_0(iid, ENOMEM);
345 return;
346 }
347 async_answer_0(iid, EOK);
348 } else {
349 fibril_mutex_unlock(&vs->mode_mtx);
350 async_answer_0(iid, ENOENT);
351 }
352}
353
354static void vs_get_current_mode(visualizer_t *vs, ipc_callid_t iid, ipc_call_t *icall)
355{
356 if (vs->mode_set) {
357 ipc_callid_t callid;
358 size_t len;
359
360 if (!async_data_read_receive(&callid, &len)) {
361 async_answer_0(iid, EINVAL);
362 return;
363 }
364 int rc = async_data_read_finalize(callid, &vs->cur_mode, len);
365 if (rc != EOK) {
366 async_answer_0(iid, ENOMEM);
367 return;
368 }
369
370 async_answer_0(iid, EOK);
371 } else {
372 async_answer_0(iid, ENOENT);
373 }
374}
375
376static void vs_get_mode(visualizer_t *vs, ipc_callid_t iid, ipc_call_t *icall)
377{
378 sysarg_t mode_idx = IPC_GET_ARG1(*icall);
379
380 fibril_mutex_lock(&vs->mode_mtx);
381 vslmode_list_element_t *mode_elem = NULL;
382 list_foreach(vs->modes, link, vslmode_list_element_t, cur) {
383 if (cur->mode.index == mode_idx) {
384 mode_elem = cur;
385 break;
386 }
387 }
388
389 vslmode_t mode;
390 if (mode_elem != NULL) {
391 mode = mode_elem->mode;
392 fibril_mutex_unlock(&vs->mode_mtx);
393
394 ipc_callid_t callid;
395 size_t len;
396
397 if (!async_data_read_receive(&callid, &len)) {
398 async_answer_0(iid, EINVAL);
399 return;
400 }
401 int rc = async_data_read_finalize(callid, &mode, len);
402 if (rc != EOK) {
403 async_answer_0(iid, ENOMEM);
404 return;
405 }
406 async_answer_0(iid, EOK);
407 } else {
408 fibril_mutex_unlock(&vs->mode_mtx);
409 async_answer_0(iid, ENOENT);
410 }
411}
412
413static void vs_set_mode(visualizer_t *vs, ipc_callid_t iid, ipc_call_t *icall)
414{
415 int rc = EOK;
416
417 /* Retrieve mode index and version. */
418 sysarg_t mode_idx = IPC_GET_ARG1(*icall);
419 sysarg_t mode_version = IPC_GET_ARG2(*icall);
420
421 /* Find mode in the list. */
422 fibril_mutex_lock(&vs->mode_mtx);
423 vslmode_list_element_t *mode_elem = NULL;
424 list_foreach(vs->modes, link, vslmode_list_element_t, cur) {
425 if (cur->mode.index == mode_idx) {
426 mode_elem = cur;
427 break;
428 }
429 }
430
431 /* Extract mode description from the list node. */
432 vslmode_t new_mode;
433 if (mode_elem != NULL) {
434 new_mode = mode_elem->mode;
435 fibril_mutex_unlock(&vs->mode_mtx);
436 } else {
437 fibril_mutex_unlock(&vs->mode_mtx);
438 async_answer_0(iid, ENOENT);
439 return;
440 }
441
442 /* Check whether the mode is still up-to-date. */
443 if (new_mode.version != mode_version) {
444 async_answer_0(iid, EINVAL);
445 return;
446 }
447
448 ipc_callid_t callid;
449 size_t size;
450 unsigned int flags;
451
452 /* Retrieve the shared cell storage for the new mode. */
453 if (!async_share_out_receive(&callid, &size, &flags)) {
454 async_answer_0(iid, EINVAL);
455 return;
456 }
457 void *new_cell_storage;
458 rc = async_share_out_finalize(callid, &new_cell_storage);
459 if ((rc != EOK) || (new_cell_storage == AS_MAP_FAILED)) {
460 async_answer_0(iid, ENOMEM);
461 return;
462 }
463
464 /* Change device internal state. */
465 rc = vs->ops.change_mode(vs, new_mode);
466
467 /* Device driver could not establish new mode. Rollback. */
468 if (rc != EOK) {
469 as_area_destroy(new_cell_storage);
470 async_answer_0(iid, ENOMEM);
471 return;
472 }
473
474 /* Because resources for the new mode were successfully claimed,
475 * it is finally possible to free resources allocated for the old mode. */
476 if (vs->mode_set) {
477 if (vs->cells.data != NULL) {
478 as_area_destroy((void *) vs->cells.data);
479 vs->cells.data = NULL;
480 }
481 }
482
483 /* Insert new mode into the visualizer. */
484 vs->cells.width = new_mode.screen_width;
485 vs->cells.height = new_mode.screen_height;
486 vs->cells.data = (pixel_t *) new_cell_storage;
487 vs->cur_mode = new_mode;
488 vs->mode_set = true;
489
490 async_answer_0(iid, EOK);
491}
492
493static void vs_update_damaged_region(visualizer_t *vs, ipc_callid_t iid, ipc_call_t *icall)
494{
495#ifdef __32_BITS__
496 sysarg_t x_offset = (IPC_GET_ARG5(*icall) >> 16);
497 sysarg_t y_offset = (IPC_GET_ARG5(*icall) & 0x0000ffff);
498#else
499 sysarg_t x_offset = (IPC_GET_ARG5(*icall) >> 32);
500 sysarg_t y_offset = (IPC_GET_ARG5(*icall) & 0xffffffff);
501#endif
502
503 int rc = vs->ops.handle_damage(vs,
504 IPC_GET_ARG1(*icall), IPC_GET_ARG2(*icall),
505 IPC_GET_ARG3(*icall), IPC_GET_ARG4(*icall),
506 x_offset, y_offset);
507 async_answer_0(iid, rc);
508}
509
510static void vs_suspend(visualizer_t *vs, ipc_callid_t iid, ipc_call_t *icall)
511{
512 int rc = vs->ops.suspend(vs);
513 async_answer_0(iid, rc);
514}
515
516static void vs_wakeup(visualizer_t *vs, ipc_callid_t iid, ipc_call_t *icall)
517{
518 int rc = vs->ops.wakeup(vs);
519 async_answer_0(iid, rc);
520}
521
522void graph_visualizer_connection(visualizer_t *vs,
523 ipc_callid_t iid, ipc_call_t *icall, void *arg)
524{
525 ipc_call_t call;
526 ipc_callid_t callid;
527
528 /* Claim the visualizer. */
529 if (!cas(&vs->ref_cnt, 0, 1)) {
530 async_answer_0(iid, ELIMIT);
531 return;
532 }
533
534 /* Accept the connection. */
535 async_answer_0(iid, EOK);
536
537 /* Establish callback session. */
538 callid = async_get_call(&call);
539 vs->notif_sess = async_callback_receive_start(EXCHANGE_SERIALIZE, &call);
540 if (vs->notif_sess != NULL) {
541 async_answer_0(callid, EOK);
542 } else {
543 async_answer_0(callid, ELIMIT);
544 }
545
546 /* Enter command loop. */
547 while (true) {
548 callid = async_get_call(&call);
549
550 if (!IPC_GET_IMETHOD(call)) {
551 async_answer_0(callid, EINVAL);
552 break;
553 }
554
555 switch (IPC_GET_IMETHOD(call)) {
556 case VISUALIZER_CLAIM:
557 vs_claim(vs, callid, &call);
558 break;
559 case VISUALIZER_YIELD:
560 vs_yield(vs, callid, &call);
561 goto terminate;
562 case VISUALIZER_ENUMERATE_MODES:
563 vs_enumerate_modes(vs, callid, &call);
564 break;
565 case VISUALIZER_GET_DEFAULT_MODE:
566 vs_get_default_mode(vs, callid, &call);
567 break;
568 case VISUALIZER_GET_CURRENT_MODE:
569 vs_get_current_mode(vs, callid, &call);
570 break;
571 case VISUALIZER_GET_MODE:
572 vs_get_mode(vs, callid, &call);
573 break;
574 case VISUALIZER_SET_MODE:
575 vs_set_mode(vs, callid, &call);
576 break;
577 case VISUALIZER_UPDATE_DAMAGED_REGION:
578 vs_update_damaged_region(vs, callid, &call);
579 break;
580 case VISUALIZER_SUSPEND:
581 vs_suspend(vs, callid, &call);
582 break;
583 case VISUALIZER_WAKE_UP:
584 vs_wakeup(vs, callid, &call);
585 break;
586 default:
587 async_answer_0(callid, EINVAL);
588 goto terminate;
589 }
590 }
591
592terminate:
593 async_hangup(vs->notif_sess);
594 vs->notif_sess = NULL;
595 atomic_set(&vs->ref_cnt, 0);
596}
597
598void graph_renderer_connection(renderer_t *rnd,
599 ipc_callid_t iid, ipc_call_t *icall, void *arg)
600{
601 // TODO
602
603 ipc_call_t call;
604 ipc_callid_t callid;
605
606 /* Accept the connection. */
607 atomic_inc(&rnd->ref_cnt);
608 async_answer_0(iid, EOK);
609
610 /* Enter command loop. */
611 while (true) {
612 callid = async_get_call(&call);
613
614 if (!IPC_GET_IMETHOD(call)) {
615 async_answer_0(callid, EINVAL);
616 break;
617 }
618
619 switch (IPC_GET_IMETHOD(call)) {
620 default:
621 async_answer_0(callid, EINVAL);
622 goto terminate;
623 }
624 }
625
626terminate:
627 atomic_dec(&rnd->ref_cnt);
628}
629
630void graph_client_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
631{
632 /* Find the visualizer or renderer with the given service ID. */
633 visualizer_t *vs = graph_get_visualizer(IPC_GET_ARG1(*icall));
634 renderer_t *rnd = graph_get_renderer(IPC_GET_ARG1(*icall));
635
636 if (vs != NULL) {
637 graph_visualizer_connection(vs, iid, icall, arg);
638 } else if (rnd != NULL) {
639 graph_renderer_connection(rnd, iid, icall, arg);
640 } else {
641 async_answer_0(iid, ENOENT);
642 }
643}
644
645/** @}
646 */
Note: See TracBrowser for help on using the repository browser.