fx2/firmware/lib/setupdat.c

397 lines
8.3 KiB
C

/**
* Copyright (C) 2009 Ubixum, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**/
//#define DEBUG_SETUPDAT
#ifdef DEBUG_SETUPDAT
#include <stdio.h> // NOTE this needs deleted
#else
#define printf(...)
#define NULL (void*)0;
#endif
#include <fx2regs.h>
#include <fx2macros.h>
#include <eputils.h>
#include <setupdat.h>
extern BOOL handle_vendorcommand(BYTE cmd);
extern BOOL handle_set_configuration(BYTE cfg);
extern BOOL handle_get_interface(BYTE ifc, BYTE * alt_ifc);
extern BOOL handle_set_interface(BYTE ifc, BYTE alt_ifc);
extern BYTE handle_get_configuration();
extern BOOL handle_set_configuration(BYTE cfg);
extern void handle_reset_ep(BYTE ep);
/**
* Predefs for handlers
**/
// GET_STATUS,
BOOL handle_get_status();
// CLEAR_FEATURE,
BOOL handle_clear_feature();
// 0x02 is reserved
// SET_FEATURE=0x03,
BOOL handle_set_feature();
// 0x04 is reserved
// SET_ADDRESS=0x05, // this is handled by EZ-USB core unless RENUM=0
// GET_DESCRIPTOR,
void handle_get_descriptor();
// SET_DESCRIPTOR,
// GET_CONFIGURATION, // handled by callback
// SET_CONFIGURATION, // handled by callback
// GET_INTERFACE, // handled by callback
// SET_INTERFACE, // handled by callback
// SYNC_FRAME // not yet implemented
/*
TRM 2.2
Setup Token ->
data transfer ->
handshake
*/
void
handle_setupdata()
{
//printf ( "Handle setupdat: %02x\n", SETUPDAT[1] );
switch (SETUPDAT[1]) {
case GET_STATUS:
if (!handle_get_status())
STALLEP0();
break;
case CLEAR_FEATURE:
if (!handle_clear_feature()) {
STALLEP0();
}
break;
case SET_FEATURE:
if (!handle_set_feature()) {
STALLEP0();
}
break;
case GET_DESCRIPTOR:
handle_get_descriptor();
break;
case GET_CONFIGURATION:
EP0BUF[0] = handle_get_configuration();
EP0BCH = 0;
EP0BCL = 1;
break;
case SET_CONFIGURATION:
// user callback
if (!handle_set_configuration(SETUPDAT[2])) {
STALLEP0();
}
break;
case GET_INTERFACE:
{
BYTE alt_ifc;
if (!handle_get_interface(SETUPDAT[4], &alt_ifc)) {
STALLEP0();
} else {
EP0BUF[0] = alt_ifc;
EP0BCH = 0;
EP0BCL = 1;
}
}
break;
case SET_INTERFACE:
// user callback
if (!handle_set_interface(SETUPDAT[4], SETUPDAT[2])) {
STALLEP0();
}
break;
default:
if (!handle_vendorcommand(SETUPDAT[1])) {
printf("Unhandled Vendor Command: %02x\n", SETUPDAT[1]);
STALLEP0();
}
}
// do the handshake
EP0CS |= bmHSNAK;
}
__xdata BYTE *
ep_addr(BYTE ep)
{ // bit 8 of ep_num is the direction
BYTE ep_num = ep & ~0x80; // mask the direction
switch (ep_num) {
case 0:
return &EP0CS;
case 1:
return ep & 0x80 ? &EP1INCS : &EP1OUTCS;
case 2:
return &EP2CS;
case 4:
return &EP4CS;
case 6:
return &EP6CS;
case 8:
return &EP8CS;
default:
return NULL;
}
}
// Get status has three request types
#define GS_DEVICE 0x80
#define GS_INTERFACE 0x81
#define GS_ENDPOINT 0x82
volatile BOOL self_powered = FALSE;
volatile BOOL remote_wakeup_allowed = FALSE;
BOOL
handle_get_status()
{
switch (SETUPDAT[0]) {
// case 0: // sometimes we get a 0 status too
case GS_INTERFACE:
EP0BUF[0] = 0;
EP0BUF[1] = 0;
EP0BCH = 0;
EP0BCL = 2;
break;
case GS_DEVICE:
// two byte response
// byte 0 bit 0 = self powered bit 1 = remote wakeup
EP0BUF[0] = (remote_wakeup_allowed << 1) | self_powered;
// byte 1 = 0
EP0BUF[1] = 0;
EP0BCH = 0;
EP0BCL = 2;
break;
case GS_ENDPOINT:
{
__xdata BYTE *pep = ep_addr(SETUPDAT[4]);
if (!pep)
return FALSE;
// byte 0 bit 0 = stall bit
EP0BUF[0] = *pep & bmEPSTALL ? 1 : 0;
EP0BUF[1] = 0;
EP0BCH = 0;
EP0BCL = 2;
}
break;
default:
printf("Unexpected Get Status: %02x\n", SETUPDAT[0]);
return FALSE;
}
return TRUE;
}
#define GF_DEVICE 0
#define GF_ENDPOINT 2
BOOL
handle_clear_feature()
{
//printf ( "Clear Feature\n" );
switch (SETUPDAT[0]) {
case GF_DEVICE:
if (SETUPDAT[2] == 1) {
remote_wakeup_allowed = FALSE;
break;
}
return FALSE;
case GF_ENDPOINT:
if (SETUPDAT[2] == 0) { // ep stall feature
__xdata BYTE *pep = ep_addr(SETUPDAT[4]);
printf("unstall endpoint %02X\n", SETUPDAT[4]);
*pep &= ~bmEPSTALL;
RESETTOGGLE(SETUPDAT[4]);
} else {
printf("unsupported ep feature %02x", SETUPDAT[2]);
return FALSE;
}
break;
default:
return handle_vendorcommand(SETUPDAT[1]);
}
return TRUE;
}
BOOL
handle_set_feature()
{
printf("Set Feature %02x\n", SETUPDAT[0]);
switch (SETUPDAT[0]) {
case GF_DEVICE:
if (SETUPDAT[2] == 2)
break; // this is TEST_MODE and we simply need to return the handshake
if (SETUPDAT[2] == 1) {
remote_wakeup_allowed = TRUE;
break;
}
return FALSE;
case GF_ENDPOINT:
if (SETUPDAT[2] == 0) { // ep stall feature
// set TRM 2.3.2
// stall and endpoint
__xdata BYTE *pep = ep_addr(SETUPDAT[4]);
printf("Stall ep %d\n", SETUPDAT[4]);
if (!pep) {
return FALSE;
}
*pep |= bmEPSTALL;
// should now reset data toggles
// write ep+dir to TOGCTL
RESETTOGGLE(SETUPDAT[4]);
// restore stalled ep to default condition
// NOTE
//handle_reset_ep(SETUPDAT[4]);
} else {
printf("unsupported ep feature %02x\n", SETUPDAT[2]);
return FALSE;
}
break;
default:
return handle_vendorcommand(SETUPDAT[1]);
}
return TRUE;
}
/* these are devined in dscr.asm
and need to be customized then
linked in by the firmware manually */
extern __code WORD dev_dscr;
extern __code WORD dev_qual_dscr;
extern __code WORD highspd_dscr;
extern __code WORD fullspd_dscr;
extern __code WORD dev_strings;
WORD pDevConfig = (WORD) & fullspd_dscr;
WORD pOtherConfig = (WORD) & highspd_dscr;
void
handle_hispeed(BOOL highspeed)
{
__critical {
printf("Hi Speed or reset Interrupt\n");
if (highspeed) {
pDevConfig = (WORD) & highspd_dscr;
pOtherConfig = (WORD) & fullspd_dscr;
} else {
pDevConfig = (WORD) & fullspd_dscr;
pOtherConfig = (WORD) & highspd_dscr;
}
}
}
/**
* Handle:
* Device Descriptor
* Device Qualifier
* Configuration
* String
* Other-Speed
**/
void
handle_get_descriptor()
{
//printf ( "Get Descriptor\n" );
switch (SETUPDAT[3]) {
case DSCR_DEVICE_TYPE:
printf("Get Device Config\n");
SUDPTRH = MSB((WORD) & dev_dscr);
SUDPTRL = LSB((WORD) & dev_dscr);
break;
case DSCR_CONFIG_TYPE:
// get the config descriptor
printf("Get Config Descriptor\n");
SUDPTRH = MSB(pDevConfig);
SUDPTRL = LSB(pDevConfig);
break;
case DSCR_STRING_TYPE:
//printf ( "Get String Descriptor idx: %d\n", SETUPDAT[2] );
{
STRING_DSCR *pStr = (STRING_DSCR *) & dev_strings;
// pStr points to string 0
BYTE idx = SETUPDAT[2];
BYTE cur = 0; // current check
do {
if (idx == cur++)
break;
//printf ( "Length of pStr: %d\n", pStr->dsc_len );
//printf ( "pstr: %04x to ", pStr );
pStr =
(STRING_DSCR *) ((BYTE *) pStr +
pStr->dsc_len);
//printf ( "%04x\n" , pStr );
if (pStr->dsc_type != DSCR_STRING_TYPE)
pStr = NULL;
} while (pStr && cur <= idx);
if (pStr) {
/* BYTE i;
//printf ( "found str: '");
for (i=0;i<pStr->dsc_len-2;++i) {
printf ( i%2==0?"%c":"%02x", *((BYTE*)(&pStr->pstr)+i));
} printf ( "\n"); */
SUDPTRH = MSB((WORD) pStr);
SUDPTRL = LSB((WORD) pStr);
//SUDPTRH = MSB((WORD)&dev_strings);
//SUDPTRL = LSB((WORD)&dev_strings);
} else {
STALLEP0();
}
}
break;
case DSCR_DEVQUAL_TYPE:
printf("Get Device Qualifier Descriptor\n");
// assumes this is a high speed capable device
SUDPTRH = MSB((WORD) & dev_qual_dscr);
SUDPTRL = LSB((WORD) & dev_qual_dscr);
break;
case DSCR_OTHERSPD_TYPE:
printf("Other Speed Descriptor\n");
SUDPTRH = MSB(pOtherConfig);
SUDPTRL = LSB(pOtherConfig);
break;
default:
printf("Unhandled Get Descriptor: %02x\n", SETUPDAT[3]);
STALLEP0();
}
}