Changes in uspace/lib/c/generic/ipc.c [63f8966:10477601] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/lib/c/generic/ipc.c
r63f8966 r10477601 38 38 */ 39 39 /** @file 40 */ 40 */ 41 41 42 42 #include <ipc/ipc.h> … … 45 45 #include <errno.h> 46 46 #include <adt/list.h> 47 #include <stdio.h>48 #include <unistd.h>49 47 #include <futex.h> 50 #include <kernel/synch/synch.h>51 #include <async.h>52 48 #include <fibril.h> 53 #include <assert.h>54 49 55 50 /** 56 * Structures of this type are used for keeping track of sent asynchronous calls57 * and queing unsent calls.51 * Structures of this type are used for keeping track 52 * of sent asynchronous calls and queing unsent calls. 58 53 */ 59 54 typedef struct { 60 55 link_t list; 61 56 62 57 ipc_async_callback_t callback; 63 58 void *private; 59 64 60 union { 65 61 ipc_callid_t callid; … … 69 65 } msg; 70 66 } u; 71 fid_t fid; /**< Fibril waiting for sending this call. */ 67 68 /** Fibril waiting for sending this call. */ 69 fid_t fid; 72 70 } async_call_t; 73 71 … … 76 74 /** List of asynchronous calls that were not accepted by kernel. 77 75 * 78 * It is protected by async_futex, because if the call cannot be sent into the 79 * kernel, the async framework is used automatically. 76 * Protected by async_futex, because if the call is not accepted 77 * by the kernel, the async framework is used automatically. 78 * 80 79 */ 81 80 LIST_INITIALIZE(queued_calls); … … 83 82 static atomic_t ipc_futex = FUTEX_INITIALIZER; 84 83 85 /** Make a fast synchronous call.86 * 87 * Only three payload arguments can be passed using this function. However, this88 * function is faster than the generic ipc_call_sync_slow() because the payload89 * is passed directly in registers.90 * 91 * @param phoneid 92 * @param method 93 * @param arg1 94 * @param arg2 95 * @param arg3 96 * @param result1 97 * @param result2 98 * @param result3 99 * @param result4 100 * @param result5 101 * 102 * @return Negative values represent errors returned by IPC.103 * Otherwise the RETVAL of the answer is returned.104 * /105 int 106 i pc_call_sync_fast(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2,107 ipcarg_t arg3, ipcarg_t *result1, ipcarg_t *result2, ipcarg_t *result3,108 ipcarg_t *result4, ipcarg_t *result5)84 /** Fast synchronous call. 85 * 86 * Only three payload arguments can be passed using this function. However, 87 * this function is faster than the generic ipc_call_sync_slow() because 88 * the payload is passed directly in registers. 89 * 90 * @param phoneid Phone handle for the call. 91 * @param method Requested method. 92 * @param arg1 Service-defined payload argument. 93 * @param arg2 Service-defined payload argument. 94 * @param arg3 Service-defined payload argument. 95 * @param result1 If non-NULL, the return ARG1 will be stored there. 96 * @param result2 If non-NULL, the return ARG2 will be stored there. 97 * @param result3 If non-NULL, the return ARG3 will be stored there. 98 * @param result4 If non-NULL, the return ARG4 will be stored there. 99 * @param result5 If non-NULL, the return ARG5 will be stored there. 100 * 101 * @return Negative values representing IPC errors. 102 * @return Otherwise the RETVAL of the answer. 103 * 104 */ 105 int ipc_call_sync_fast(int phoneid, sysarg_t method, sysarg_t arg1, 106 sysarg_t arg2, sysarg_t arg3, sysarg_t *result1, sysarg_t *result2, 107 sysarg_t *result3, sysarg_t *result4, sysarg_t *result5) 109 108 { 110 109 ipc_call_t resdata; 111 int callres; 112 113 callres = __SYSCALL6(SYS_IPC_CALL_SYNC_FAST, phoneid, method, arg1, 110 int callres = __SYSCALL6(SYS_IPC_CALL_SYNC_FAST, phoneid, method, arg1, 114 111 arg2, arg3, (sysarg_t) &resdata); 115 112 if (callres) 116 113 return callres; 114 117 115 if (result1) 118 116 *result1 = IPC_GET_ARG1(resdata); … … 125 123 if (result5) 126 124 *result5 = IPC_GET_ARG5(resdata); 127 125 128 126 return IPC_GET_RETVAL(resdata); 129 127 } 130 128 131 /** Make a synchronous call transmitting 5 arguments of payload. 132 * 133 * @param phoneid Phone handle for the call. 134 * @param method Requested method. 135 * @param arg1 Service-defined payload argument. 136 * @param arg2 Service-defined payload argument. 137 * @param arg3 Service-defined payload argument. 138 * @param arg4 Service-defined payload argument. 139 * @param arg5 Service-defined payload argument. 140 * @param result1 If non-NULL, storage for the first return argument. 141 * @param result2 If non-NULL, storage for the second return argument. 142 * @param result3 If non-NULL, storage for the third return argument. 143 * @param result4 If non-NULL, storage for the fourth return argument. 144 * @param result5 If non-NULL, storage for the fifth return argument. 145 * 146 * @return Negative value means IPC error. 147 * Otherwise the RETVAL of the answer. 148 */ 149 int 150 ipc_call_sync_slow(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2, 151 ipcarg_t arg3, ipcarg_t arg4, ipcarg_t arg5, ipcarg_t *result1, 152 ipcarg_t *result2, ipcarg_t *result3, ipcarg_t *result4, ipcarg_t *result5) 129 /** Synchronous call transmitting 5 arguments of payload. 130 * 131 * @param phoneid Phone handle for the call. 132 * @param imethod Requested interface and method. 133 * @param arg1 Service-defined payload argument. 134 * @param arg2 Service-defined payload argument. 135 * @param arg3 Service-defined payload argument. 136 * @param arg4 Service-defined payload argument. 137 * @param arg5 Service-defined payload argument. 138 * @param result1 If non-NULL, storage for the first return argument. 139 * @param result2 If non-NULL, storage for the second return argument. 140 * @param result3 If non-NULL, storage for the third return argument. 141 * @param result4 If non-NULL, storage for the fourth return argument. 142 * @param result5 If non-NULL, storage for the fifth return argument. 143 * 144 * @return Negative values representing IPC errors. 145 * @return Otherwise the RETVAL of the answer. 146 * 147 */ 148 int ipc_call_sync_slow(int phoneid, sysarg_t imethod, sysarg_t arg1, 149 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5, 150 sysarg_t *result1, sysarg_t *result2, sysarg_t *result3, sysarg_t *result4, 151 sysarg_t *result5) 153 152 { 154 153 ipc_call_t data; 155 int callres; 156 157 IPC_SET_METHOD(data, method); 154 155 IPC_SET_IMETHOD(data, imethod); 158 156 IPC_SET_ARG1(data, arg1); 159 157 IPC_SET_ARG2(data, arg2); … … 161 159 IPC_SET_ARG4(data, arg4); 162 160 IPC_SET_ARG5(data, arg5); 163 164 callres = __SYSCALL3(SYS_IPC_CALL_SYNC_SLOW, phoneid, (sysarg_t) &data,165 (sysarg_t) &data );161 162 int callres = __SYSCALL3(SYS_IPC_CALL_SYNC_SLOW, phoneid, 163 (sysarg_t) &data, (sysarg_t) &data); 166 164 if (callres) 167 165 return callres; 168 166 169 167 if (result1) 170 168 *result1 = IPC_GET_ARG1(data); … … 177 175 if (result5) 178 176 *result5 = IPC_GET_ARG5(data); 179 177 180 178 return IPC_GET_RETVAL(data); 181 179 } 182 180 183 /** Syscall to send asynchronous message. 184 * 185 * @param phoneid Phone handle for the call. 186 * @param data Call data with the request. 187 * 188 * @return Hash of the call or an error code. 189 */ 190 static ipc_callid_t _ipc_call_async(int phoneid, ipc_call_t *data) 181 /** Send asynchronous message via syscall. 182 * 183 * @param phoneid Phone handle for the call. 184 * @param data Call data with the request. 185 * 186 * @return Hash of the call or an error code. 187 * 188 */ 189 static ipc_callid_t ipc_call_async_internal(int phoneid, ipc_call_t *data) 191 190 { 192 191 return __SYSCALL2(SYS_IPC_CALL_ASYNC_SLOW, phoneid, (sysarg_t) data); 193 192 } 194 193 195 /** Prolog to ipc_call_async_*() functions. 196 * 197 * @param private Argument for the answer/error callback. 198 * @param callback Answer/error callback. 199 * 200 * @return New, partially initialized async_call structure or NULL. 194 /** Prolog for ipc_call_async_*() functions. 195 * 196 * @param private Argument for the answer/error callback. 197 * @param callback Answer/error callback. 198 * 199 * @return New, partially initialized async_call structure or NULL. 200 * 201 201 */ 202 202 static inline async_call_t *ipc_prepare_async(void *private, 203 203 ipc_async_callback_t callback) 204 204 { 205 async_call_t *call; 206 207 call = malloc(sizeof(*call)); 205 async_call_t *call = 206 (async_call_t *) malloc(sizeof(async_call_t)); 208 207 if (!call) { 209 208 if (callback) 210 209 callback(private, ENOMEM, NULL); 210 211 211 return NULL; 212 212 } 213 213 214 call->callback = callback; 214 215 call->private = private; 215 216 216 217 return call; 217 218 } 218 219 219 /** Epilogue of ipc_call_async_*() functions. 220 * 221 * @param callid Value returned by the SYS_IPC_CALL_ASYNC_* syscall. 222 * @param phoneid Phone handle through which the call was made. 223 * @param call async_call structure returned by ipc_prepare_async(). 224 * @param can_preempt If non-zero, the current fibril can be preempted in this 225 * call. 220 /** Epilog for ipc_call_async_*() functions. 221 * 222 * @param callid Value returned by the SYS_IPC_CALL_ASYNC_* syscall. 223 * @param phoneid Phone handle through which the call was made. 224 * @param call Structure returned by ipc_prepare_async(). 225 * @param can_preempt If true, the current fibril can be preempted 226 * in this call. 227 * 226 228 */ 227 229 static inline void ipc_finish_async(ipc_callid_t callid, int phoneid, 228 async_call_t *call, int can_preempt) 229 { 230 if (!call) { /* Nothing to do regardless if failed or not */ 230 async_call_t *call, bool can_preempt) 231 { 232 if (!call) { 233 /* Nothing to do regardless if failed or not */ 231 234 futex_up(&ipc_futex); 232 235 return; 233 236 } 234 237 235 238 if (callid == (ipc_callid_t) IPC_CALLRET_FATAL) { 236 239 futex_up(&ipc_futex); 240 237 241 /* Call asynchronous handler with error code */ 238 242 if (call->callback) 239 243 call->callback(call->private, ENOENT, NULL); 244 240 245 free(call); 241 246 return; 242 247 } 243 248 244 249 if (callid == (ipc_callid_t) IPC_CALLRET_TEMPORARY) { 245 250 futex_up(&ipc_futex); 246 251 247 252 call->u.msg.phoneid = phoneid; 248 253 249 254 futex_down(&async_futex); 250 255 list_append(&call->list, &queued_calls); 251 256 252 257 if (can_preempt) { 253 258 call->fid = fibril_get_id(); … … 258 263 futex_up(&async_futex); 259 264 } 265 260 266 return; 261 267 } 268 262 269 call->u.callid = callid; 270 263 271 /* Add call to the list of dispatched calls */ 264 272 list_append(&call->list, &dispatched_calls); 265 273 futex_up(&ipc_futex); 266 267 } 268 269 /** Make a fast asynchronous call. 274 } 275 276 /** Fast asynchronous call. 270 277 * 271 278 * This function can only handle four arguments of payload. It is, however, … … 273 280 * 274 281 * Note that this function is a void function. 275 * During normal opertation, answering this call will trigger the callback. 276 * In case of fatal error, call the callback handler with the proper error code. 277 * If the call cannot be temporarily made, queue it. 278 * 279 * @param phoneid Phone handle for the call. 280 * @param method Requested method. 281 * @param arg1 Service-defined payload argument. 282 * @param arg2 Service-defined payload argument. 283 * @param arg3 Service-defined payload argument. 284 * @param arg4 Service-defined payload argument. 285 * @param private Argument to be passed to the answer/error callback. 286 * @param callback Answer or error callback. 287 * @param can_preempt If non-zero, the current fibril will be preempted in 288 * case the kernel temporarily refuses to accept more 289 * asynchronous calls. 290 */ 291 void ipc_call_async_fast(int phoneid, ipcarg_t method, ipcarg_t arg1, 292 ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, void *private, 293 ipc_async_callback_t callback, int can_preempt) 282 * 283 * During normal operation, answering this call will trigger the callback. 284 * In case of fatal error, the callback handler is called with the proper 285 * error code. If the call cannot be temporarily made, it is queued. 286 * 287 * @param phoneid Phone handle for the call. 288 * @param imethod Requested interface and method. 289 * @param arg1 Service-defined payload argument. 290 * @param arg2 Service-defined payload argument. 291 * @param arg3 Service-defined payload argument. 292 * @param arg4 Service-defined payload argument. 293 * @param private Argument to be passed to the answer/error callback. 294 * @param callback Answer or error callback. 295 * @param can_preempt If true, the current fibril will be preempted in 296 * case the kernel temporarily refuses to accept more 297 * asynchronous calls. 298 * 299 */ 300 void ipc_call_async_fast(int phoneid, sysarg_t imethod, sysarg_t arg1, 301 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, void *private, 302 ipc_async_callback_t callback, bool can_preempt) 294 303 { 295 304 async_call_t *call = NULL; 296 ipc_callid_t callid; 297 305 298 306 if (callback) { 299 307 call = ipc_prepare_async(private, callback); … … 301 309 return; 302 310 } 303 311 304 312 /* 305 * We need to make sure that we get callid before another thread306 * accesses the queue again.313 * We need to make sure that we get callid 314 * before another thread accesses the queue again. 307 315 */ 316 308 317 futex_down(&ipc_futex); 309 callid = __SYSCALL6(SYS_IPC_CALL_ASYNC_FAST, phoneid, method, arg1,310 arg2, arg3, arg4);311 318 ipc_callid_t callid = __SYSCALL6(SYS_IPC_CALL_ASYNC_FAST, phoneid, 319 imethod, arg1, arg2, arg3, arg4); 320 312 321 if (callid == (ipc_callid_t) IPC_CALLRET_TEMPORARY) { 313 322 if (!call) { … … 316 325 return; 317 326 } 318 IPC_SET_METHOD(call->u.msg.data, method); 327 328 IPC_SET_IMETHOD(call->u.msg.data, imethod); 319 329 IPC_SET_ARG1(call->u.msg.data, arg1); 320 330 IPC_SET_ARG2(call->u.msg.data, arg2); 321 331 IPC_SET_ARG3(call->u.msg.data, arg3); 322 332 IPC_SET_ARG4(call->u.msg.data, arg4); 333 323 334 /* 324 335 * To achieve deterministic behavior, we always zero out the 325 336 * arguments that are beyond the limits of the fast version. 326 337 */ 338 327 339 IPC_SET_ARG5(call->u.msg.data, 0); 328 340 } 341 329 342 ipc_finish_async(callid, phoneid, call, can_preempt); 330 343 } 331 344 332 /** Make an asynchronous call transmitting the entire payload.345 /** Asynchronous call transmitting the entire payload. 333 346 * 334 347 * Note that this function is a void function. 335 * During normal opertation, answering this call will trigger the callback. 336 * In case of fatal error, call the callback handler with the proper error code. 337 * If the call cannot be temporarily made, queue it. 338 * 339 * @param phoneid Phone handle for the call. 340 * @param method Requested method. 341 * @param arg1 Service-defined payload argument. 342 * @param arg2 Service-defined payload argument. 343 * @param arg3 Service-defined payload argument. 344 * @param arg4 Service-defined payload argument. 345 * @param arg5 Service-defined payload argument. 346 * @param private Argument to be passed to the answer/error callback. 347 * @param callback Answer or error callback. 348 * @param can_preempt If non-zero, the current fibril will be preempted in 349 * case the kernel temporarily refuses to accept more 350 * asynchronous calls. 351 * 352 */ 353 void ipc_call_async_slow(int phoneid, ipcarg_t method, ipcarg_t arg1, 354 ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, ipcarg_t arg5, void *private, 355 ipc_async_callback_t callback, int can_preempt) 356 { 357 async_call_t *call; 358 ipc_callid_t callid; 359 360 call = ipc_prepare_async(private, callback); 348 * 349 * During normal operation, answering this call will trigger the callback. 350 * In case of fatal error, the callback handler is called with the proper 351 * error code. If the call cannot be temporarily made, it is queued. 352 * 353 * @param phoneid Phone handle for the call. 354 * @param imethod Requested interface and method. 355 * @param arg1 Service-defined payload argument. 356 * @param arg2 Service-defined payload argument. 357 * @param arg3 Service-defined payload argument. 358 * @param arg4 Service-defined payload argument. 359 * @param arg5 Service-defined payload argument. 360 * @param private Argument to be passed to the answer/error callback. 361 * @param callback Answer or error callback. 362 * @param can_preempt If true, the current fibril will be preempted in 363 * case the kernel temporarily refuses to accept more 364 * asynchronous calls. 365 * 366 */ 367 void ipc_call_async_slow(int phoneid, sysarg_t imethod, sysarg_t arg1, 368 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5, void *private, 369 ipc_async_callback_t callback, bool can_preempt) 370 { 371 async_call_t *call = ipc_prepare_async(private, callback); 361 372 if (!call) 362 373 return; 363 364 IPC_SET_ METHOD(call->u.msg.data,method);374 375 IPC_SET_IMETHOD(call->u.msg.data, imethod); 365 376 IPC_SET_ARG1(call->u.msg.data, arg1); 366 377 IPC_SET_ARG2(call->u.msg.data, arg2); … … 368 379 IPC_SET_ARG4(call->u.msg.data, arg4); 369 380 IPC_SET_ARG5(call->u.msg.data, arg5); 381 370 382 /* 371 * We need to make sure that we get callid before another thread372 * accesses the queue again.383 * We need to make sure that we get callid 384 * before another threadaccesses the queue again. 373 385 */ 386 374 387 futex_down(&ipc_futex); 375 callid = _ipc_call_async(phoneid, &call->u.msg.data); 376 388 ipc_callid_t callid = 389 ipc_call_async_internal(phoneid, &call->u.msg.data); 390 377 391 ipc_finish_async(callid, phoneid, call, can_preempt); 378 392 } 379 393 380 381 /** Answer a received call - fast version. 394 /** Answer received call (fast version). 382 395 * 383 396 * The fast answer makes use of passing retval and first four arguments in 384 397 * registers. If you need to return more, use the ipc_answer_slow() instead. 385 398 * 386 * @param callid Hash of the call being answered. 387 * @param retval Return value. 388 * @param arg1 First return argument. 389 * @param arg2 Second return argument. 390 * @param arg3 Third return argument. 391 * @param arg4 Fourth return argument. 392 * 393 * @return Zero on success or a value from @ref errno.h on failure. 394 */ 395 ipcarg_t ipc_answer_fast(ipc_callid_t callid, ipcarg_t retval, ipcarg_t arg1, 396 ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4) 399 * @param callid Hash of the call being answered. 400 * @param retval Return value. 401 * @param arg1 First return argument. 402 * @param arg2 Second return argument. 403 * @param arg3 Third return argument. 404 * @param arg4 Fourth return argument. 405 * 406 * @return Zero on success. 407 * @return Value from @ref errno.h on failure. 408 * 409 */ 410 sysarg_t ipc_answer_fast(ipc_callid_t callid, sysarg_t retval, sysarg_t arg1, 411 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4) 397 412 { 398 413 return __SYSCALL6(SYS_IPC_ANSWER_FAST, callid, retval, arg1, arg2, arg3, … … 400 415 } 401 416 402 /** Answer a received call - slow full version. 403 * 404 * @param callid Hash of the call being answered. 405 * @param retval Return value. 406 * @param arg1 First return argument. 407 * @param arg2 Second return argument. 408 * @param arg3 Third return argument. 409 * @param arg4 Fourth return argument. 410 * @param arg5 Fifth return argument. 411 * 412 * @return Zero on success or a value from @ref errno.h on failure. 413 */ 414 ipcarg_t ipc_answer_slow(ipc_callid_t callid, ipcarg_t retval, ipcarg_t arg1, 415 ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, ipcarg_t arg5) 417 /** Answer received call (entire payload). 418 * 419 * @param callid Hash of the call being answered. 420 * @param retval Return value. 421 * @param arg1 First return argument. 422 * @param arg2 Second return argument. 423 * @param arg3 Third return argument. 424 * @param arg4 Fourth return argument. 425 * @param arg5 Fifth return argument. 426 * 427 * @return Zero on success. 428 * @return Value from @ref errno.h on failure. 429 * 430 */ 431 sysarg_t ipc_answer_slow(ipc_callid_t callid, sysarg_t retval, sysarg_t arg1, 432 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5) 416 433 { 417 434 ipc_call_t data; 418 435 419 436 IPC_SET_RETVAL(data, retval); 420 437 IPC_SET_ARG1(data, arg1); … … 423 440 IPC_SET_ARG4(data, arg4); 424 441 IPC_SET_ARG5(data, arg5); 425 442 426 443 return __SYSCALL2(SYS_IPC_ANSWER_SLOW, callid, (sysarg_t) &data); 427 444 } 428 445 429 430 /** Try to dispatch queued calls from the async queue. */ 431 static void try_dispatch_queued_calls(void) 432 { 433 async_call_t *call; 434 ipc_callid_t callid; 435 446 /** Try to dispatch queued calls from the async queue. 447 * 448 */ 449 static void dispatch_queued_calls(void) 450 { 436 451 /** @todo 437 * Integrate intelligently ipc_futex ,so that it is locked during438 * ipc_call_async_*() ,until it is added to dispatched_calls.452 * Integrate intelligently ipc_futex so that it is locked during 453 * ipc_call_async_*() until it is added to dispatched_calls. 439 454 */ 455 440 456 futex_down(&async_futex); 457 441 458 while (!list_empty(&queued_calls)) { 442 call = list_get_instance(queued_calls.next, async_call_t, list); 443 callid = _ipc_call_async(call->u.msg.phoneid, 444 &call->u.msg.data); 445 if (callid == (ipc_callid_t) IPC_CALLRET_TEMPORARY) { 459 async_call_t *call = 460 list_get_instance(queued_calls.next, async_call_t, list); 461 ipc_callid_t callid = 462 ipc_call_async_internal(call->u.msg.phoneid, &call->u.msg.data); 463 464 if (callid == (ipc_callid_t) IPC_CALLRET_TEMPORARY) 446 465 break; 447 }466 448 467 list_remove(&call->list); 449 468 450 469 futex_up(&async_futex); 470 451 471 if (call->fid) 452 472 fibril_add_ready(call->fid); … … 455 475 if (call->callback) 456 476 call->callback(call->private, ENOENT, NULL); 477 457 478 free(call); 458 479 } else { 459 480 call->u.callid = callid; 481 460 482 futex_down(&ipc_futex); 461 483 list_append(&call->list, &dispatched_calls); 462 484 futex_up(&ipc_futex); 463 485 } 486 464 487 futex_down(&async_futex); 465 488 } 489 466 490 futex_up(&async_futex); 467 491 } 468 492 469 /** Handle areceived answer.493 /** Handle received answer. 470 494 * 471 495 * Find the hash of the answer and call the answer callback. 472 496 * 473 * @todo Make it use hash table. 474 * 475 * @param callid Hash of the received answer. 476 * The answer has the same hash as the request OR'ed with 477 * the IPC_CALLID_ANSWERED bit. 478 * @param data Call data of the answer. 497 * The answer has the same hash as the request OR'ed with 498 * the IPC_CALLID_ANSWERED bit. 499 * 500 * @todo Use hash table. 501 * 502 * @param callid Hash of the received answer. 503 * @param data Call data of the answer. 504 * 479 505 */ 480 506 static void handle_answer(ipc_callid_t callid, ipc_call_t *data) 481 507 { 508 callid &= ~IPC_CALLID_ANSWERED; 509 510 futex_down(&ipc_futex); 511 482 512 link_t *item; 483 async_call_t *call;484 485 callid &= ~IPC_CALLID_ANSWERED;486 487 futex_down(&ipc_futex);488 513 for (item = dispatched_calls.next; item != &dispatched_calls; 489 514 item = item->next) { 490 call = list_get_instance(item, async_call_t, list); 515 async_call_t *call = 516 list_get_instance(item, async_call_t, list); 517 491 518 if (call->u.callid == callid) { 492 519 list_remove(&call->list); 520 493 521 futex_up(&ipc_futex); 522 494 523 if (call->callback) 495 call->callback(call->private, 524 call->callback(call->private, 496 525 IPC_GET_RETVAL(*data), data); 526 497 527 free(call); 498 528 return; 499 529 } 500 530 } 531 501 532 futex_up(&ipc_futex); 502 533 } 503 534 504 505 /** Wait for a first call to come. 506 * 507 * @param call Storage where the incoming call data will be stored.508 * @param usec Timeout in microseconds509 * @param flags Flags passed to SYS_IPC_WAIT (blocking, nonblocking).510 * 511 * @return Hash of the call. Note that certain bits have special512 * meaning. IPC_CALLID_ANSWERED will be set in an answer513 * and IPC_CALLID_NOTIFICATION is used for notifications.514 * 515 */ 516 ipc_callid_t ipc_wait_cycle(ipc_call_t *call, uint32_t usec,int flags)517 { 518 ipc_callid_t callid ;519 520 callid = __SYSCALL3(SYS_IPC_WAIT, (sysarg_t) call, usec, flags);535 /** Wait for first IPC call to come. 536 * 537 * @param call Incoming call storage. 538 * @param usec Timeout in microseconds 539 * @param flags Flags passed to SYS_IPC_WAIT (blocking, nonblocking). 540 * 541 * @return Hash of the call. Note that certain bits have special 542 * meaning: IPC_CALLID_ANSWERED is set in an answer 543 * and IPC_CALLID_NOTIFICATION is used for notifications. 544 * 545 */ 546 ipc_callid_t ipc_wait_cycle(ipc_call_t *call, sysarg_t usec, 547 unsigned int flags) 548 { 549 ipc_callid_t callid = 550 __SYSCALL3(SYS_IPC_WAIT, (sysarg_t) call, usec, flags); 551 521 552 /* Handle received answers */ 522 553 if (callid & IPC_CALLID_ANSWERED) { 523 554 handle_answer(callid, call); 524 try_dispatch_queued_calls();555 dispatch_queued_calls(); 525 556 } 526 557 527 558 return callid; 528 559 } 529 560 530 /** Wait some time for an IPC call. 531 * 532 * The call will return after an answer is received. 533 * 534 * @param call Storage where the incoming call data will be stored. 535 * @param usec Timeout in microseconds. 536 * 537 * @return Hash of the answer. 538 */ 539 ipc_callid_t ipc_wait_for_call_timeout(ipc_call_t *call, uint32_t usec) 561 /** Interrupt one thread of this task from waiting for IPC. 562 * 563 */ 564 void ipc_poke(void) 565 { 566 __SYSCALL0(SYS_IPC_POKE); 567 } 568 569 /** Wait for first IPC call to come. 570 * 571 * Only requests are returned, answers are processed internally. 572 * 573 * @param call Incoming call storage. 574 * @param usec Timeout in microseconds 575 * 576 * @return Hash of the call. 577 * 578 */ 579 ipc_callid_t ipc_wait_for_call_timeout(ipc_call_t *call, sysarg_t usec) 540 580 { 541 581 ipc_callid_t callid; 542 582 543 583 do { 544 584 callid = ipc_wait_cycle(call, usec, SYNCH_FLAGS_NONE); 545 585 } while (callid & IPC_CALLID_ANSWERED); 546 586 547 587 return callid; 548 588 } … … 550 590 /** Check if there is an IPC call waiting to be picked up. 551 591 * 552 * @param call Storage where the incoming call will be stored. 553 * @return Hash of the answer. 592 * Only requests are returned, answers are processed internally. 593 * 594 * @param call Incoming call storage. 595 * 596 * @return Hash of the call. 597 * 554 598 */ 555 599 ipc_callid_t ipc_trywait_for_call(ipc_call_t *call) 556 600 { 557 601 ipc_callid_t callid; 558 602 559 603 do { 560 604 callid = ipc_wait_cycle(call, SYNCH_NO_TIMEOUT, 561 605 SYNCH_FLAGS_NON_BLOCKING); 562 606 } while (callid & IPC_CALLID_ANSWERED); 563 607 564 608 return callid; 565 609 } 566 610 567 /** Interrupt one thread of this task from waiting for IPC. */ 568 void ipc_poke(void) 569 { 570 __SYSCALL0(SYS_IPC_POKE); 571 } 572 573 /** Ask destination to do a callback connection. 574 * 575 * @param phoneid Phone handle used for contacting the other side. 576 * @param arg1 Service-defined argument. 577 * @param arg2 Service-defined argument. 578 * @param arg3 Service-defined argument. 579 * @param phonehash Storage where the library will store an opaque 580 * identifier of the phone that will be used for incoming 581 * calls. This identifier can be used for connection 582 * tracking. 583 * 584 * @return Zero on success or a negative error code. 585 */ 586 int ipc_connect_to_me(int phoneid, int arg1, int arg2, int arg3, 587 ipcarg_t *phonehash) 611 /** Request callback connection. 612 * 613 * The @a taskhash and @a phonehash identifiers returned 614 * by the kernel can be used for connection tracking. 615 * 616 * @param phoneid Phone handle used for contacting the other side. 617 * @param arg1 User defined argument. 618 * @param arg2 User defined argument. 619 * @param arg3 User defined argument. 620 * @param taskhash Opaque identifier of the client task. 621 * @param phonehash Opaque identifier of the phone that will 622 * be used for incoming calls. 623 * 624 * @return Zero on success or a negative error code. 625 * 626 */ 627 int ipc_connect_to_me(int phoneid, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, 628 sysarg_t *taskhash, sysarg_t *phonehash) 588 629 { 589 630 return ipc_call_sync_3_5(phoneid, IPC_M_CONNECT_TO_ME, arg1, arg2, 590 arg3, NULL, NULL, NULL, NULL, phonehash); 591 } 592 593 /** Ask through phone for a new connection to some service. 594 * 595 * @param phoneid Phone handle used for contacting the other side. 596 * @param arg1 User defined argument. 597 * @param arg2 User defined argument. 598 * @param arg3 User defined argument. 599 * 600 * @return New phone handle on success or a negative error code. 601 */ 602 int ipc_connect_me_to(int phoneid, int arg1, int arg2, int arg3) 603 { 604 ipcarg_t newphid; 605 int res; 606 607 res = ipc_call_sync_3_5(phoneid, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3, 631 arg3, NULL, NULL, NULL, taskhash, phonehash); 632 } 633 634 /** Request new connection. 635 * 636 * @param phoneid Phone handle used for contacting the other side. 637 * @param arg1 User defined argument. 638 * @param arg2 User defined argument. 639 * @param arg3 User defined argument. 640 * 641 * @return New phone handle on success or a negative error code. 642 * 643 */ 644 int ipc_connect_me_to(int phoneid, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3) 645 { 646 sysarg_t newphid; 647 int res = ipc_call_sync_3_5(phoneid, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3, 608 648 NULL, NULL, NULL, NULL, &newphid); 609 649 if (res) 610 650 return res; 651 611 652 return newphid; 612 653 } 613 654 614 /** Ask through phone for a new connection to some service.655 /** Request new connection (blocking) 615 656 * 616 657 * If the connection is not available at the moment, the 617 * call will block. 618 * 619 * @param phoneid Phone handle used for contacting the other side. 620 * @param arg1 User defined argument. 621 * @param arg2 User defined argument. 622 * @param arg3 User defined argument. 623 * 624 * @return New phone handle on success or a negative error code. 625 */ 626 int ipc_connect_me_to_blocking(int phoneid, int arg1, int arg2, int arg3) 627 { 628 ipcarg_t newphid; 629 int res; 630 631 res = ipc_call_sync_4_5(phoneid, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3, 658 * call should block. This has to be, however, implemented 659 * on the server side. 660 * 661 * @param phoneid Phone handle used for contacting the other side. 662 * @param arg1 User defined argument. 663 * @param arg2 User defined argument. 664 * @param arg3 User defined argument. 665 * 666 * @return New phone handle on success or a negative error code. 667 * 668 */ 669 int ipc_connect_me_to_blocking(int phoneid, sysarg_t arg1, sysarg_t arg2, 670 sysarg_t arg3) 671 { 672 sysarg_t newphid; 673 int res = ipc_call_sync_4_5(phoneid, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3, 632 674 IPC_FLAG_BLOCKING, NULL, NULL, NULL, NULL, &newphid); 633 675 if (res) 634 676 return res; 677 635 678 return newphid; 636 679 } … … 638 681 /** Hang up a phone. 639 682 * 640 * @param phoneid Handle of the phone to be hung up. 641 * 642 * @return Zero on success or a negative error code. 683 * @param phoneid Handle of the phone to be hung up. 684 * 685 * @return Zero on success or a negative error code. 686 * 643 687 */ 644 688 int ipc_hangup(int phoneid) … … 647 691 } 648 692 649 /** Register IRQ notification.650 *651 * @param inr IRQ number.652 * @param devno Device number of the device generating inr.653 * @param method Use this method for notifying me.654 * @param ucode Top-half pseudocode handler.655 *656 * @return Value returned by the kernel.657 */658 int ipc_register_irq(int inr, int devno, int method, irq_code_t *ucode)659 {660 return __SYSCALL4(SYS_IPC_REGISTER_IRQ, inr, devno, method,661 (sysarg_t) ucode);662 }663 664 /** Unregister IRQ notification.665 *666 * @param inr IRQ number.667 * @param devno Device number of the device generating inr.668 *669 * @return Value returned by the kernel.670 */671 int ipc_unregister_irq(int inr, int devno)672 {673 return __SYSCALL2(SYS_IPC_UNREGISTER_IRQ, inr, devno);674 }675 676 693 /** Forward a received call to another destination. 677 694 * 678 * @param callid Hash of the call to forward. 679 * @param phoneid Phone handle to use for forwarding. 680 * @param method New method for the forwarded call. 681 * @param arg1 New value of the first argument for the forwarded call. 682 * @param arg2 New value of the second argument for the forwarded call. 683 * @param mode Flags specifying mode of the forward operation. 684 * 685 * @return Zero on success or an error code. 686 * 687 * For non-system methods, the old method, arg1 and arg2 are rewritten by the 688 * new values. For system methods, the new method, arg1 and arg2 are written 689 * to the old arg1, arg2 and arg3, respectivelly. Calls with immutable 690 * methods are forwarded verbatim. 691 */ 692 int ipc_forward_fast(ipc_callid_t callid, int phoneid, int method, 693 ipcarg_t arg1, ipcarg_t arg2, int mode) 694 { 695 return __SYSCALL6(SYS_IPC_FORWARD_FAST, callid, phoneid, method, arg1, 695 * For non-system methods, the old method, arg1 and arg2 are rewritten 696 * by the new values. For system methods, the new method, arg1 and arg2 697 * are written to the old arg1, arg2 and arg3, respectivelly. Calls with 698 * immutable methods are forwarded verbatim. 699 * 700 * @param callid Hash of the call to forward. 701 * @param phoneid Phone handle to use for forwarding. 702 * @param imethod New interface and method for the forwarded call. 703 * @param arg1 New value of the first argument for the forwarded call. 704 * @param arg2 New value of the second argument for the forwarded call. 705 * @param mode Flags specifying mode of the forward operation. 706 * 707 * @return Zero on success or an error code. 708 * 709 */ 710 int ipc_forward_fast(ipc_callid_t callid, int phoneid, sysarg_t imethod, 711 sysarg_t arg1, sysarg_t arg2, unsigned int mode) 712 { 713 return __SYSCALL6(SYS_IPC_FORWARD_FAST, callid, phoneid, imethod, arg1, 696 714 arg2, mode); 697 715 } 698 716 699 700 int ipc_forward_slow(ipc_callid_t callid, int phoneid, int method, 701 ipcarg_t arg1, ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, ipcarg_t arg5, 702 int mode) 717 int ipc_forward_slow(ipc_callid_t callid, int phoneid, sysarg_t imethod, 718 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5, 719 unsigned int mode) 703 720 { 704 721 ipc_call_t data; 705 706 IPC_SET_ METHOD(data,method);722 723 IPC_SET_IMETHOD(data, imethod); 707 724 IPC_SET_ARG1(data, arg1); 708 725 IPC_SET_ARG2(data, arg2); … … 710 727 IPC_SET_ARG4(data, arg4); 711 728 IPC_SET_ARG5(data, arg5); 712 713 return __SYSCALL4(SYS_IPC_FORWARD_SLOW, callid, phoneid, (sysarg_t) &data, mode); 714 } 715 716 /** Wrapper for making IPC_M_SHARE_IN calls. 717 * 718 * @param phoneid Phone that will be used to contact the receiving side. 719 * @param dst Destination address space area base. 720 * @param size Size of the destination address space area. 721 * @param arg User defined argument. 722 * @param flags Storage where the received flags will be stored. Can be 723 * NULL. 724 * 725 * @return Zero on success or a negative error code from errno.h. 726 */ 727 int ipc_share_in_start(int phoneid, void *dst, size_t size, ipcarg_t arg, 728 int *flags) 729 730 return __SYSCALL4(SYS_IPC_FORWARD_SLOW, callid, phoneid, (sysarg_t) &data, 731 mode); 732 } 733 734 /** Wrapper for IPC_M_SHARE_IN calls. 735 * 736 * @param phoneid Phone that will be used to contact the receiving side. 737 * @param dst Destination address space area base. 738 * @param size Size of the destination address space area. 739 * @param arg User defined argument. 740 * @param flags Storage for received flags. Can be NULL. 741 * 742 * @return Zero on success or a negative error code from errno.h. 743 * 744 */ 745 int ipc_share_in_start(int phoneid, void *dst, size_t size, sysarg_t arg, 746 unsigned int *flags) 729 747 { 730 748 sysarg_t tmp_flags = 0; 731 int res = ipc_call_sync_3_2(phoneid, IPC_M_SHARE_IN, ( ipcarg_t) dst,732 ( ipcarg_t) size, arg, NULL, &tmp_flags);749 int res = ipc_call_sync_3_2(phoneid, IPC_M_SHARE_IN, (sysarg_t) dst, 750 (sysarg_t) size, arg, NULL, &tmp_flags); 733 751 734 752 if (flags) 735 *flags = tmp_flags;753 *flags = (unsigned int) tmp_flags; 736 754 737 755 return res; … … 740 758 /** Wrapper for answering the IPC_M_SHARE_IN calls. 741 759 * 742 * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ calls 743 * so that the user doesn't have to remember the meaning of each IPC argument. 744 * 745 * @param callid Hash of the IPC_M_DATA_READ call to answer. 746 * @param src Source address space base. 747 * @param flags Flags to be used for sharing. Bits can be only cleared. 748 * 749 * @return Zero on success or a value from @ref errno.h on failure. 750 */ 751 int ipc_share_in_finalize(ipc_callid_t callid, void *src, int flags) 752 { 753 return ipc_answer_2(callid, EOK, (ipcarg_t) src, (ipcarg_t) flags); 754 } 755 756 /** Wrapper for making IPC_M_SHARE_OUT calls. 757 * 758 * @param phoneid Phone that will be used to contact the receiving side. 759 * @param src Source address space area base address. 760 * @param flags Flags to be used for sharing. Bits can be only cleared. 761 * 762 * @return Zero on success or a negative error code from errno.h. 763 */ 764 int ipc_share_out_start(int phoneid, void *src, int flags) 765 { 766 return ipc_call_sync_3_0(phoneid, IPC_M_SHARE_OUT, (ipcarg_t) src, 0, 767 (ipcarg_t) flags); 760 * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ 761 * calls so that the user doesn't have to remember the meaning of each 762 * IPC argument. 763 * 764 * @param callid Hash of the IPC_M_DATA_READ call to answer. 765 * @param src Source address space base. 766 * @param flags Flags to be used for sharing. Bits can be only cleared. 767 * 768 * @return Zero on success or a value from @ref errno.h on failure. 769 * 770 */ 771 int ipc_share_in_finalize(ipc_callid_t callid, void *src, unsigned int flags) 772 { 773 return ipc_answer_2(callid, EOK, (sysarg_t) src, (sysarg_t) flags); 774 } 775 776 /** Wrapper for IPC_M_SHARE_OUT calls. 777 * 778 * @param phoneid Phone that will be used to contact the receiving side. 779 * @param src Source address space area base address. 780 * @param flags Flags to be used for sharing. Bits can be only cleared. 781 * 782 * @return Zero on success or a negative error code from errno.h. 783 * 784 */ 785 int ipc_share_out_start(int phoneid, void *src, unsigned int flags) 786 { 787 return ipc_call_sync_3_0(phoneid, IPC_M_SHARE_OUT, (sysarg_t) src, 0, 788 (sysarg_t) flags); 768 789 } 769 790 770 791 /** Wrapper for answering the IPC_M_SHARE_OUT calls. 771 792 * 772 * This wrapper only makes it more comfortable to answer IPC_M_SHARE_OUT calls 773 * so that the user doesn't have to remember the meaning of each IPC argument. 774 * 775 * @param callid Hash of the IPC_M_DATA_WRITE call to answer. 776 * @param dst Destination address space area base address. 777 * 778 * @return Zero on success or a value from @ref errno.h on failure. 793 * This wrapper only makes it more comfortable to answer IPC_M_SHARE_OUT 794 * calls so that the user doesn't have to remember the meaning of each 795 * IPC argument. 796 * 797 * @param callid Hash of the IPC_M_DATA_WRITE call to answer. 798 * @param dst Destination address space area base address. 799 * 800 * @return Zero on success or a value from @ref errno.h on failure. 801 * 779 802 */ 780 803 int ipc_share_out_finalize(ipc_callid_t callid, void *dst) 781 804 { 782 return ipc_answer_1(callid, EOK, ( ipcarg_t) dst);783 } 784 785 786 /** Wrapper for making IPC_M_DATA_READ calls. 787 * 788 * @param phoneid Phone that will be used to contact the receiving side.789 * @param dst Address of the beginningof the destination buffer.790 * @param size Size of the destination buffer.791 * 792 * @return Zero on success or a negative error code from errno.h.805 return ipc_answer_1(callid, EOK, (sysarg_t) dst); 806 } 807 808 /** Wrapper for IPC_M_DATA_READ calls. 809 * 810 * @param phoneid Phone that will be used to contact the receiving side. 811 * @param dst Address of the beginning of the destination buffer. 812 * @param size Size of the destination buffer. 813 * 814 * @return Zero on success or a negative error code from errno.h. 815 * 793 816 */ 794 817 int ipc_data_read_start(int phoneid, void *dst, size_t size) 795 818 { 796 return ipc_call_sync_2_0(phoneid, IPC_M_DATA_READ, ( ipcarg_t) dst,797 ( ipcarg_t) size);819 return ipc_call_sync_2_0(phoneid, IPC_M_DATA_READ, (sysarg_t) dst, 820 (sysarg_t) size); 798 821 } 799 822 800 823 /** Wrapper for answering the IPC_M_DATA_READ calls. 801 824 * 802 * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ calls 803 * so that the user doesn't have to remember the meaning of each IPC argument. 804 * 805 * @param callid Hash of the IPC_M_DATA_READ call to answer. 806 * @param src Source address for the IPC_M_DATA_READ call. 807 * @param size Size for the IPC_M_DATA_READ call. Can be smaller than 808 * the maximum size announced by the sender. 809 * 810 * @return Zero on success or a value from @ref errno.h on failure. 825 * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ 826 * calls so that the user doesn't have to remember the meaning of each 827 * IPC argument. 828 * 829 * @param callid Hash of the IPC_M_DATA_READ call to answer. 830 * @param src Source address for the IPC_M_DATA_READ call. 831 * @param size Size for the IPC_M_DATA_READ call. Can be smaller than 832 * the maximum size announced by the sender. 833 * 834 * @return Zero on success or a value from @ref errno.h on failure. 835 * 811 836 */ 812 837 int ipc_data_read_finalize(ipc_callid_t callid, const void *src, size_t size) 813 838 { 814 return ipc_answer_2(callid, EOK, (ipcarg_t) src, (ipcarg_t) size); 815 } 816 817 /** Wrapper for making IPC_M_DATA_WRITE calls. 818 * 819 * @param phoneid Phone that will be used to contact the receiving side. 820 * @param src Address of the beginning of the source buffer. 821 * @param size Size of the source buffer. 822 * 823 * @return Zero on success or a negative error code from errno.h. 839 return ipc_answer_2(callid, EOK, (sysarg_t) src, (sysarg_t) size); 840 } 841 842 /** Wrapper for IPC_M_DATA_WRITE calls. 843 * 844 * @param phoneid Phone that will be used to contact the receiving side. 845 * @param src Address of the beginning of the source buffer. 846 * @param size Size of the source buffer. 847 * 848 * @return Zero on success or a negative error code from errno.h. 849 * 824 850 */ 825 851 int ipc_data_write_start(int phoneid, const void *src, size_t size) 826 852 { 827 return ipc_call_sync_2_0(phoneid, IPC_M_DATA_WRITE, ( ipcarg_t) src,828 ( ipcarg_t) size);853 return ipc_call_sync_2_0(phoneid, IPC_M_DATA_WRITE, (sysarg_t) src, 854 (sysarg_t) size); 829 855 } 830 856 831 857 /** Wrapper for answering the IPC_M_DATA_WRITE calls. 832 858 * 833 * This wrapper only makes it more comfortable to answer IPC_M_DATA_WRITE calls 834 * so that the user doesn't have to remember the meaning of each IPC argument. 835 * 836 * @param callid Hash of the IPC_M_DATA_WRITE call to answer. 837 * @param dst Final destination address for the IPC_M_DATA_WRITE call. 838 * @param size Final size for the IPC_M_DATA_WRITE call. 839 * 840 * @return Zero on success or a value from @ref errno.h on failure. 859 * This wrapper only makes it more comfortable to answer IPC_M_DATA_WRITE 860 * calls so that the user doesn't have to remember the meaning of each 861 * IPC argument. 862 * 863 * @param callid Hash of the IPC_M_DATA_WRITE call to answer. 864 * @param dst Final destination address for the IPC_M_DATA_WRITE call. 865 * @param size Final size for the IPC_M_DATA_WRITE call. 866 * 867 * @return Zero on success or a value from @ref errno.h on failure. 868 * 841 869 */ 842 870 int ipc_data_write_finalize(ipc_callid_t callid, void *dst, size_t size) 843 871 { 844 return ipc_answer_2(callid, EOK, (ipcarg_t) dst, (ipcarg_t) size); 845 } 846 847 #include <kernel/syscall/sysarg64.h> 872 return ipc_answer_2(callid, EOK, (sysarg_t) dst, (sysarg_t) size); 873 } 874 848 875 /** Connect to a task specified by id. 876 * 849 877 */ 850 878 int ipc_connect_kbox(task_id_t id) 851 879 { 852 sysarg64_t arg; 853 854 arg.value = (unsigned long long) id; 855 880 #ifdef __32_BITS__ 881 sysarg64_t arg = (sysarg64_t) id; 856 882 return __SYSCALL1(SYS_IPC_CONNECT_KBOX, (sysarg_t) &arg); 857 } 858 883 #endif 884 885 #ifdef __64_BITS__ 886 return __SYSCALL1(SYS_IPC_CONNECT_KBOX, (sysarg_t) id); 887 #endif 888 } 889 859 890 /** @} 860 891 */
Note:
See TracChangeset
for help on using the changeset viewer.