592
									
								
								include/grainuum.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										592
									
								
								include/grainuum.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,592 @@
 | 
			
		||||
/****************************************************************************
 | 
			
		||||
 * Grainuum Software USB Stack                                              *
 | 
			
		||||
 *                                                                          *
 | 
			
		||||
 * MIT License:                                                             *
 | 
			
		||||
 * Copyright (c) 2016 Sean Cross                                            *
 | 
			
		||||
 *                                                                          *
 | 
			
		||||
 * Permission is hereby granted, free of charge, to any person obtaining a  *
 | 
			
		||||
 * copy of this software and associated documentation files (the            *
 | 
			
		||||
 * "Software"), to deal in the Software without restriction, including      *
 | 
			
		||||
 * without limitation the rights to use, copy, modify, merge, publish,      *
 | 
			
		||||
 * distribute, distribute with modifications, sublicense, and/or sell       *
 | 
			
		||||
 * copies of the Software, and to permit persons to whom the Software is    *
 | 
			
		||||
 * furnished to do so, subject to the following conditions:                 *
 | 
			
		||||
 *                                                                          *
 | 
			
		||||
 * The above copyright notice and this permission notice shall be included  *
 | 
			
		||||
 * in all copies or substantial portions of the Software.                   *
 | 
			
		||||
 *                                                                          *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
 | 
			
		||||
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
 | 
			
		||||
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
 | 
			
		||||
 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
 | 
			
		||||
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
 | 
			
		||||
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
 | 
			
		||||
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
 | 
			
		||||
 *                                                                          *
 | 
			
		||||
 * Except as contained in this notice, the name(s) of the above copyright   *
 | 
			
		||||
 * holders shall not be used in advertising or otherwise to promote the     *
 | 
			
		||||
 * sale, use or other dealings in this Software without prior written       *
 | 
			
		||||
 * authorization.                                                           *
 | 
			
		||||
 ****************************************************************************/
 | 
			
		||||
 | 
			
		||||
#ifndef _GRAINUUM_H
 | 
			
		||||
#define _GRAINUUM_H
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Extra fields for GrainuumState struct.
 | 
			
		||||
 * @note    You probably can ignore this.
 | 
			
		||||
 */
 | 
			
		||||
#ifndef GRAINUUM_STATE_EXTRA
 | 
			
		||||
#define GRAINUUM_STATE_EXTRA
 | 
			
		||||
#endif /* GRAINUUM_STATE_EXTRA */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Extra fields for GrainuumUSB struct.
 | 
			
		||||
 * @note    Use this to store context and thread information.
 | 
			
		||||
 */
 | 
			
		||||
#ifndef GRAINUUM_EXTRA
 | 
			
		||||
#define GRAINUUM_EXTRA
 | 
			
		||||
#endif /* GRAINUUM_EXTRA */
 | 
			
		||||
 | 
			
		||||
#define GET_STATUS 0
 | 
			
		||||
#define CLEAR_FEATURE 1
 | 
			
		||||
#define SET_FEATURE 3
 | 
			
		||||
#define SET_ADDRESS 5
 | 
			
		||||
#define GET_DESCRIPTOR 6
 | 
			
		||||
#define SET_DESCRIPTOR 7
 | 
			
		||||
#define GET_CONFIGURATION 8
 | 
			
		||||
#define SET_CONFIGURATION 9
 | 
			
		||||
#define GET_INTERFACE 10
 | 
			
		||||
#define SET_INTERFACE 11
 | 
			
		||||
#define SYNC_FRAME 12
 | 
			
		||||
 | 
			
		||||
#define GET_REPORT 1
 | 
			
		||||
#define GET_IDLE 2
 | 
			
		||||
#define GET_PROTOCOL 3
 | 
			
		||||
#define SET_REPORT 9
 | 
			
		||||
#define SET_IDLE 10
 | 
			
		||||
#define SET_PROTOCOL 11
 | 
			
		||||
 | 
			
		||||
enum usb_pids {
 | 
			
		||||
  USB_PID_RESERVED = 0xf0,
 | 
			
		||||
  USB_PID_OUT = 0xe1,
 | 
			
		||||
  USB_PID_ACK = 0xd2,
 | 
			
		||||
  USB_PID_DATA0 = 0xc3,
 | 
			
		||||
  USB_PID_PING = 0xb4,
 | 
			
		||||
  USB_PID_SOF = 0xa5,
 | 
			
		||||
  USB_PID_NYET = 0x96,
 | 
			
		||||
  USB_PID_DATA2 = 0x87,
 | 
			
		||||
  USB_PID_SPLIT = 0x78,
 | 
			
		||||
  USB_PID_IN = 0x69,
 | 
			
		||||
  USB_PID_NAK = 0x5a,
 | 
			
		||||
  USB_PID_DATA1 = 0x4b,
 | 
			
		||||
  USB_PID_ERR = 0x3c,
 | 
			
		||||
  USB_PID_SETUP = 0x2d,
 | 
			
		||||
  USB_PID_STALL = 0x1e,
 | 
			
		||||
  USB_PID_MDATA = 0x0f,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GrainuumUSB;
 | 
			
		||||
struct GrainuumState;
 | 
			
		||||
struct GrainuumConfig;
 | 
			
		||||
 | 
			
		||||
/* Function callbacks */
 | 
			
		||||
 | 
			
		||||
/* Each of these functions are called by the USB system to get a buffer.
 | 
			
		||||
 * On return, *data will point to the buffer, and the number of bytes
 | 
			
		||||
 * in the buffer will be returned.
 | 
			
		||||
 *
 | 
			
		||||
 * If the data does not exist, return 0.
 | 
			
		||||
 */
 | 
			
		||||
typedef int (*get_usb_descriptor_t)(struct GrainuumUSB *usb,
 | 
			
		||||
                                    const void *pkt,
 | 
			
		||||
                                    const void **data);
 | 
			
		||||
typedef void (*usb_set_config_num_t)(struct GrainuumUSB *usb,
 | 
			
		||||
                                     int configNum);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Called when doing an OUT xfer (data to device) to get a buffer for
 | 
			
		||||
 * the specified endpoint.
 | 
			
		||||
 * It is up to the user to ensure the buffer is large enough.
 | 
			
		||||
 */
 | 
			
		||||
typedef void * (*usb_get_buffer_t)(struct GrainuumUSB *usb,
 | 
			
		||||
                                   uint8_t epnum,
 | 
			
		||||
                                   int32_t *size);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * When data is received (i.e. OUT EP), this function will be called.
 | 
			
		||||
 */
 | 
			
		||||
typedef int (*usb_data_in_t)(struct GrainuumUSB *usb,
 | 
			
		||||
                             uint8_t epnum,
 | 
			
		||||
                             uint32_t bytes,
 | 
			
		||||
                             const void *data);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Called immediately after @p grainuumSendData() has queued data.
 | 
			
		||||
 * @note    This function can be used to e.g. sleep a thread.
 | 
			
		||||
 * @param[in] usb     pointer to the @p GrainuumUSB object
 | 
			
		||||
 * @param[in] epnum   endpoint number of the transfer
 | 
			
		||||
 * @param[in] data    pointer to the data being written
 | 
			
		||||
 * @param[in] size   number of bytes being written
 | 
			
		||||
 * @api
 | 
			
		||||
 */
 | 
			
		||||
typedef void (*usb_data_out_start_t)(struct GrainuumUSB *usb,
 | 
			
		||||
                                     int epnum,
 | 
			
		||||
                                     const void *data,
 | 
			
		||||
                                     int size);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Called once all data has been sent.
 | 
			
