Source file: /~heha/basteln/PC/oszi/PCS500/Firmware.zip/Usb.c

/*
 * File:   Usb.c
 * Author: Szymon Roslowski
 *
 * Created on 13 October 2014, 17:46
 *
 * Firmware framework for USB I/O on PIC 16F1455 (and siblings)
 *
 * Based On
 *
 * Firmware framework for USB I/O on PIC 18F2455 (and siblings)
 * Copyright (C) 2005 Alexander Enzmann
 * adapted to MCC18 by Alberto Maccioni on 1/8/09
 */

#ifdef SDCC
# define NO_BIT_DEFINES	// sonst hagelt es Fehler wegen Doppeldefinitionen
# include <pic16f1454.h>
# define __persistent	// not available here
#else
# include <htc.h>
#endif
#include "Usb.h"
#include "UsbDescriptors.h"

/***********************/
/* Local Definitions   */
/***********************/

// Buffer Descriptor bit masks (from PIC datasheet)
#define UOWN	0x80	// USB Own Bit
#define DTS	0x40	// Data Toggle Synchronization Bit
#define KEN	0x20	// BD Keep Enable Bit
#define INCDIS	0x10	// Address Increment Disable Bit
#define DTSEN	0x08	// Data Toggle Synchronization Enable Bit
#define BSTALL	0x04	// Buffer Stall Enable Bit
#define BC98	0x03	// Byte count bit 9+8

// Control Transfer Stages - see USB spec chapter 5
enum{
 SETUP_STAGE,		// Start of a control transfer (followed by 0 or more data stages)
 DATA_OUT_STAGE,	// Data from host to device
 DATA_IN_STAGE,		// Data from device to host
};

/************************/
/* Structures		*/
/************************/

typedef struct{
 BYTE Stat;
 BYTE Cnt;
 WORD ADDR;	// must be a 16-bit pointer! (void* will be assigned as 8-bit)
}BDT;		// Buffer Descriptor Table
// Interestingly, banked addresses work too.

// Every device request starts with an 8 byte setup packet (USB 2.0, chap 9.3)
// with a standard layout.  The meaning of wValue and wIndex will
// vary depending on the request type and specific request.
union{
 struct{
  BYTE bmRequestType;	// D7: Direction, D6..5: Type, D4..0: Recipient
  BYTE bRequest;	// Specific request
  BYTE wValueL;		// LSB of wValue
  BYTE wValueH;		// MSB of wValue
  BYTE wIndexL;		// LSB of wIndex
  BYTE wIndexH;		// MSB of wIndex
  BYTE wLengthL;	// Number of bytes to transfer if there's a data stage
  BYTE wLengthH;	// always zero here
 };
 BYTE data[E0SZ];
}E0buf;

/************************/
/* Local Variables	*/
/************************/

BYTE CtrlTransferStage; // Holds the current stage in a control transfer
BYTE usbCfg;		// Bit 0 = configured, Bit 1 = Remote Wakeup enabled

BYTE*outPtr;		// Data to send to the host
BYTE*inPtr;		// Data from the host
BYTE bCount;		// Number of bytes of data
BYTE address;		// for deferred address set

__persistent BYTE answer[64];
// BDTs must point to 0x2000, start of linear dual-port memory
// The same data is at 0x20 in banked memory, much easier accessible
__at(0x20) volatile BDT Pipes[6];

/************************/
/* Implementation	*/
/************************/

BYTE IsUsbDataAvaialble(void) {
 if (!(Pipes[2].Stat & UOWN))
   return Pipes[2].Cnt;
 return 0;
}

static void toggle(BYTE*stat) {
#ifdef SDCC
 *stat=(BYTE)(*stat&DTS?UOWN|DTSEN:UOWN|DTS|DTSEN);
#else
 FSR1=(BYTE)stat;
 INDF1=~INDF1&DTS|UOWN|DTSEN;
#endif
}

void ReArmInterface(void) {
	//If there is data received in the reeived buffer
	//Indicate that we have processed it and get the endpoint
	//ready to receive next packet.
 if (Pipes[2].Stat&UOWN) return;
 Pipes[2].Cnt =sizeof OutReport;
 toggle(&Pipes[2].Stat);
}

void HIDSend(void) {
    // If the CPU still owns the SIE, then don't try to send anything.
 if (Pipes[3].Stat&UOWN) return;
    // Toggle the data bit and give control to the SIE
 Pipes[3].Cnt =sizeof InReport;
 toggle(&Pipes[3].Stat);
}

