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
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void usb_isr(void);
 | 
					void usb_isr(void);
 | 
				
			||||||
 | 
					void usb_init(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef __cplusplus
 | 
					#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_setmask(0);
 | 
				
			||||||
    irq_setie(1);
 | 
					    irq_setie(1);
 | 
				
			||||||
    uart_init();
 | 
					    uart_init();
 | 
				
			||||||
 | 
					    usb_init();
 | 
				
			||||||
    init_printf(NULL, rv_putchar);
 | 
					    init_printf(NULL, rv_putchar);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										29
									
								
								src/usb.c
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								src/usb.c
									
									
									
									
									
								
							@@ -1,5 +1,34 @@
 | 
				
			|||||||
 | 
					#include <grainuum.h>
 | 
				
			||||||
#include <usb.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) {
 | 
					void usb_isr(void) {
 | 
				
			||||||
 | 
					    grainuumCaptureI(&usb, usb_buf);
 | 
				
			||||||
    return;
 | 
					    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