This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Expanding In/Out Reports on Keil USB HID example code

I'm implementing an HID class device and have based my program on the HID USB program supplied by Keil. I'm new to USB but am a fairly experienced programmer otherwise.

I have got a basic program working fine bit with single byte input and output reports. I've tested the functionality using a client called SimpleHIDWrite.exe

I need to expand both in and out reports to 8 bytes each to communicate with the host. Has anybody successfully modified this example program or does anyone have any advice on how to do it properly?

My guess is that I need to edit the report descriptor and also set up the inreport and outreport as arrays. Is there anything I need to watch out for?

My target is the LPC2141.

Any advice or information would be much appreciated!

Thanks,
Gareth.

  • Sorry - should have mentioned that I'm not using the basic example, I'm using the RTX USB HID example supplied with RL-ARM.

  • The report byte size (count) on the report descriptor is defined by these #defines
    Increase these figures.

    C:\Keil\ARM\Boards\Keil\MCB2140\RL\USB\RTX_HID\usbdesc.c
    
    #define HID_INPUT_REPORT_BYTES       1              /* size of report in Bytes */
    #define HID_OUTPUT_REPORT_BYTES      1              /* size of report in Bytes */
    #define HID_FEATURE_REPORT_BYTES     1              /* size of report in Bytes */
    

    The example declares just interrupt IN endpoint.
    Increase wMaxPacketSize, so that it fits to the report byte size (up to 64 bytes)

    C:\Keil\ARM\Boards\Keil\MCB2140\RL\USB\RTX_HID\usbdesc.c
    
    /* Endpoint, HID Interrupt In */
      USB_ENDPOINT_DESC_SIZE,            /* bLength */
      USB_ENDPOINT_DESCRIPTOR_TYPE,      /* bDescriptorType */
      USB_ENDPOINT_IN(1),                /* bEndpointAddress */
      USB_ENDPOINT_TYPE_INTERRUPT,       /* bmAttributes */
      WBVAL(0x0004),                     /* wMaxPacketSize */  // <---------
      0x20,          /* 32ms */          /* bInterval */
    

    The buffers for the reports are defined in demo.c
    Increase the buffer size.
    GetInReport() and SetOutReport() are revised, so that they fill/retrieve increased report size

    C:\Keil\ARM\Boards\Keil\MCB2140\RL\USB\RTX_HID\demo.c
    
    U8 InReport;                         /* HID Input Report    */  <---- make these variable to array
    U8 OutReport;                        /* HID Out Report      */
    
    void GetInReport (void) {
       ...
    }
    
    void SetOutReport (void) {
       ...
    }
    

    When you use control transfer for input/output/feature report, ie. Get_Report/Set_Report requests,
    HID_GetReport() / HID_SetReport() handlers are called on the stack.
    Modify these handlers.

    C:\Keil\ARM\Boards\Keil\MCB2140\RL\USB\RTX_HID\hiduser.c
    
    BOOL HID_GetReport (void) {
    
      /* ReportID = SetupPacket.wValue.WB.L; */
      switch (SetupPacket.wValue.WB.H) {
        case HID_REPORT_INPUT:
          GetInReport();
          EP0Buf[0] = InReport;         // <------ memcpy( EP0Buf, InReport, sizeof(InReport) );
          break;
        case HID_REPORT_OUTPUT:
          return (__FALSE);        /* Not Supported */
        case HID_REPORT_FEATURE:
          /* EP0Buf[] = ...; */
          /* break; */
          return (__FALSE);        /* Not Supported */
      }
      return (__TRUE);
    }
    
    BOOL HID_SetReport (void) {
    
      /* ReportID = SetupPacket.wValue.WB.L; */
      switch (SetupPacket.wValue.WB.H) {
        case HID_REPORT_INPUT:
          return (__FALSE);        /* Not Supported */
        case HID_REPORT_OUTPUT:
          OutReport = EP0Buf[0];        // <------ memcpy( OutReport, EP0Buf, sizeof(OutReport) );
          SetOutReport();
          break;
        case HID_REPORT_FEATURE:
          return (__FALSE);        /* Not Supported */
      }
      return (__TRUE);
    }
    

    The size of EP0Buf array is USB_MAX_PACKET0. Increase it, too (8, 16, 32, or 64 bytes are allowed)

    C:\Keil\ARM\Boards\Keil\MCB2140\RL\USB\RTX_HID\usbcfg.h
    
    #define USB_MAX_PACKET0     8
    

    Tsuneo

  • Fantastic response Tsuneo - many thanks!

    I actually ended up working out how to do it on my own and it all seems to be working nicely. I ended up just working through it methodically and learned quite a bit about USB comms at the same time.

    I ended up doing pretty much exactly what you have suggested but I will check back through and compare my solution with your suggestions.

    I edited the HID_Get/SetReport functions slightly differently (and not as elegantly) by using a simple 'for' loop to fill up/empty the Endpoint 0 buffer but it seems to work fine.

    The USB comms protocol with the host specifies commands from the host as 8 byte packets and the same for data transferred from the device to the host. I am using SimpleHIDWrite.exe to simulate host/device transfers on my PC and so far everything seems to work fine.

    I notice that the OUT transfers are done on Endpoint 0 while IN transfers use Endpoint 1 - what is the advantage of doing this? Should I set up a separate endpoint for the OUT transfers too? I'm not sure what the benefit would be.

    I found quite a bit of advice on the ARM website in the end but when I did my original post I was pannicking a bit. It's great that I got such a comprehensive and easy to understand response from an obvious expert - so that you very much!!

    Gareth.

  • > I notice that the OUT transfers are done on Endpoint 0 while IN transfers use Endpoint 1 - what is the advantage of doing this? Should I set up a separate endpoint for the OUT transfers too? I'm not sure what the benefit would be.

    The advantage of interrupt OUT endpoint (EP) is light and quick execution on the firmware. When your PC app puts output reports frequently, the OUT EP reduces MCU load.

    Another feature of the OUT EP is that your firmware is allowed to delay the timing to parse new report on the OUT EP, as long as you like. This feature is useful, for example, when your firmware doesn't want to execute new command passed by PC app, until the execution of last command finishes. Instead, for an output report over Set_Report request, your firmware should finish the request within given timeout.



    As of the background of this matter,
    HID spec defines that an interrupt IN EP is mandatory for every HID device, but an interrupt OUT EP is optional.

    The report type (input/output/feature) determines the pipe available for the report.

    input report - interrupt IN EP, Get_Report request
    output report - interrupt OUT EP, Set_Report request
    feature report - Get_Report, Set_Report requests

    For Windows app,
    - ReadFile() reads out input report(s) passed over the interrupt IN EP. HidD_GetInputReport() issues a Get_Report( input ) request to the device.

    - When the HID device has an interrupt OUT EP, WriteFile() puts an output report to the OUT EP. Without the OUT EP, the WriteFile() call is redirected to Set_Report( output ) request. HidD_SetOutputReport() always issues a Set_Report( output ) request to the device.

    - Feature reports are exchanged using HidD_GetFeature() and HidD_SetFeature(). These APIs issue Get_Report( feature ) / Set_Report( feature ) request, respectively.



    When you run this this KEIL example on SimpleHIDWrite.exe, you would notice that this firmware always puts input reports repeatedly. In most of practical applications, this behavior is never the desired one. Many programmers like query-reply communication, query over an output report, and reply over single input report. Maybe, the next thing is changing this behavior to more practical one.

    Is it your case?

    Tsuneo

  • In my application the device firmware is required to accept and react to commands from the host. Information is sent back to the host a)when requested by the host b) when important data changes. So I guess that sending InReports as a stream sort of defeats the object.

    I should definitely start thinking about changing to a system of InReports which are sent as specific times to specific requests from the host - or at the other times when variables have changed. More like the query/reply system that you mentioned.

    I guess I can do this by setting up a separate IN EP? My assumption is that I would simply have to set up an event handler to deal with this case - similar to the OUT EP that is already in place?

    Them I would see reports being issued only at the designated times when using SimpleHIDWrite.

    Any further comments/advice you have would be greatly appreciated!

  • ... and obviously I will need to edit the Config Descriptor to declare the IN EP!

  • The repeated input reports are NEVER the requirement of HID. It's just firmware implementation problem.
    For example, your USB mouse and keyboard runs on HID, but these devices don't work like KEIL example. They put an input report just when user moves the mouse, just when user keys in.

    On the KEIL example, the repeated input reports are generated in these code.

    C:\Keil\ARM\Boards\Keil\MCB2140\RL\USB\RTX_HID\usbuser.c
    
    /*
     *  USB Core Task
     *   Handles USB Core Events
     */
    
    __task void USB_Core (void) {
        ...
        ...
    #if USB_CONFIGURE_EVENT
        if (evt & USB_EVT_SET_CFG) {
          if (USB_Configuration) {              /* Check if USB is configured */
            GetInReport();                                         // <------------
            USB_WriteEP(HID_EP_IN, &InReport, sizeof(InReport));   // <------------
          }
        }
    #endif
        ...
        ...
    
    /*
     *  USB Endpoint 1 Task
     *   Handles USB Endpoint 1 Events
     */
    
    #if (USB_EP_EVENT & (1 << 1))
    __task void USB_EndPoint1 (void) {
      U16 evt;
    
      for (;;) {
        os_evt_wait_or(0xFFFF, 0xFFFF);         /* Wait for an Event */
        evt = os_evt_get();                     /* Get Event Flags */
    
        if (evt & USB_EVT_IN) {
          GetInReport();                                         // <------------
          USB_WriteEP(HID_EP_IN, &InReport, sizeof(InReport));   // <------------
        }
      }
    }
    #endif
    


    In above code,
    When Set_Configuration request comes at the end of enumeration, the example loads the first input report to the interrupt EP, using USB_WriteEP(). When an IN transaction completes on the EP (ie. the report on the EP is sent to host), the USB engine generates an interrupt on the EP. This interrupt is always caught by USB_EndPoint1() task. This task charges new input report to the EP.

    On the host side, PC HID class driver always polls the IN EP using IN transactions at the bInterval rate (defined on the endpoint descriptor). Because the example puts an input report to every IN transaction, repeated input reports are sent to the host.

    There is no need for the firmware to respond to every IN transaction from host. When no packet (report) is loaded on the IN EP, the USB engine automatically returns NAK. NAK is a normal response on USB communication, used for flow-control, which means try again afterword. When host receives NAK, it repeats the last transaction again without any error.

    Tsuneo

  • Here is modification of the KEIL example.
    In this snippet, we implement,
    - Interrupt OUT EP
    - Feature report handling
    - Loopback of an output report to an input report from interrupt OUT EP to interrupt IN EP
    - Loopback of a feature report from Set_Report( feature ) to Get_Report( feature )

    Jan Axelson's generic_hid_cs is good for the PC test application of this mods (Thanks to Jan! )
    "The HID Page" http://www.lvr.com/hidpage.htm
    generic_hid_cs www.lvr.com/.../generic_hid_cs_46.zip

    The snippet below shows just mods on the firmware code.

    usbcfg.h
    
    #define USB_MAX_PACKET0     64
    

    usbdesc.c
    
    #include "hiduser.h"
    
    //  moved to hiduser.h
    //#define HID_INPUT_REPORT_BYTES       1             /* size of report in Bytes */
    //#define HID_OUTPUT_REPORT_BYTES      1             /* size of report in Bytes */
    //#define HID_FEATURE_REPORT_BYTES     1             /* size of report in Bytes */
    
    /* USB Configuration Descriptor */
    /*   All Descriptors (Configuration, Interface, Endpoint, Class, Vendor) */
    const U8 USB_ConfigDescriptor[] = {
    /* Configuration 1 */
      USB_CONFIGUARTION_DESC_SIZE,       /* bLength */
      USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType */
      WBVAL(                             /* wTotalLength */
        USB_CONFIGUARTION_DESC_SIZE +
        USB_INTERFACE_DESC_SIZE     +
        HID_DESC_SIZE               +
        USB_ENDPOINT_DESC_SIZE      +                              // <--------
        USB_ENDPOINT_DESC_SIZE                                     // <-------- increased by OUT EP
      ),
      0x01,                              /* bNumInterfaces */
      0x01,                              /* bConfigurationValue: 0x01 is used to select this configuration */
      0x00,                              /* iConfiguration: no string to describe this configuration */
      USB_CONFIG_BUS_POWERED /*|*/       /* bmAttributes */
    /*USB_CONFIG_REMOTE_WAKEUP*/,
      USB_CONFIG_POWER_MA(100),          /* bMaxPower, device power consumption is 100 mA */
    
    /* Interface 0, Alternate Setting 0, HID Class */
      USB_INTERFACE_DESC_SIZE,           /* bLength */
      USB_INTERFACE_DESCRIPTOR_TYPE,     /* bDescriptorType */
      0x00,                              /* bInterfaceNumber */
      0x00,                              /* bAlternateSetting */
      0x02,                              /* bNumEndpoints */       // <-------- increased to 2 EP
      USB_DEVICE_CLASS_HUMAN_INTERFACE,  /* bInterfaceClass */
      HID_SUBCLASS_NONE,                 /* bInterfaceSubClass */
      HID_PROTOCOL_NONE,                 /* bInterfaceProtocol */
      0x04,                              /* iInterface */
    /* HID Class Descriptor */
    /* HID_DESC_OFFSET = 0x0012 */
      HID_DESC_SIZE,                     /* bLength */
      HID_HID_DESCRIPTOR_TYPE,           /* bDescriptorType */
      WBVAL(0x0100), /* 1.00 */          /* bcdHID */
      0x00,                              /* bCountryCode */
      0x01,                              /* bNumDescriptors */
      HID_REPORT_DESCRIPTOR_TYPE,        /* bDescriptorType */
      WBVAL(HID_REPORT_DESC_SIZE),       /* wDescriptorLength */
    /* Endpoint, HID Interrupt In */
      USB_ENDPOINT_DESC_SIZE,            /* bLength */
      USB_ENDPOINT_DESCRIPTOR_TYPE,      /* bDescriptorType */
      USB_ENDPOINT_IN(1),                /* bEndpointAddress */
      USB_ENDPOINT_TYPE_INTERRUPT,       /* bmAttributes */
      WBVAL(0x0040),                     /* wMaxPacketSize */      // <-------- increased to 64 bytes
      0x01,          /* 1ms */           /* bInterval */           // <-------- changed to 1ms
    /* Endpoint, HID Interrupt Out */                              // <-------- added OUT EP
      USB_ENDPOINT_DESC_SIZE,            /* bLength */
      USB_ENDPOINT_DESCRIPTOR_TYPE,      /* bDescriptorType */
      USB_ENDPOINT_OUT(1),               /* bEndpointAddress */
      USB_ENDPOINT_TYPE_INTERRUPT,       /* bmAttributes */
      WBVAL(0x0040),                     /* wMaxPacketSize */
      0x01,          /* 1ms */           /* bInterval */
    /* Terminator */
      0                                  /* bLength */
    };
    

    usbuser.c
    
    
    #include <string.h>
    static OS_SEM USB_IN_EP1_Semaphore; // semaphore for IN EP1
    /* * USB Endpoint 1 Task * Handles USB Endpoint 1 Events */
    #if (USB_EP_EVENT & (1 << 1)) __task void USB_EndPoint1 (void) { U16 evt; U32 report_size;
    for (;;) { os_evt_wait_or(0xFFFF, 0xFFFF); /* Wait for an Event */ evt = os_evt_get(); /* Get Event Flags */
    if (evt & USB_EVT_OUT) { // OUT EP1 interrupt comes report_size = USB_ReadEP( HID_EP_OUT, OutReport ); // read out the output report // usually, your firmware parse the output report, here // in this snippet, just copy output report to input one directly, for loopback memcpy( InReport, OutReport, report_size ); // send an input report to IN EP os_sem_wait( USB_IN_EP1_Semaphore, 0xffff ); // wait until IN EP is available USB_WriteEP( HID_EP_IN, InReport, HID_INPUT_REPORT_BYTES ); // pass it to the endpoint }
    if (evt & USB_EVT_IN) { // IN EP1 interrupt comes os_sem_send( USB_IN_EP1_Semaphore ); // IN EP1 is free /* GetInReport(); USB_WriteEP(HID_EP_IN, &InReport, sizeof(InReport)); */ } } } #endif
    /* * USB Core Task * Handles USB Core Events */
    __task void USB_Core (void) { #if (USB_CONFIGURE_EVENT || USB_INTERFACE_EVENT || USB_FEATURE_EVENT) U16 evt; #endif
    for (;;) {
    os_evt_wait_or(0xFFFF, 0xFFFF); /* Wait for an Event */
    #if (USB_CONFIGURE_EVENT || USB_INTERFACE_EVENT || USB_FEATURE_EVENT) evt = os_evt_get(); /* Get Event Flags */ #endif
    #if USB_CONFIGURE_EVENT if (evt & USB_EVT_SET_CFG) { if (USB_Configuration) { /* Check if USB is configured */
    os_sem_init( USB_IN_EP1_Semaphore, 1 ); // IN EP1 is available now /* GetInReport(); USB_WriteEP(HID_EP_IN, &InReport, sizeof(InReport)); */ } } #endif
    #if USB_INTERFACE_EVENT if (evt & USB_EVT_SET_IF) { } #endif
    #if USB_FEATURE_EVENT if (evt & USB_EVT_SET_FEATURE) { } if (evt & USB_EVT_CLR_FEATURE) { } #endif
    } }

  • hiduser.h
    
    /* HID report size (count) */
    #define HID_INPUT_REPORT_BYTES       64             /* size of report in Bytes */
    #define HID_OUTPUT_REPORT_BYTES      64             /* size of report in Bytes */
    #define HID_FEATURE_REPORT_BYTES     64             /* size of report in Bytes */
    
    /* HID In/Out Endpoint Address */
    #define HID_EP_OUT      0x01
    #define HID_EP_IN       0x81
    

    hiduser.c
    
    #include <string.h>
    
    BOOL HID_GetReport (void) {
    
      /* ReportID = SetupPacket.wValue.WB.L; */
      switch (SetupPacket.wValue.WB.H) {
        case HID_REPORT_INPUT:
          GetInReport();
    //      EP0Buf[0] = InReport;
          memcpy( EP0Buf, InReport, SetupPacket.wLength );
          break;
        case HID_REPORT_OUTPUT:
          return (__FALSE);        /* Not Supported */
        case HID_REPORT_FEATURE:
          GetFeatureReport();
          memcpy( EP0Buf, FeatureReport, SetupPacket.wLength );
          break;
      }
      return (__TRUE);
    }
    
    BOOL HID_SetReport (void) {
    
      /* ReportID = SetupPacket.wValue.WB.L; */
      switch (SetupPacket.wValue.WB.H) {
        case HID_REPORT_INPUT:
          return (__FALSE);        /* Not Supported */
        case HID_REPORT_OUTPUT:
    //      OutReport = EP0Buf[0];
          memcpy( OutReport, EP0Buf, SetupPacket.wLength );
          SetOutReport();
          break;
        case HID_REPORT_FEATURE:
          memcpy( FeatureReport, EP0Buf, SetupPacket.wLength );
          SetFeatureReport();
          break;
      }
      return (__TRUE);
    }
    

    demo.h
    
    
    /* HID Demo Variables */ extern U8 InReport[ HID_INPUT_REPORT_BYTES ]; extern U8 OutReport[ HID_OUTPUT_REPORT_BYTES ]; extern U8 FeatureReport[ HID_FEATURE_REPORT_BYTES ];
    /* HID Demo Functions */ extern void GetInReport (void); extern void SetOutReport (void); extern void GetFeatureReport (void); extern void SetFeatureReport (void);

    demo.c
    
    #include "hiduser.h"
    #include "demo.h"
    
    U8 InReport[ HID_INPUT_REPORT_BYTES ];           /* HID Input Report    */
    U8 OutReport[ HID_OUTPUT_REPORT_BYTES ];         /* HID Out Report      */
    U8 FeatureReport[ HID_FEATURE_REPORT_BYTES ];    /* HID Feature Report  */
    
    /*------------------------------------------------------------------------------
      Get HID Input Report -> InReport
     *------------------------------------------------------------------------------*/
    void GetInReport (void) {
        // this function is called just when Get_Report( input ) request comes
        // fill InReport[] with an input report, here
    
    //  if ((FIO2PIN & PB_INT0) == 0) {           /* Check if PBINT is pressed */
    //    InReport = 0x01;
    //  } else {
    //    InReport = 0x00;
    //  }
    }
    
    /*------------------------------------------------------------------------------
      Set HID Output Report <- OutReport
     *------------------------------------------------------------------------------*/
    void SetOutReport (void) {
        // this function is called just when Set_Report( output ) request comes
        // At the entry of this function, OutReport[] holds an output report from the host
    
    //    IOPIN1 = (IOPIN1 & ~LED_MSK) | (OutReport << 16);
    }
    
    /*------------------------------------------------------------------------------
      Get HID Feature Report -> FeatureReport
     *------------------------------------------------------------------------------*/
    void GetFeatureReport (void) {
        // this function is called just when Get_Report( feature ) request comes
        // fill FeatureReport[] with an input report, here
    }
    
    /*------------------------------------------------------------------------------
      Set HID Feature Report <- FeatureReport
     *------------------------------------------------------------------------------*/
    void SetFeatureReport (void) {
        // this function is called just when Set_Report( feature ) request comes
        // At the entry of this function, FeatureReport[] holds an feature report from the host
    }
    

    Tsuneo

  • This makes sense and I suspected that the streaming input reports were just the way the example was set up.

    Can I check my understanding of your latest post? ...

    In order to put out InReports as and when required I need to set up an IN EP (interrupt) on Endpoint 1 - with an event handler like the one in the lower code sample? So I need to use the descriptor to describe this EP.

    I just need to properly understand what mechanism prompts the system to generate the interrupt which causes the report to be transferred to the host. I suppose what I'm asking is how the GetInReport function in the main program is related to the mechanism within the USB engine which then transmits the report via the EP to the host?

    Just for example ... an important variable in my system changes (similar to the mouse moving) and I want to send that new value to the host. When this happens I can detect it and update the InReport array within GetInReport. What is the part which knows that this must be transmitted NOW?

    Thanks for your ongoing assistance - I am making fast progress and I feel that my understanding is moving forward all the time!

    Gareth

  • Hi Tsuneo,

    So much information - THANKS!!!!
    I will need to work through it slowly to make sure I understand the changes!

    The last section - specific to my application for receiving commands and replying with the required report I understand!

    The section about creating OUT EP I understand!

    I wasn't so sure about the purpose of the loop backs and feature reports ... are these changes required for my application? If so, could you provide a (very) brief explanation?

    Gareth.

  • Good day Tsuneo and all

    quite interesting to find this thread right on top ... :D

    Here is the situation:
    I already changed HID reports successfully on ARM7/2366. (to 60)
    This was almost straight forward and works fine. So I am not new to all the stuff
    (But of course still light year's away from other 'T' - user's expertise)

    Now I wanted to do the same with the latest USB HID sample provided for MCB1700
    (so this included a switch to CORTEX)

    I did all the stuff above, and in principal, it works too, but only for the Report Sizes 1,2,3 and 4. (and yes, I enlarged EP0Buf to 64 (resp MAX_PACKET_SIZE0)

    whenever I got to reports > 4, it stops working

    is there somewhere a known maximum or bug ?

    MANY MANY thanks in advance for any illumination
    ULI

  • In order to put out InReports as and when required I need to set up an IN EP (interrupt) on Endpoint 1 - with an event handler like the one in the lower code sample?

    An interrupt IN EP is mandatory for every HID device implementation.
    Input reports are sent over this EP.

    >I just need to properly understand what mechanism prompts the system to generate the interrupt which causes the report to be transferred to the host.

    As USB is host centric, devices don't have any method to send data actively.
    Just when an IN transaction comes from host, the device gets chance to send data.
    This is the reason why PC HID class driver polls the interrupt IN EP repeatedly by IN transactions.

    The sequence to send an input report is as follows,
    1) First, firmware fills the IN endpoint buffer with an input report using USB_WriteEP() call
    2) The USB engine waits for an IN transaction from host
    3) When an IN transaction comes, the engine sends the data on the endpoint buffer
    4) When the IN transaction completes, the engine generates a hardware interrupt on this EP
    5) RL-ARM library interprets the hardware interrupt to an event

    Please note, the EP hardware interrupt occurs just after the transaction finishes successfully (data is sent).
    The hardware interrupt doesn't occur when the IN endpoint buffer is empty on transaction, and the engine returns NAK.
    ie. the IN EP hardware interrupt means the timing when the endpoint buffer becomes empty right now.

    Then, when are we allowed to put an input report to the EP buffer?
    The answer is any time, unless the EP is occupied by the last report.
    In above snippet, the IN EP event is used just to release the semaphore, to notify that the IN EP is available now.
    Also, your firmware can put an input report everywhere on your code, where required.

    Tsuneo

  • I wasn't so sure about the purpose of the loop backs and feature reports ... are these changes required for my application? If so, could you provide a (very) brief explanation?

    Loop back is the basics for the development of communication code. :-)
    It's good for debug.
    I recommend you to implement the loopback first, as the snippet does. And run it on Jan's PC app for confirmation.
    Loop back code is easily modified into query-reply communication, as you see in above mods.

    Feature reports are implemented because Jan's PC app supports them. Just for completeness. :-)
    Anyway, the mods for feature report take no more than a couple of lines of the code.

    Tsuneo

  • Hi Tsuneo,

    I had sort of worked out that the loop back was a 'stepping stone' to be able to get where I want to be! Thanks for confirming - also on the feature reports.

    I'll start my changes this afternoon and let you know how I get on. I have re-read everything you have sent and I am starting to understand the structure better - things starting to become clearer.

    ALSO I think I got my IN's and OUT's mixed up in one of my earlier responses! I meant - create and OUT EP (not IN) - but you have pointed that out (in a very patient and kind way!)

    So - I will proceed with care and make the changes!!!!

    Gareth.