		||||
 * @note    This function can be used to e.g. wake up a thread.
 | 
			
		||||
 * @param[out] usb     pointer to the @p GrainuumUSB object
 | 
			
		||||
 * @param[out] result  whether the transfer was successful (0), or had an error.
 | 
			
		||||
 * @api
 | 
			
		||||
 */
 | 
			
		||||
typedef int (*usb_data_out_finish_t)(struct GrainuumUSB *usb,
 | 
			
		||||
                                     int result);
 | 
			
		||||
 | 
			
		||||
/* Structure of a USB packet on the wire, plus size field */
 | 
			
		||||
struct usb_packet {
 | 
			
		||||
  union {
 | 
			
		||||
    struct {
 | 
			
		||||
      uint8_t pid;
 | 
			
		||||
      uint8_t data[10]; /* Including CRC */
 | 
			
		||||
    } __attribute((packed, aligned(4)));
 | 
			
		||||
    uint8_t raw_data[11];
 | 
			
		||||
  } __attribute((packed, aligned(4)));
 | 
			
		||||
  uint8_t size; /* Not including pid (so may be 0) */
 | 
			
		||||
  /* Checksum omitted */
 | 
			
		||||
} __attribute__((packed, aligned(4)));
 | 
			
		||||
 | 
			
		||||
/* USB Descriptors */
 | 
			
		||||
 | 
			
		||||
#define DT_DEVICE 0x01
 | 
			
		||||
#define DT_CONFIGURATION 0x02
 | 
			
		||||
#define DT_STRING 0x03
 | 
			
		||||
#define DT_INTERFACE 0x04
 | 
			
		||||
#define DT_ENDPOINT 0x05
 | 
			
		||||
#define DT_DEVICE_QUALIFIER 0x06
 | 
			
		||||
#define DT_OTHER_SPEED_CONFIGURATION 0x07
 | 
			
		||||
#define DT_INTERFACE_POWER 0x08
 | 
			
		||||
 | 
			
		||||
#define DT_HID 0x21
 | 
			
		||||
#define DT_HID_REPORT 0x22
 | 
			
		||||
#define DT_PID 0x23
 | 
			
		||||
 | 
			
		||||
struct usb_setup_packet {
 | 
			
		||||
  uint8_t bmRequestType;
 | 
			
		||||
  uint8_t bRequest;
 | 
			
		||||
  union {
 | 
			
		||||
    uint16_t wValue;
 | 
			
		||||
    struct {
 | 
			
		||||
      uint8_t wValueL;
 | 
			
		||||
      uint8_t wValueH;
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
  uint16_t wIndex;
 | 
			
		||||
  uint16_t wLength;
 | 
			
		||||
} __attribute__((packed, aligned(4)));
 | 
			
		||||
 | 
			
		||||
struct usb_device_descriptor {
 | 
			
		||||
  uint8_t  bLength;
 | 
			
		||||
  uint8_t  bDescriptorType;
 | 
			
		||||
  uint16_t bcdUSB;
 | 
			
		||||
  uint8_t  bDeviceClass;
 | 
			
		||||
  uint8_t  bDeviceSubClass;
 | 
			
		||||
  uint8_t  bDeviceProtocol;
 | 
			
		||||
  uint8_t  bMaxPacketSize0;
 | 
			
		||||
  uint16_t idVendor;
 | 
			
		||||
  uint16_t idProduct;
 | 
			
		||||
  uint16_t bcdDevice;
 | 
			
		||||
  uint8_t  iManufacturer;
 | 
			
		||||
  uint8_t  iProduct;
 | 
			
		||||
  uint8_t  iSerialNumber;
 | 
			
		||||
  uint8_t  bNumConfigurations;
 | 
			
		||||
} __attribute__((packed, aligned(4)));
 | 
			
		||||
 | 
			
		||||
struct usb_configuration_descriptor {
 | 
			
		||||
  uint8_t  bLength;             /* Size of this descriptor, in bytes (9) */
 | 
			
		||||
  uint8_t  bDescriptorType;     /* DT_CONFIGURATION (2) */
 | 
			
		||||
  uint16_t wTotalLength;        /* Total length of this, plus sizeof(data) */
 | 
			
		||||
  uint8_t  bNumInterfaces;      /* Number of interfaces supported by config */
 | 
			
		||||
  uint8_t  bConfigurationValue; /* Value used by Set Configuration */
 | 
			
		||||
  uint8_t  iConfiguration;      /* index of string descriptor for config */
 | 
			
		||||
  uint8_t  bmAttributes;        /* Bitmap of attributes.  D7 must be 1. */
 | 
			
		||||
  uint8_t  bMaxPower;           /* Maximum power, in units of 2mA */
 | 
			
		||||
  uint8_t  data[];              /* Remaining descriptors */
 | 
			
		||||
} __attribute__((packed, aligned(4)));
 | 
			
		||||
 | 
			
		||||
struct usb_string_descriptor {
 | 
			
		||||
  uint8_t bLength;          /* sizeof(usb_string_descriptor) + sizeof(data) */
 | 
			
		||||
  uint8_t bDescriptorType;  /* DT_STRING (3) */
 | 
			
		||||
  uint8_t data[];           /* UTF-16LE string data or lang data(for string 0 */
 | 
			
		||||
} __attribute__((packed, aligned(4)));
 | 
			
		||||
 | 
			
		||||
struct usb_interface_descriptor {
 | 
			
		||||
  uint8_t bLength;            /* sizeof(usb_interface_descriptor) (9) */
 | 
			
		||||
  uint8_t bDescriptorType;    /* DT_INTERFACE (4) */
 | 
			
		||||
  uint8_t bInterfaceNumber;   /* Which interface this describes.  Usually 0. */
 | 
			
		||||
  uint8_t bAlternateSetting;  /* ??? */
 | 
			
		||||
  uint8_t bNumEndpoints;      /* Number of endpoints, minus 1 */
 | 
			
		||||
  uint8_t bInterfaceClass;    /* Class code */
 | 
			
		||||
  uint8_t bInterfaceSubclass; /* Class sub-code */
 | 
			
		||||
  uint8_t bInterfaceProtocol; /* Protocol code, assigned by USB */
 | 
			
		||||
  uint8_t iInterface;         /* Index of string for this interface */
 | 
			
		||||
} __attribute__((packed, aligned(4)));
 | 
			
		||||
 | 
			
		||||
struct usb_endpoint_descriptor {
 | 
			
		||||
  uint8_t  bLength;           /* sizeof(usb_endpoint_descriptor) (7) */
 | 
			
		||||
  uint8_t  bDescriptorType;   /* DT_ENDPOINT (5) */
 | 
			
		||||
  uint8_t  bEndpointAddress;  /* High bit 1:IN, 0:OUT. Lower 4-bits are EP# */
 | 
			
		||||
  uint8_t  bmAttributes;      /* 0=control, 2=bulk, 3=interrupt */
 | 
			
		||||
  uint16_t wMaxPacketSize;    /* Max packet size for this EP */
 | 
			
		||||
  uint8_t  bInterval;         /* Polling rate (in 1ms units) */
 | 
			
		||||
} __attribute__((packed, aligned(4)));
 | 
			
		||||
 | 
			
		||||
struct usb_hid_descriptor {
 | 
			
		||||
  uint8_t  bLength;           /* sizeof(usb_hid_descriptor) (9) */
 | 
			
