Changes in uspace/lib/c/generic/async.c [0b4a67a:9c31643] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/lib/c/generic/async.c
r0b4a67a r9c31643 43 43 * framework will automatically take care of most synchronization problems. 44 44 * 45 * Default semantics:46 * - async_send_*(): Send asynchronously. If the kernel refuses to send47 * more messages, [ try to get responses from kernel, if48 * nothing found, might try synchronous ]49 *50 45 * Example of use (pseudo C): 51 46 * … … 58 53 * int fibril1(void *arg) 59 54 * { 60 * conn = ipc_connect_me_to();55 * conn = async_connect_me_to(); 61 56 * c1 = async_send(conn); 62 57 * c2 = async_send(conn); … … 77 72 * { 78 73 * if (want_refuse) { 79 * ipc_answer_0(icallid, ELIMIT);74 * async_answer_0(icallid, ELIMIT); 80 75 * return; 81 76 * } 82 * ipc_answer_0(icallid, EOK);77 * async_answer_0(icallid, EOK); 83 78 * 84 79 * callid = async_get_call(&call); 85 80 * somehow_handle_the_call(callid, call); 86 * ipc_answer_2(callid, 1, 2, 3);81 * async_answer_2(callid, 1, 2, 3); 87 82 * 88 83 * callid = async_get_call(&call); … … 92 87 */ 93 88 89 #define LIBC_ASYNC_C_ 90 #include <ipc/ipc.h> 91 #include <async.h> 92 #undef LIBC_ASYNC_C_ 93 94 94 #include <futex.h> 95 #include <async.h>96 #include <async_priv.h>97 95 #include <fibril.h> 98 96 #include <stdio.h> 99 97 #include <adt/hash_table.h> 100 98 #include <adt/list.h> 101 #include <ipc/ipc.h>102 99 #include <assert.h> 103 100 #include <errno.h> … … 105 102 #include <arch/barrier.h> 106 103 #include <bool.h> 104 #include "private/async.h" 107 105 108 106 atomic_t async_futex = FUTEX_INITIALIZER; … … 120 118 ipc_call_t *dataptr; 121 119 122 ipcarg_t retval;120 sysarg_t retval; 123 121 } amsg_t; 124 122 125 123 /** 126 * Structures of this type are used to group information about a call and a127 * message queue link.124 * Structures of this type are used to group information about 125 * a call and about a message queue link. 128 126 */ 129 127 typedef struct { … … 134 132 135 133 typedef struct { 134 sysarg_t in_task_hash; 135 link_t link; 136 int refcnt; 137 void *data; 138 } client_t; 139 140 typedef struct { 136 141 awaiter_t wdata; 137 142 … … 139 144 link_t link; 140 145 146 /** Incoming client task hash. */ 147 sysarg_t in_task_hash; 141 148 /** Incoming phone hash. */ 142 ipcarg_t in_phone_hash; 149 sysarg_t in_phone_hash; 150 151 /** Link to the client tracking structure. */ 152 client_t *client; 143 153 144 154 /** Messages that should be delivered to this fibril. */ … … 158 168 159 169 /** Identifier of the incoming connection handled by the current fibril. */ 160 fibril_local connection_t *FIBRIL_connection; 161 162 static void default_client_connection(ipc_callid_t callid, ipc_call_t *call); 163 static void default_interrupt_received(ipc_callid_t callid, ipc_call_t *call); 170 static fibril_local connection_t *FIBRIL_connection; 171 172 static void *default_client_data_constructor(void) 173 { 174 return NULL; 175 } 176 177 static void default_client_data_destructor(void *data) 178 { 179 } 180 181 static async_client_data_ctor_t async_client_data_create = 182 default_client_data_constructor; 183 static async_client_data_dtor_t async_client_data_destroy = 184 default_client_data_destructor; 185 186 void async_set_client_data_constructor(async_client_data_ctor_t ctor) 187 { 188 async_client_data_create = ctor; 189 } 190 191 void async_set_client_data_destructor(async_client_data_dtor_t dtor) 192 { 193 async_client_data_destroy = dtor; 194 } 195 196 void *async_client_data_get(void) 197 { 198 assert(FIBRIL_connection); 199 return FIBRIL_connection->client->data; 200 } 201 202 /** Default fibril function that gets called to handle new connection. 203 * 204 * This function is defined as a weak symbol - to be redefined in user code. 205 * 206 * @param callid Hash of the incoming call. 207 * @param call Data of the incoming call. 208 * 209 */ 210 static void default_client_connection(ipc_callid_t callid, ipc_call_t *call) 211 { 212 ipc_answer_0(callid, ENOENT); 213 } 164 214 165 215 /** … … 167 217 */ 168 218 static async_client_conn_t client_connection = default_client_connection; 219 220 /** Default fibril function that gets called to handle interrupt notifications. 221 * 222 * This function is defined as a weak symbol - to be redefined in user code. 223 * 224 * @param callid Hash of the incoming call. 225 * @param call Data of the incoming call. 226 * 227 */ 228 static void default_interrupt_received(ipc_callid_t callid, ipc_call_t *call) 229 { 230 } 169 231 170 232 /** … … 174 236 static async_client_conn_t interrupt_received = default_interrupt_received; 175 237 238 static hash_table_t client_hash_table; 176 239 static hash_table_t conn_hash_table; 177 240 static LIST_INITIALIZE(timeout_list); 178 241 179 #define CONN_HASH_TABLE_CHAINS 32 242 #define CLIENT_HASH_TABLE_BUCKETS 32 243 #define CONN_HASH_TABLE_BUCKETS 32 244 245 static hash_index_t client_hash(unsigned long key[]) 246 { 247 assert(key); 248 return (((key[0]) >> 4) % CLIENT_HASH_TABLE_BUCKETS); 249 } 250 251 static int client_compare(unsigned long key[], hash_count_t keys, link_t *item) 252 { 253 client_t *client = hash_table_get_instance(item, client_t, link); 254 return (key[0] == client->in_task_hash); 255 } 256 257 static void client_remove(link_t *item) 258 { 259 } 260 261 /** Operations for the client hash table. */ 262 static hash_table_operations_t client_hash_table_ops = { 263 .hash = client_hash, 264 .compare = client_compare, 265 .remove_callback = client_remove 266 }; 180 267 181 268 /** Compute hash into the connection hash table based on the source phone hash. … … 186 273 * 187 274 */ 188 static hash_index_t conn_hash(unsigned long *key)275 static hash_index_t conn_hash(unsigned long key[]) 189 276 { 190 277 assert(key); 191 return ((( *key) >> 4) % CONN_HASH_TABLE_CHAINS);278 return (((key[0]) >> 4) % CONN_HASH_TABLE_BUCKETS); 192 279 } 193 280 … … 203 290 static int conn_compare(unsigned long key[], hash_count_t keys, link_t *item) 204 291 { 205 connection_t *hs = hash_table_get_instance(item, connection_t, link); 206 return (key[0] == hs->in_phone_hash); 207 } 208 209 /** Connection hash table removal callback function. 210 * 211 * This function is called whenever a connection is removed from the connection 212 * hash table. 213 * 214 * @param item Connection hash table item being removed. 215 * 216 */ 292 connection_t *conn = hash_table_get_instance(item, connection_t, link); 293 return (key[0] == conn->in_phone_hash); 294 } 295 217 296 static void conn_remove(link_t *item) 218 297 { 219 free(hash_table_get_instance(item, connection_t, link)); 220 } 221 298 } 222 299 223 300 /** Operations for the connection hash table. */ … … 240 317 link_t *tmp = timeout_list.next; 241 318 while (tmp != &timeout_list) { 242 awaiter_t *cur ;243 244 cur = list_get_instance(tmp, awaiter_t, to_event.link);319 awaiter_t *cur 320 = list_get_instance(tmp, awaiter_t, to_event.link); 321 245 322 if (tv_gteq(&cur->to_event.expires, &wd->to_event.expires)) 246 323 break; 324 247 325 tmp = tmp->next; 248 326 } … … 261 339 * 262 340 * @return False if the call doesn't match any connection. 263 * 341 * @return True if the call was passed to the respective connection fibril. 264 342 * 265 343 */ … … 288 366 list_append(&msg->link, &conn->msg_queue); 289 367 290 if (IPC_GET_ METHOD(*call) == IPC_M_PHONE_HUNGUP)368 if (IPC_GET_IMETHOD(*call) == IPC_M_PHONE_HUNGUP) 291 369 conn->close_callid = callid; 292 370 … … 398 476 * the first IPC_M_PHONE_HUNGUP call and continues to 399 477 * call async_get_call_timeout(). Repeat 400 * IPC_M_PHONE_HUNGUP until the caller notices. 478 * IPC_M_PHONE_HUNGUP until the caller notices. 401 479 */ 402 480 memset(call, 0, sizeof(ipc_call_t)); 403 IPC_SET_ METHOD(*call, IPC_M_PHONE_HUNGUP);481 IPC_SET_IMETHOD(*call, IPC_M_PHONE_HUNGUP); 404 482 futex_up(&async_futex); 405 483 return conn->close_callid; 406 484 } 407 485 408 486 if (usecs) 409 487 async_insert_timeout(&conn->wdata); … … 443 521 } 444 522 445 /** Default fibril function that gets called to handle new connection.446 *447 * This function is defined as a weak symbol - to be redefined in user code.448 *449 * @param callid Hash of the incoming call.450 * @param call Data of the incoming call.451 *452 */453 static void default_client_connection(ipc_callid_t callid, ipc_call_t *call)454 {455 ipc_answer_0(callid, ENOENT);456 }457 458 /** Default fibril function that gets called to handle interrupt notifications.459 *460 * This function is defined as a weak symbol - to be redefined in user code.461 *462 * @param callid Hash of the incoming call.463 * @param call Data of the incoming call.464 *465 */466 static void default_interrupt_received(ipc_callid_t callid, ipc_call_t *call)467 {468 }469 470 523 /** Wrapper for client connection fibril. 471 524 * … … 481 534 { 482 535 /* 483 * Setup fibril-local connection pointer and call client_connection(). 484 * 536 * Setup fibril-local connection pointer. 485 537 */ 486 538 FIBRIL_connection = (connection_t *) arg; 539 540 futex_down(&async_futex); 541 542 /* 543 * Add our reference for the current connection in the client task 544 * tracking structure. If this is the first reference, create and 545 * hash in a new tracking structure. 546 */ 547 548 unsigned long key = FIBRIL_connection->in_task_hash; 549 link_t *lnk = hash_table_find(&client_hash_table, &key); 550 551 client_t *client; 552 553 if (lnk) { 554 client = hash_table_get_instance(lnk, client_t, link); 555 client->refcnt++; 556 } else { 557 client = malloc(sizeof(client_t)); 558 if (!client) { 559 ipc_answer_0(FIBRIL_connection->callid, ENOMEM); 560 futex_up(&async_futex); 561 return 0; 562 } 563 564 client->in_task_hash = FIBRIL_connection->in_task_hash; 565 566 async_serialize_start(); 567 client->data = async_client_data_create(); 568 async_serialize_end(); 569 570 client->refcnt = 1; 571 hash_table_insert(&client_hash_table, &key, &client->link); 572 } 573 574 futex_up(&async_futex); 575 576 FIBRIL_connection->client = client; 577 578 /* 579 * Call the connection handler function. 580 */ 487 581 FIBRIL_connection->cfibril(FIBRIL_connection->callid, 488 582 &FIBRIL_connection->call); 489 583 490 /* Remove myself from the connection hash table */ 584 /* 585 * Remove the reference for this client task connection. 586 */ 587 bool destroy; 588 491 589 futex_down(&async_futex); 492 unsigned long key = FIBRIL_connection->in_phone_hash; 590 591 if (--client->refcnt == 0) { 592 hash_table_remove(&client_hash_table, &key, 1); 593 destroy = true; 594 } else 595 destroy = false; 596 597 futex_up(&async_futex); 598 599 if (destroy) { 600 if (client->data) 601 async_client_data_destroy(client->data); 602 603 free(client); 604 } 605 606 /* 607 * Remove myself from the connection hash table. 608 */ 609 futex_down(&async_futex); 610 key = FIBRIL_connection->in_phone_hash; 493 611 hash_table_remove(&conn_hash_table, &key, 1); 494 612 futex_up(&async_futex); 495 613 496 /* Answer all remaining messages with EHANGUP */ 614 /* 615 * Answer all remaining messages with EHANGUP. 616 */ 497 617 while (!list_empty(&FIBRIL_connection->msg_queue)) { 498 msg_t *msg ;499 500 msg = list_get_instance(FIBRIL_connection->msg_queue.next,501 msg_t, link);618 msg_t *msg = 619 list_get_instance(FIBRIL_connection->msg_queue.next, msg_t, 620 link); 621 502 622 list_remove(&msg->link); 503 623 ipc_answer_0(msg->callid, EHANGUP); … … 505 625 } 506 626 627 /* 628 * If the connection was hung-up, answer the last call, 629 * i.e. IPC_M_PHONE_HUNGUP. 630 */ 507 631 if (FIBRIL_connection->close_callid) 508 632 ipc_answer_0(FIBRIL_connection->close_callid, EOK); 509 633 634 free(FIBRIL_connection); 510 635 return 0; 511 636 } … … 517 642 * particular fibrils. 518 643 * 644 * @param in_task_hash Identification of the incoming connection. 519 645 * @param in_phone_hash Identification of the incoming connection. 520 646 * @param callid Hash of the opening IPC_M_CONNECT_ME_TO call. … … 529 655 * 530 656 */ 531 fid_t async_new_connection(ipcarg_t in_phone_hash, ipc_callid_t callid, 532 ipc_call_t *call, void (*cfibril)(ipc_callid_t, ipc_call_t *)) 657 fid_t async_new_connection(sysarg_t in_task_hash, sysarg_t in_phone_hash, 658 ipc_callid_t callid, ipc_call_t *call, 659 void (*cfibril)(ipc_callid_t, ipc_call_t *)) 533 660 { 534 661 connection_t *conn = malloc(sizeof(*conn)); … … 536 663 if (callid) 537 664 ipc_answer_0(callid, ENOMEM); 665 538 666 return (uintptr_t) NULL; 539 667 } 540 668 669 conn->in_task_hash = in_task_hash; 541 670 conn->in_phone_hash = in_phone_hash; 542 671 list_initialize(&conn->msg_queue); … … 582 711 static void handle_call(ipc_callid_t callid, ipc_call_t *call) 583 712 { 584 /* Unrouted call - do some default behaviour*/713 /* Unrouted call - take some default action */ 585 714 if ((callid & IPC_CALLID_NOTIFICATION)) { 586 715 process_notification(callid, call); 587 goto out;588 } 589 590 switch (IPC_GET_ METHOD(*call)) {716 return; 717 } 718 719 switch (IPC_GET_IMETHOD(*call)) { 591 720 case IPC_M_CONNECT_ME: 592 721 case IPC_M_CONNECT_ME_TO: 593 /* Open new connection with fibril etc. */594 async_new_connection( IPC_GET_ARG5(*call), callid, call,595 c lient_connection);596 goto out;722 /* Open new connection with fibril, etc. */ 723 async_new_connection(call->in_task_hash, IPC_GET_ARG5(*call), 724 callid, call, client_connection); 725 return; 597 726 } 598 727 599 728 /* Try to route the call through the connection hash table */ 600 729 if (route_call(callid, call)) 601 goto out;730 return; 602 731 603 732 /* Unknown call from unknown phone - hang it up */ 604 733 ipc_answer_0(callid, EHANGUP); 605 return;606 607 out:608 ;609 734 } 610 735 … … 619 744 link_t *cur = timeout_list.next; 620 745 while (cur != &timeout_list) { 621 awaiter_t *waiter ;622 623 waiter = list_get_instance(cur, awaiter_t, to_event.link);746 awaiter_t *waiter = 747 list_get_instance(cur, awaiter_t, to_event.link); 748 624 749 if (tv_gt(&waiter->to_event.expires, &tv)) 625 750 break; 626 751 627 752 cur = cur->next; 628 753 629 754 list_remove(&waiter->to_event.link); 630 755 waiter->to_event.inlist = false; … … 653 778 while (true) { 654 779 if (fibril_switch(FIBRIL_FROM_MANAGER)) { 655 futex_up(&async_futex); 780 futex_up(&async_futex); 656 781 /* 657 782 * async_futex is always held when entering a manager … … 676 801 continue; 677 802 } else 678 timeout = tv_sub(&waiter->to_event.expires, 679 &tv); 803 timeout = tv_sub(&waiter->to_event.expires, &tv); 680 804 } else 681 805 timeout = SYNCH_NO_TIMEOUT; 682 806 683 807 futex_up(&async_futex); 684 808 685 809 atomic_inc(&threads_in_ipc_wait); 686 810 … … 690 814 691 815 atomic_dec(&threads_in_ipc_wait); 692 816 693 817 if (!callid) { 694 818 handle_expired_timeouts(); … … 740 864 /** Initialize the async framework. 741 865 * 742 * @return Zero on success or an error code. 743 */ 744 int __async_init(void) 745 { 746 if (!hash_table_create(&conn_hash_table, CONN_HASH_TABLE_CHAINS, 1, 747 &conn_hash_table_ops)) { 748 printf("%s: Cannot create async hash table\n", "libc"); 749 return ENOMEM; 750 } 751 752 return 0; 866 */ 867 void __async_init(void) 868 { 869 if (!hash_table_create(&client_hash_table, CLIENT_HASH_TABLE_BUCKETS, 1, 870 &client_hash_table_ops)) 871 abort(); 872 873 if (!hash_table_create(&conn_hash_table, CONN_HASH_TABLE_BUCKETS, 1, 874 &conn_hash_table_ops)) 875 abort(); 753 876 } 754 877 … … 763 886 * @param retval Value returned in the answer. 764 887 * @param data Call data of the answer. 888 * 765 889 */ 766 890 static void reply_received(void *arg, int retval, ipc_call_t *data) … … 807 931 * 808 932 */ 809 aid_t async_send_fast(int phoneid, ipcarg_t method, ipcarg_t arg1,810 ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, ipc_call_t *dataptr)811 { 812 amsg_t *msg = malloc(sizeof( *msg));933 aid_t async_send_fast(int phoneid, sysarg_t method, sysarg_t arg1, 934 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, ipc_call_t *dataptr) 935 { 936 amsg_t *msg = malloc(sizeof(amsg_t)); 813 937 814 938 if (!msg) … … 819 943 820 944 msg->wdata.to_event.inlist = false; 821 /* We may sleep in the next method, but it will use its own mechanism */ 945 946 /* 947 * We may sleep in the next method, 948 * but it will use its own means 949 */ 822 950 msg->wdata.active = true; 823 951 … … 846 974 * 847 975 */ 848 aid_t async_send_slow(int phoneid, ipcarg_t method, ipcarg_t arg1,849 ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, ipcarg_t arg5,976 aid_t async_send_slow(int phoneid, sysarg_t method, sysarg_t arg1, 977 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5, 850 978 ipc_call_t *dataptr) 851 979 { 852 amsg_t *msg = malloc(sizeof( *msg));980 amsg_t *msg = malloc(sizeof(amsg_t)); 853 981 854 982 if (!msg) … … 859 987 860 988 msg->wdata.to_event.inlist = false; 861 /* We may sleep in next method, but it will use its own mechanism */ 989 990 /* 991 * We may sleep in the next method, 992 * but it will use its own means 993 */ 862 994 msg->wdata.active = true; 863 995 … … 875 1007 * 876 1008 */ 877 void async_wait_for(aid_t amsgid, ipcarg_t *retval)1009 void async_wait_for(aid_t amsgid, sysarg_t *retval) 878 1010 { 879 1011 amsg_t *msg = (amsg_t *) amsgid; … … 911 1043 * 912 1044 */ 913 int async_wait_timeout(aid_t amsgid, ipcarg_t *retval, suseconds_t timeout)1045 int async_wait_timeout(aid_t amsgid, sysarg_t *retval, suseconds_t timeout) 914 1046 { 915 1047 amsg_t *msg = (amsg_t *) amsgid; … … 958 1090 void async_usleep(suseconds_t timeout) 959 1091 { 960 amsg_t *msg = malloc(sizeof( *msg));1092 amsg_t *msg = malloc(sizeof(amsg_t)); 961 1093 962 1094 if (!msg) … … 1023 1155 * 1024 1156 */ 1025 ipcarg_t async_req_fast(int phoneid, ipcarg_t method, ipcarg_t arg1,1026 ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, ipcarg_t *r1, ipcarg_t *r2,1027 ipcarg_t *r3, ipcarg_t *r4, ipcarg_t *r5)1157 sysarg_t async_req_fast(int phoneid, sysarg_t method, sysarg_t arg1, 1158 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t *r1, sysarg_t *r2, 1159 sysarg_t *r3, sysarg_t *r4, sysarg_t *r5) 1028 1160 { 1029 1161 ipc_call_t result; … … 1031 1163 &result); 1032 1164 1033 ipcarg_t rc;1165 sysarg_t rc; 1034 1166 async_wait_for(eid, &rc); 1035 1167 … … 1072 1204 * 1073 1205 */ 1074 ipcarg_t async_req_slow(int phoneid, ipcarg_t method, ipcarg_t arg1,1075 ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, ipcarg_t arg5, ipcarg_t *r1,1076 ipcarg_t *r2, ipcarg_t *r3, ipcarg_t *r4, ipcarg_t *r5)1206 sysarg_t async_req_slow(int phoneid, sysarg_t method, sysarg_t arg1, 1207 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5, sysarg_t *r1, 1208 sysarg_t *r2, sysarg_t *r3, sysarg_t *r4, sysarg_t *r5) 1077 1209 { 1078 1210 ipc_call_t result; … … 1080 1212 &result); 1081 1213 1082 ipcarg_t rc;1214 sysarg_t rc; 1083 1215 async_wait_for(eid, &rc); 1084 1216 … … 1101 1233 } 1102 1234 1235 void async_msg_0(int phone, sysarg_t imethod) 1236 { 1237 ipc_call_async_0(phone, imethod, NULL, NULL, true); 1238 } 1239 1240 void async_msg_1(int phone, sysarg_t imethod, sysarg_t arg1) 1241 { 1242 ipc_call_async_1(phone, imethod, arg1, NULL, NULL, true); 1243 } 1244 1245 void async_msg_2(int phone, sysarg_t imethod, sysarg_t arg1, sysarg_t arg2) 1246 { 1247 ipc_call_async_2(phone, imethod, arg1, arg2, NULL, NULL, true); 1248 } 1249 1250 void async_msg_3(int phone, sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, 1251 sysarg_t arg3) 1252 { 1253 ipc_call_async_3(phone, imethod, arg1, arg2, arg3, NULL, NULL, true); 1254 } 1255 1256 void async_msg_4(int phone, sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, 1257 sysarg_t arg3, sysarg_t arg4) 1258 { 1259 ipc_call_async_4(phone, imethod, arg1, arg2, arg3, arg4, NULL, NULL, 1260 true); 1261 } 1262 1263 void async_msg_5(int phone, sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, 1264 sysarg_t arg3, sysarg_t arg4, sysarg_t arg5) 1265 { 1266 ipc_call_async_5(phone, imethod, arg1, arg2, arg3, arg4, arg5, NULL, 1267 NULL, true); 1268 } 1269 1270 sysarg_t async_answer_0(ipc_callid_t callid, sysarg_t retval) 1271 { 1272 return ipc_answer_0(callid, retval); 1273 } 1274 1275 sysarg_t async_answer_1(ipc_callid_t callid, sysarg_t retval, sysarg_t arg1) 1276 { 1277 return ipc_answer_1(callid, retval, arg1); 1278 } 1279 1280 sysarg_t async_answer_2(ipc_callid_t callid, sysarg_t retval, sysarg_t arg1, 1281 sysarg_t arg2) 1282 { 1283 return ipc_answer_2(callid, retval, arg1, arg2); 1284 } 1285 1286 sysarg_t async_answer_3(ipc_callid_t callid, sysarg_t retval, sysarg_t arg1, 1287 sysarg_t arg2, sysarg_t arg3) 1288 { 1289 return ipc_answer_3(callid, retval, arg1, arg2, arg3); 1290 } 1291 1292 sysarg_t async_answer_4(ipc_callid_t callid, sysarg_t retval, sysarg_t arg1, 1293 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4) 1294 { 1295 return ipc_answer_4(callid, retval, arg1, arg2, arg3, arg4); 1296 } 1297 1298 sysarg_t async_answer_5(ipc_callid_t callid, sysarg_t retval, sysarg_t arg1, 1299 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5) 1300 { 1301 return ipc_answer_5(callid, retval, arg1, arg2, arg3, arg4, arg5); 1302 } 1303 1304 int async_forward_fast(ipc_callid_t callid, int phoneid, sysarg_t imethod, 1305 sysarg_t arg1, sysarg_t arg2, unsigned int mode) 1306 { 1307 return ipc_forward_fast(callid, phoneid, imethod, arg1, arg2, mode); 1308 } 1309 1310 int async_forward_slow(ipc_callid_t callid, int phoneid, sysarg_t imethod, 1311 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5, 1312 unsigned int mode) 1313 { 1314 return ipc_forward_slow(callid, phoneid, imethod, arg1, arg2, arg3, arg4, 1315 arg5, mode); 1316 } 1317 1318 /** Wrapper for making IPC_M_CONNECT_TO_ME calls using the async framework. 1319 * 1320 * Ask through phone for a new connection to some service. 1321 * 1322 * @param phone Phone handle used for contacting the other side. 1323 * @param arg1 User defined argument. 1324 * @param arg2 User defined argument. 1325 * @param arg3 User defined argument. 1326 * @param client_receiver Connection handing routine. 1327 * 1328 * @return New phone handle on success or a negative error code. 1329 * 1330 */ 1331 int async_connect_to_me(int phone, sysarg_t arg1, sysarg_t arg2, 1332 sysarg_t arg3, async_client_conn_t client_receiver) 1333 { 1334 sysarg_t task_hash; 1335 sysarg_t phone_hash; 1336 int rc = async_req_3_5(phone, IPC_M_CONNECT_TO_ME, arg1, arg2, arg3, 1337 NULL, NULL, NULL, &task_hash, &phone_hash); 1338 if (rc != EOK) 1339 return rc; 1340 1341 if (client_receiver != NULL) 1342 async_new_connection(task_hash, phone_hash, 0, NULL, 1343 client_receiver); 1344 1345 return EOK; 1346 } 1347 1103 1348 /** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework. 1104 * 1349 * 1105 1350 * Ask through phone for a new connection to some service. 1106 1351 * 1107 * @param phoneid Phone handle used for contacting the other side. 1108 * @param arg1 User defined argument. 1109 * @param arg2 User defined argument. 1110 * @param arg3 User defined argument. 1111 * 1112 * @return New phone handle on success or a negative error code. 1113 */ 1114 int 1115 async_connect_me_to(int phoneid, ipcarg_t arg1, ipcarg_t arg2, ipcarg_t arg3) 1116 { 1117 int rc; 1118 ipcarg_t newphid; 1119 1120 rc = async_req_3_5(phoneid, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3, NULL, 1121 NULL, NULL, NULL, &newphid); 1122 1123 if (rc != EOK) 1352 * @param phone Phone handle used for contacting the other side. 1353 * @param arg1 User defined argument. 1354 * @param arg2 User defined argument. 1355 * @param arg3 User defined argument. 1356 * 1357 * @return New phone handle on success or a negative error code. 1358 * 1359 */ 1360 int async_connect_me_to(int phone, sysarg_t arg1, sysarg_t arg2, 1361 sysarg_t arg3) 1362 { 1363 sysarg_t newphid; 1364 int rc = async_req_3_5(phone, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3, 1365 NULL, NULL, NULL, NULL, &newphid); 1366 1367 if (rc != EOK) 1124 1368 return rc; 1125 1369 1126 1370 return newphid; 1127 1371 } 1128 1372 1129 1373 /** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework. 1130 * 1374 * 1131 1375 * Ask through phone for a new connection to some service and block until 1132 1376 * success. 1133 1377 * 1134 * @param phoneid Phone handle used for contacting the other side. 1135 * @param arg1 User defined argument. 1136 * @param arg2 User defined argument. 1137 * @param arg3 User defined argument. 1138 * 1139 * @return New phone handle on success or a negative error code. 1140 */ 1141 int 1142 async_connect_me_to_blocking(int phoneid, ipcarg_t arg1, ipcarg_t arg2, 1143 ipcarg_t arg3) 1144 { 1145 int rc; 1146 ipcarg_t newphid; 1147 1148 rc = async_req_4_5(phoneid, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3, 1378 * @param phoneid Phone handle used for contacting the other side. 1379 * @param arg1 User defined argument. 1380 * @param arg2 User defined argument. 1381 * @param arg3 User defined argument. 1382 * 1383 * @return New phone handle on success or a negative error code. 1384 * 1385 */ 1386 int async_connect_me_to_blocking(int phoneid, sysarg_t arg1, sysarg_t arg2, 1387 sysarg_t arg3) 1388 { 1389 sysarg_t newphid; 1390 int rc = async_req_4_5(phoneid, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3, 1149 1391 IPC_FLAG_BLOCKING, NULL, NULL, NULL, NULL, &newphid); 1150 1392 1151 if (rc != EOK) 1393 if (rc != EOK) 1152 1394 return rc; 1153 1395 1154 1396 return newphid; 1155 1397 } 1156 1398 1157 /** Wrapper for making IPC_M_SHARE_IN calls using the async framework. 1158 * 1159 * @param phoneid Phone that will be used to contact the receiving side. 1160 * @param dst Destination address space area base. 1161 * @param size Size of the destination address space area. 1162 * @param arg User defined argument. 1163 * @param flags Storage where the received flags will be stored. Can be 1164 * NULL. 1165 * 1166 * @return Zero on success or a negative error code from errno.h. 1167 */ 1168 int async_share_in_start(int phoneid, void *dst, size_t size, ipcarg_t arg, 1169 int *flags) 1170 { 1171 int res; 1399 /** Connect to a task specified by id. 1400 * 1401 */ 1402 int async_connect_kbox(task_id_t id) 1403 { 1404 return ipc_connect_kbox(id); 1405 } 1406 1407 /** Wrapper for ipc_hangup. 1408 * 1409 * @param phone Phone handle to hung up. 1410 * 1411 * @return Zero on success or a negative error code. 1412 * 1413 */ 1414 int async_hangup(int phone) 1415 { 1416 return ipc_hangup(phone); 1417 } 1418 1419 /** Interrupt one thread of this task from waiting for IPC. */ 1420 void async_poke(void) 1421 { 1422 ipc_poke(); 1423 } 1424 1425 /** Wrapper for IPC_M_SHARE_IN calls using the async framework. 1426 * 1427 * @param phoneid Phone that will be used to contact the receiving side. 1428 * @param dst Destination address space area base. 1429 * @param size Size of the destination address space area. 1430 * @param arg User defined argument. 1431 * @param flags Storage for the received flags. Can be NULL. 1432 * 1433 * @return Zero on success or a negative error code from errno.h. 1434 * 1435 */ 1436 int async_share_in_start(int phoneid, void *dst, size_t size, sysarg_t arg, 1437 unsigned int *flags) 1438 { 1172 1439 sysarg_t tmp_flags; 1173 res = async_req_3_2(phoneid, IPC_M_SHARE_IN, (ipcarg_t) dst, 1174 (ipcarg_t) size, arg, NULL, &tmp_flags); 1440 int res = async_req_3_2(phoneid, IPC_M_SHARE_IN, (sysarg_t) dst, 1441 (sysarg_t) size, arg, NULL, &tmp_flags); 1442 1175 1443 if (flags) 1176 *flags = tmp_flags; 1444 *flags = (unsigned int) tmp_flags; 1445 1177 1446 return res; 1178 1447 } … … 1180 1449 /** Wrapper for receiving the IPC_M_SHARE_IN calls using the async framework. 1181 1450 * 1182 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_IN calls 1183 * so that the user doesn't have to remember the meaning of each IPC argument. 1451 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_IN 1452 * calls so that the user doesn't have to remember the meaning of each IPC 1453 * argument. 1184 1454 * 1185 1455 * So far, this wrapper is to be used from within a connection fibril. 1186 1456 * 1187 * @param callid Storage where the hash of the IPC_M_SHARE_IN call will 1188 * be stored. 1189 * @param size Destination address space area size. 1190 * 1191 * @return Non-zero on success, zero on failure. 1192 */ 1193 int async_share_in_receive(ipc_callid_t *callid, size_t *size) 1194 { 1195 ipc_call_t data; 1196 1457 * @param callid Storage for the hash of the IPC_M_SHARE_IN call. 1458 * @param size Destination address space area size. 1459 * 1460 * @return True on success, false on failure. 1461 * 1462 */ 1463 bool async_share_in_receive(ipc_callid_t *callid, size_t *size) 1464 { 1197 1465 assert(callid); 1198 1466 assert(size); 1199 1467 1468 ipc_call_t data; 1200 1469 *callid = async_get_call(&data); 1201 if (IPC_GET_METHOD(data) != IPC_M_SHARE_IN) 1202 return 0; 1470 1471 if (IPC_GET_IMETHOD(data) != IPC_M_SHARE_IN) 1472 return false; 1473 1203 1474 *size = (size_t) IPC_GET_ARG2(data); 1204 return 1;1475 return true; 1205 1476 } 1206 1477 1207 1478 /** Wrapper for answering the IPC_M_SHARE_IN calls using the async framework. 1208 1479 * 1209 * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ calls 1210 * so that the user doesn't have to remember the meaning of each IPC argument. 1211 * 1212 * @param callid Hash of the IPC_M_DATA_READ call to answer. 1213 * @param src Source address space base. 1214 * @param flags Flags to be used for sharing. Bits can be only cleared. 1215 * 1216 * @return Zero on success or a value from @ref errno.h on failure. 1217 */ 1218 int async_share_in_finalize(ipc_callid_t callid, void *src, int flags) 1480 * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ 1481 * calls so that the user doesn't have to remember the meaning of each IPC 1482 * argument. 1483 * 1484 * @param callid Hash of the IPC_M_DATA_READ call to answer. 1485 * @param src Source address space base. 1486 * @param flags Flags to be used for sharing. Bits can be only cleared. 1487 * 1488 * @return Zero on success or a value from @ref errno.h on failure. 1489 * 1490 */ 1491 int async_share_in_finalize(ipc_callid_t callid, void *src, unsigned int flags) 1219 1492 { 1220 1493 return ipc_share_in_finalize(callid, src, flags); 1221 1494 } 1222 1495 1223 /** Wrapper for making IPC_M_SHARE_OUT calls using the async framework. 1224 * 1225 * @param phoneid Phone that will be used to contact the receiving side. 1226 * @param src Source address space area base address. 1227 * @param flags Flags to be used for sharing. Bits can be only cleared. 1228 * 1229 * @return Zero on success or a negative error code from errno.h. 1230 */ 1231 int async_share_out_start(int phoneid, void *src, int flags) 1232 { 1233 return async_req_3_0(phoneid, IPC_M_SHARE_OUT, (ipcarg_t) src, 0, 1234 (ipcarg_t) flags); 1496 /** Wrapper for IPC_M_SHARE_OUT calls using the async framework. 1497 * 1498 * @param phoneid Phone that will be used to contact the receiving side. 1499 * @param src Source address space area base address. 1500 * @param flags Flags to be used for sharing. Bits can be only cleared. 1501 * 1502 * @return Zero on success or a negative error code from errno.h. 1503 * 1504 */ 1505 int async_share_out_start(int phoneid, void *src, unsigned int flags) 1506 { 1507 return async_req_3_0(phoneid, IPC_M_SHARE_OUT, (sysarg_t) src, 0, 1508 (sysarg_t) flags); 1235 1509 } 1236 1510 1237 1511 /** Wrapper for receiving the IPC_M_SHARE_OUT calls using the async framework. 1238 1512 * 1239 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_OUT calls 1240 * so that the user doesn't have to remember the meaning of each IPC argument. 1513 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_OUT 1514 * calls so that the user doesn't have to remember the meaning of each IPC 1515 * argument. 1241 1516 * 1242 1517 * So far, this wrapper is to be used from within a connection fibril. 1243 1518 * 1244 * @param callid Storage where the hash of the IPC_M_SHARE_OUT call will 1245 * be stored. 1246 * @param size Storage where the source address space area size will be 1247 * stored. 1248 * @param flags Storage where the sharing flags will be stored. 1249 * 1250 * @return Non-zero on success, zero on failure. 1251 */ 1252 int async_share_out_receive(ipc_callid_t *callid, size_t *size, int *flags) 1253 { 1254 ipc_call_t data; 1255 1519 * @param callid Storage for the hash of the IPC_M_SHARE_OUT call. 1520 * @param size Storage for the source address space area size. 1521 * @param flags Storage for the sharing flags. 1522 * 1523 * @return True on success, false on failure. 1524 * 1525 */ 1526 bool async_share_out_receive(ipc_callid_t *callid, size_t *size, unsigned int *flags) 1527 { 1256 1528 assert(callid); 1257 1529 assert(size); 1258 1530 assert(flags); 1259 1531 1532 ipc_call_t data; 1260 1533 *callid = async_get_call(&data); 1261 if (IPC_GET_METHOD(data) != IPC_M_SHARE_OUT) 1262 return 0; 1534 1535 if (IPC_GET_IMETHOD(data) != IPC_M_SHARE_OUT) 1536 return false; 1537 1263 1538 *size = (size_t) IPC_GET_ARG2(data); 1264 *flags = ( int) IPC_GET_ARG3(data);1265 return 1;1539 *flags = (unsigned int) IPC_GET_ARG3(data); 1540 return true; 1266 1541 } 1267 1542 1268 1543 /** Wrapper for answering the IPC_M_SHARE_OUT calls using the async framework. 1269 1544 * 1270 * This wrapper only makes it more comfortable to answer IPC_M_SHARE_OUT calls 1271 * so that the user doesn't have to remember the meaning of each IPC argument. 1272 * 1273 * @param callid Hash of the IPC_M_DATA_WRITE call to answer. 1274 * @param dst Destination address space area base address. 1275 * 1276 * @return Zero on success or a value from @ref errno.h on failure. 1545 * This wrapper only makes it more comfortable to answer IPC_M_SHARE_OUT 1546 * calls so that the user doesn't have to remember the meaning of each IPC 1547 * argument. 1548 * 1549 * @param callid Hash of the IPC_M_DATA_WRITE call to answer. 1550 * @param dst Destination address space area base address. 1551 * 1552 * @return Zero on success or a value from @ref errno.h on failure. 1553 * 1277 1554 */ 1278 1555 int async_share_out_finalize(ipc_callid_t callid, void *dst) … … 1281 1558 } 1282 1559 1283 1284 /** Wrapper for making IPC_M_DATA_READ calls using the async framework. 1285 * 1286 * @param phoneid Phone that will be used to contact the receiving side.1287 * @param dst Address of the beginningof the destination buffer.1288 * @param size Size of the destination buffer.1289 * 1290 * @return Zero on success or a negative error code from errno.h.1560 /** Wrapper for IPC_M_DATA_READ calls using the async framework. 1561 * 1562 * @param phoneid Phone that will be used to contact the receiving side. 1563 * @param dst Address of the beginning of the destination buffer. 1564 * @param size Size of the destination buffer. 1565 * 1566 * @return Zero on success or a negative error code from errno.h. 1567 * 1291 1568 */ 1292 1569 int async_data_read_start(int phoneid, void *dst, size_t size) 1293 1570 { 1294 return async_req_2_0(phoneid, IPC_M_DATA_READ, ( ipcarg_t) dst,1295 ( ipcarg_t) size);1571 return async_req_2_0(phoneid, IPC_M_DATA_READ, (sysarg_t) dst, 1572 (sysarg_t) size); 1296 1573 } 1297 1574 1298 1575 /** Wrapper for receiving the IPC_M_DATA_READ calls using the async framework. 1299 1576 * 1300 * This wrapper only makes it more comfortable to receive IPC_M_DATA_READ calls 1301 * so that the user doesn't have to remember the meaning of each IPC argument. 1577 * This wrapper only makes it more comfortable to receive IPC_M_DATA_READ 1578 * calls so that the user doesn't have to remember the meaning of each IPC 1579 * argument. 1302 1580 * 1303 1581 * So far, this wrapper is to be used from within a connection fibril. 1304 1582 * 1305 * @param callid Storage where the hash of the IPC_M_DATA_READ call will 1306 * be stored. 1307 * @param size Storage where the maximum size will be stored. Can be 1308 * NULL. 1309 * 1310 * @return Non-zero on success, zero on failure. 1311 */ 1312 int async_data_read_receive(ipc_callid_t *callid, size_t *size) 1313 { 1583 * @param callid Storage for the hash of the IPC_M_DATA_READ. 1584 * @param size Storage for the maximum size. Can be NULL. 1585 * 1586 * @return True on success, false on failure. 1587 * 1588 */ 1589 bool async_data_read_receive(ipc_callid_t *callid, size_t *size) 1590 { 1591 assert(callid); 1592 1314 1593 ipc_call_t data; 1315 1316 assert(callid);1317 1318 1594 *callid = async_get_call(&data); 1319 if (IPC_GET_METHOD(data) != IPC_M_DATA_READ) 1320 return 0; 1595 1596 if (IPC_GET_IMETHOD(data) != IPC_M_DATA_READ) 1597 return false; 1598 1321 1599 if (size) 1322 1600 *size = (size_t) IPC_GET_ARG2(data); 1323 return 1; 1601 1602 return true; 1324 1603 } 1325 1604 1326 1605 /** Wrapper for answering the IPC_M_DATA_READ calls using the async framework. 1327 1606 * 1328 * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ calls 1329 * so that the user doesn't have to remember the meaning of each IPC argument. 1330 * 1331 * @param callid Hash of the IPC_M_DATA_READ call to answer. 1332 * @param src Source address for the IPC_M_DATA_READ call. 1333 * @param size Size for the IPC_M_DATA_READ call. Can be smaller than 1334 * the maximum size announced by the sender. 1335 * 1336 * @return Zero on success or a value from @ref errno.h on failure. 1607 * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ 1608 * calls so that the user doesn't have to remember the meaning of each IPC 1609 * argument. 1610 * 1611 * @param callid Hash of the IPC_M_DATA_READ call to answer. 1612 * @param src Source address for the IPC_M_DATA_READ call. 1613 * @param size Size for the IPC_M_DATA_READ call. Can be smaller than 1614 * the maximum size announced by the sender. 1615 * 1616 * @return Zero on success or a value from @ref errno.h on failure. 1617 * 1337 1618 */ 1338 1619 int async_data_read_finalize(ipc_callid_t callid, const void *src, size_t size) … … 1343 1624 /** Wrapper for forwarding any read request 1344 1625 * 1345 * 1346 */ 1347 int async_data_read_forward_fast(int phoneid, ipcarg_t method, ipcarg_t arg1, 1348 ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, ipc_call_t *dataptr) 1626 */ 1627 int async_data_read_forward_fast(int phoneid, sysarg_t method, sysarg_t arg1, 1628 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, ipc_call_t *dataptr) 1349 1629 { 1350 1630 ipc_callid_t callid; … … 1369 1649 } 1370 1650 1371 ipcarg_t rc;1651 sysarg_t rc; 1372 1652 async_wait_for(msg, &rc); 1373 1653 … … 1375 1655 } 1376 1656 1377 /** Wrapper for makingIPC_M_DATA_WRITE calls using the async framework.1657 /** Wrapper for IPC_M_DATA_WRITE calls using the async framework. 1378 1658 * 1379 1659 * @param phoneid Phone that will be used to contact the receiving side. … … 1386 1666 int async_data_write_start(int phoneid, const void *src, size_t size) 1387 1667 { 1388 return async_req_2_0(phoneid, IPC_M_DATA_WRITE, ( ipcarg_t) src,1389 ( ipcarg_t) size);1668 return async_req_2_0(phoneid, IPC_M_DATA_WRITE, (sysarg_t) src, 1669 (sysarg_t) size); 1390 1670 } 1391 1671 1392 1672 /** Wrapper for receiving the IPC_M_DATA_WRITE calls using the async framework. 1393 1673 * 1394 * This wrapper only makes it more comfortable to receive IPC_M_DATA_WRITE calls 1395 * so that the user doesn't have to remember the meaning of each IPC argument. 1674 * This wrapper only makes it more comfortable to receive IPC_M_DATA_WRITE 1675 * calls so that the user doesn't have to remember the meaning of each IPC 1676 * argument. 1396 1677 * 1397 1678 * So far, this wrapper is to be used from within a connection fibril. 1398 1679 * 1399 * @param callid Storage where the hash of the IPC_M_DATA_WRITE call will1400 * be stored.1401 * @param size Storage where the suggested size will be stored. May be1402 * NULL1403 * 1404 * @return Non-zero on success, zero on failure.1405 * 1406 */ 1407 int async_data_write_receive(ipc_callid_t *callid, size_t *size) 1408 { 1680 * @param callid Storage for the hash of the IPC_M_DATA_WRITE. 1681 * @param size Storage for the suggested size. May be NULL. 1682 * 1683 * @return True on success, false on failure. 1684 * 1685 */ 1686 bool async_data_write_receive(ipc_callid_t *callid, size_t *size) 1687 { 1688 assert(callid); 1689 1409 1690 ipc_call_t data; 1410 1411 assert(callid);1412 1413 1691 *callid = async_get_call(&data); 1414 if (IPC_GET_METHOD(data) != IPC_M_DATA_WRITE) 1415 return 0; 1692 1693 if (IPC_GET_IMETHOD(data) != IPC_M_DATA_WRITE) 1694 return false; 1416 1695 1417 1696 if (size) 1418 1697 *size = (size_t) IPC_GET_ARG2(data); 1419 1698 1420 return 1;1699 return true; 1421 1700 } 1422 1701 1423 1702 /** Wrapper for answering the IPC_M_DATA_WRITE calls using the async framework. 1424 1703 * 1425 * This wrapper only makes it more comfortable to answer IPC_M_DATA_WRITE calls 1426 * so that the user doesn't have to remember the meaning of each IPC argument. 1704 * This wrapper only makes it more comfortable to answer IPC_M_DATA_WRITE 1705 * calls so that the user doesn't have to remember the meaning of each IPC 1706 * argument. 1427 1707 * 1428 1708 * @param callid Hash of the IPC_M_DATA_WRITE call to answer. … … 1520 1800 * 1521 1801 */ 1522 void async_data_write_void( const int retval)1802 void async_data_write_void(sysarg_t retval) 1523 1803 { 1524 1804 ipc_callid_t callid; … … 1529 1809 /** Wrapper for forwarding any data that is about to be received 1530 1810 * 1531 * 1532 */ 1533 int async_data_write_forward_fast(int phoneid, ipcarg_t method, ipcarg_t arg1, 1534 ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, ipc_call_t *dataptr) 1811 */ 1812 int async_data_write_forward_fast(int phoneid, sysarg_t method, sysarg_t arg1, 1813 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, ipc_call_t *dataptr) 1535 1814 { 1536 1815 ipc_callid_t callid; … … 1555 1834 } 1556 1835 1557 ipcarg_t rc;1836 sysarg_t rc; 1558 1837 async_wait_for(msg, &rc); 1559 1838
Note:
See TracChangeset
for help on using the changeset viewer.