Changes in / [c9d5577:ff8ed06] in mainline
- Location:
- uspace
- Files:
-
- 4 added
- 11 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/app/virtusbkbd/virtusbkbd.c
rc9d5577 rff8ed06 271 271 printf("%s: Simulating keyboard events...\n", NAME); 272 272 fibril_sleep(10); 273 while (1) {273 //while (1) { 274 274 kb_process_events(&status, keyboard_events, keyboard_events_count, 275 275 on_keyboard_change); 276 }276 //} 277 277 278 278 printf("%s: Terminating...\n", NAME); -
uspace/drv/usbhub/main.c
rc9d5577 rff8ed06 52 52 while(true){ 53 53 usb_hub_check_hub_changes(); 54 async_usleep(1000 * 1000 );54 async_usleep(1000 * 1000 );/// \TODO proper number once 55 55 } 56 56 return 0; -
uspace/drv/usbhub/usbhub.c
rc9d5577 rff8ed06 46 46 #include "usbhub_private.h" 47 47 #include "port_status.h" 48 #include "usb/usb.h" 48 49 49 50 static usb_iface_t hub_usb_iface = { … … 85 86 86 87 // get hub descriptor 87 usb_target_t target;88 target.address = addr;89 target.endpoint = 0;90 usb_device_request_setup_packet_t request;91 //printf("[usb_hub] creating descriptor request\n");92 usb_hub_set_descriptor_request(&request);93 88 94 89 //printf("[usb_hub] creating serialized descriptor\n"); … … 98 93 int opResult; 99 94 //printf("[usb_hub] starting control transaction\n"); 100 opResult = usb_drv_sync_control_read( 101 hc, target, &request, serialized_descriptor, 95 96 opResult = usb_drv_req_get_descriptor(hc, addr, 97 USB_REQUEST_TYPE_CLASS, 98 USB_DESCTYPE_HUB, 0, 0, serialized_descriptor, 102 99 USB_HUB_MAX_DESCRIPTOR_SIZE, &received_size); 100 103 101 if (opResult != EOK) { 104 102 dprintf(1,"[usb_hub] failed when receiving hub descriptor, badcode = %d",opResult); … … 155 153 int port; 156 154 int opResult; 157 usb_device_request_setup_packet_t request;158 155 usb_target_t target; 159 156 target.address = hub_info->usb_device->address; … … 173 170 if(std_descriptor.configuration_count<1){ 174 171 dprintf(1,"[usb_hub] THERE ARE NO CONFIGURATIONS AVAILABLE"); 172 //shouldn`t I return? 175 173 } 176 174 /// \TODO check other configurations … … 184 182 } 185 183 //set configuration 186 request.request_type = 0; 187 request.request = USB_DEVREQ_SET_CONFIGURATION; 188 request.index=0; 189 request.length=0; 190 request.value_high=0; 191 request.value_low = config_descriptor.configuration_number; 192 opResult = usb_drv_sync_control_write(hc, target, &request, NULL, 0); 184 opResult = usb_drv_req_set_configuration(hc, target.address, 185 config_descriptor.configuration_number); 186 193 187 if (opResult != EOK) { 194 188 dprintf(1,"[usb_hub]something went wrong when setting hub`s configuration, %d", opResult); 195 189 } 196 190 191 usb_device_request_setup_packet_t request; 197 192 for (port = 1; port < hub_info->port_count+1; ++port) { 198 193 usb_hub_set_power_port_request(&request, port); … … 229 224 230 225 231 232 226 //********************************************* 233 227 // … … 237 231 238 232 /** 239 * convenience function for releasing default address and writing debug info240 * (these few lines are used too often to be written again and again) 233 * Convenience function for releasing default address and writing debug info 234 * (these few lines are used too often to be written again and again). 241 235 * @param hc 242 236 * @return … … 253 247 254 248 /** 255 * reset the port with new device and reserve the default address249 * Reset the port with new device and reserve the default address. 256 250 * @param hc 257 251 * @param port … … 282 276 283 277 /** 284 * finalize adding new device after port reset278 * Finalize adding new device after port reset 285 279 * @param hc 286 280 * @param port … … 345 339 346 340 /** 347 * unregister device address in hc341 * Unregister device address in hc 348 342 * @param hc 349 343 * @param port … … 355 349 int opResult; 356 350 357 /// \TODO remove device 351 /** \TODO remove device from device manager - not yet implemented in 352 * devide manager 353 */ 358 354 359 355 hub->attached_devs[port].devman_handle=0; … … 376 372 377 373 /** 378 * process interrupts on given hub port374 * Process interrupts on given hub port 379 375 * @param hc 380 376 * @param port … … 434 430 usb_port_set_reset_completed(&status, false); 435 431 usb_port_set_dev_connected(&status, false); 436 if (status) { 437 dprintf(1,"[usb_hub]there was some unsupported change on port %d",port); 432 if (status>>16) { 433 dprintf(1,"[usb_hub]there was some unsupported change on port %d: %X",port,status); 434 438 435 } 439 436 /// \TODO handle other changes … … 442 439 } 443 440 444 /* Check changes on all known hubs. 441 /** 442 * Check changes on all known hubs. 445 443 */ 446 444 void usb_hub_check_hub_changes(void) { … … 462 460 target.address = hub_info->usb_device->address; 463 461 target.endpoint = 1;/// \TODO get from endpoint descriptor 464 dprintf(1,"[usb_hub] checking changes for hub at addr %d",465 target.address); 462 /*dprintf(1,"[usb_hub] checking changes for hub at addr %d", 463 target.address);*/ 466 464 467 465 size_t port_count = hub_info->port_count; -
uspace/drv/usbhub/usbhub_private.h
rc9d5577 rff8ed06 31 31 */ 32 32 /** @file 33 * @brief Hub driver .33 * @brief Hub driver private definitions 34 34 */ 35 35 … … 68 68 69 69 /** 70 * create hub structure instance70 * Create hub structure instance 71 71 * 72 72 * Set the address and port count information most importantly. … … 78 78 usb_hub_info_t * usb_create_hub_info(device_t * device, int hc); 79 79 80 /** list of hubs maanged by this driver */80 /** List of hubs maanged by this driver */ 81 81 extern usb_general_list_t usb_hub_list; 82 82 83 /** lock for hub list*/83 /** Lock for hub list*/ 84 84 extern futex_t usb_hub_list_lock; 85 85 86 86 87 87 /** 88 * perform complete control read transaction88 * Perform complete control read transaction 89 89 * 90 * manages all three steps of transaction: setup, read and finalize90 * Manages all three steps of transaction: setup, read and finalize 91 91 * @param phone 92 92 * @param target 93 * @param request request for data94 * @param rcvd_buffer received data93 * @param request Request packet 94 * @param rcvd_buffer Received data 95 95 * @param rcvd_size 96 * @param actual_size actual size of received data96 * @param actual_size Actual size of received data 97 97 * @return error code 98 98 */ … … 104 104 105 105 /** 106 * perform complete control write transaction106 * Perform complete control write transaction 107 107 * 108 * manages all three steps of transaction: setup, write and finalize108 * Manages all three steps of transaction: setup, write and finalize 109 109 * @param phone 110 110 * @param target 111 * @param request request to send data111 * @param request Request packet to send data 112 112 * @param sent_buffer 113 113 * @param sent_size … … 121 121 122 122 /** 123 * set the device request to be a get hub descriptor request.123 * Set the device request to be a get hub descriptor request. 124 124 * @warning the size is allways set to USB_HUB_MAX_DESCRIPTOR_SIZE 125 125 * @param request … … 137 137 } 138 138 139 /** 140 * Clear feature on hub port. 141 * 142 * @param hc Host controller telephone 143 * @param address Hub address 144 * @param port_index Port 145 * @param feature Feature selector 146 * @return Operation result 147 */ 139 148 static inline int usb_hub_clear_port_feature(int hc, usb_address_t address, 140 149 int port_index, -
uspace/drv/usbkbd/Makefile
rc9d5577 rff8ed06 33 33 34 34 SOURCES = \ 35 main.c 35 main.c \ 36 descparser.c \ 37 descdump.c 36 38 37 39 include $(USPACE_PREFIX)/Makefile.common -
uspace/drv/usbkbd/main.c
rc9d5577 rff8ed06 38 38 #include <usb/devreq.h> 39 39 #include <usb/descriptor.h> 40 #include "descparser.h" 41 #include "descdump.h" 40 42 41 43 #define BUFFER_SIZE 32 … … 88 90 * Callbacks for parser 89 91 */ 90 static void usbkbd_process_keycodes(const uint16_t *key_codes, size_t count, 91 void *arg) 92 { 93 92 static void usbkbd_process_keycodes(const uint8_t *key_codes, size_t count, 93 uint8_t modifiers, void *arg) 94 { 95 printf("Got keys: "); 96 unsigned i; 97 for (i = 0; i < count; ++i) { 98 printf("%d ", key_codes[i]); 99 } 100 printf("\n"); 94 101 } 95 102 … … 97 104 * Kbd functions 98 105 */ 99 static int usbkbd_parse_descriptors(usb_hid_dev_kbd_t *kbd_dev, 100 const uint8_t *data, size_t size) 101 { 102 // const uint8_t *pos = data; 103 104 // // get the configuration descriptor (should be first) 105 // if (*pos != sizeof(usb_standard_configuration_descriptor_t) 106 // || *(pos + 1) != USB_DESCTYPE_CONFIGURATION) { 107 // fprintf(stderr, "Wrong format of configuration descriptor"); 108 // return EINVAL; 109 // } 110 111 // usb_standard_configuration_descriptor_t config_descriptor; 112 // memcpy(&config_descriptor, pos, 113 // sizeof(usb_standard_configuration_descriptor_t)); 114 // pos += sizeof(usb_standard_configuration_descriptor_t); 115 116 // // parse other descriptors 117 // while (pos - data < size) { 118 // //uint8_t desc_size = *pos; 119 // uint8_t desc_type = *(pos + 1); 120 // switch (desc_type) { 121 // case USB_DESCTYPE_INTERFACE: 122 // break; 123 // case USB_DESCTYPE_ENDPOINT: 124 // break; 125 // case USB_DESCTYPE_HID: 126 // break; 127 // case USB_DESCTYPE_HID_REPORT: 128 // break; 129 // case USB_DESCTYPE_HID_PHYSICAL: 130 // break; 131 // } 132 // } 133 106 static int usbkbd_get_report_descriptor(usb_hid_dev_kbd_t *kbd_dev) 107 { 108 // iterate over all configurations and interfaces 109 // TODO: more configurations!! 110 unsigned i; 111 for (i = 0; i < kbd_dev->conf->config_descriptor.interface_count; ++i) { 112 // TODO: endianness 113 uint16_t length = 114 kbd_dev->conf->interfaces[i].hid_desc.report_desc_info.length; 115 size_t actual_size = 0; 116 117 // allocate space for the report descriptor 118 kbd_dev->conf->interfaces[i].report_desc = (uint8_t *)malloc(length); 119 120 // get the descriptor from the device 121 int rc = usb_drv_req_get_descriptor(kbd_dev->device->parent_phone, 122 kbd_dev->address, USB_REQUEST_TYPE_CLASS, USB_DESCTYPE_HID_REPORT, 123 0, i, kbd_dev->conf->interfaces[i].report_desc, length, 124 &actual_size); 125 126 if (rc != EOK) { 127 return rc; 128 } 129 130 assert(actual_size == length); 131 132 //dump_hid_class_descriptor(0, USB_DESCTYPE_HID_REPORT, 133 // kbd_dev->conf->interfaces[i].report_desc, length); 134 } 135 134 136 return EOK; 135 137 } 136 138 137 static int usbkbd_ get_descriptors(usb_hid_dev_kbd_t *kbd_dev)138 { 139 // get the first configuration descriptor (TODO: or some other??)139 static int usbkbd_process_descriptors(usb_hid_dev_kbd_t *kbd_dev) 140 { 141 // get the first configuration descriptor (TODO: parse also other!) 140 142 usb_standard_configuration_descriptor_t config_desc; 141 143 … … 166 168 } 167 169 168 rc = usbkbd_parse_descriptors(kbd_dev, descriptors, transferred); 170 kbd_dev->conf = (usb_hid_configuration_t *)calloc(1, 171 sizeof(usb_hid_configuration_t)); 172 if (kbd_dev->conf == NULL) { 173 free(descriptors); 174 return ENOMEM; 175 } 176 177 rc = usbkbd_parse_descriptors(descriptors, transferred, kbd_dev->conf); 169 178 free(descriptors); 170 171 return rc; 179 if (rc != EOK) { 180 printf("Problem with parsing standard descriptors.\n"); 181 return rc; 182 } 183 184 // get and report descriptors 185 rc = usbkbd_get_report_descriptor(kbd_dev); 186 if (rc != EOK) { 187 printf("Problem with parsing HID REPORT descriptor.\n"); 188 return rc; 189 } 190 191 //usbkbd_print_config(kbd_dev->conf); 192 193 /* 194 * TODO: 195 * 1) select one configuration (lets say the first) 196 * 2) how many interfaces?? how to select one?? 197 * ("The default setting for an interface is always alternate setting zero.") 198 * 3) find endpoint which is IN and INTERRUPT (parse), save its number 199 * as the endpoint for polling 200 */ 201 202 return EOK; 172 203 } 173 204 174 205 static usb_hid_dev_kbd_t *usbkbd_init_device(device_t *dev) 175 206 { 176 usb_hid_dev_kbd_t *kbd_dev = (usb_hid_dev_kbd_t *) malloc(177 207 usb_hid_dev_kbd_t *kbd_dev = (usb_hid_dev_kbd_t *)calloc(1, 208 sizeof(usb_hid_dev_kbd_t)); 178 209 179 210 if (kbd_dev == NULL) { … … 186 217 // get phone to my HC and save it as my parent's phone 187 218 // TODO: maybe not a good idea if DDF will use parent_phone 188 kbd_dev->device->parent_phone = usb_drv_hc_connect_auto(dev, 0); 189 190 kbd_dev->address = usb_drv_get_my_address(dev->parent_phone, 191 dev); 219 int rc = kbd_dev->device->parent_phone = usb_drv_hc_connect_auto(dev, 0); 220 if (rc < 0) { 221 printf("Problem setting phone to HC.\n"); 222 free(kbd_dev); 223 return NULL; 224 } 225 226 rc = kbd_dev->address = usb_drv_get_my_address(dev->parent_phone, dev); 227 if (rc < 0) { 228 printf("Problem getting address of the device.\n"); 229 free(kbd_dev); 230 return NULL; 231 } 192 232 193 233 // doesn't matter now that we have no address … … 208 248 */ 209 249 210 // TODO: get descriptors 211 usbkbd_get_descriptors(kbd_dev); 212 // TODO: parse descriptors and save endpoints 250 // TODO: get descriptors, parse descriptors and save endpoints 251 usbkbd_process_descriptors(kbd_dev); 213 252 214 253 return kbd_dev; … … 218 257 uint8_t *buffer, size_t actual_size) 219 258 { 220 /*221 * here, the parser will be called, probably with some callbacks222 * now only take last 6 bytes and process, i.e. send to kbd223 */224 225 259 usb_hid_report_in_callbacks_t *callbacks = 226 260 (usb_hid_report_in_callbacks_t *)malloc( … … 228 262 callbacks->keyboard = usbkbd_process_keycodes; 229 263 230 usb_hid_parse_report(kbd_dev->parser, buffer, actual_size, callbacks, 231 NULL); 264 //usb_hid_parse_report(kbd_dev->parser, buffer, actual_size, callbacks, 265 // NULL); 266 printf("Calling usb_hid_boot_keyboard_input_report()...\n)"); 267 usb_hid_boot_keyboard_input_report(buffer, actual_size, callbacks, NULL); 232 268 } 233 269 234 270 static void usbkbd_poll_keyboard(usb_hid_dev_kbd_t *kbd_dev) 235 271 { 272 return; 273 236 274 int rc; 237 275 usb_handle_t handle; -
uspace/drv/vhc/hcd.c
rc9d5577 rff8ed06 116 116 sleep(5); 117 117 118 usb_dprintf_enable(NAME, 0);118 usb_dprintf_enable(NAME, -1); 119 119 120 120 printf(NAME ": virtual USB host controller driver.\n"); -
uspace/lib/usb/include/usb/classes/hid.h
rc9d5577 rff8ed06 39 39 #include <driver.h> 40 40 #include <usb/classes/hidparser.h> 41 #include <usb/descriptor.h> 41 42 42 43 /** USB/HID device requests. */ … … 63 64 */ 64 65 typedef struct { 65 /** Type of class descriptor (Report or Physical). */66 uint8_t class_descriptor_type;67 /** Length of class descriptor. */68 uint16_t class_descriptor_length;69 } __attribute__ ((packed)) usb_standard_hid_ descriptor_class_item_t;66 /** Type of class-specific descriptor (Report or Physical). */ 67 uint8_t type; 68 /** Length of class-specific descriptor in bytes. */ 69 uint16_t length; 70 } __attribute__ ((packed)) usb_standard_hid_class_descriptor_info_t; 70 71 71 72 /** Standard USB HID descriptor. … … 73 74 * (See HID Specification, p.22) 74 75 * 75 * It is actually only the "header" of the descriptor, as it may have arbitrary 76 * length if more than one class descritor is provided. 76 * It is actually only the "header" of the descriptor, it does not contain 77 * the last two mandatory fields (type and length of the first class-specific 78 * descriptor). 77 79 */ 78 80 typedef struct { 79 /** Size of this descriptor in bytes. */ 81 /** Total size of this descriptor in bytes. 82 * 83 * This includes all class-specific descriptor info - type + length 84 * for each descriptor. 85 */ 80 86 uint8_t length; 81 87 /** Descriptor type (USB_DESCTYPE_HID). */ … … 85 91 /** Country code of localized hardware. */ 86 92 uint8_t country_code; 87 /** Total number of class (i.e. Report and Physical) descriptors. */ 88 uint8_t class_count; 89 /** First mandatory class descriptor info. */ 90 usb_standard_hid_descriptor_class_item_t class_descriptor; 93 /** Total number of class-specific (i.e. Report and Physical) 94 * descriptors. 95 * 96 * @note There is always only one Report descriptor. 97 */ 98 uint8_t class_desc_count; 99 /** First mandatory class descriptor (Report) info. */ 100 usb_standard_hid_class_descriptor_info_t report_desc_info; 91 101 } __attribute__ ((packed)) usb_standard_hid_descriptor_t; 92 102 103 /** 104 * 105 */ 106 typedef struct { 107 usb_standard_interface_descriptor_t iface_desc; 108 usb_standard_endpoint_descriptor_t *endpoints; 109 usb_standard_hid_descriptor_t hid_desc; 110 uint8_t *report_desc; 111 //usb_standard_hid_class_descriptor_info_t *class_desc_info; 112 //uint8_t **class_descs; 113 } usb_hid_iface_t; 114 115 /** 116 * 117 */ 118 typedef struct { 119 usb_standard_configuration_descriptor_t config_descriptor; 120 usb_hid_iface_t *interfaces; 121 } usb_hid_configuration_t; 93 122 94 123 /** … … 99 128 typedef struct { 100 129 device_t *device; 130 usb_hid_configuration_t *conf; 101 131 usb_address_t address; 102 132 usb_endpoint_t poll_endpoint; … … 104 134 } usb_hid_dev_kbd_t; 105 135 136 // TODO: more configurations! 137 106 138 #endif 107 139 /** -
uspace/lib/usb/include/usb/classes/hidparser.h
rc9d5577 rff8ed06 38 38 #include <stdint.h> 39 39 40 /** 41 * Description of report items 42 */ 43 typedef struct { 44 45 uint8_t usage_min; 46 uint8_t usage_max; 47 uint8_t logical_min; 48 uint8_t logical_max; 49 uint8_t size; 50 uint8_t count; 51 uint8_t offset; 52 53 } usb_hid_report_item_t; 54 55 40 56 /** HID report parser structure. */ 41 57 typedef struct { 42 58 } usb_hid_report_parser_t; 59 43 60 44 61 /** HID parser callbacks for IN items. */ … … 50 67 * @param arg Custom argument. 51 68 */ 52 void (*keyboard)(const uint 16_t *key_codes, size_t count, void *arg);69 void (*keyboard)(const uint8_t *key_codes, size_t count, const uint8_t modifiers, void *arg); 53 70 } usb_hid_report_in_callbacks_t; 71 72 #define USB_HID_BOOT_KEYBOARD_NUM_LOCK 0x01 73 #define USB_HID_BOOT_KEYBOARD_CAPS_LOCK 0x02 74 #define USB_HID_BOOT_KEYBOARD_SCROLL_LOCK 0x04 75 #define USB_HID_BOOT_KEYBOARD_COMPOSE 0x08 76 #define USB_HID_BOOT_KEYBOARD_KANA 0x10 77 78 /* 79 * modifiers definitions 80 */ 81 82 int usb_hid_boot_keyboard_input_report(const uint8_t *data, size_t size, 83 const usb_hid_report_in_callbacks_t *callbacks, void *arg); 84 85 int usb_hid_boot_keyboard_output_report(uint8_t leds, uint8_t *data, size_t size); 54 86 55 87 int usb_hid_parse_report_descriptor(usb_hid_report_parser_t *parser, … … 60 92 const usb_hid_report_in_callbacks_t *callbacks, void *arg); 61 93 94 95 int usb_hid_free_report_parser(usb_hid_report_parser_t *parser); 96 62 97 #endif 63 98 /** -
uspace/lib/usb/src/hidparser.c
rc9d5577 rff8ed06 40 40 * @param parser Opaque HID report parser structure. 41 41 * @param data Data describing the report. 42 * @param size Size of the descriptor in bytes.43 42 * @return Error code. 44 43 */ … … 55 54 * @param parser Opaque HID report parser structure. 56 55 * @param data Data for the report. 57 * @param size Size of the data in bytes.58 56 * @param callbacks Callbacks for report actions. 59 57 * @param arg Custom argument (passed through to the callbacks). … … 66 64 int i; 67 65 68 // TODO: parse report 66 /* main parsing loop */ 67 while(0){ 68 } 69 69 70 uint16_t keys[6]; 70 71 uint8_t keys[6]; 71 72 72 73 for (i = 0; i < 6; ++i) { … … 74 75 } 75 76 76 callbacks->keyboard(keys, 6, arg); 77 77 callbacks->keyboard(keys, 6, 0, arg); 78 79 return EOK; 80 } 81 82 /** Free the HID report parser structure 83 * 84 * @param parser Opaque HID report parser structure 85 * @return Error code 86 */ 87 int usb_hid_free_report_parser(usb_hid_report_parser_t *parser) 88 { 89 78 90 return EOK; 79 91 } … … 81 93 82 94 /** 95 * Parse input report. 96 * 97 * @param data Data for report 98 * @param size Size of report 99 * @param callbacks Callbacks for report actions 100 * @param arg Custom arguments 101 * 102 * @return Error code 103 */ 104 int usb_hid_boot_keyboard_input_report(const uint8_t *data, size_t size, 105 const usb_hid_report_in_callbacks_t *callbacks, void *arg) 106 { 107 int i; 108 usb_hid_report_item_t item; 109 110 /* fill item due to the boot protocol report descriptor */ 111 // modifier keys are in the first byte 112 uint8_t modifiers = data[0]; 113 114 item.offset = 2; /* second byte is reserved */ 115 item.size = 8; 116 item.count = 6; 117 item.usage_min = 0; 118 item.usage_max = 255; 119 item.logical_min = 0; 120 item.logical_max = 255; 121 122 if(size != 8){ 123 return -1; 124 } 125 126 uint8_t keys[6]; 127 for(i=item.offset; i<item.count; i++) { 128 keys[i-2] = data[i]; 129 } 130 131 callbacks->keyboard(keys, 6, modifiers, arg); 132 return EOK; 133 } 134 135 /** 136 * Makes output report for keyboard boot protocol 137 * 138 * @param leds 139 * @param output Output report data buffer 140 * @param size Size of the output buffer 141 * @return Error code 142 */ 143 int usb_hid_boot_keyboard_output_report(uint8_t leds, uint8_t *data, size_t size) 144 { 145 if(size != 1){ 146 return -1; 147 } 148 149 /* used only first five bits, others are only padding*/ 150 *data = leds; 151 return EOK; 152 } 153 154 /** 83 155 * @} 84 156 */ -
uspace/srv/devman/main.c
rc9d5577 rff8ed06 479 479 } 480 480 481 printf(NAME ": devman_forward: forward connection to device %s to "482 "driver %s.\n", dev->pathname, driver->name);481 // printf(NAME ": devman_forward: forward connection to device %s to " 482 // "driver %s.\n", dev->pathname, driver->name); 483 483 ipc_forward_fast(iid, driver->phone, method, dev->handle, 0, IPC_FF_NONE); 484 484 }
Note:
See TracChangeset
for help on using the changeset viewer.