/*
* 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
|