// Data stage for a Control Transfer that sends data to the host
// Because transfer buffer must reside in dual-port RAM,
// constant data must be shuffled from flash space.
static void InDataStage(void) {
 BYTE i;    // Determine how many bytes are going to the host
 BYTE transferSize=bCount<E0SZ?bCount:E0SZ;
    // Load the high two bits of the byte count into BC98
 Pipes[1].Cnt  = transferSize;
    // Update the number of bytes that still need to be sent.  Getting
    // all the data back to the host can take multiple transactions, so
    // we need to track how far along we are.
 bCount-=transferSize;
    // Move data to the USB output buffer from wherever it sits now.
 inPtr=E0buf.data;
 for(i=0;i<transferSize;i++) *inPtr++ = *outPtr++;
}

// Configures the buffer descriptor for endpoint 0 so that it is waiting for
// the status stage of a control transfer.
static void PrepareSetup(void) {
 CtrlTransferStage = SETUP_STAGE;
 Pipes[0].Cnt  = 8;
 Pipes[0].Stat = UOWN|DTSEN;	// Give to SIE, enable data toggle checks
 Pipes[1].Stat = 0;		// Give control to CPU
}

static void BuildStringFromUTF8(const char*src) {
 WORD*d=(WORD*)(answer+2);
 for(;;) {
  WORD c=*src;
  if (!c) break;
  src++;
  if (c&0x80) c=(c&0x3F)<<6 | *src++&0x3F;
  *d++=c;
 }
 answer[0]=(BYTE)((BYTE*)d-answer);
 answer[1]=3;			// USBDESCR_STRING
}

static void usbPollEP0(void) {
 bCount = 0;		// No bytes transferred
 if (E0buf.bmRequestType&0x80) {
  answer[0]=0;
  answer[1]=0;
  outPtr=answer;
 }
 switch (E0buf.bmRequestType) {
  case 0x00: switch (E0buf.bRequest) {	// out,device,device
   case 1:	// CLEAR_FEATURE
   case 3:	// SET_FEATURE
   if (E0buf.wValueL==1) {			// Remote Wakeup
    usbCfg=usbCfg&~2|E0buf.bRequest&2;
    return;
   }break;
   case 5: address=E0buf.wValueL; return;	// SET_ADDRESS
   case 9: if (E0buf.wValueL<2) {		// SET_CONFIGURATION
    if (usbCfg=E0buf.wValueL) {
     UEP2=UEP1=0x1E;	// EPHSHK|EPCONDIS|EPOUTEN|EPINEN: Turn on both in and out for endpoints
     Pipes[2].Cnt  = sizeof OutReport;
     Pipes[2].ADDR = PTR16(&OutReport);
     Pipes[2].Stat = UOWN|DTSEN;
     Pipes[3].ADDR = PTR16(&InReport);
     Pipes[3].Stat = DTS;
     Pipes[4].Cnt  = sizeof TmcOut;
     Pipes[4].ADDR = PTR16(TmcOut);
     Pipes[4].Stat = UOWN|DTSEN;
     Pipes[5].ADDR = PTR16(TmcIn);
     Pipes[5].Stat = DTS;
     return;
    }
   }break;
  }break;
  case 0x01: switch (E0buf.bRequest) {	// out,device,interface
   case 11: return;			// SET_INTERFACE
  }break;
  case 0x02: switch (E0buf.bRequest) {	// out,device,endpoint
   case 1:				// CLEAR_FEATURE
   case 3: {				// SET_FEATURE
    BYTE i=E0buf.wIndexL;
    i=(BYTE)(i>>7|i<<1);	// This rotation evaluates to correct Pipe index
    if (!E0buf.wValueL && i<elemof(Pipes)) {	// ENDPOINT_HALT
	// Halt endpoint (as long as it isn't endpoint 0)
     Pipes[i].Stat=(BYTE)(E0buf.bRequest&2?UOWN|BSTALL:i&1?0:UOWN|DTSEN);
     return;
    }
   }
  }break;
  case 0x80: switch (E0buf.bRequest) {	// in,device,device
   case 0: goto handled2;		// Set bits for self powered device and remote wakeup.
   case 6: switch (E0buf.wValueH) {	// GET_DESCRIPTOR
    case 1: {				// DEVICE_DESCRIPTOR
     outPtr=DeviceDescriptor;
    }goto loadlen;
    case 2: {				// CONFIGURATION_DESCRIPTOR
     outPtr = ConfigurationDescriptor;
     bCount = sizeof ConfigurationDescriptor;
    }return;
    case 3: switch (E0buf.wValueL) {	// STRING_DESCRIPTOR
     case 0: BuildStringFromUTF8("\xD0\x89"); goto loadlen;
     case 1: BuildStringFromUTF8("h#s"); goto loadlen;
     case 2: BuildStringFromUTF8("PCS500 USB Adapter"); goto loadlen;
     case 3: BuildStringFromUTF8("HID Interface"); goto loadlen;
     case 4: BuildStringFromUTF8("USBTMC Interface"); goto loadlen;
    }break;
   }break;
   case 8: {				// GET_CONFIGURATION
    answer[0]=usbCfg&0x01;
   }goto onebyte;
  }break;
  case 0x81: switch (E0buf.wIndexL) {	// in,device,interface
   case 0: switch (E0buf.bRequest) {	// interface(0): HID
    case 6: switch (E0buf.wValueH) {	// GET_DESCRIPTOR 
     case 0x21: {			// HID_DESCRIPTOR
      outPtr=ConfigurationDescriptor+18;
loadlen:
      bCount=*outPtr;
     }return;
     case 0x22: {			// HID_REPORT_DESCRIPTOR
      outPtr=HIDReport;
      bCount=sizeof HIDReport;
     }return;
    }break;
    case 10: goto onebyte;		// GET_INTERFACE
   }break;
   case 1: switch (E0buf.bRequest) {	// interface(1): USBTMC
    case 10: {				// GET_INTERFACE
onebyte:	// No support for alternate interfaces. Send zero to host.
     bCount=1;
    }return;
   }break;
  }break;
  case 0x82: switch (E0buf.bRequest) {	// in,device,endpoint
   case 0: {				// GET_STATUS
    BYTE i=E0buf.wIndexL;
    i=(BYTE)(i>>7|i<<1);
    if (Pipes[i].Stat&BSTALL) answer[0] = 0x01;
handled2:
    bCount=2;
   }return;
  }break;
  case 0xA1: switch (E0buf.wIndexL) {	// in,class,interface
   case 1: switch (E0buf.bRequest) {	// interface(1): USBTMC
    case 7: {				// GET_CAPABILITIES
     outPtr=TmcCap;
     bCount=sizeof TmcCap;		// 24 bytes
    }return;
   }break;
  }break;
 }
 Pipes[0].Cnt  = E0SZ;
 Pipes[0].Stat = UOWN|BSTALL;		// Stall EP0
 Pipes[1].Stat = UOWN|BSTALL;
 UCONbits.PKTDIS = 0;
}

