Volume Control using Consumer Devices HID

General discussions about V-USB, our firmware-only implementation of a low speed USB device on Atmel's AVR microcontrollers
Post Reply
coolblaze03

Volume Control using Consumer Devices HID

Post by coolblaze03 » Sun Apr 11, 2010 10:39 pm

Ive been trying to modify HID Keys to Control media, but I just cant make it work. Can someone tell me what im doing wrong or even point me to a working example of using the media controls in the consumer devices class.

Code: Select all

#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/interrupt.h>  /* for sei() */
#include <util/delay.h>     /* for _delay_ms() */
#include <avr/eeprom.h>

#include <avr/pgmspace.h>   /* required by usbdrv.h */
#include "usbdrv.h"
#include "oddebug.h"        /* This is also an example for using debug macros */


static void hardwareInit(void)
{
uchar   i, j;

    PORTB = 0xff;   /* activate all pull-ups */
    DDRB = 0;       /* all pins input */
    PORTC = 0xff;   /* activate all pull-ups */
    DDRC = 0;       /* all pins input */
    PORTD = 0xfa;   /* 1111 1010 bin: activate pull-ups except on USB lines */
    DDRD = 0x07;    /* 0000 0111 bin: all pins input except USB (-> USB reset) */
   j = 0;
   while(--j){     /* USB Reset by device only required on Watchdog Reset */
      i = 0;
      while(--i); /* delay >10ms for USB reset */
   }
    DDRD = 0x02;    /* 0000 0010 bin: remove USB reset condition */
    /* configure timer 0 for a rate of 12M/(1024 * 256) = 45.78 Hz (~22ms) */
    TCCR0B = 5;      /* timer 0 prescaler: 1024 */
}

#define NUM_KEYS    17

/* The following function returns an index for the first key pressed. It
 * returns 0 if no key is pressed.
 */
static uchar    keyPressed(void)
{
uchar   i, mask, x;

    x = PINB;
    mask = 1;
    for(i=0;i<6;i++){
        if((x & mask) == 0)
            return i + 1;
        mask <<= 1;
    }
    x = PINC;
    mask = 1;
    for(i=0;i<6;i++){
        if((x & mask) == 0)
            return i + 7;
        mask <<= 1;
    }
    x = PIND;
    mask = 1 << 3;
    for(i=0;i<5;i++){
        if((x & mask) == 0)
            return i + 13;
        mask <<= 1;
    }
    return 0;
}

/* ------------------------------------------------------------------------- */
/* ----------------------------- USB interface ----------------------------- */
/* ------------------------------------------------------------------------- */

static uchar    reportBuffer[2];    /* buffer for HID reports */
static uchar    idleRate;           /* in 4 ms units */

PROGMEM char usbHidReportDescriptor[47] = { /* USB report descriptor */
    0x05, 0x0c,                    // USAGE_PAGE (Consumer Devices)
    0x09, 0x01,                    // USAGE (Consumer Control)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x05, 0x0c,                    //   USAGE_PAGE (Consumer Devices)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x75, 0x02,                    //   REPORT_SIZE (2)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x09, 0xe2,                    //   USAGE (Mute)
    0x09, 0xb6,                    //   USAGE (Scan Previous Track)
    0x09, 0xb5,                    //   USAGE (Scan Next Track)
    0x09, 0xb7,                    //   USAGE (Stop)
    0x09, 0xb1,                    //   USAGE (Pause)
    0x09, 0xb0,                    //   USAGE (Play)
    0x09, 0xea,                    //   USAGE (Volume Down)
    0x09, 0xe9,                    //   USAGE (Volume Up)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x75, 0x02,                    //   REPORT_SIZE (2)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x81, 0x00,                    //   INPUT (Data,Ary,Abs)
    0x15, 0x02,                    //   LOGICAL_MINIMUM (2)
    0x81, 0x03,                    //   INPUT (Cnst,Var,Abs)
    0xc0                           // END_COLLECTION
};

#define MUTE 0x00E2

static const uchar  keyReport[NUM_KEYS + 1][2] PROGMEM = {
/* none */  {0, 0},                     /* no key pressed */
/*  1 */    {MUTE,0},
/*  2 */    {MUTE,0},
/*  3 */    {MUTE,0},
/*  4 */    {MUTE,0},
/*  5 */    {MUTE,0},
/*  6 */    {MUTE,0},
/*  7 */    {MUTE,0},
/*  8 */    {MUTE,0},
/*  9 */    {MUTE,0},
/* 10 */    {MUTE,0},
/* 11 */    {MUTE,0},
/* 12 */    {MUTE,0},
/* 13 */    {MUTE,0},
/* 14 */    {MUTE,0},
/* 15 */    {MUTE,0},
/* 16 */    {MUTE,0},
/* 17 */    {MUTE,0},
};

static void buildReport(uchar key)
{
/* This (not so elegant) cast saves us 10 bytes of program memory */
    *(int *)reportBuffer = pgm_read_word(keyReport[key]);
   //reportBuffer[0] = keyReport[key][0];
   //reportBuffer[1] = keyReport[key][1];
   //reportBuffer[2] = keyReport[key][2];
   
}

/* The following variables store the status of the current data transfer */
static uchar    currentAddress;
static uchar    bytesRemaining;

/* ------------------------------------------------------------------------- */

