hidkbd.c

Go to the documentation of this file.
00001 /***************************************************************************/
00015 #include "em_device.h"
00016 #include "em_common.h"
00017 #include "em_usb.h"
00018 #include "hidkbd.h"
00019 
00020 /**************************************************************************/
00059 #define DEFAULT_IDLE_RATE  500
00060 
00066 EFM32_ALIGN(4)
00067 const char HIDKBD_ReportDescriptor[ 69 ] __attribute__ ((aligned(4)))=
00068 {
00069     0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
00070     0x09, 0x06,                    // USAGE (Keyboard)
00071     0xa1, 0x01,                    // COLLECTION (Application)
00072     0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
00073     0x19, 0xe0,                    //   USAGE_MINIMUM (Keyboard LeftControl)
00074     0x29, 0xe7,                    //   USAGE_MAXIMUM (Keyboard Right GUI)
00075     0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
00076     0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
00077     0x75, 0x01,                    //   REPORT_SIZE (1)
00078     0x95, 0x08,                    //   REPORT_COUNT (8)
00079     0x81, 0x02,                    //   INPUT (Data,Var,Abs)
00080     0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
00081     0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
00082     0x75, 0x01,                    //   REPORT_SIZE (1)
00083     0x95, 0x08,                    //   REPORT_COUNT (8)
00084     0x81, 0x01,                    //   INPUT (Cnst,Ary,Abs)
00085     0x19, 0x00,                    //   USAGE_MINIMUM (Reserved (no event indicated))
00086     0x29, 0x65,                    //   USAGE_MAXIMUM (Keyboard Application)
00087     0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
00088     0x25, 0x65,                    //   LOGICAL_MAXIMUM (101)
00089     0x75, 0x08,                    //   REPORT_SIZE (8)
00090     0x95, 0x06,                    //   REPORT_COUNT (6)
00091     0x81, 0x00,                    //   INPUT (Data,Ary,Abs)
00092     0x05, 0x08,                    //   USAGE_PAGE (LEDs)
00093     0x19, 0x01,                    //   USAGE_MINIMUM (Num Lock)
00094     0x29, 0x03,                    //   USAGE_MAXIMUM (Scroll Lock)
00095     0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
00096     0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
00097     0x75, 0x01,                    //   REPORT_SIZE (1)
00098     0x95, 0x03,                    //   REPORT_COUNT (3)
00099     0x91, 0x02,                    //   OUTPUT (Data,Var,Abs)
00100     0x75, 0x01,                    //   REPORT_SIZE (1)
00101     0x95, 0x05,                    //   REPORT_COUNT (5)
00102     0x91, 0x01,                    //   OUTPUT (Cnst,Ary,Abs)
00103     0xc0                           // END_COLLECTION
00104 };
00105 
00108 static uint32_t                 tmpBuffer;
00109 static uint8_t                  idleRate;
00110 static void                     *hidDescriptor = NULL;
00111 static HIDKBD_SetReportFunc_t   setReportFunc = NULL;
00112 
00113 /* The last keyboard report sent to host. */
00114 EFM32_ALIGN(4)
00115 static HIDKBD_KeyReport_t lastSentReport __attribute__ ((aligned(4)));
00116 
00117 /* The last keyboard report reported to the driver. */
00118 EFM32_ALIGN(4)
00119 static HIDKBD_KeyReport_t lastKnownReport __attribute__ ((aligned(4)));
00120 
00121 static bool QueueEmpty( void );
00122 static bool QueueFull( void );
00123 static bool QueueGet( HIDKBD_KeyReport_t *element );
00124 static void QueueInit( void );
00125 static bool QueuePut( HIDKBD_KeyReport_t *element );
00126 
00127 /**************************************************************************/
00134 static void IdleTimeout( void )
00135 {
00136   /* If there is a keyboard event in the queue, we send it to the host. */
00137   /* If not we just resend the last one sent. */
00138   if ( !QueueEmpty() )
00139   {
00140     /* A new keyboard event. */
00141     QueueGet( &lastSentReport );
00142   }
00143 
00144   USBD_Write( HIDKBD_INTR_IN_EP_ADDR, &lastSentReport,
00145               sizeof( HIDKBD_KeyReport_t ), NULL );
00146 
00147   /* Schedule next idle event at current idle rate, idleRate unit is 4 ms. */
00148   USBTIMER_Start( HIDKBD_IDLE_TIMER, idleRate * 4, IdleTimeout );
00149 }
00150 
00151 /**************************************************************************/
00162 static int OutputReportReceived( USB_Status_TypeDef status,
00163                                  uint32_t xferred,
00164                                  uint32_t remaining )
00165 {
00166   (void) remaining;
00167 
00168   if (     ( status        == USB_STATUS_OK )
00169         && ( xferred       == 1             )
00170         && ( setReportFunc != NULL          ) )
00171   {
00172     setReportFunc( (uint8_t)tmpBuffer );
00173   }
00174 
00175   return USB_STATUS_OK;
00176 }
00177 
00180 /***************************************************************************/
00187 void HIDKBD_Init( HIDKBD_Init_t *init )
00188 {
00189   hidDescriptor = init->hidDescriptor;
00190   setReportFunc = init->setReportFunc;
00191   memset( &lastSentReport,  0, sizeof( HIDKBD_KeyReport_t ) );
00192   memset( &lastKnownReport, 0, sizeof( HIDKBD_KeyReport_t ) );
00193 }
00194 
00195 /***************************************************************************/
00202 void HIDKBD_KeyboardEvent( HIDKBD_KeyReport_t *report )
00203 {
00204   lastKnownReport = *report;
00205 
00206   if ( idleRate != 0 )      /* Put keyboard events into a queue. */
00207   {
00208     /* Put the kbd event in the queue, it will be retrieved and reported */
00209     /* to host in the idleRate timeout function.                         */
00210     QueuePut( report );
00211   }
00212   else /* idleRate == 0, send report immediately. */
00213   {
00214     lastSentReport = *report;
00215     USBD_Write( HIDKBD_INTR_IN_EP_ADDR, &lastSentReport,
00216                 sizeof( HIDKBD_KeyReport_t ), NULL );
00217   }
00218 }
00219 
00220 /**************************************************************************/
00232 int HIDKBD_SetupCmd( const USB_Setup_TypeDef *setup )
00233 {
00234   STATIC_UBUF( hidDesc, USB_HID_DESCSIZE );
00235 
00236   int retVal = USB_STATUS_REQ_UNHANDLED;
00237 
00238   if ( ( setup->Type      == USB_SETUP_TYPE_STANDARD       ) &&
00239        ( setup->Direction == USB_SETUP_DIR_IN              ) &&
00240        ( setup->Recipient == USB_SETUP_RECIPIENT_INTERFACE )    )
00241   {
00242     /* A HID device must extend the standard GET_DESCRIPTOR command   */
00243     /* with support for HID descriptors.                              */
00244     switch (setup->bRequest)
00245     {
00246     case GET_DESCRIPTOR:
00247       /********************/
00248       if ( ( setup->wValue >> 8 ) == USB_HID_REPORT_DESCRIPTOR )
00249       {
00250         USBD_Write( 0, (void*)HIDKBD_ReportDescriptor,
00251                     EFM32_MIN(sizeof(HIDKBD_ReportDescriptor), setup->wLength),
00252                     NULL );
00253         retVal = USB_STATUS_OK;
00254       }
00255       else if ( ( setup->wValue >> 8 ) == USB_HID_DESCRIPTOR )
00256       {
00257         /* The HID descriptor might be misaligned ! */
00258         memcpy( hidDesc, hidDescriptor, USB_HID_DESCSIZE );
00259         USBD_Write( 0, hidDesc, EFM32_MIN(USB_HID_DESCSIZE, setup->wLength),
00260                     NULL );
00261         retVal = USB_STATUS_OK;
00262       }
00263       break;
00264     }
00265   }
00266 
00267   else if ( ( setup->Type      == USB_SETUP_TYPE_CLASS          ) &&
00268             ( setup->Recipient == USB_SETUP_RECIPIENT_INTERFACE ) &&
00269             ( setup->wIndex    == HIDKBD_INTERFACE_NO           )    )
00270   {
00271     /* Implement the necessary HID class specific commands.           */
00272     switch ( setup->bRequest )
00273     {
00274     case USB_HID_SET_REPORT:
00275       /********************/
00276       if ( ( ( setup->wValue >> 8   ) == 2                ) &&  /* Output report */
00277            ( ( setup->wValue & 0xFF ) == 0                ) &&  /* Report ID     */
00278            ( setup->wLength           == 1                ) &&  /* Report length */
00279            ( setup->Direction         != USB_SETUP_DIR_IN )    )
00280       {
00281         USBD_Read( 0, (void*)&tmpBuffer, 1, OutputReportReceived );
00282         retVal = USB_STATUS_OK;
00283       }
00284       break;
00285 
00286     case USB_HID_GET_REPORT:
00287       /********************/
00288       if ( ( ( setup->wValue >> 8   ) == 1                ) &&  /* Input report  */
00289            ( ( setup->wValue & 0xFF ) == 0                ) &&  /* Report ID     */
00290            ( setup->wLength           == 8                ) &&  /* Report length */
00291            ( setup->Direction         == USB_SETUP_DIR_IN )    )
00292       {
00293         USBD_Write( HIDKBD_INTR_IN_EP_ADDR, &lastKnownReport,
00294                     sizeof( HIDKBD_KeyReport_t ), NULL );
00295         retVal = USB_STATUS_OK;
00296       }
00297       break;
00298 
00299     case USB_HID_SET_IDLE:
00300       /********************/
00301       if ( ( ( setup->wValue & 0xFF) == 0                ) &&  /* Report ID     */
00302            ( setup->wLength          == 0                ) &&
00303            ( setup->Direction        != USB_SETUP_DIR_IN )    )
00304       {
00305         idleRate = setup->wValue >> 8;
00306         if ( ( idleRate != 0 ) && ( idleRate < ( HIDKBD_POLL_RATE / 4 ) ) )
00307         {
00308           idleRate = HIDKBD_POLL_RATE / 4;
00309         }
00310         USBTIMER_Stop( HIDKBD_IDLE_TIMER );
00311         if ( idleRate != 0 )
00312         {
00313           IdleTimeout();
00314         }
00315         retVal = USB_STATUS_OK;
00316       }
00317       break;
00318 
00319     case USB_HID_GET_IDLE:
00320       /********************/
00321       if ( ( setup->wValue    == 0                ) &&  /* Report ID     */
00322            ( setup->wLength   == 1                ) &&
00323            ( setup->Direction == USB_SETUP_DIR_IN )    )
00324       {
00325         *(uint8_t*)&tmpBuffer = idleRate;
00326         USBD_Write( 0, (void*)&tmpBuffer, 1, NULL );
00327         retVal = USB_STATUS_OK;
00328       }
00329       break;
00330     }
00331   }
00332 
00333   return retVal;
00334 }
00335 
00336 /**************************************************************************/
00344 void HIDKBD_StateChangeEvent( USBD_State_TypeDef oldState,
00345                               USBD_State_TypeDef newState )
00346 {
00347   if ( newState == USBD_STATE_CONFIGURED )
00348   {
00349     /* We have been configured, start HID functionality ! */
00350     if ( oldState != USBD_STATE_SUSPENDED ) /* Resume ?   */
00351     {
00352       idleRate = DEFAULT_IDLE_RATE / 4;     /* Unit is 4 millisecond. */
00353       QueueInit();
00354     }
00355     if ( idleRate )
00356     {
00357       USBTIMER_Start( HIDKBD_IDLE_TIMER, idleRate * 4, IdleTimeout );
00358     }
00359   }
00360 
00361   else if ( ( oldState == USBD_STATE_CONFIGURED ) &&
00362             ( newState != USBD_STATE_SUSPENDED  )    )
00363   {
00364     /* We have been de-configured, stop HID functionality */
00365     USBTIMER_Stop( HIDKBD_IDLE_TIMER );
00366   }
00367 
00368   else if ( newState == USBD_STATE_SUSPENDED )
00369   {
00370     /* We have been suspended, stop HID functionality */
00371     /* Reduce current consumption to below 2.5 mA.    */
00372     USBTIMER_Stop( HIDKBD_IDLE_TIMER );
00373   }
00374 }
00375 
00378 /* Minimal circular buffer implementation. */
00379 
00380 #define QUEUE_SIZE 16             /* Must be 2^n !! */
00381 
00382 typedef struct ringBuffer_t
00383 {
00384   unsigned int        putIdx;
00385   unsigned int        getIdx;
00386   HIDKBD_KeyReport_t  buf[ QUEUE_SIZE ];
00387 } ringBuffer_t;
00388 
00389 ringBuffer_t queue;
00390 
00391 static bool QueueEmpty( void )
00392 {
00393   return ( queue.putIdx - queue.getIdx ) == 0;
00394 }
00395 
00396 static bool QueueFull( void )
00397 {
00398   return ( queue.putIdx - queue.getIdx ) >= QUEUE_SIZE;
00399 }
00400 
00401 static bool QueueGet( HIDKBD_KeyReport_t *element )
00402 {
00403   if ( !QueueEmpty() )
00404   {
00405     *element = queue.buf[ queue.getIdx++ & (QUEUE_SIZE - 1) ];
00406     queue.getIdx = ( queue.getIdx + 1 ) % QUEUE_SIZE;
00407     return true;
00408   }
00409   return false;
00410 }
00411 
00412 static void QueueInit( void )
00413 {
00414   queue.getIdx = 0;
00415   queue.putIdx = 0;
00416 }
00417 
00418 static bool QueuePut( HIDKBD_KeyReport_t *element )
00419 {
00420   if ( !QueueFull() )
00421   {
00422     queue.buf[ queue.putIdx++ & (QUEUE_SIZE - 1) ] = *element;
00423     queue.putIdx = ( queue.putIdx + 1 ) % QUEUE_SIZE;
00424     return true;
00425   }
00426   return false;
00427 }
00428