// This is the starting point for processing a Control Transfer.  The code directly
// follows the sequence of transactions described in the USB spec chapter 5.  The
// only Control Pipe in this firmware is the Default Control Pipe (endpoint 0).
// Control messages that have a different destination will be discarded.
static void ProcessControlTransfer(void) {
 switch (USTAT) {
  case 0x00:		// Endpoint 0:out
	// Pull PID from middle of BD0STAT
  if ((Pipes[0].Stat&0x3C)==0x0D<<2) {	// SETUP PID - a transaction is starting
	// Process the Setup stage of a control transfer.
	// This code initializes the flags that let the firmware know
	// what to do during subsequent stages of the transfer.
	// Note: Microchip says to turn off the UOWN bit on the IN direction as
	// soon as possible after detecting that a SETUP has been received.
   Pipes[1].Stat &= ~UOWN;
   Pipes[0].Stat &= ~UOWN;
	// Initialize the transfer process
   CtrlTransferStage = SETUP_STAGE;
   usbPollEP0();
   if (!UCONbits.PKTDIS) return;
   if (E0buf.bmRequestType & 0x80) {
	// Device-to-host
    if (bCount>E0buf.wLengthL) bCount=E0buf.wLengthL;
    InDataStage();
    CtrlTransferStage = DATA_IN_STAGE;
	// Reset the out buffer descriptor for endpoint 0
    Pipes[0].Cnt  = E0SZ;
    Pipes[0].Stat = UOWN;
	// Give to SIE, DATA1 packet, enable data toggle checks
    Pipes[1].Stat = UOWN|DTS|DTSEN;
   }else{	// Host-to-device
    CtrlTransferStage = DATA_OUT_STAGE;
	// Clear the input buffer descriptor
    Pipes[1].Cnt  = 0;
    Pipes[1].Stat = UOWN|DTS|DTSEN;
	// Set the out buffer descriptor on endpoint 0 to receive data
    Pipes[0].Cnt  = E0SZ;
	// Give to SIE, DATA1 packet, enable data toggle checks
    Pipes[0].Stat = UOWN|DTS|DTSEN;
   }
	// Enable SIE token and packet processing
   UCONbits.PKTDIS = 0;
  }else if (CtrlTransferStage == DATA_OUT_STAGE) {
	// Data stage for a Control Transfer that reads data from the host
   BYTE i, bufferSize=Pipes[0].Cnt;
   bCount+=bufferSize;	// Accumulate total number of bytes read
   outPtr=E0buf.data;
   for (i=0;i<bufferSize;i++)  *inPtr++ = *outPtr++;
   toggle(&Pipes[0].Stat);
  }else{	// Prepare for the Setup stage of a control transfer
   goto waitforsetup;
  }
  break;
  case 0x04:	// Endpoint 0:in (Status phase?)
  if (address) {UADDR=address; address=0;}	// Now it's time to set USB address
  if (CtrlTransferStage==DATA_IN_STAGE) {
   InDataStage();	// Start (or continue) transmitting data
		// Turn control over to the SIE and toggle the data bit
   toggle(&Pipes[1].Stat);
  }else{
waitforsetup:
   PrepareSetup();	// Prepare for the Setup stage of a control transfer
  }
  break;
 }
}