/* usbFunctionRead() is called when the host requests a chunk of data from
 * the device. For more information see the documentation in usbdrv/usbdrv.h.
 */
uchar   usbFunctionRead(uchar *data, uchar len)
{
    if(len > bytesRemaining)
        len = bytesRemaining;
    eeprom_read_block(data, (uchar *)0 + currentAddress, len);
    currentAddress += len;
    bytesRemaining -= len;
    return len;
}

/* usbFunctionWrite() is called when the host sends a chunk of data to the
 * device. For more information see the documentation in usbdrv/usbdrv.h.
 */
uchar   usbFunctionWrite(uchar *data, uchar len)
{
    if(bytesRemaining == 0)
        return 1;               /* end of transfer */
    if(len > bytesRemaining)
        len = bytesRemaining;
    eeprom_write_block(data, (uchar *)0 + currentAddress, len);
    currentAddress += len;
    bytesRemaining -= len;
    return bytesRemaining == 0; /* return 1 if this was the last chunk */
}

/* ------------------------------------------------------------------------- */

usbMsgLen_t usbFunctionSetup(uchar data[8])
{
usbRequest_t    *rq = (void *)data;

    if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){    /* HID class request */
        if(rq->bRequest == USBRQ_HID_GET_REPORT){  /* wValue: ReportType (highbyte), ReportID (lowbyte) */
            /* since we have only one report type, we can ignore the report-ID */
            bytesRemaining = 128;
            currentAddress = 0;
         //usbMsgPtr = reportBuffer;

           //return sizeof (reportBuffer);
            return USB_NO_MSG;  /* use usbFunctionRead() to obtain data */
        }else if(rq->bRequest == USBRQ_HID_SET_REPORT){
            /* since we have only one report type, we can ignore the report-ID */
            bytesRemaining = 128;
            currentAddress = 0;
            return USB_NO_MSG;  /* use usbFunctionWrite() to receive data from host */
        }
    }else{
        /* ignore vendor type requests, we don't use any */
    }
    return 0;
}

/* ------------------------------------------------------------------------- */

int main(void)
{

uchar   key, lastKey = 0, keyDidChange = 0;
uchar   idleCounter = 0;
uchar   i;

     wdt_enable(WDTO_1S);
    /* Even if you don't use the watchdog, turn it off here. On newer devices,
     * the status of the watchdog (on/off, period) is PRESERVED OVER RESET!
     */
    hardwareInit();
    DBG1(0x00, 0, 0);       /* debug output: main starts */
    /* RESET status: all port bits are inputs without pull-up.
     * That's the way we need D+ and D-. Therefore we don't need any
     * additional hardware initialization.
     */
    odDebugInit();
    usbInit();
    usbDeviceDisconnect();  /* enforce re-enumeration, do this while interrupts are disabled! */
    i = 0;
    while(--i){             /* fake USB disconnect for > 250 ms */
        wdt_reset();
        _delay_ms(1);
    }
    usbDeviceConnect();
    sei();
    DBG1(0x01, 0, 0);       /* debug output: main loop starts */
   for(;;){   /* main event loop */
      wdt_reset();
      usbPoll();
        key = keyPressed();
        if(lastKey != key){
            lastKey = key;
            keyDidChange = 1;
        }
        if(TIFR0 & (1<<TOV0)){   /* 22 ms timer */
            TIFR0 = 1<<TOV0;
            if(idleRate != 0){
                if(idleCounter > 4){
                    idleCounter -= 5;   /* 22 ms in units of 4 ms */
                }else{
                    idleCounter = idleRate;
                    keyDidChange = 1;
                }
            }
        }
        if(keyDidChange && usbInterruptIsReady()){
            keyDidChange = 0;
            /* use last key and not current key status in order to avoid lost
               changes in key status. */
            buildReport(lastKey);
            usbSetInterrupt(reportBuffer, sizeof(reportBuffer));
        }
   }

    return 0;
}


coolblaze03

Re: Volume Control using Consumer Devices HID

Post by coolblaze03 » Thu Apr 15, 2010 7:36 am

Anyone got anything?

coolblaze03

Re: Volume Control using Consumer Devices HID

Post by coolblaze03 » Fri Apr 16, 2010 6:28 am

Since no one can answer my question, can someone tell me how to use the report buffer when you create your own HID Report Descriptor based off a Consumer Device HID?

PHermansson
Posts: 10
Joined: Tue Oct 07, 2008 11:44 am

Re: Volume Control using Consumer Devices HID

Post by PHermansson » Fri Apr 16, 2010 8:24 am

You might try to describe your problem a bit more. What's happening and what's not happening?

I haven't worked with HID myself, my plan for a volume control includes libusb, a potentiometer and Linux amixer.

coolblaze03
Posts: 1
Joined: Fri Apr 16, 2010 6:33 am

Re: Volume Control using Consumer Devices HID

Post by coolblaze03 » Fri Apr 16, 2010 7:49 pm

Ok heres the issue. Ive gotten windows to recognize the chip as a consumer device. But when i submit the commands to the buffer nothing happens. I think im using the buffer wrong. My code is above. Can anyone give me an idea on how to sumbit to the buffer according to the report descriptor you submit. Does the order in the report descriptor matter? If so how should i load my buffer.

Post Reply