libusrp/firmware/lib/usb_common.c

387 lines
8.1 KiB
C

/* -*- c++ -*- */
/*
* Copyright 2003 Free Software Foundation, Inc.
*
* This file is part of GNU Radio
*
* GNU Radio is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* GNU Radio 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Radio; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "usb_common.h"
#include "fx2regs.h"
#include "syncdelay.h"
#include "fx2utils.h"
#include "isr.h"
#include "usb_descriptors.h"
#include "usb_requests.h"
extern xdata char str0[];
extern xdata char str1[];
extern xdata char str2[];
extern xdata char str3[];
extern xdata char str4[];
extern xdata char str5[];
#define bRequestType SETUPDAT[0]
#define bRequest SETUPDAT[1]
#define wValueL SETUPDAT[2]
#define wValueH SETUPDAT[3]
#define wIndexL SETUPDAT[4]
#define wIndexH SETUPDAT[5]
#define wLengthL SETUPDAT[6]
#define wLengthH SETUPDAT[7]
#define MSB(x) (((unsigned short) x) >> 8)
#define LSB(x) (((unsigned short) x) & 0xff)
volatile bit _usb_got_SUDAV;
unsigned char _usb_config = 0;
unsigned char _usb_alt_setting = 0; // FIXME really 1/interface
xdata unsigned char *current_device_descr;
xdata unsigned char *current_devqual_descr;
xdata unsigned char *current_config_descr;
xdata unsigned char *other_config_descr;
static void
setup_descriptors (void)
{
if (USBCS & bmHSM){ // high speed mode
current_device_descr = high_speed_device_descr;
current_devqual_descr = high_speed_devqual_descr;
current_config_descr = high_speed_config_descr;
other_config_descr = full_speed_config_descr;
}
else {
current_device_descr = full_speed_device_descr;
current_devqual_descr = full_speed_devqual_descr;
current_config_descr = full_speed_config_descr;
other_config_descr = high_speed_config_descr;
}
// whack the type fields
// FIXME, may not be required.
// current_config_descr[1] = DT_CONFIG;
// other_config_descr[1] = DT_OTHER_SPEED;
}
static void
isr_SUDAV (void) interrupt
{
clear_usb_irq ();
_usb_got_SUDAV = 1;
}
static void
isr_USBRESET (void) interrupt
{
clear_usb_irq ();
setup_descriptors ();
}
static void
isr_HIGHSPEED (void) interrupt
{
clear_usb_irq ();
setup_descriptors ();
}
void
usb_install_handlers (void)
{
setup_descriptors (); // ensure that they're set before use
hook_uv (UV_SUDAV, (unsigned short) isr_SUDAV);
hook_uv (UV_USBRESET, (unsigned short) isr_USBRESET);
hook_uv (UV_HIGHSPEED, (unsigned short) isr_HIGHSPEED);
USBIE = bmSUDAV | bmURES | bmHSGRANT;
}
// On the FX2 the only plausible endpoints are 0, 1, 2, 4, 6, 8
// This doesn't check to see that they're enabled
unsigned char
plausible_endpoint (unsigned char ep)
{
ep &= ~0x80; // ignore direction bit
if (ep > 8)
return 0;
if (ep == 1)
return 1;
return (ep & 0x1) == 0; // must be even
}
// return pointer to control and status register for endpoint.
// only called with plausible_endpoints
xdata volatile unsigned char *
epcs (unsigned char ep)
{
if (ep == 0x01) // ep1 has different in and out CS regs
return EP1OUTCS;
if (ep == 0x81)
return EP1INCS;
ep &= ~0x80; // ignore direction bit
if (ep == 0x00) // ep0
return EP0CS;
return EP2CS + (ep >> 1); // 2, 4, 6, 8 are consecutive
}
void
usb_handle_setup_packet (void)
{
_usb_got_SUDAV = 0;
// handle the standard requests...
switch (bRequestType & bmRT_TYPE_MASK){
case bmRT_TYPE_CLASS:
case bmRT_TYPE_RESERVED:
fx2_stall_ep0 (); // we don't handle these. indicate error
break;
case bmRT_TYPE_VENDOR:
// call the application code.
// If it handles the command it returns non-zero
if (!app_vendor_cmd ())
fx2_stall_ep0 ();
break;
case bmRT_TYPE_STD:
// these are the standard requests...
if ((bRequestType & bmRT_DIR_MASK) == bmRT_DIR_IN){
////////////////////////////////////
// handle the IN requests
////////////////////////////////////
switch (bRequest){
case RQ_GET_CONFIG:
EP0BUF[0] = _usb_config; // FIXME app should handle
EP0BCH = 0;
EP0BCL = 1;
break;
// --------------------------------
case RQ_GET_INTERFACE:
EP0BUF[0] = _usb_alt_setting; // FIXME app should handle
EP0BCH = 0;
EP0BCL = 1;
break;
// --------------------------------
case RQ_GET_DESCR:
switch (wValueH){
case DT_DEVICE:
SUDPTRH = MSB (current_device_descr);
SUDPTRL = LSB (current_device_descr);
break;
case DT_DEVQUAL:
SUDPTRH = MSB (current_devqual_descr);
SUDPTRL = LSB (current_devqual_descr);
break;
case DT_CONFIG:
if (0 && wValueL != 1) // FIXME only a single configuration
fx2_stall_ep0 ();
else {
SUDPTRH = MSB (current_config_descr);
SUDPTRL = LSB (current_config_descr);
}
break;
case DT_OTHER_SPEED:
if (0 && wValueL != 1) // FIXME only a single configuration
fx2_stall_ep0 ();
else {
SUDPTRH = MSB (other_config_descr);
SUDPTRL = LSB (other_config_descr);
}
break;
case DT_STRING:
if (wValueL >= nstring_descriptors)
fx2_stall_ep0 ();
else {
xdata char *p = string_descriptors[wValueL];
SUDPTRH = MSB (p);
SUDPTRL = LSB (p);
}
break;
default:
fx2_stall_ep0 (); // invalid request
break;
}
break;
// --------------------------------
case RQ_GET_STATUS:
switch (bRequestType & bmRT_RECIP_MASK){
case bmRT_RECIP_DEVICE:
EP0BUF[0] = bmGSDA_SELF_POWERED; // FIXME app should handle
EP0BUF[1] = 0;
EP0BCH = 0;
EP0BCL = 2;
break;
case bmRT_RECIP_INTERFACE:
EP0BUF[0] = 0;
EP0BUF[1] = 0;
EP0BCH = 0;
EP0BCL = 2;
break;
case bmRT_RECIP_ENDPOINT:
if (plausible_endpoint (wIndexL)){
EP0BUF[0] = *epcs (wIndexL) & bmEPSTALL;
EP0BUF[1] = 0;
EP0BCH = 0;
EP0BCL = 2;
}
else
fx2_stall_ep0 ();
break;
default:
fx2_stall_ep0 ();
break;
}
break;
// --------------------------------
case RQ_SYNCH_FRAME: // not implemented
default:
fx2_stall_ep0 ();
break;
}
}
else {
////////////////////////////////////
// handle the OUT requests
////////////////////////////////////
switch (bRequest){
case RQ_SET_CONFIG:
_usb_config = wValueL; // FIXME app should handle
break;
case RQ_SET_INTERFACE:
_usb_alt_setting = wValueL; // FIXME app should handle
break;
// --------------------------------
case RQ_CLEAR_FEATURE:
switch (bRequestType & bmRT_RECIP_MASK){
case bmRT_RECIP_DEVICE:
switch (wValueL){
case FS_DEV_REMOTE_WAKEUP:
default:
fx2_stall_ep0 ();
}
break;
case bmRT_RECIP_ENDPOINT:
if (wValueL == FS_ENDPOINT_HALT && plausible_endpoint (wIndexL)){
*epcs (wIndexL) &= ~bmEPSTALL;
fx2_reset_data_toggle (wIndexL);
}
else
fx2_stall_ep0 ();
break;
default:
fx2_stall_ep0 ();
break;
}
break;
// --------------------------------
case RQ_SET_FEATURE:
switch (bRequestType & bmRT_RECIP_MASK){
case bmRT_RECIP_DEVICE:
switch (wValueL){
case FS_TEST_MODE:
// hardware handles this after we complete SETUP phase handshake
break;
case FS_DEV_REMOTE_WAKEUP:
default:
fx2_stall_ep0 ();
break;
}
}
break;
case bmRT_RECIP_ENDPOINT:
switch (wValueL){
case FS_ENDPOINT_HALT:
if (plausible_endpoint (wIndexL))
*epcs (wIndexL) |= bmEPSTALL;
else
fx2_stall_ep0 ();
break;
default:
fx2_stall_ep0 ();
break;
}
break;
// --------------------------------
case RQ_SET_ADDRESS: // handled by fx2 hardware
case RQ_SET_DESCR: // not implemented
default:
fx2_stall_ep0 ();
}
}
break;
} // bmRT_TYPE_MASK
// ack handshake phase of device request
EP0CS |= bmHSNAK;
}