		||||
  uint8_t  bDescriptorType;   /* DT_HID (0x21) */
 | 
			
		||||
  uint16_t bcdHID;            /* HID class version number, in BCD */
 | 
			
		||||
  uint8_t  bCountryCode;      /* Target country (usually 0) */
 | 
			
		||||
  uint8_t  bNumDescriptors;   /* Number of HID class descriptors (usually 1) */
 | 
			
		||||
  uint8_t  bReportDescriptorType;   /* Report descriptor type (usually 0x22) */
 | 
			
		||||
  uint16_t wReportDescriptorLength; /* Length of the HID/PID report descriptor */
 | 
			
		||||
} __attribute__((packed, aligned(4)));
 | 
			
		||||
 | 
			
		||||
#define GRAINUUM_BUFFER_ELEMENT_SIZE 12 /* 1 PID, 8 data, 2 CRC16, 1 size */
 | 
			
		||||
/* grainuum_buffer is aligned such that its first byte is on a word boundary.
 | 
			
		||||
 * This is because the first byte of every packet is a PID, which is
 | 
			
		||||
 * immediately discarded.  This leaves the remainder of the packet
 | 
			
		||||
 * word-aligned.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define GRAINUUM_BUFFER(name, sz)                           \
 | 
			
		||||
struct {                                                    \
 | 
			
		||||
  uint8_t  head;                                            \
 | 
			
		||||
  uint8_t  tail;                                            \
 | 
			
		||||
  uint8_t  padding;                                         \
 | 
			
		||||
  union {                                                   \
 | 
			
		||||
    uint8_t  buffer[(sz) * GRAINUUM_BUFFER_ELEMENT_SIZE];   \
 | 
			
		||||
    uint8_t  elements[sz][GRAINUUM_BUFFER_ELEMENT_SIZE];    \
 | 
			
		||||
  };                                                        \
 | 
			
		||||
} name __attribute__((aligned(4)));                         \
 | 
			
		||||
uint8_t * name ## _head_ptr;
 | 
			
		||||
#define GRAINUUM_BUFFER_INIT(name)                          \
 | 
			
		||||
  do {                                                      \
 | 
			
		||||
    (name).head = 0;                                        \
 | 
			
		||||
    (name).tail = 0;                                        \
 | 
			
		||||
    name ## _head_ptr = (name).buffer;                      \
 | 
			
		||||
  } while(0)
 | 
			
		||||
#define GRAINUUM_BUFFER_ADVANCE(name)                       \
 | 
			
		||||
  do {                                                      \
 | 
			
		||||
    (name).head += GRAINUUM_BUFFER_ELEMENT_SIZE;            \
 | 
			
		||||
    if ((name).head >= sizeof((name).buffer))               \
 | 
			
		||||
      (name).head = 0;                                      \
 | 
			
		||||
    name ## _head_ptr = ((name).buffer + (name).head);      \
 | 
			
		||||
  } while(0)
 | 
			
		||||
#define GRAINUUM_BUFFER_TOP(name)                           \
 | 
			
		||||
  (&((name).buffer[(name).tail]))
 | 
			
		||||
#define GRAINUUM_BUFFER_REMOVE(name)                        \
 | 
			
		||||
  do {                                                      \
 | 
			
		||||
    (name).tail += GRAINUUM_BUFFER_ELEMENT_SIZE;            \
 | 
			
		||||
  if ((name).tail >= sizeof((name).buffer))                 \
 | 
			
		||||
    (name).tail = 0;                                        \
 | 
			
		||||
  } while(0)
 | 
			
		||||
#define GRAINUUM_BUFFER_IS_EMPTY(name)                      \
 | 
			
		||||
  ((name).head == (name).tail)
 | 
			
		||||
#define GRAINUUM_BUFFER_ENTRY(name)                         \
 | 
			
		||||
  name ## _head_ptr
 | 
			
		||||
 | 
			
		||||
/* Grainuum Structs */
 | 
			
		||||
 | 
			
		||||
struct GrainuumConfig {
 | 
			
		||||
  get_usb_descriptor_t      getDescriptor;
 | 
			
		||||
  usb_set_config_num_t      setConfigNum;
 | 
			
		||||
  usb_get_buffer_t          getReceiveBuffer;
 | 
			
		||||
  usb_data_in_t             receiveData;
 | 
			
		||||
  usb_data_out_start_t      sendDataStarted;
 | 
			
		||||
  usb_data_out_finish_t     sendDataFinished;
 | 
			
		||||
  void                     *data;
 | 
			
		||||
  struct GrainuumUSB       *usb;
 | 
			
		||||
} __attribute__((packed, aligned(4)));
 | 
			
		||||
 | 
			
		||||
struct GrainuumState {
 | 
			
		||||
  struct GrainuumUSB *usb;
 | 
			
		||||
 | 
			
		||||
  const void *data_out;     /* Pointer to the data that's being sent */
 | 
			
		||||
  int32_t data_out_left;    /* How much data has yet to be sent */
 | 
			
		||||
  int32_t data_out_max;     /* The maximum number of bytes to send */
 | 
			
		||||
  int32_t data_out_epnum;   /* Which endpoint the data is for */
 | 
			
		||||
 | 
			
		||||
  struct usb_packet packet; /* Currently-queued packet */
 | 
			
		||||
  int packet_queued;        /* Whether a packet is queued */
 | 
			
		||||
 | 
			
		||||
  uint32_t tok_pos;         /* Position within the current token */
 | 
			
		||||
  void *tok_buf;            /* Buffer storing current token's data */
 | 
			
		||||
  uint8_t tok_epnum;        /* Last token's endpoint */
 | 
			
		||||
 | 
			
		||||
  uint8_t data_buffer;      /* Whether we're sending DATA0 or DATA1 */
 | 
			
		||||
  uint8_t packet_type;      /* PACKET_SETUP, PACKET_IN, or PACKET_OUT */
 | 
			
		||||
 | 
			
		||||
  uint8_t address;          /* Our configured address */
 | 
			
		||||
 | 
			
		||||
  GRAINUUM_STATE_EXTRA
 | 
			
		||||
} __attribute__((packed, aligned(4)));
 | 
			
		||||
 | 
			
		||||
struct GrainuumUSB {
 | 
			
		||||
 | 
			
		||||
  struct GrainuumConfig *cfg; /* Callbacks */
 | 
			
		||||
  int initialized;
 | 
			
		||||
 | 
			
		||||
  /* USB D- pin specification */
 | 
			
		||||
  uint32_t usbdnIAddr;
 | 
			
		||||
  uint32_t usbdnSAddr;
 | 
			
		||||
  uint32_t usbdnCAddr;
 | 
			
		||||
  uint32_t usbdnDAddr;
 | 
			
		||||
  uint32_t usbdnShift;
 | 
			
		||||
 | 
			
		||||
  /* USB D+ pin specification */
 | 
			
		||||
  uint32_t usbdpIAddr;
 | 
			
		||||
  uint32_t usbdpSAddr;
 | 
			
		||||
  uint32_t usbdpCAddr;
 | 
			
		||||
  uint32_t usbdpDAddr;
 | 
			
		||||
  uint32_t usbdpShift;
 | 
			
		||||
 | 
			
		||||
  uint32_t usbdnMask;
 | 
			
		||||
  uint32_t usbdpMask;
 | 
			
		||||
 | 
			
		||||
  uint32_t queued_size;
 | 
			
