Exemple #1
0
void usb_handle_StandardInterfaceRequest(BDentry *bdp) {
    BYTE *packet = bdp->BDADDR;

    switch (packet[USB_bRequest]) {
        case USB_REQUEST_GET_STATUS:
            EP0_Inbdp->BDADDR[0] = 0x00;
            EP0_Inbdp->BDADDR[1] = 0x00;
            usb_ack_dat1(2);
            break;
        case USB_REQUEST_GET_INTERFACE:
            if (USB_NUM_INTERFACES > packet[USB_bInterface]) {
                // TODO: Implement alternative interfaces, or move responsibility to class/vendor functions.
                EP0_Inbdp->BDADDR[0] = 0;
                usb_ack_dat1(1);
            } else
                usb_RequestError();
            break;
        case USB_REQUEST_SET_INTERFACE:
            if (USB_NUM_INTERFACES > packet[USB_bInterface] && 0u == packet[USB_wValue]) {
                // TODO: Implement alternative interfaces...
                usb_ack_dat1(0);
            } else
                usb_RequestError();
            break;
        case USB_REQUEST_CLEAR_FEATURE: // JTR N/A for interface
        case USB_REQUEST_SET_FEATURE: // This is correct and finished code.
        default:
            usb_RequestError();
    }
}
Exemple #2
0
void usb_handle_StandardInterfaceRequest(BDentry *bdp) {
    unsigned char *packet = bdp->BDADDR;

    switch (packet[USB_bRequest]) {
        case USB_REQUEST_GET_STATUS:
            rbdp->BDADDR[0] = 0x00;
            rbdp->BDADDR[1] = 0x00;
            usb_ack_dat1(rbdp, 2); // JTR common addition for STD and CLASS ACK
            //		rbdp->BDCNT = 2;
            //		rbdp->BDSTAT = UOWN + DTS + DTSEN;
            break;
        case USB_REQUEST_GET_INTERFACE:
            if (USB_NUM_INTERFACES > packet[USB_bInterface]) {
                // TODO: Implement alternative interfaces, or move responsibility to class/vendor functions.
                rbdp->BDADDR[0] = 0;
                usb_ack_dat1(rbdp, 1); // JTR common addition for STD and CLASS ACK
            } else
                usb_RequestError();
            break;
        case USB_REQUEST_SET_INTERFACE:
            if (USB_NUM_INTERFACES > packet[USB_bInterface] && 0u == packet[USB_wValue]) {
                // TODO: Implement alternative interfaces...
                usb_ack_dat1(rbdp, 0); // JTR common addition for STD and CLASS ACK
            } else
                usb_RequestError();
            break;
        case USB_REQUEST_CLEAR_FEATURE: // JTR N/A for interface
        case USB_REQUEST_SET_FEATURE: // This is correct and finished code.
        default:
            usb_RequestError();
    }
}
Exemple #3
0
void usb_handle_setup(void) {
    rbdp->BDSTAT = DTSEN; // Reclaim reply buffer
    switch (bdp->BDADDR[USB_bmRequestType] & USB_bmRequestType_TypeMask) {
        case USB_bmRequestType_Standard:
            switch (bdp->BDADDR[USB_bmRequestType] & USB_bmRequestType_RecipientMask) {
                case USB_bmRequestType_Device:
                    usb_handle_StandardDeviceRequest(bdp);
                    break;
                case USB_bmRequestType_Interface:
                    usb_handle_StandardInterfaceRequest(bdp);
                    break;
                case USB_bmRequestType_Endpoint:
                    usb_handle_StandardEndpointRequest(bdp);
                    break;
                default:
                    usb_RequestError();
            }
            break;
        case USB_bmRequestType_Class:
            if (class_setup_handler) class_setup_handler();
            break;
        case USB_bmRequestType_Vendor:
            if (vendor_setup_handler) class_setup_handler();
            break;
        default:
            usb_RequestError();
    }
    /* Prepare endpoint for new reception */

    bdp->BDCNT = USB_EP0_BUFFER_SIZE; // Size  of EP0, should always be ep0? (JTR in practice YES)

    // JTR, is the next OUT transfer to be a setup packet (DAT0) or a DATA packet (DAT1)?
    // note that this is not an entirely robust way of doing things. See the microchip stack for
    // further comments and a better system as this does not account for errors and retries.

    // JTR Breakdown of what is happening here FYI.

    //bdp->BDSTAT = (!(bdp->BDADDR[USB_bmRequestType] & USB_bmRequestType_PhaseMask) &&
    // JTR meaning. If transfer was a CONTROL OUT then B7 (USB_bmRequestType_PhaseMask) of bmRequestType is '0'

    // &&

    //bdp->BDADDR[USB_wLength] || bdp->BDADDR[USB_wLengthHigh]))? UOWN + DTS + DTSEN : UOWN + DTSEN;
    // If there is a CONTROL OUT DATA PACKET to follow then its count in (int) wLength will be != 0
    // When both conditions are true then set for DAT1 (UOWN + DTS + DTSEN)
    // else set for DAT0 (UOWN + DTSEN)

    // See USB 2.0 spec 8.5.3

    bdp->BDSTAT = (!(bdp->BDADDR[USB_bmRequestType] & USB_bmRequestType_PhaseMask) &&
            (bdp->BDADDR[USB_wLength] || bdp->BDADDR[USB_wLengthHigh])) ? UOWN + DTS + DTSEN : UOWN + DTSEN;

    // JTR Note. For rbdp after a setup packet, the standard or class request handler will force the correct DTS state
    // JTR Note. that CONTROL IN and OUT DATA packet transfers do not come back here and there is no
    // univesal way and place of setting up EP0 after these DATA transfers in this stack.

    // JTR Note. that there is a PIC18 silicon errata issue that this does not address by being here.  See DS80220F-page 6
    EnablePacketTransfer();
}
void cdc_setup(void) {
    BYTE *packet;
    size_t reply_len;
    packet = EP0_Outbdp->BDADDR;

    switch (packet[USB_bmRequestType] & (USB_bmRequestType_TypeMask | USB_bmRequestType_RecipientMask)) {
        case (USB_bmRequestType_Class | USB_bmRequestType_Interface):
            switch (packet[USB_bRequest]) {

                    //JTR This is just a dummy, nothing defined to do for CDC ACM
                case CDC_SEND_ENCAPSULATED_COMMAND:
                    usb_ack_dat1(0);
                    break;

                    //JTR This is just a dummy, nothing defined to do for CDC ACM
                case CDC_GET_ENCAPSULATED_RESPONSE:
                    //usb_ack_zero(rbdp);
                    usb_ack_dat1(0);
                    break;

                case CDC_SET_COMM_FEATURE: // Optional
                case CDC_GET_COMM_FEATURE: // Optional
                case CDC_CLEAR_COMM_FEATURE: // Optional
                    usb_RequestError(); // Not advertised in ACM functional descriptor
                    break;

                case CDC_SET_LINE_CODING: // Optional, strongly recomended
                    usb_set_out_handler(0, cdc_set_line_coding_data); // Register out handler function
                    break;

                case CDC_GET_LINE_CODING: // Optional, strongly recomended
                    // JTR reply length (7) is always going to be less than minimum EP0 size (8)

                    reply_len = *((unsigned int *) &packet[USB_wLength]);
                    if (sizeof (struct cdc_LineCodeing) < reply_len) {
                        reply_len = sizeof (struct cdc_LineCodeing);
                    }
                    memcpy(EP0_Inbdp->BDADDR, (const void *) &linecodeing, reply_len);
                    usb_ack_dat1(reply_len); // JTR common addition for STD and CLASS ACK
                    usb_set_in_handler(0, cdc_get_line_coding);
                    break;

                case CDC_SET_CONTROL_LINE_STATE: // Optional
                    cls = *((struct _cdc_ControlLineState *) &packet[USB_wValue]);
                    usb_set_in_handler(0, cdc_set_control_line_state_status); // JTR why bother?
                    usb_ack_dat1(0); // JTR common addition for STD and CLASS ACK
                    LineStateUpdated = 1;
                    break;

                case CDC_SEND_BREAK: // Optional
                default:
                    usb_RequestError();
            }
            break;
        default:
            usb_RequestError();
    }
}
Exemple #5
0
void usb_handle_setup(void) {

    EP0_Inbdp->BDSTAT = DTSEN; // Reclaim reply buffer

    EnablePacketTransfer(); // JTR this is placed here to overcome a errate issue with early PIC18 USB pics.

    switch (EP0_Outbdp->BDADDR[USB_bmRequestType] & USB_bmRequestType_TypeMask) {
        case USB_bmRequestType_Standard:
            switch (EP0_Outbdp->BDADDR[USB_bmRequestType] & USB_bmRequestType_RecipientMask) {
                case USB_bmRequestType_Device:
                    usb_handle_StandardDeviceRequest(EP0_Outbdp);
                    break;
                case USB_bmRequestType_Interface:
                    usb_handle_StandardInterfaceRequest(EP0_Outbdp);
                    break;
                case USB_bmRequestType_Endpoint:
                    usb_handle_StandardEndpointRequest(EP0_Outbdp);
                    break;
                default:
                    usb_RequestError();
            }
            break;
        case USB_bmRequestType_Class:
            if (class_setup_handler) class_setup_handler();
            break;
        case USB_bmRequestType_Vendor:
			//ROBOTS FIX: http://dangerousprototypes.com/forum/viewtopic.php?f=39&t=3849&view=unread#unread
			// did call class_setup_handler();
            if (vendor_setup_handler) vendor_setup_handler();
            break;
        default:
            usb_RequestError();
    }
    /* Prepare endpoint for new reception */

    EP0_Outbdp->BDCNT = USB_EP0_BUFFER_SIZE; // Size  of EP0, should always be ep0?

    // JTR, is the next OUT transfer to be a setup packet (DAT0) or a DATA packet (DAT1)?
    // note that this is not an entirely robust way of doing things. See the microchip stack for
    // further comments and a better system as this does not account for errors and retries
    // and it results in the SIE not accepting the final out ZLP status packet for IN transfers
    // with a data stage. However it works but it is not anything to be proud of...

    EP0_Outbdp->BDSTAT = (!(EP0_Outbdp->BDADDR[USB_bmRequestType] & USB_bmRequestType_PhaseMask) &&
            (EP0_Outbdp->BDADDR[USB_wLength] || EP0_Outbdp->BDADDR[USB_wLengthHigh])) ? UOWN + DTS + DTSEN : UOWN + DTSEN;
}
Exemple #6
0
void usb_handle_StandardEndpointRequest(BDentry *bdp) {
    unsigned char *packet;
    unsigned char epnum;
    unsigned char dir;
    BDentry *epbd;

    packet = bdp->BDADDR;

    switch (packet[USB_bRequest]) {
        case USB_REQUEST_GET_STATUS:
            rbdp->BDADDR[0] = 0x00; // Assume no stall
            rbdp->BDADDR[1] = 0x00; // Same for stall or not
            epnum = packet[USB_wIndex] & 0x0F;
            dir = packet[USB_wIndex] >> 7;
            epbd = &usb_bdt[USB_CALC_BD(epnum, dir, USB_PP_EVEN)];
            if (epbd->BDSTAT &= ~BSTALL)
                rbdp->BDADDR[0] = 0x01; // EVEN BD is stall flag set?
            epbd = &usb_bdt[USB_CALC_BD(epnum, dir, USB_PP_ODD)];
            if (epbd->BDSTAT &= ~BSTALL)
                rbdp->BDADDR[0] = 0x01; // ODD BD is stall flag set?
            usb_ack_dat1(rbdp, 2); // JTR common addition for STD and CLASS ACK
            break;

        case USB_REQUEST_CLEAR_FEATURE:
            epnum = packet[USB_wIndex] & 0x0F;
            dir = packet[USB_wIndex] >> 7;
            epbd = &usb_bdt[USB_CALC_BD(epnum, dir, USB_PP_EVEN)];
            epbd->BDSTAT &= ~BSTALL;
            if (dir) epbd->BDSTAT |= DTS; // JTR added IN EP set DTS as it will be toggled to zero next transfer
            if (0 == dir) epbd->BDSTAT &= ~DTS; // JTR added

            // JTR this pointless ATM. If ping-pong is enabled then you need to track PPBI
            // and set up ODD and EVEN BDs in respect to this. See complicated system in
            // microchip stack >= 2.8

            //		epbd = &usb_bdt[USB_CALC_BD(epnum, dir, USB_PP_ODD)];
            //		epbd->BDSTAT &= ~BSTALL;
            //		if (dir) epbd->BDSTAT |= DTS;		// JTR added
            //		if (0 == dir) epbd->BDSTAT &= ~DTS;	// JTR added


            usb_ack_dat1(rbdp, 0); // JTR common addition for STD and CLASS ACK
            //		rbdp->BDCNT = 0;
            //		rbdp->BDSTAT = UOWN + DTS + DTSEN;
            break;


        case USB_REQUEST_SET_FEATURE:
            epnum = packet[USB_wIndex] & 0x0F;
            dir = packet[USB_wIndex] >> 7;
            epbd = &usb_bdt[USB_CALC_BD(epnum, dir, USB_PP_EVEN)];
            epbd->BDSTAT |= BSTALL;
            epbd = &usb_bdt[USB_CALC_BD(epnum, dir, USB_PP_ODD)];
            epbd->BDSTAT |= BSTALL;
            usb_ack_dat1(rbdp, 0); // JTR common addition for STD and CLASS ACK
            //		rbdp->BDCNT = 0;
            //		rbdp->BDSTAT = UOWN + DTS + DTSEN;
            break;
        case USB_REQUEST_SYNCH_FRAME:
        default:
            usb_RequestError();
    }
}
Exemple #7
0
void usb_handle_StandardDeviceRequest(BDentry *bdp) {
    unsigned char *packet = bdp->BDADDR;
    int i;

    switch (packet[USB_bRequest]) {
        case USB_REQUEST_GET_STATUS:
            rbdp->BDADDR[0] = usb_device_status & 0xFF;
            rbdp->BDADDR[1] = usb_device_status >> 8;

            // JTR I added usb_ack_dat1() simply so that the handling
            // of STANDARD and CLASS requests (in cdc.c) were consistant
            // rather than have the same thing done in two different ways.

            usb_ack_dat1(rbdp, 2); // JTR common addition for STD and CLASS ACK
            break;
        case USB_REQUEST_CLEAR_FEATURE:
            if (0x01u == packet[USB_wValue]) { // TODO: Remove magic (REMOTE_WAKEUP_FEATURE)
                usb_device_status &= ~0x0002;
                usb_ack_dat1(rbdp, 0); // JTR common addition for STD and CLASS ACK
            } else
                usb_RequestError();
            break;
        case USB_REQUEST_SET_FEATURE:
            if (0x01u == packet[USB_wValue]) { // TODO: Remove magic (REMOTE_WAKEUP_FEATURE)
                usb_device_status |= 0x0002;
                usb_ack_dat1(rbdp, 0); // JTR common addition for STD and CLASS ACK
            } else
                usb_RequestError();
            break;
        case USB_REQUEST_SET_ADDRESS:
            if (0x00u == packet[USB_wValueHigh] && 0x7Fu >= packet[USB_wValue]) {
                usb_addr_pending = packet[USB_wValue];
                usb_ack_dat1(rbdp, 0); // JTR common addition for STD and CLASS ACK
                usb_set_in_handler(0, usb_set_address);
            } else
                usb_RequestError();
            break;


        case USB_REQUEST_GET_DESCRIPTOR:
            switch (packet[USB_bDescriptorType]) {
                case USB_DEVICE_DESCRIPTOR_TYPE:
                    usb_desc_ptr = usb_device_descriptor;
                    usb_desc_len = usb_device_descriptor[0];
                    if ((0 == packet[USB_wLengthHigh] && packet[USB_wLength] < usb_desc_ptr[0]))
                        usb_desc_len = packet[USB_wLength];
                    break;
                case USB_CONFIGURATION_DESCRIPTOR_TYPE:
                    if (packet[USB_bDescriptorIndex] >= usb_device_descriptor[17]) // TODO: remove magic
                        usb_RequestError();
                    usb_desc_ptr = usb_config_descriptor;
                    usb_desc_len = usb_desc_ptr[2] + usb_desc_ptr[3] * 256;
                    for (i = 0; i < packet[USB_bDescriptorIndex]; i++) { // Implicit linked list traversal until requested configuration
                        usb_desc_ptr += usb_desc_len;
                        usb_desc_len = usb_desc_ptr[2] + usb_desc_ptr[3] * 256;
                    }
                    if ((packet[USB_wLengthHigh] < usb_desc_ptr[3]) ||
                            (packet[USB_wLengthHigh] == usb_desc_ptr[3] && packet[USB_wLength] < usb_desc_ptr[2]))
                        usb_desc_len = packet[USB_wLength] + packet[USB_wLengthHigh] * 256;
                    break;
                case USB_STRING_DESCRIPTOR_TYPE:
                    // TODO: Handle language request. For now return standard language.
                    if (packet[USB_bDescriptorIndex] >= usb_num_string_descriptors)
                        usb_RequestError();
                    usb_desc_ptr = usb_string_descriptor;
                    usb_desc_len = usb_desc_ptr[0];
                    for (i = 0; i < packet[USB_bDescriptorIndex]; i++) { // Implicit linked list traversal until requested configuration
                        usb_desc_ptr += usb_desc_len;
                        usb_desc_len = usb_desc_ptr[0];
                    }
                    if ((0 == packet[USB_wLengthHigh] && packet[USB_wLength] < usb_desc_ptr[0]))
                        usb_desc_len = packet[USB_wLength];
                    break;
                case USB_INTERFACE_DESCRIPTOR_TYPE:
                case USB_ENDPOINT_DESCRIPTOR_TYPE:
                default:
                    usb_RequestError();
            }
            usb_send_descriptor(); // Send first part of packet right away
            usb_set_in_handler(0, usb_send_descriptor);
            break;
        case USB_REQUEST_GET_CONFIGURATION:
            rbdp->BDADDR[0] = usb_current_cfg;
            usb_ack_dat1(rbdp, 1); // JTR common addition for STD and CLASS ACK
            //		rbdp->BDCNT = 1;
            //		rbdp->BDSTAT = UOWN + DTS + DTSEN;
            break;

        case USB_REQUEST_SET_CONFIGURATION:
            if (USB_NUM_CONFIGURATIONS >= packet[USB_wValue]) {
                // TODO: Support multiple configurations
                /* Configure endpoints (USB_UEPn - registers) */
                usb_current_cfg = packet[USB_wValue];
                if (usb_current_cfg != 0) {

                    // JTR user_configured_init major addition. This is a CALLBACK to the USER when the device is enumerated.
                    // This is when we setup non EP0 endpoints.
                    // TODO: This really could be a function pointer

                    usb_device_state = CONFIGURED_STATE;
                    user_configured_init();
                } else {
                    usb_device_state = ADDRESS_STATE;
                }

                usb_ack_dat1(rbdp, 0); // JTR common addition for STD and CLASS ACK

            } else
                usb_RequestError();
            break;

        case USB_REQUEST_SET_DESCRIPTOR:
        default:
            usb_RequestError();
    }
}
Exemple #8
0
void usb_handle_StandardEndpointRequest(BDentry *bdp) {
    BYTE *packet;
    BYTE epnum;
    BYTE dir;
    BDentry *epbd;
    usb_uep_t *pUEP;

    packet = bdp->BDADDR;

    switch (packet[USB_bRequest]) {
        case USB_REQUEST_GET_STATUS:
            EP0_Inbdp->BDADDR[0] = 0x00; // Assume no stall
            EP0_Inbdp->BDADDR[1] = 0x00; // Same for stall or not
            epnum = packet[USB_wIndex] & 0x0F;
            dir = packet[USB_wIndex] >> 7;
            epbd = &usb_bdt[USB_CALC_BD(epnum, dir, USB_PP_EVEN)];
            if (epbd->BDSTAT &= ~BSTALL)
                EP0_Inbdp->BDADDR[0] = 0x01; // EVEN BD is stall flag set?
            //epbd = &usb_bdt[USB_CALC_BD(epnum, dir, USB_PP_ODD)];
            //if (epbd->BDSTAT &= ~BSTALL)
            //    rbdp->BDADDR[0] = 0x01; // ODD BD is stall flag set?
            usb_ack_dat1(2);
            break;

        case USB_REQUEST_CLEAR_FEATURE:
            // As this is really is an application event and there
            // should be a call back and protocol for handling the
            // possible lost of a data packet.
            // TODO: ping-ping support.

            epnum = packet[USB_wIndex] & 0x0F; // JTR Added V0.2 after microchip stuff up with their documentation.
            pUEP = USB_UEP;
            pUEP += epnum;
            *pUEP &= ~USB_UEP_EPSTALL;

            dir = packet[USB_wIndex] >> 7;
            epbd = &usb_bdt[USB_CALC_BD(epnum, dir, USB_PP_EVEN)];
            epbd->BDSTAT &= ~BSTALL;
            if (dir) epbd->BDSTAT |= DTS; // JTR added IN EP set DTS as it will be toggled to zero next transfer
            if (0 == dir) epbd->BDSTAT &= ~DTS; // JTR added

            // JTR this pointless ATM. If ping-pong is enabled then you need to track PPBI
            // and set up ODD and EVEN BDs in respect to this. See complicated system in
            // microchip stack >= 2.8

            //		epbd = &usb_bdt[USB_CALC_BD(epnum, dir, USB_PP_ODD)];
            //		epbd->BDSTAT &= ~BSTALL;
            //		if (dir) epbd->BDSTAT |= DTS;		// JTR added
            //		if (0 == dir) epbd->BDSTAT &= ~DTS;	// JTR added


            usb_ack_dat1(0);
            break;


        case USB_REQUEST_SET_FEATURE:
            epnum = packet[USB_wIndex] & 0x0F;
            dir = packet[USB_wIndex] >> 7;
            epbd = &usb_bdt[USB_CALC_BD(epnum, dir, USB_PP_EVEN)];
            epbd->BDSTAT |= BSTALL;
            //epbd = &usb_bdt[USB_CALC_BD(epnum, dir, USB_PP_ODD)];
            //epbd->BDSTAT |= BSTALL;
            usb_ack_dat1(0);
            break;
        case USB_REQUEST_SYNCH_FRAME:
        default:
            usb_RequestError();
    }
}
Exemple #9
0
void usb_handle_StandardDeviceRequest(BDentry *bdp) {
    BYTE *packet = bdp->BDADDR;
    int i;

    switch (packet[USB_bRequest]) {
        case USB_REQUEST_GET_STATUS:
            EP0_Inbdp->BDADDR[0] = usb_device_status & 0xFF;
            EP0_Inbdp->BDADDR[1] = usb_device_status >> 8;
            usb_ack_dat1(2);
            break;
        case USB_REQUEST_CLEAR_FEATURE:
            if (0x01u == packet[USB_wValue]) { // TODO: Remove magic (REMOTE_WAKEUP_FEATURE)
                usb_device_status &= ~0x0002;
                usb_ack_dat1(0);
            } else
                usb_RequestError();
            break;
        case USB_REQUEST_SET_FEATURE:
            if (0x01u == packet[USB_wValue]) { // TODO: Remove magic (REMOTE_WAKEUP_FEATURE)
                usb_device_status |= 0x0002;
                usb_ack_dat1(0);
            } else
                usb_RequestError();
            break;
        case USB_REQUEST_SET_ADDRESS:
            if (0x00u == packet[USB_wValueHigh] && 0x7Fu >= packet[USB_wValue]) {
                usb_addr_pending = packet[USB_wValue];
                usb_set_in_handler(0, usb_set_address);
                usb_ack_dat1(0);
            } else
                usb_RequestError();
            break;


        case USB_REQUEST_GET_DESCRIPTOR:
            switch (packet[USB_bDescriptorType]) {
                case USB_DEVICE_DESCRIPTOR_TYPE: // There is only every one in pratice.
                    usb_rom_ptr = usb_device_descriptor;
                    usb_rom_len = usb_device_descriptor[0]; // Get BYTE length from descriptor always at byte [0]
                    if ((0 == packet[USB_wLengthHigh] && packet[USB_wLength] < usb_rom_ptr[0]))
                        usb_rom_len = packet[USB_wLength]; // If the HOST asked for LESS then must adjust count to the smaller number
                    break;

                case USB_CONFIGURATION_DESCRIPTOR_TYPE:
                    if (packet[USB_bDescriptorIndex] >= usb_device_descriptor[17]) {
                        flag_usb_RequestError();
                        break;
                    }

                    usb_rom_ptr = usb_config_descriptor;
                    usb_rom_len = usb_rom_ptr[2] + usb_rom_ptr[3] * 256; // Get WORD length from descriptor always at bytes 2&3 (Low-High)
                    for (i = 0; i < packet[USB_bDescriptorIndex]; i++) { // Implicit linked list traversal until requested configuration
                        usb_rom_ptr += usb_rom_len;
                        usb_rom_len = usb_rom_ptr[2] + usb_rom_ptr[3] * 256; // Get (next) WORD length from descriptor always at bytes 2&3 (Low-High)
                    }
                    if ((packet[USB_wLengthHigh] < usb_rom_ptr[3]) ||
                            (packet[USB_wLengthHigh] == usb_rom_ptr[3] && packet[USB_wLength] < usb_rom_ptr[2]))
                        usb_rom_len = packet[USB_wLength] + packet[USB_wLengthHigh] * 256; // If the HOST asked for LESS then must adjust count to the smaller number
                    break;
                case USB_STRING_DESCRIPTOR_TYPE:
                    // TODO: Handle language request. For now return standard language.
                    if (packet[USB_bDescriptorIndex] >= usb_num_string_descriptors) {
                        flag_usb_RequestError();
                        break;
                    }
                    usb_rom_ptr = usb_string_descriptor;
                    usb_rom_len = usb_rom_ptr[0]; // Get BYTE length from descriptor always at byte [0]
                    for (i = 0; i < packet[USB_bDescriptorIndex]; i++) { // Implicit linked list traversal until requested configuration
                        usb_rom_ptr += usb_rom_len;
                        usb_rom_len = usb_rom_ptr[0];
                    }
                    if ((0 == packet[USB_wLengthHigh] && packet[USB_wLength] < usb_rom_ptr[0]))
                        usb_rom_len = packet[USB_wLength];
                    break;
                case USB_INTERFACE_DESCRIPTOR_TYPE:
                case USB_ENDPOINT_DESCRIPTOR_TYPE:
                default:
                    flag_usb_RequestError();
            }
            if (0 == usbrequesterrorflag) {
                usb_send_rom(); // Send first part of packet right away, the rest is handled by the EP0 IN handler.
                usb_set_in_handler(0, usb_send_rom);
            } else {
                usb_RequestError();
            }

            break;
        case USB_REQUEST_GET_CONFIGURATION:
            EP0_Inbdp->BDADDR[0] = usb_current_cfg;
            usb_ack_dat1(1);
            break;

        case USB_REQUEST_SET_CONFIGURATION:
            if (USB_NUM_CONFIGURATIONS >= packet[USB_wValue]) {
                // TODO: Support multiple configurations
                /* Configure endpoints (USB_UEPn - registers) */
                usb_current_cfg = packet[USB_wValue];
                if (usb_current_cfg != 0) {

                    // JTR user_configured_init major addition. This is a CALLBACK to the USER when the device is enumerated.
                    // This is when we setup non EP0 endpoints.
                    // TODO: This really could be a function pointer

                    usb_device_state = CONFIGURED_STATE;
                    user_configured_init();
                } else {
                    usb_device_state = ADDRESS_STATE;
                }

                usb_ack_dat1(0);

            } else
                usb_RequestError();
            break;

        case USB_REQUEST_SET_DESCRIPTOR:
        default:
            usb_RequestError();
    }
}