Changes in uspace/srv/devmap/devmap.c [ab108be4:4ae90f9] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/srv/devmap/devmap.c
rab108be4 r4ae90f9 46 46 #include <str.h> 47 47 #include <ipc/devmap.h> 48 #include <assert.h> 48 49 49 50 #define NAME "devmap" … … 61 62 link_t devices; 62 63 /** Phone asociated with this driver */ 63 ipcarg_t phone;64 sysarg_t phone; 64 65 /** Device driver name */ 65 66 char *name; … … 75 76 link_t namespaces; 76 77 /** Unique namespace identifier */ 77 dev _handle_t handle;78 devmap_handle_t handle; 78 79 /** Namespace name */ 79 80 char *name; … … 92 93 link_t driver_devices; 93 94 /** Unique device identifier */ 94 dev _handle_t handle;95 devmap_handle_t handle; 95 96 /** Device namespace */ 96 97 devmap_namespace_t *namespace; … … 99 100 /** Device driver handling this device */ 100 101 devmap_driver_t *driver; 102 /** Use this interface when forwarding to driver. */ 103 sysarg_t forward_interface; 101 104 } devmap_device_t; 102 105 … … 118 121 static FIBRIL_MUTEX_INITIALIZE(null_devices_mutex); 119 122 120 static dev _handle_t last_handle = 0;123 static devmap_handle_t last_handle = 0; 121 124 static devmap_device_t *null_devices[NULL_DEVICES]; 122 125 123 static dev_handle_t devmap_create_handle(void) 126 /* 127 * Dummy list for null devices. This is necessary so that null devices can 128 * be used just as any other devices, e.g. in devmap_device_unregister_core(). 129 */ 130 static LIST_INITIALIZE(dummy_null_driver_devices); 131 132 static devmap_handle_t devmap_create_handle(void) 124 133 { 125 134 /* TODO: allow reusing old handles after their unregistration … … 206 215 } 207 216 208 /** Find namespace with given name. 209 * 210 * The devices_list_mutex should be already held when 211 * calling this function. 212 * 213 */ 217 /** Find namespace with given name. */ 214 218 static devmap_namespace_t *devmap_namespace_find_name(const char *name) 215 219 { 216 220 link_t *item; 221 222 assert(fibril_mutex_is_locked(&devices_list_mutex)); 223 217 224 for (item = namespaces_list.next; item != &namespaces_list; item = item->next) { 218 225 devmap_namespace_t *namespace = … … 227 234 /** Find namespace with given handle. 228 235 * 229 * The devices_list_mutex should be already held when230 * calling this function.231 *232 236 * @todo: use hash table 233 237 * 234 238 */ 235 static devmap_namespace_t *devmap_namespace_find_handle(dev _handle_t handle)239 static devmap_namespace_t *devmap_namespace_find_handle(devmap_handle_t handle) 236 240 { 237 241 link_t *item; 242 243 assert(fibril_mutex_is_locked(&devices_list_mutex)); 244 238 245 for (item = namespaces_list.next; item != &namespaces_list; item = item->next) { 239 246 devmap_namespace_t *namespace = … … 246 253 } 247 254 248 /** Find device with given name. 249 * 250 * The devices_list_mutex should be already held when 251 * calling this function. 252 * 253 */ 255 /** Find device with given name. */ 254 256 static devmap_device_t *devmap_device_find_name(const char *ns_name, 255 257 const char *name) 256 258 { 257 259 link_t *item; 260 261 assert(fibril_mutex_is_locked(&devices_list_mutex)); 262 258 263 for (item = devices_list.next; item != &devices_list; item = item->next) { 259 264 devmap_device_t *device = … … 269 274 /** Find device with given handle. 270 275 * 271 * The devices_list_mutex should be already held when272 * calling this function.273 *274 276 * @todo: use hash table 275 277 * 276 278 */ 277 static devmap_device_t *devmap_device_find_handle(dev _handle_t handle)279 static devmap_device_t *devmap_device_find_handle(devmap_handle_t handle) 278 280 { 279 281 link_t *item; 282 283 assert(fibril_mutex_is_locked(&devices_list_mutex)); 284 280 285 for (item = devices_list.next; item != &devices_list; item = item->next) { 281 286 devmap_device_t *device = … … 288 293 } 289 294 290 /** Create a namespace (if not already present) 291 * 292 * The devices_list_mutex should be already held when 293 * calling this function. 294 * 295 */ 295 /** Create a namespace (if not already present). */ 296 296 static devmap_namespace_t *devmap_namespace_create(const char *ns_name) 297 297 { 298 devmap_namespace_t *namespace = devmap_namespace_find_name(ns_name); 298 devmap_namespace_t *namespace; 299 300 assert(fibril_mutex_is_locked(&devices_list_mutex)); 301 302 namespace = devmap_namespace_find_name(ns_name); 299 303 if (namespace != NULL) 300 304 return namespace; … … 321 325 } 322 326 323 /** Destroy a namespace (if it is no longer needed) 324 * 325 * The devices_list_mutex should be already held when 326 * calling this function. 327 * 328 */ 327 /** Destroy a namespace (if it is no longer needed). */ 329 328 static void devmap_namespace_destroy(devmap_namespace_t *namespace) 330 329 { 330 assert(fibril_mutex_is_locked(&devices_list_mutex)); 331 331 332 if (namespace->refcnt == 0) { 332 333 list_remove(&(namespace->namespaces)); … … 337 338 } 338 339 339 /** Increase namespace reference count by including device 340 * 341 * The devices_list_mutex should be already held when 342 * calling this function. 343 * 344 */ 340 /** Increase namespace reference count by including device. */ 345 341 static void devmap_namespace_addref(devmap_namespace_t *namespace, 346 342 devmap_device_t *device) 347 343 { 344 assert(fibril_mutex_is_locked(&devices_list_mutex)); 345 348 346 device->namespace = namespace; 349 347 namespace->refcnt++; 350 348 } 351 349 352 /** Decrease namespace reference count 353 * 354 * The devices_list_mutex should be already held when 355 * calling this function. 356 * 357 */ 350 /** Decrease namespace reference count. */ 358 351 static void devmap_namespace_delref(devmap_namespace_t *namespace) 359 352 { 353 assert(fibril_mutex_is_locked(&devices_list_mutex)); 354 360 355 namespace->refcnt--; 361 356 devmap_namespace_destroy(namespace); 362 357 } 363 358 364 /** Unregister device and free it 365 * 366 * The devices_list_mutex should be already held when 367 * calling this function. 368 * 369 */ 359 /** Unregister device and free it. */ 370 360 static void devmap_device_unregister_core(devmap_device_t *device) 371 361 { 362 assert(fibril_mutex_is_locked(&devices_list_mutex)); 363 372 364 devmap_namespace_delref(device->namespace); 373 365 list_remove(&(device->devices)); … … 387 379 ipc_callid_t iid = async_get_call(&icall); 388 380 389 if (IPC_GET_ METHOD(icall) != DEVMAP_DRIVER_REGISTER) {390 ipc_answer_0(iid, EREFUSED);381 if (IPC_GET_IMETHOD(icall) != DEVMAP_DRIVER_REGISTER) { 382 async_answer_0(iid, EREFUSED); 391 383 return NULL; 392 384 } … … 395 387 (devmap_driver_t *) malloc(sizeof(devmap_driver_t)); 396 388 if (driver == NULL) { 397 ipc_answer_0(iid, ENOMEM);389 async_answer_0(iid, ENOMEM); 398 390 return NULL; 399 391 } … … 406 398 if (rc != EOK) { 407 399 free(driver); 408 ipc_answer_0(iid, rc);400 async_answer_0(iid, rc); 409 401 return NULL; 410 402 } … … 416 408 ipc_callid_t callid = async_get_call(&call); 417 409 418 if (IPC_GET_ METHOD(call) != IPC_M_CONNECT_TO_ME) {410 if (IPC_GET_IMETHOD(call) != IPC_M_CONNECT_TO_ME) { 419 411 free(driver->name); 420 412 free(driver); 421 ipc_answer_0(callid, ENOTSUP);422 ipc_answer_0(iid, ENOTSUP);413 async_answer_0(callid, ENOTSUP); 414 async_answer_0(iid, ENOTSUP); 423 415 return NULL; 424 416 } 425 417 426 418 driver->phone = IPC_GET_ARG5(call); 427 ipc_answer_0(callid, EOK);419 async_answer_0(callid, EOK); 428 420 429 421 /* … … 437 429 */ 438 430 list_initialize(&driver->devices); 439 list_initialize(&(driver->drivers)); 431 432 link_initialize(&driver->drivers); 440 433 441 434 fibril_mutex_lock(&drivers_list_mutex); … … 452 445 fibril_mutex_unlock(&drivers_list_mutex); 453 446 454 ipc_answer_0(iid, EOK);447 async_answer_0(iid, EOK); 455 448 456 449 return driver; … … 470 463 471 464 if (driver->phone != 0) 472 ipc_hangup(driver->phone);465 async_hangup(driver->phone); 473 466 474 467 /* Remove it from list of drivers */ … … 505 498 { 506 499 if (driver == NULL) { 507 ipc_answer_0(iid, EREFUSED);500 async_answer_0(iid, EREFUSED); 508 501 return; 509 502 } … … 513 506 (devmap_device_t *) malloc(sizeof(devmap_device_t)); 514 507 if (device == NULL) { 515 ipc_answer_0(iid, ENOMEM); 516 return; 517 } 518 508 async_answer_0(iid, ENOMEM); 509 return; 510 } 511 512 /* Set the interface, if any. */ 513 device->forward_interface = IPC_GET_ARG1(*icall); 514 519 515 /* Get fqdn */ 520 516 char *fqdn; … … 523 519 if (rc != EOK) { 524 520 free(device); 525 ipc_answer_0(iid, rc);521 async_answer_0(iid, rc); 526 522 return; 527 523 } … … 531 527 free(fqdn); 532 528 free(device); 533 ipc_answer_0(iid, EINVAL);529 async_answer_0(iid, EINVAL); 534 530 return; 535 531 } … … 545 541 free(device->name); 546 542 free(device); 547 ipc_answer_0(iid, ENOMEM);548 return; 549 } 550 551 li st_initialize(&(device->devices));552 li st_initialize(&(device->driver_devices));543 async_answer_0(iid, ENOMEM); 544 return; 545 } 546 547 link_initialize(&device->devices); 548 link_initialize(&device->driver_devices); 553 549 554 550 /* Check that device is not already registered */ 555 551 if (devmap_device_find_name(namespace->name, device->name) != NULL) { 556 552 printf("%s: Device '%s/%s' already registered\n", NAME, 557 device->namespace, device->name);553 namespace->name, device->name); 558 554 devmap_namespace_destroy(namespace); 559 555 fibril_mutex_unlock(&devices_list_mutex); 560 556 free(device->name); 561 557 free(device); 562 ipc_answer_0(iid, EEXISTS);558 async_answer_0(iid, EEXISTS); 563 559 return; 564 560 } … … 566 562 /* Get unique device handle */ 567 563 device->handle = devmap_create_handle(); 568 564 569 565 devmap_namespace_addref(namespace, device); 570 566 device->driver = driver; … … 582 578 fibril_mutex_unlock(&devices_list_mutex); 583 579 584 ipc_answer_1(iid, EOK, device->handle);580 async_answer_1(iid, EOK, device->handle); 585 581 } 586 582 … … 608 604 * Get handle from request 609 605 */ 610 dev _handle_t handle = IPC_GET_ARG2(*call);606 devmap_handle_t handle = IPC_GET_ARG2(*call); 611 607 devmap_device_t *dev = devmap_device_find_handle(handle); 612 608 613 609 if ((dev == NULL) || (dev->driver == NULL) || (dev->driver->phone == 0)) { 614 610 fibril_mutex_unlock(&devices_list_mutex); 615 ipc_answer_0(callid, ENOENT); 616 return; 617 } 618 619 ipc_forward_fast(callid, dev->driver->phone, dev->handle, 620 IPC_GET_ARG3(*call), 0, IPC_FF_NONE); 611 async_answer_0(callid, ENOENT); 612 return; 613 } 614 615 if (dev->forward_interface == 0) { 616 async_forward_fast(callid, dev->driver->phone, 617 dev->handle, 0, 0, 618 IPC_FF_NONE); 619 } else { 620 async_forward_fast(callid, dev->driver->phone, 621 dev->forward_interface, dev->handle, 0, 622 IPC_FF_NONE); 623 } 621 624 622 625 fibril_mutex_unlock(&devices_list_mutex); … … 637 640 DEVMAP_NAME_MAXLEN, 0, NULL); 638 641 if (rc != EOK) { 639 ipc_answer_0(iid, rc);642 async_answer_0(iid, rc); 640 643 return; 641 644 } … … 645 648 if (!devmap_fqdn_split(fqdn, &ns_name, &name)) { 646 649 free(fqdn); 647 ipc_answer_0(iid, EINVAL);650 async_answer_0(iid, EINVAL); 648 651 return; 649 652 } … … 672 675 } 673 676 674 ipc_answer_0(iid, ENOENT);677 async_answer_0(iid, ENOENT); 675 678 free(ns_name); 676 679 free(name); … … 679 682 } 680 683 681 ipc_answer_1(iid, EOK, dev->handle);684 async_answer_1(iid, EOK, dev->handle); 682 685 683 686 fibril_mutex_unlock(&devices_list_mutex); … … 700 703 DEVMAP_NAME_MAXLEN, 0, NULL); 701 704 if (rc != EOK) { 702 ipc_answer_0(iid, rc);705 async_answer_0(iid, rc); 703 706 return; 704 707 } … … 725 728 } 726 729 727 ipc_answer_0(iid, ENOENT);730 async_answer_0(iid, ENOENT); 728 731 free(name); 729 732 fibril_mutex_unlock(&devices_list_mutex); … … 731 734 } 732 735 733 ipc_answer_1(iid, EOK, namespace->handle);736 async_answer_1(iid, EOK, namespace->handle); 734 737 735 738 fibril_mutex_unlock(&devices_list_mutex); … … 747 750 devmap_device_find_handle(IPC_GET_ARG1(*icall)); 748 751 if (dev == NULL) 749 ipc_answer_1(iid, EOK, DEV_HANDLE_NONE);752 async_answer_1(iid, EOK, DEV_HANDLE_NONE); 750 753 else 751 ipc_answer_1(iid, EOK, DEV_HANDLE_DEVICE);754 async_answer_1(iid, EOK, DEV_HANDLE_DEVICE); 752 755 } else 753 ipc_answer_1(iid, EOK, DEV_HANDLE_NAMESPACE);756 async_answer_1(iid, EOK, DEV_HANDLE_NAMESPACE); 754 757 755 758 fibril_mutex_unlock(&devices_list_mutex); … … 759 762 { 760 763 fibril_mutex_lock(&devices_list_mutex); 761 ipc_answer_1(iid, EOK, list_count(&namespaces_list));764 async_answer_1(iid, EOK, list_count(&namespaces_list)); 762 765 fibril_mutex_unlock(&devices_list_mutex); 763 766 } … … 770 773 devmap_namespace_find_handle(IPC_GET_ARG1(*icall)); 771 774 if (namespace == NULL) 772 ipc_answer_0(iid, EEXISTS);775 async_answer_0(iid, EEXISTS); 773 776 else 774 ipc_answer_1(iid, EOK, namespace->refcnt);777 async_answer_1(iid, EOK, namespace->refcnt); 775 778 776 779 fibril_mutex_unlock(&devices_list_mutex); … … 782 785 size_t size; 783 786 if (!async_data_read_receive(&callid, &size)) { 784 ipc_answer_0(callid, EREFUSED);785 ipc_answer_0(iid, EREFUSED);787 async_answer_0(callid, EREFUSED); 788 async_answer_0(iid, EREFUSED); 786 789 return; 787 790 } 788 791 789 792 if ((size % sizeof(dev_desc_t)) != 0) { 790 ipc_answer_0(callid, EINVAL);791 ipc_answer_0(iid, EINVAL);793 async_answer_0(callid, EINVAL); 794 async_answer_0(iid, EINVAL); 792 795 return; 793 796 } … … 798 801 if (count != list_count(&namespaces_list)) { 799 802 fibril_mutex_unlock(&devices_list_mutex); 800 ipc_answer_0(callid, EOVERFLOW);801 ipc_answer_0(iid, EOVERFLOW);803 async_answer_0(callid, EOVERFLOW); 804 async_answer_0(iid, EOVERFLOW); 802 805 return; 803 806 } … … 806 809 if (desc == NULL) { 807 810 fibril_mutex_unlock(&devices_list_mutex); 808 ipc_answer_0(callid, ENOMEM);809 ipc_answer_0(iid, ENOMEM);811 async_answer_0(callid, ENOMEM); 812 async_answer_0(iid, ENOMEM); 810 813 return; 811 814 } … … 823 826 } 824 827 825 ipcarg_t retval = async_data_read_finalize(callid, desc, size);828 sysarg_t retval = async_data_read_finalize(callid, desc, size); 826 829 827 830 free(desc); 828 831 fibril_mutex_unlock(&devices_list_mutex); 829 832 830 ipc_answer_0(iid, retval);833 async_answer_0(iid, retval); 831 834 } 832 835 … … 839 842 size_t size; 840 843 if (!async_data_read_receive(&callid, &size)) { 841 ipc_answer_0(callid, EREFUSED);842 ipc_answer_0(iid, EREFUSED);844 async_answer_0(callid, EREFUSED); 845 async_answer_0(iid, EREFUSED); 843 846 return; 844 847 } 845 848 846 849 if ((size % sizeof(dev_desc_t)) != 0) { 847 ipc_answer_0(callid, EINVAL);848 ipc_answer_0(iid, EINVAL);850 async_answer_0(callid, EINVAL); 851 async_answer_0(iid, EINVAL); 849 852 return; 850 853 } … … 856 859 if (namespace == NULL) { 857 860 fibril_mutex_unlock(&devices_list_mutex); 858 ipc_answer_0(callid, ENOENT);859 ipc_answer_0(iid, ENOENT);861 async_answer_0(callid, ENOENT); 862 async_answer_0(iid, ENOENT); 860 863 return; 861 864 } … … 864 867 if (count != namespace->refcnt) { 865 868 fibril_mutex_unlock(&devices_list_mutex); 866 ipc_answer_0(callid, EOVERFLOW);867 ipc_answer_0(iid, EOVERFLOW);869 async_answer_0(callid, EOVERFLOW); 870 async_answer_0(iid, EOVERFLOW); 868 871 return; 869 872 } … … 872 875 if (desc == NULL) { 873 876 fibril_mutex_unlock(&devices_list_mutex); 874 ipc_answer_0(callid, ENOMEM);875 ipc_answer_0(iid, EREFUSED);877 async_answer_0(callid, ENOMEM); 878 async_answer_0(iid, EREFUSED); 876 879 return; 877 880 } … … 890 893 } 891 894 892 ipcarg_t retval = async_data_read_finalize(callid, desc, size);895 sysarg_t retval = async_data_read_finalize(callid, desc, size); 893 896 894 897 free(desc); 895 898 fibril_mutex_unlock(&devices_list_mutex); 896 899 897 ipc_answer_0(iid, retval);900 async_answer_0(iid, retval); 898 901 } 899 902 … … 914 917 if (!fnd) { 915 918 fibril_mutex_unlock(&null_devices_mutex); 916 ipc_answer_0(iid, ENOMEM);919 async_answer_0(iid, ENOMEM); 917 920 return; 918 921 } … … 924 927 if (dev_name == NULL) { 925 928 fibril_mutex_unlock(&null_devices_mutex); 926 ipc_answer_0(iid, ENOMEM);929 async_answer_0(iid, ENOMEM); 927 930 return; 928 931 } … … 932 935 if (device == NULL) { 933 936 fibril_mutex_unlock(&null_devices_mutex); 934 ipc_answer_0(iid, ENOMEM);937 async_answer_0(iid, ENOMEM); 935 938 return; 936 939 } … … 942 945 fibril_mutex_lock(&devices_list_mutex); 943 946 fibril_mutex_unlock(&null_devices_mutex); 944 ipc_answer_0(iid, ENOMEM);945 return; 946 } 947 948 li st_initialize(&(device->devices));949 li st_initialize(&(device->driver_devices));947 async_answer_0(iid, ENOMEM); 948 return; 949 } 950 951 link_initialize(&device->devices); 952 link_initialize(&device->driver_devices); 950 953 951 954 /* Get unique device handle */ … … 956 959 device->name = dev_name; 957 960 958 /* Insert device into list of all devices 959 and into null devices array */ 961 /* 962 * Insert device into list of all devices and into null devices array. 963 * Insert device into a dummy list of null driver's devices so that it 964 * can be safely removed later. 965 */ 960 966 list_append(&device->devices, &devices_list); 967 list_append(&device->driver_devices, &dummy_null_driver_devices); 961 968 null_devices[i] = device; 962 969 … … 964 971 fibril_mutex_unlock(&null_devices_mutex); 965 972 966 ipc_answer_1(iid, EOK, (ipcarg_t) i);973 async_answer_1(iid, EOK, (sysarg_t) i); 967 974 } 968 975 969 976 static void devmap_null_destroy(ipc_callid_t iid, ipc_call_t *icall) 970 977 { 971 ipcarg_t i = IPC_GET_ARG1(*icall);978 sysarg_t i = IPC_GET_ARG1(*icall); 972 979 if (i >= NULL_DEVICES) { 973 ipc_answer_0(iid, ELIMIT);980 async_answer_0(iid, ELIMIT); 974 981 return; 975 982 } … … 979 986 if (null_devices[i] == NULL) { 980 987 fibril_mutex_unlock(&null_devices_mutex); 981 ipc_answer_0(iid, ENOENT);988 async_answer_0(iid, ENOENT); 982 989 return; 983 990 } … … 990 997 991 998 fibril_mutex_unlock(&null_devices_mutex); 992 ipc_answer_0(iid, EOK);999 async_answer_0(iid, EOK); 993 1000 } 994 1001 … … 1016 1023 { 1017 1024 /* Accept connection */ 1018 ipc_answer_0(iid, EOK);1025 async_answer_0(iid, EOK); 1019 1026 1020 1027 devmap_driver_t *driver = devmap_driver_register(); … … 1027 1034 ipc_callid_t callid = async_get_call(&call); 1028 1035 1029 switch (IPC_GET_ METHOD(call)) {1036 switch (IPC_GET_IMETHOD(call)) { 1030 1037 case IPC_M_PHONE_HUNGUP: 1031 1038 cont = false; … … 1033 1040 case DEVMAP_DRIVER_UNREGISTER: 1034 1041 if (NULL == driver) 1035 ipc_answer_0(callid, ENOENT);1042 async_answer_0(callid, ENOENT); 1036 1043 else 1037 ipc_answer_0(callid, EOK);1044 async_answer_0(callid, EOK); 1038 1045 break; 1039 1046 case DEVMAP_DEVICE_REGISTER: … … 1052 1059 break; 1053 1060 default: 1054 if (!(callid & IPC_CALLID_NOTIFICATION)) 1055 ipc_answer_0(callid, ENOENT); 1061 async_answer_0(callid, ENOENT); 1056 1062 } 1057 1063 } … … 1072 1078 { 1073 1079 /* Accept connection */ 1074 ipc_answer_0(iid, EOK);1080 async_answer_0(iid, EOK); 1075 1081 1076 1082 bool cont = true; … … 1079 1085 ipc_callid_t callid = async_get_call(&call); 1080 1086 1081 switch (IPC_GET_ METHOD(call)) {1087 switch (IPC_GET_IMETHOD(call)) { 1082 1088 case IPC_M_PHONE_HUNGUP: 1083 1089 cont = false; … … 1111 1117 break; 1112 1118 default: 1113 if (!(callid & IPC_CALLID_NOTIFICATION)) 1114 ipc_answer_0(callid, ENOENT); 1119 async_answer_0(callid, ENOENT); 1115 1120 } 1116 1121 } … … 1123 1128 { 1124 1129 /* Select interface */ 1125 switch (( ipcarg_t) (IPC_GET_ARG1(*icall))) {1130 switch ((sysarg_t) (IPC_GET_ARG1(*icall))) { 1126 1131 case DEVMAP_DRIVER: 1127 1132 devmap_connection_driver(iid, icall); … … 1136 1141 default: 1137 1142 /* No such interface */ 1138 ipc_answer_0(iid, ENOENT);1143 async_answer_0(iid, ENOENT); 1139 1144 } 1140 1145 } … … 1156 1161 1157 1162 /* Register device mapper at naming service */ 1158 ipcarg_t phonead; 1159 if (ipc_connect_to_me(PHONE_NS, SERVICE_DEVMAP, 0, 0, &phonead) != 0) 1163 if (service_register(SERVICE_DEVMAP) != EOK) 1160 1164 return -1; 1161 1165
Note:
See TracChangeset
for help on using the changeset viewer.