Volume Control using Consumer Devices HID
Posted: 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;
}