		||||
  uint32_t queued_epnum;
 | 
			
		||||
  const void *queued_data;
 | 
			
		||||
 | 
			
		||||
  struct GrainuumState state;     /* Associated state */
 | 
			
		||||
 | 
			
		||||
  GRAINUUM_EXTRA
 | 
			
		||||
} __attribute__((packed, aligned(4)));
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static inline void grainuumWritel(uint32_t value, uint32_t addr)
 | 
			
		||||
{
 | 
			
		||||
  *((volatile uint32_t *)addr) = value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline uint32_t grainuumReadl(uint32_t addr)
 | 
			
		||||
{
 | 
			
		||||
  return *(volatile uint32_t *)addr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*===========================================================================*/
 | 
			
		||||
/* Weak hook functions.                                                      */
 | 
			
		||||
/*===========================================================================*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Called just before the USB device is plugged in.
 | 
			
		||||
 * @param[in] usb     pointer to the @p GrainuumUSB object
 | 
			
		||||
 * @api
 | 
			
		||||
 */
 | 
			
		||||
void grainuumConnectPre(struct GrainuumUSB *usb);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Called just after the USB device is plugged in.
 | 
			
		||||
 * @param[in] usb     pointer to the @p GrainuumUSB object
 | 
			
		||||
 * @api
 | 
			
		||||
 */
 | 
			
		||||
void grainuumConnectPost(struct GrainuumUSB *usb);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Called just before the USB device is unplugged.
 | 
			
		||||
 * @param[in] usb     pointer to the @p GrainuumUSB object
 | 
			
		||||
 * @api
 | 
			
		||||
 */
 | 
			
		||||
void grainuumDisconnectPre(struct GrainuumUSB *usb);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Called just after the USB device is unplugged.
 | 
			
		||||
 * @param[in] usb     pointer to the @p GrainuumUSB object
 | 
			
		||||
 * @api
 | 
			
		||||
 */
 | 
			
		||||
void grainuumDisconnectPost(struct GrainuumUSB *usb);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Called just before the USB device is first initialized.
 | 
			
		||||
 * @param[in] usb     pointer to the @p GrainuumUSB object
 | 
			
		||||
 * @api
 | 
			
		||||
 */
 | 
			
		||||
void grainuumInitPre(struct GrainuumUSB *usb);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Called just before the USB device is first initialized.
 | 
			
		||||
 * @param[in] usb     pointer to the @p GrainuumUSB object
 | 
			
		||||
 * @api
 | 
			
		||||
 */
 | 
			
		||||
void grainuumInitPost(struct GrainuumUSB *usb);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Called immediately after a packet has been received.
 | 
			
		||||
 * @note    This is called from an interrupt context.  Data will
 | 
			
		||||
 *          be stored in the buffer that was passed to @p grainuumCaptureI()
 | 
			
		||||
 * @param[in] usb     pointer to the @p GrainuumUSB object
 | 
			
		||||
 * @iclass
 | 
			
		||||
 */
 | 
			
		||||
void grainuumReceivePacket(struct GrainuumUSB *usb);
 | 
			
		||||
 | 
			
		||||
/*===========================================================================*/
 | 
			
		||||
/* External declarations.                                                    */
 | 
			
		||||
/*===========================================================================*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Returns nonzero if Grainuum has been initialized.
 | 
			
		||||
 * @param[in] usb     pointer to the @p GrainuumUSB object.
 | 
			
		||||
 * @return            nonzero if @p GrainuumUSB is initialized.
 | 
			
		||||
 * @retval 0          Object is not initilized.
 | 
			
		||||
 * @api
 | 
			
		||||
 */
 | 
			
		||||
int grainuumInitialized(struct GrainuumUSB *usb);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Queues some data to be sent to the host.
 | 
			
		||||
 * @note    After the first 8 bytes, @p data must remain valid
 | 
			
		||||
 *          until the transfer has completed.  This generally
 | 
			
		||||
 *          means you can send const data stored in the text
 | 
			
		||||
 *          section, or small 8-byte packets.
 | 
			
		||||
 * @param[in] usb     pointer to the @p GrainuumUSB object.
 | 
			
		||||
 * @param[in] epnum   endpoint number of the transfer.
 | 
			
		||||
 * @param[in] data    pointer to the data being written.
 | 
			
		||||
 * @param[in] size    number of bytes being written.
 | 
			
		||||
 * @return            0 if the transfer completed successfully.
 | 
			
		||||
 * @retval 0          Transfer completed successfully.
 | 
			
		||||
 * @api
 | 
			
		||||
 */
 | 
			
		||||
int grainuumSendData(struct GrainuumUSB *usb, int epnum, const void *data, int size);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Clears the send buffer, if not empty.
 | 
			
		||||
 * @note    If data has already been queued for the PHY, then
 | 
			
		||||
 *          this will not prevent it from being sent.
 | 
			
		||||
 *          This function is intended to be used to prevent
 | 
			
		||||
 *          grainuumSendData() from returning -EAGAIN.
 | 
			
		||||
 * @param[in] usb     pointer to the @p GrainuumUSB object.
 | 
			
		||||
 * @api
 | 
			
		||||
 */
 | 
			
		||||
void grainuumDropData(struct GrainuumUSB *usb);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Determines if data is already queued.
 | 
			
		||||
 * @note    If data has been queued, then this will return
 | 
			
		||||
 *          nonzero.  If this returns zero, then you can
 | 
			
		||||
 *          trust grainuumSendData() will succeed.
 | 
			
		||||
 * @param[in] usb     pointer to the @p GrainuumUSB object.
 | 
			
		||||
 * @return            Nonzero if data is already queued.
 | 
			
		||||
 * @api
 | 
			
		||||
 */
 | 
			
		||||
int grainuumDataQueued(struct GrainuumUSB *usb);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Process one received packet through the Grainuum state machine.
 | 
			
		||||
 * @note    This feeds USB packets into the state machine.  It should not
 | 
			
		||||
 *          be called as part of an interrupt.
 | 
			
		||||
 * @param[in] usb     pointer to the @p GrainuumUSB object.
 | 
			
		||||
 * @param[in] packet  The USB packet that was most recently received, with byte 12 holding the size.
 | 
			
		||||
 * @api
 | 
			
		||||
 */
 | 
			
		||||
void grainuumProcess(struct GrainuumUSB *usb,
 | 
			
		||||
                     const uint8_t packet[12]);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Initialize the Grainuum USB system.
 | 
			
		||||
 * @note    This is meant to run as part of an interrupt.  Pass
 | 
			
		||||
 *          the storage buffer in as @p samples.  The number
 | 
			
		||||
 *          of bytes that were read will be stored in the last
 | 
			
		||||
 *          byte of the array.  For best performance, make
 | 
			
		||||
 *          sure that @p sample is on byte 3 of a 4-byte boundary,
 | 
			
		||||
 *          so that samples[1] is on a word boundary. The @p GrainuumUSB
 | 
			
		||||
 *          object will start out disconnected.
 | 
			
		||||
 * @param[in] usb   Pointer to the @p GrainuumUSB object to initialize.
 | 
			
		||||
 * @param[in] link  Pointer to the @p GrainuumConfig object to use.
 | 
			
		||||
 * @api
 | 
			
		||||
 */
 | 
			
		||||
void grainuumInit(struct GrainuumUSB *usb, struct GrainuumConfig *link);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Capture a USB packet from the wire.
 | 
			
		||||
 * @note    This is meant to run as part of an interrupt.  Pass
 | 
			
		||||
 *          the storage buffer in as @p samples.  The number
 | 
			
		||||
 *          of bytes that were read will be stored in the last
 | 
			
		||||
 *          byte of the array.  For best performance, make
 | 
			
		||||
 *          sure that @p sample is on byte 3 of a 4-byte boundary,
 | 
			
		||||
 *          so that samples[1] is on a  word boundary.
 | 
			
		||||
 * @param[in] usb     pointer to the @p GrainuumUSB object.
 | 
			
		||||
 * @param[in] packet  Buffer to store the read samples.
 | 
			
		||||
 * @api
 | 
			
		||||
 */
 | 
			
		||||
int grainuumCaptureI(struct GrainuumUSB *usb, uint8_t samples[67]);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Internal function. Queues 8 bytes to be sent by the phy.
 | 
			
		||||
 * @note    This is an internal function, and is not meant to be called.
 | 
			
		||||
 *          It is meant to queue properly-formatted USB packets complete
 | 
			
		||||
 *          with CRC-16 (if required).
 | 
			
		||||
 * @param[in] usb     pointer to the @p GrainuumUSB object.
 | 
			
		||||
 * @param[in] epnum   The endpoint number to queue data for.
 | 
			
		||||
 * @param[in] buffer  The data to queue.
 | 
			
		||||
 * @param[in] size    The number of bytes that are queued.
 | 
			
		||||
 * @notapi
 | 
			
		||||
 */
 | 
			
		||||
void grainuumWriteQueue(struct GrainuumUSB *usb, int epnum,
 | 
			
		||||
                        const void *buffer, int size);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Simulates plugging the device into USB.
 | 
			
		||||
 * @note    All USB Connect hooks will be called.
 | 
			
		||||
 *          The default USB state is "disconnected",
 | 
			
		||||
 *          so @p grainuumConnect() must be called
 | 
			
		||||
 *          to start communications.
 | 
			
		||||
 * @param[in] usb     pointer to the @p GrainuumUSB object.
 | 
			
		||||
 * @api
 | 
			
		||||
 */
 | 
			
		||||
void grainuumConnect(struct GrainuumUSB *usb);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Simulates unplugging the device from USB.
 | 
			
		||||
 * @note    All USB Disconnect hooks will be called.
 | 
			
		||||
 * @param[in] usb     pointer to the @p GrainuumUSB object.
 | 
			
		||||
 * @api
 | 
			
		||||
 */
 | 
			
		||||
void grainuumDisconnect(struct GrainuumUSB *usb);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Reads one packet from the wire.
 | 
			
		||||
 * @note    This must be called from an interrupt context with
 | 
			
		||||
 *          interrupts disabled.
 | 
			
		||||
 * @param[in] usb       Pointer to the @p GrainuumUSB object.
 | 
			
		||||
 * @param[out] samples  Buffer where the samples will be stored.
 | 
			
		||||
 * @return              The number of bytes read, or negative on error
 | 
			
		||||
 * @retval -1           Timeout while reading.
 | 
			
		||||
 * @retval -2           Read too many bits.
 | 
			
		||||
 * @retval -3           Unable to find sync end.
 | 
			
		||||
 * @retval -4           Probably a keepalive packet.
 | 
			
		||||
 * @notapi
 | 
			
		||||
 */
 | 
			
		||||
int usbPhyReadI(const struct GrainuumUSB *usb, uint8_t samples[11]);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Writes one packet from the wire.
 | 
			
		||||
 * @note    This must be called from an interrupt context with
 | 
			
		||||
 *          interrupts disabled.
 | 
			
		||||
 * @param[in] usb       Pointer to the @p GrainuumUSB object.
 | 
			
		||||
 * @param[in] samples   Buffer where the samples will be stored.
 | 
			
		||||
 * @param[in] size      Number of bytes to write.
 | 
			
		||||
 * @notapi
 | 
			
		||||
 */
 | 
			
		||||
void usbPhyWriteI(const struct GrainuumUSB *usb, const void *buffer, uint32_t size);
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif /* _GRAINUUM_H */
 | 
			
		||||
@@ -6,6 +6,7 @@ extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void usb_isr(void);
 | 
			
		||||
void usb_init(void);
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										187
									
								
								src/grainuum-phy.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								src/grainuum-phy.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,187 @@
 | 
			
		||||
/****************************************************************************
 | 
			
		||||
 * Grainuum Software USB Stack                                              *
 | 
			
		||||
 *                                                                          *
 | 
			
		||||
 * MIT License:                                                             *
 | 
			
		||||
 * Copyright (c) 2016 Sean Cross                                            *
 | 
			
		||||
 *                                                                          *
 | 
			
		||||
 * Permission is hereby granted, free of charge, to any person obtaining a  *
 | 
			
		||||
 * copy of this software and associated documentation files (the            *
 | 
			
		||||
 * "Software"), to deal in the Software without restriction, including      *
 | 
			
		||||
 * without limitation the rights to use, copy, modify, merge, publish,      *
 | 
			
		||||
 * distribute, distribute with modifications, sublicense, and/or sell       *
 | 
			
		||||
 * copies of the Software, and to permit persons to whom the Software is    *
 | 
			
		||||
 * furnished to do so, subject to the following conditions:                 *
 | 
			
		||||
 *                                                                          *
 | 
			
		||||
 * The above copyright notice and this permission notice shall be included  *
 | 
			
		||||
 * in all copies or substantial portions of the Software.                   *
 | 
			
		||||
 *                                                                          *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
 | 
			
		||||
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
 | 
			
		||||
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
 | 
			
		||||
 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
 | 
			
		||||
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
 | 
			
		||||
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
 | 
			
		||||
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
 | 
			
		||||
 *                                                                          *
 | 
			
		||||
 * Except as contained in this notice, the name(s) of the above copyright   *
 | 
			
		||||
 * holders shall not be used in advertising or otherwise to promote the     *
 | 
			
		||||
 * sale, use or other dealings in this Software without prior written       *
 | 
			
		||||
 * authorization.                                                           *
 | 
			
		||||
 ****************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include <generated/csr.h> 
 | 
			
		||||
#include <grainuum.h>
 | 
			
		||||
 | 
			
		||||
__attribute__((weak))
 | 
			
		||||
void grainuumConnectPre(struct GrainuumUSB *usb)
 | 
			
		||||
{
 | 
			
		||||
  (void)usb;
 | 
			
		||||
}
 | 
			
		||||
__attribute__((weak))
 | 
			
		||||
void grainuumConnectPost(struct GrainuumUSB *usb)
 | 
			
		||||
{
 | 
			
		||||
  (void)usb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((weak))
 | 
			
		||||
void grainuumDisconnectPre(struct GrainuumUSB *usb)
 | 
			
		||||
{
 | 
			
		||||
  (void)usb;
 | 
			
		||||
}
 | 
			
		||||
__attribute__((weak))
 | 
			
		||||
void grainuumDisconnectPost(struct GrainuumUSB *usb)
 | 
			
		||||
{
 | 
			
		||||
  (void)usb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((weak))
 | 
			
		||||
void grainuumReceivePacket(struct GrainuumUSB *usb)
 | 
			
		||||
{
 | 
			
		||||
  (void)usb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((weak))
 | 
			
		||||
void grainuumInitPre(struct GrainuumUSB *usb)
 | 
			
		||||
{
 | 
			
		||||
  (void)usb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((weak))
 | 
			
		||||
void grainuumInitPost(struct GrainuumUSB *usb)
 | 
			
		||||
{
 | 
			
		||||
  (void)usb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* --- */
 | 
			
		||||
 | 
			
		||||
void grainuum_receive_packet(struct GrainuumUSB *usb) {
 | 
			
		||||
  grainuumReceivePacket(usb);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int grainuumCaptureI(struct GrainuumUSB *usb, uint8_t samples[67])
 | 
			
		||||
{
 | 
			
		||||
  int ret;
 | 
			
		||||
  const uint8_t nak_pkt[] = {USB_PID_NAK};
 | 
			
		||||
  const uint8_t ack_pkt[] = {USB_PID_ACK};
 | 
			
		||||
 | 
			
		||||
  ret = usbPhyReadI(usb, samples);
 | 
			
		||||
  if (ret <= 0) {
 | 
			
		||||
    if (ret != -1)
 | 
			
		||||
      usbPhyWriteI(usb, nak_pkt, sizeof(nak_pkt));
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Save the byte counter for later inspection */
 | 
			
		||||
  samples[11] = ret;
 | 
			
		||||
 | 
			
		||||
  switch (samples[0]) {
 | 
			
		||||
  case USB_PID_IN:
 | 
			
		||||
    /* Make sure we have queued data, and that it's for this particular EP */
 | 
			
		||||
    if ((!usb->queued_size)
 | 
			
		||||
    || (((((const uint16_t *)(samples+1))[0] >> 7) & 0xf) != usb->queued_epnum))
 | 
			
		||||
    {
 | 
			
		||||
      usbPhyWriteI(usb, nak_pkt, sizeof(nak_pkt));
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    usbPhyWriteI(usb, usb->queued_data, usb->queued_size);
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case USB_PID_SETUP:
 | 
			
		||||
    grainuum_receive_packet(usb);
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case USB_PID_OUT:
 | 
			
		||||
    grainuum_receive_packet(usb);
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case USB_PID_ACK:
 | 
			
		||||
    /* Allow the next byte to be sent */
 | 
			
		||||
    usb->queued_size = 0;
 | 
			
		||||
    grainuum_receive_packet(usb);
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case USB_PID_DATA0:
 | 
			
		||||
  case USB_PID_DATA1:
 | 
			
		||||
    usbPhyWriteI(usb, ack_pkt, sizeof(ack_pkt));
 | 
			
		||||
    grainuum_receive_packet(usb);
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  default:
 | 
			
		||||
    usbPhyWriteI(usb, nak_pkt, sizeof(nak_pkt));
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int grainuumInitialized(struct GrainuumUSB *usb)
 | 
			
		||||
{
 | 
			
		||||
  if (!usb)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  return usb->initialized;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void grainuumWriteQueue(struct GrainuumUSB *usb, int epnum,
 | 
			
		||||
                        const void *buffer, int size)
 | 
			
		||||
{
 | 
			
		||||
  usb->queued_data = buffer;
 | 
			
		||||
  usb->queued_epnum = epnum;
 | 
			
		||||
  usb->queued_size = size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void grainuumInit(struct GrainuumUSB *usb,
 | 
			
		||||
                  struct GrainuumConfig *cfg) {
 | 
			
		||||
 | 
			
		||||
  if (usb->initialized)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  grainuumInitPre(usb);
 | 
			
		||||
 | 
			
		||||
  usb->cfg = cfg;
 | 
			
		||||
  usb->state.usb = usb;
 | 
			
		||||
  cfg->usb = usb;
 | 
			
		||||
 | 
			
		||||
  usb->initialized = 1;
 | 
			
		||||
 | 
			
		||||
  grainuumInitPost(usb);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void grainuumDisconnect(struct GrainuumUSB *usb) {
 | 
			
		||||
 | 
			
		||||
  grainuumDisconnectPre(usb);
 | 
			
		||||
 | 
			
		||||
  usb_pullup_out_write(0);
 | 
			
		||||
 | 
			
		||||
  grainuumDisconnectPost(usb);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void grainuumConnect(struct GrainuumUSB *usb) {
 | 
			
		||||
 | 
			
		||||
  grainuumConnectPre(usb);
 | 
			
		||||
 | 
			
		||||
  usb_pullup_out_write(1);
 | 
			
		||||
 | 
			
		||||
  grainuumConnectPost(usb);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										362
									
								
								src/grainuum-state.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										362
									
								
								src/grainuum-state.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,362 @@
 | 
			
		||||
/****************************************************************************
 | 
			
		||||
 * Grainuum Software USB Stack                                              *
 | 
			
		||||
 *                                                                          *
 | 
			
		||||
 * MIT License:                                                             *
 | 
			
		||||
 * Copyright (c) 2016 Sean Cross                                            *
 | 
			
		||||
 *                                                                          *
 | 
			
		||||
 * Permission is hereby granted, free of charge, to any person obtaining a  *
 | 
			
		||||
 * copy of this software and associated documentation files (the            *
 | 
			
		||||
 * "Software"), to deal in the Software without restriction, including      *
 | 
			
		||||
 * without limitation the rights to use, copy, modify, merge, publish,      *
 | 
			
		||||
 * distribute, distribute with modifications, sublicense, and/or sell       *
 | 
			
		||||
 * copies of the Software, and to permit persons to whom the Software is    *
 | 
			
		||||
 * furnished to do so, subject to the following conditions:                 *
 | 
			
		||||
 *                                                                          *
 | 
			
		||||
 * The above copyright notice and this permission notice shall be included  *
 | 
			
		||||
 * in all copies or substantial portions of the Software.                   *
 | 
			
		||||
 *                                                                          *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
 | 
			
		||||
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
 | 
			
		||||
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
 | 
			
		||||
 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
 | 
			
		||||
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
 | 
			
		||||
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
 | 
			
		||||
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
 | 
			
		||||
 *                                                                          *
 | 
			
		||||
 * Except as contained in this notice, the name(s) of the above copyright   *
 | 
			
		||||
 * holders shall not be used in advertising or otherwise to promote the     *
 | 
			
		||||
 * sale, use or other dealings in this Software without prior written       *
 | 
			
		||||
 * authorization.                                                           *
 | 
			
		||||
 ****************************************************************************/
 | 
			
		||||
 #include "grainuum.h"
 | 
			
		||||
 | 
			
		||||
#ifndef NULL
 | 
			
		||||
#define NULL ((void *)0)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void *memcpy(void *dest, const void *src, unsigned int n);
 | 
			
		||||
 | 
			
		||||
enum usb_state_packet_type {
 | 
			
		||||
  packet_type_none,
 | 
			
		||||
  packet_type_setup,
 | 
			
		||||
  packet_type_setup_in,
 | 
			
		||||
  packet_type_setup_out,
 | 
			
		||||
  packet_type_in,
 | 
			
		||||
  packet_type_out,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
__attribute__((weak))
 | 
			
		||||
void grainuumSendWait(struct GrainuumUSB *usb, int epnum,
 | 
			
		||||
                      const void *data, int size)
 | 
			
		||||
{
 | 
			
		||||
  (void)usb;
 | 
			
		||||
  (void)epnum;
 | 
			
		||||
  (void)data;
 | 
			
		||||
  (void)size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint16_t crc16_add(uint16_t crc, uint8_t c, uint16_t poly)
 | 
			
		||||
{
 | 
			
		||||
  uint8_t  i;
 | 
			
		||||
 | 
			
		||||
  for (i = 0; i < 8; i++) {
 | 
			
		||||
    if ((crc ^ c) & 1)
 | 
			
		||||
      crc = (crc >> 1) ^ poly;
 | 
			
		||||
    else
 | 
			
		||||
      crc >>= 1;
 | 
			
		||||
    c >>= 1;
 | 
			
		||||
  }
 | 
			
		||||
  return crc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint16_t crc16(const uint8_t *data, uint32_t size,
 | 
			
		||||
                      uint16_t init, uint32_t poly)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
  while (size--)
 | 
			
		||||
    init = crc16_add(init, *data++, poly);
 | 
			
		||||
 | 
			
		||||
  return init;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void grainuum_state_clear_tx(struct GrainuumState *state, int result)
 | 
			
		||||
{
 | 
			
		||||
  struct GrainuumUSB *usb = state->usb;
 | 
			
		||||
 | 
			
		||||
  /* If a thread is blocking, wake it up with a failure */
 | 
			
		||||
  if (usb->cfg->sendDataFinished && state->packet_queued)
 | 
			
		||||
    usb->cfg->sendDataFinished(usb, result);
 | 
			
		||||
  state->data_out_left = 0;
 | 
			
		||||
  state->data_out_max = 0;
 | 
			
		||||
  state->data_out = NULL;
 | 
			
		||||
  state->packet_queued = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void grainuum_state_process_tx(struct GrainuumState *state)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
  uint16_t crc;
 | 
			
		||||
  struct GrainuumUSB *usb = state->usb;
 | 
			
		||||
 | 
			
		||||
  /* Don't allow us to re-prepare data */
 | 
			
		||||
  if (state->packet_queued) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  state->packet_queued = 1;
 | 
			
		||||
 | 
			
		||||
  /* If there's no data to send, then don't send any */
 | 
			
		||||
  if (!state->data_out) {
 | 
			
		||||
    state->packet_queued = 0;
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* If we've sent all of our data, then there's nothing else to send */
 | 
			
		||||
  if ((state->data_out_left < 0) || (state->data_out_max < 0)) {
 | 
			
		||||
    grainuum_state_clear_tx(state, 0);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Pick the correct PID, DATA0 or DATA1 */
 | 
			
		||||
  if (state->data_buffer & (1 << state->tok_epnum))
 | 
			
		||||
    state->packet.pid = USB_PID_DATA1;
 | 
			
		||||
  else
 | 
			
		||||
    state->packet.pid = USB_PID_DATA0;
 | 
			
		||||
 | 
			
		||||
  /* If there's no data, prepare a special NULL packet */
 | 
			
		||||
  if ((state->data_out_left == 0) || (state->data_out_max == 0)) {
 | 
			
		||||
 | 
			
		||||
    /* The special-null thing only happens for EP0 */
 | 
			
		||||
    if (state->data_out_epnum != 0) {
 | 
			
		||||
      grainuum_state_clear_tx(state, 0);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    state->packet.data[0] = 0;  /* CRC16 for empty packets is 0 */
 | 
			
		||||
    state->packet.data[1] = 0;
 | 
			
		||||
    state->packet.size = 2;
 | 
			
		||||
    grainuumWriteQueue(usb, state->data_out_epnum,
 | 
			
		||||
                         &state->packet, state->packet.size + 1);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Keep the packet size to 8 bytes max */
 | 
			
		||||
  if (state->data_out_left > 8)
 | 
			
		||||
    state->packet.size = 8;
 | 
			
		||||
  else
 | 
			
		||||
    state->packet.size = state->data_out_left;
 | 
			
		||||
 | 
			
		||||
  /* Limit the amount of data transferred to data_out_max */
 | 
			
		||||
  if (state->packet.size > state->data_out_max)
 | 
			
		||||
    state->packet.size = state->data_out_max;
 | 
			
		||||
 | 
			
		||||
  /* Copy over data bytes */
 | 
			
		||||
  memcpy(state->packet.data, state->data_out, state->packet.size);
 | 
			
		||||
 | 
			
		||||
  /* Calculate and copy the crc16 */
 | 
			
		||||
  crc = ~crc16(state->packet.data, state->packet.size, 0xffff, 0xa001);
 | 
			
		||||
  state->packet.data[state->packet.size++] = crc;
 | 
			
		||||
  state->packet.data[state->packet.size++] = crc >> 8;
 | 
			
		||||
 | 
			
		||||
  /* Prepare the packet, including the PID at the end */
 | 
			
		||||
  grainuumWriteQueue(usb, state->data_out_epnum,
 | 
			
		||||
                       &state->packet, state->packet.size + 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called when a packet is ACKed.
 | 
			
		||||
 * Updates the outgoing packet buffer.
 | 
			
		||||
 */
 | 
			
		||||
static void usbStateTransferSuccess(struct GrainuumState *state)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
  /* Reduce the amount of data left.
 | 
			
		||||
   * If the packet is divisible by 8, this will cause one more call
 | 
			
		||||
   * to this function with state->data_out_left == 0.  This will send
 | 
			
		||||
   * a NULL packet, which indicates end-of-transfer.
 | 
			
		||||
   */
 | 
			
		||||
  state->data_out_left -= 8;
 | 
			
		||||
  state->data_out_max -= 8;
 | 
			
		||||
  state->data_out += 8;
 | 
			
		||||
 | 
			
		||||
  if ((state->data_out_left < 0) || (state->data_out_max < 0)) {
 | 
			
		||||
    grainuum_state_clear_tx(state, 0);
 | 
			
		||||
 | 
			
		||||
    /* End of a State setup packet */
 | 
			
		||||
    if (state->packet_type == packet_type_setup_out)
 | 
			
		||||
      state->packet_type = packet_type_none;
 | 
			
		||||
    if (state->packet_type == packet_type_setup_in)
 | 
			
		||||
      state->packet_type = packet_type_none;
 | 
			
		||||
    if (state->packet_type == packet_type_out)
 | 
			
		||||
      state->packet_type = packet_type_none;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  state->packet_queued = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Send data down the wire, interrupting any existing
 | 
			
		||||
 * data that may be queued.
 | 
			
		||||
 */
 | 
			
		||||
static int grainuum_state_send_data(struct GrainuumState *state,
 | 
			
		||||
                             int epnum,
 | 
			
		||||
                             const void *data,
 | 
			
		||||
                             int size,
 | 
			
		||||
                             int max)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
  /* De-queue any data that may already be queued. */
 | 
			
		||||
  grainuum_state_clear_tx(state, 1);
 | 
			
		||||
 | 
			
		||||
  state->data_out_epnum = epnum;
 | 
			
		||||
  state->data_out_left = size;
 | 
			
		||||
  state->data_out_max = max;
 | 
			
		||||
  state->data_out = data;
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void grainuumDropData(struct GrainuumUSB *usb)
 | 
			
		||||
{
 | 
			
		||||
  usb->state.packet_queued = 0;
 | 
			
		||||
  usb->state.data_out = 0;
 | 
			
		||||
  grainuumWriteQueue(usb, 0, NULL, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int grainuumDataQueued(struct GrainuumUSB *usb)
 | 
			
		||||
{
 | 
			
		||||
  return (usb->state.data_out || usb->state.packet_queued);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int grainuumSendData(struct GrainuumUSB *usb, int epnum,
 | 
			
		||||
                     const void *data, int size)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
  struct GrainuumState *state = &usb->state;
 | 
			
		||||
  int ret;
 | 
			
		||||
 | 
			
		||||
  if (state->data_out || !state->address || state->packet_queued) {
 | 
			
		||||
    return -11; /* EAGAIN */
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ret = grainuum_state_send_data(state, epnum, data, size, size);
 | 
			
		||||
  if (ret)
 | 
			
		||||
    return ret;
 | 
			
		||||
 | 
			
		||||
  grainuum_state_process_tx(state);
 | 
			
		||||
 | 
			
		||||
  if (usb->cfg->sendDataStarted)
 | 
			
		||||
    usb->cfg->sendDataStarted(usb, epnum, data, size);
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int grainuum_state_process_setup(struct GrainuumState *state, const uint8_t packet[10])
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
  const struct usb_setup_packet *setup;
 | 
			
		||||
  const void *response = (void *)-1;
 | 
			
		||||
  uint32_t response_len = 0;
 | 
			
		||||
  struct GrainuumUSB *usb = state->usb;
 | 
			
		||||
  struct GrainuumConfig *cfg = usb->cfg;
 | 
			
		||||
 | 
			
		||||
  setup = (const struct usb_setup_packet *)packet;
 | 
			
		||||
 | 
			
		||||
  if ((setup->bmRequestType == 0x00) && (setup->bRequest == SET_ADDRESS)) {
 | 
			
		||||
    state->address = setup->wValue;
 | 
			
		||||
  }
 | 
			
		||||
  else if ((setup->bmRequestType == 0x00) && (setup->bRequest == SET_CONFIGURATION)) {
 | 
			
		||||
    if (cfg->setConfigNum)
 | 
			
		||||
      cfg->setConfigNum(usb, setup->wValue);
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    response_len = cfg->getDescriptor(usb, setup, &response);
 | 
			
		||||
  }
 | 
			
		||||
  grainuum_state_send_data(state, state->tok_epnum, response, response_len, setup->wLength);
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void grainuum_state_parse_data(struct GrainuumState *state,
 | 
			
		||||
                               const uint8_t packet[10],
 | 
			
		||||
                               uint32_t size)
 | 
			
		||||
{
 | 
			
		||||
  (void)size;
 | 
			
		||||
  struct GrainuumUSB *usb = state->usb;
 | 
			
		||||
 | 
			
		||||
  switch (state->packet_type) {
 | 
			
		||||
 | 
			
		||||
  case packet_type_setup:
 | 
			
		||||
    grainuum_state_process_setup(state, packet);
 | 
			
		||||
    grainuum_state_process_tx(state);
 | 
			
		||||
    state->packet_type = packet_type_none;
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case packet_type_out:
 | 
			
		||||
    // XXX HACK: An OUT packet gets generated (on Windows at least) when
 | 
			
		||||
    // terminating a SETUP sequence.  This seems odd.
 | 
			
		||||
    if (state->tok_epnum == 0)
 | 
			
		||||
      break;
 | 
			
		||||
    // Copy over the packet, minus the CRC16
 | 
			
		||||
    memcpy(state->tok_buf + state->tok_pos, packet, size - 2);
 | 
			
		||||
    state->tok_pos += (size - 2);
 | 
			
		||||
    if (!usb->cfg->receiveData(usb, state->tok_epnum, size - 2, packet))
 | 
			
		||||
      state->packet_type = packet_type_none;
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case packet_type_in:
 | 
			
		||||
  case packet_type_none:
 | 
			
		||||
  default:
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void grainuum_state_parse_token(struct GrainuumState *state,
 | 
			
		||||
                                       const uint8_t packet[2])
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
  state->tok_epnum = (((const uint16_t *)packet)[0] >> 7) & 0xf;
 | 
			
		||||
  /*state->tok_addr  = (((const uint16_t *)packet)[0] >> 11) & 0x1f; // Field unused in this code*/
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void grainuumProcess(struct GrainuumUSB *usb,
 | 
			
		||||
                     const uint8_t packet[12])
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
  uint32_t size = packet[11];
 | 
			
		||||
  struct GrainuumState *state = &usb->state;
 | 
			
		||||
  switch(packet[0]) {
 | 
			
		||||
  case USB_PID_SETUP:
 | 
			
		||||
    state->packet_type = packet_type_setup;
 | 
			
		||||
    grainuum_state_clear_tx(state, 1);
 | 
			
		||||
    grainuum_state_parse_token(state, packet + 1);
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case USB_PID_DATA0:
 | 
			
		||||
    state->data_buffer |= (1 << state->tok_epnum);
 | 
			
		||||
    grainuum_state_parse_data(state, packet + 1, size - 1);
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case USB_PID_DATA1:
 | 
			
		||||
    state->data_buffer &= ~(1 << state->tok_epnum);
 | 
			
		||||
    grainuum_state_parse_data(state, packet + 1, size - 1);
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case USB_PID_OUT:
 | 
			
		||||
    grainuum_state_parse_token(state, packet + 1);
 | 
			
		||||
    state->packet_type = packet_type_out;
 | 
			
		||||
    state->tok_pos = 0;
 | 
			
		||||
    state->tok_buf = usb->cfg->getReceiveBuffer(usb, state->tok_epnum, NULL);
 | 
			
		||||
  break;
 | 
			
		||||
 | 
			
		||||
  case USB_PID_ACK:
 | 
			
		||||
    state->data_buffer ^= (1 << state->tok_epnum);
 | 
			
		||||
    usbStateTransferSuccess(state);
 | 
			
		||||
    if (state->data_out) {
 | 
			
		||||
      grainuum_state_process_tx(state);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      grainuum_state_clear_tx(state, 0);
 | 
			
		||||
    }
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  default:
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -27,6 +27,7 @@ static void init(void) {
 | 
			
		||||
    irq_setmask(0);
 | 
			
		||||
    irq_setie(1);
 | 
			
		||||
    uart_init();
 | 
			
		||||
    usb_init();
 | 
			
		||||
    init_printf(NULL, rv_putchar);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										29
									
								
								src/usb.c
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								src/usb.c
									
									
									
									
									
								
							@@ -1,5 +1,34 @@
 | 
			
		||||
#include <grainuum.h>
 | 
			
		||||
#include <usb.h>
 | 
			
		||||
#include <generated/csr.h>
 | 
			
		||||
 | 
			
		||||
static struct GrainuumConfig cfg;
 | 
			
		||||
static struct GrainuumUSB usb;
 | 
			
		||||
static uint8_t usb_buf[67];
 | 
			
		||||
 | 
			
		||||
void usb_isr(void) {
 | 
			
		||||
    grainuumCaptureI(&usb, usb_buf);
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void usb_init(void) {
 | 
			
		||||
    grainuumInit(&usb, &cfg);
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void usbPhyWriteI(const struct GrainuumUSB *usb, const void *buffer, uint32_t size) {
 | 
			
		||||
    (void)usb;
 | 
			
		||||
    const uint8_t *ubuffer = (const uint8_t *)buffer;
 | 
			
		||||
    uint32_t i = 0;
 | 
			
		||||
    while (i < size)
 | 
			
		||||
        usb_obuf_head_write(ubuffer[i]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int usbPhyReadI(const struct GrainuumUSB *usb, uint8_t *samples) {
 | 
			
		||||
    (void)usb;
 | 
			
		||||
    int count = 0;
 | 
			
		||||
    while (!usb_ibuf_empty_read()) {
 | 
			
		||||
        samples[count++] = usb_ibuf_head_read();
 | 
			
		||||
    }
 | 
			
		||||
    return count;
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user