void InitializeUSB(void) {
 UCFG = 0x14;	// UPUEN|FSEN: Enable pullup resistors; full speed mode; No PingPong
 UCON = 0x08;	// USBEN
 UIE  = 0x11;	// USB Reset + Idle Detect Interrupt Enable bit
}

// Main entry point for USB tasks.  Checks interrupts, then checks for transactions.
void ProcessUSBTransactions(void) {
	// If the USB became active then wake up from suspend
 if (UIRbits.ACTVIF && UIEbits.ACTVIE) {
  UCONbits.SUSPND = 0;		// Bring USB module out of power conserve
  UIEbits.ACTVIE = 0;
  UIRbits.ACTVIF = 0;
 }
// If we are supposed to be suspended, then don't try performing any processing.
 if (UCONbits.SUSPND) {
  UIR = 0;			// Clear All Interrupt Flags
  PIR2bits.USBIF = 0;		// Clear Global Usb Interrupt Flag
  return;
 }
	// Process a bus reset
 if (UIRbits.URSTIF) {
  UEIR  = 0x00;
  UIR   = 0x00;
  UEIE  = 0x9F;		// BTSEE|BTOEE|DFN8EE|CRC16EE|CRC5EE|PIDEE
  UIE   = 0x7B;		// SOFIE|STALLIE|IDLEIE|TRNIE|UERRIE|URSTIE
  UEP0  = 0x16;			// Set endpoint 0 as a control pipe
	// Flush any pending transactions
  while (UIRbits.TRNIF) UIRbits.TRNIF = 0;
  UCONbits.PKTDIS = 0;		// Enable packet processing
  Pipes[1].ADDR = Pipes[0].ADDR = PTR16(E0buf.data);
  PrepareSetup();		// Prepare for the Setup stage of a control transfer
  usbCfg = 0;	// Clear active configuration
  UIRbits.URSTIF=0;
 }
 if (UIRbits.IDLEIF) {
	// No bus activity for a while - suspend the firmware
  UIEbits.ACTVIE = 1;		// Enable bus activity interrupt
  UCONbits.SUSPND= 1;		// Put USB module in power conserve
  UIRbits.IDLEIF = 0;
 }
 if (UIRbits.SOFIF) {
// Add code for Start Of Frame (1 ms) here
  UIRbits.SOFIF = 0;
 }
 if (UIRbits.STALLIF) {
  if (UEP0bits.EPSTALL) {
			// Prepare for the Setup stage of a control transfer
   PrepareSetup();
   UEP0bits.EPSTALL=0;
  }
  UIRbits.STALLIF=0;
 }
 if (UIRbits.UERRIF) {
			// TBD: See where the error came from.
  UIRbits.UERRIF=0;	// Clear errors
  UEIR=0 ;		// Clear All Usb Error Interrupt Flags
 }
	// A transaction has finished.  Try default processing on endpoint 0.
 if (UIRbits.TRNIF) {
  ProcessControlTransfer();
  UIRbits.TRNIF=0;
 }
 PIR2bits.USBIF = 0;	// Clear Global Usb Interrupt Flag
}
Detected encoding: ASCII (7 bit)2