Full Code of xobs/grainuum for AI

master 16d0fa671786 cached
6 files
74.4 KB
19.8k tokens
69 symbols
1 requests
Download .txt
Repository: xobs/grainuum
Branch: master
Commit: 16d0fa671786
Files: 6
Total size: 74.4 KB

Directory structure:
gitextract_9qw8xfqk/

├── LICENSE
├── README.md
├── grainuum-phy-ll.s
├── grainuum-phy.c
├── grainuum-state.c
└── grainuum.h

================================================
FILE CONTENTS
================================================

================================================
FILE: LICENSE
================================================
Copyright (c) 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, 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 AUTHORS OR 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.


================================================
FILE: README.md
================================================
Grainuum USB
============

A software implementation of a USB stack for small CPUs.

Grainuum is designed to run on Cortex-M0+ CPUs running at 48 MHz with
single-cycle IO.  In practice, this means it runs well on a wide variety
of Kinetis chips.


Usage
=======

To start with, create a GrainuumUSB object that defines your device's pin layout.
Specify the offsets for setting and clearing pins, sampling pins, changing the
pin direction, and the offsets of the two pins in the various banks.

The structure is defined as such:

    static struct USBPHY {
      // USB D- line descriptor 
      uint32_t dpIAddr; // GPIO "sample-whole-bank" address
      uint32_t dpSAddr; // GPIO "set-pin-level" address
      uint32_t dpCAddr; // GPIO "clear-pin-level" address
      uint32_t dpDAddr; // GPIO "pin-direction" address, where 1 = output
      uint32_t dpShift; // Shift of GPIO pin in S/C/D/I addresses
      
      // USB D+ line descriptor, as above
      uint32_t dnIAddr;
      uint32_t dnSAddr;
      uint32_t dnCAddr;
      uint32_t dnDAddr;
      uint32_t dnShift;
      
      // USB masks
      uint32_t dpMask;  // Mask of GPIO pin in S/C/D/I addresses
      uint32_t dnMask;
      ...
    };


For example, if D+ was on pin PTA4 and D- was on PTB0, you might specify the
following layout:

    static struct GrainuumUSB myUSB = {
      /* PTB0 */
      .usbdnIAddr = 0xf8000050, /* FGPIOB_PDIR */
      .usbdnSAddr = 0xf8000044, /* FGPIOB_PSOR */
      .usbdnCAddr = 0xf8000048, /* FGPIOB_PCOR */
      .usbdnDAddr = 0xf8000054, /* FGPIOB_PDDR */
      .usbdnMask  = (1 << 0),
      .usbdnShift = 0,
      
      /* PTA4 */
      .usbdpIAddr = 0xf8000010, /* FGPIOA_PDIR */
      .usbdpSAddr = 0xf8000004, /* FGPIOA_PSOR */
      .usbdpCAddr = 0xf8000008, /* FGPIOA_PCOR */
      .usbdpDAddr = 0xf8000014, /* FGPIOA_PDDR */
      .usbdpMask  = (1 << 4),
      .usbdpShift = 4,
    };

You should also set up a GrainuumConfig device to handle USB communication:

    struct GrainuumConfig {
        /* Called by GrainuumUSB to send descriptors to the host */
      get_usb_descriptor_t      getDescriptor;

        /* Called by GrainuumUSB when the host sets the configuration number */
      usb_set_config_num_t      setConfigNum;

        /* Called by GrainuumUSB to get space to store incoming data */
      usb_get_buffer_t          getReceiveBuffer;

        /* Called by GrainuumUSB when data has been received from the host */
      usb_data_in_t             receiveData;
      
        /* Called by GrainuumUSB after sendData() has queued data, but before it is sent */
      usb_data_out_start_t      sendDataStarted;

        /* Called by GrainuumUSB after sendData() has sent the data to the host */
      usb_data_out_finish_t     sendDataFinished;
    } __attribute__((packed, aligned(4)));

The most important function to fill in is getDescriptor(), which will
allow the USB system to respond to requests from the host.  Most other
entries are optional.

Register these two objects with GrainuumUSB:

  void grainuumInit(struct GrainuumUSB *usb, struct GrainuumConfig *cfg);

This will initialize the PHY and put it in "Disconnected" mode.  To connect, call grainuumConnect();

  void grainuumConnect(struct GrainuumUSB *usb);

Now you can hook your interrupt handler.  When an ISR hits, call grainuumCaptureI()
with a buffer big enough to hold one USB packet:

  void grainuumCaptureI(struct GrainuumUSB *usb, uint8_t packet[12]);

Then, sometime later once the interrupt is finished, pass the same buffer to grainuumProcess():

  void grainuumProcess(struct GrainuumUSB *usb,
                       const uint8_t packet[12]);

*The packet that is passed to grainuumProcess() and grainuumCaptureI() MUST be aligned
such that packet[1] is word-aligned.  One way to do this might be to define packet[16]
as being aligned, and pass &packet[3] to these functions.  Or you can use Granuum Buffers,
which are described below.*

To send data to the host, use grainuumSendData():

  int grainuumSendData(struct GrainuumUSB *usb, int epnum, const void *data, int size);


Grainuum Buffers
----------------

The USB PHY uses a ring buffer to log all incoming data as it enters
the device.  This data has special alignment requirements.  You can use
Grainuum Buffers to manage this data.

Grainuum Buffers are a set of macros that wrap all of the alignment
magic.

Declare a Grainuum Buffer using the GRAINUUM_BUFFER macro, specifying the
number of complete packets to buffer.  To declare a buffer named
*usb_buffer* with four elements, write:

    GRAINUUM_BUFFER(usb_buffer, 4);

In your program code, you must initialize the buffer before you use it:

    GRAINUUM_BUFFER_INIT(usb_buffer);

To check if the buffer is empty, use is_empty:

    if (!GRAINUUM_BUFFER_IS_EMPTY(usb_buffer)) {
        ... work on the buffer ...
    }

You'll generally want to get a pointer to the top of the buffer,
and advance it only if the data is filled.  To get a pointer
to the top of the buffer (and pass it to grainuumCaptureI()), type:

    grainuumCaptureI(usb, GRAINUUM_BUFFER_ENTRY(usb_buffer));

If the buffer is filled, advance the buffer with advance():

    GRAINUUM_BUFFER_ADVANCE(usb_buffer);

To get the oldest item in the queue, use top():

    uint8_t *usb_pkt = GRAINUUM_BUFFER_TOP(usb_buffer);

When you're done with the packet and want to advance tne end
of the buffer (i.e. remove the oldest item), use remove():

    GRAINUUM_BUFFER_REMOVE(usb_buffer);

Callbacks and Hooks
-------------------

Most of the normal configuration is done through the GrainuumConfig structure.

================================================
FILE: grainuum-phy-ll.s
================================================
/****************************************************************************
 * 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_SECTION
#define GRAINUUM_SECTION .ramtext
#endif 
.section GRAINUUM_SECTION /* Can also run out of .section .text */

.cpu    cortex-m0plus
.fpu    softvfp

#if 0
  /***************************************************************************
   * USB PHY low-level code
   *
   * Exports the following functions:
   *
   *    int usbPhyReadI(struct GrainuumUSB *usb, uint8_t buffer[11])
   *    void usbPhyWriteI(struct GrainuumUSB *usb, const uint8_t buffer[11], uint32_t count)
   *
   * Interrupts are disabled during this code, since it is time-critical.
   * Note that as a Kinetis "feature", jumps of more than 48 bytes can
   * cause random amounts of jitter.  Make sure you don't do that.
   *
   * Both functions take the following struct as their first parameter:
   *
   *  static struct USBPHY {
   *    // USB D- line descriptor 
   *    uint32_t dpIAddr; // GPIO "sample-whole-bank" address
   *    uint32_t dpSAddr; // GPIO "set-pin-level" address
   *    uint32_t dpCAddr; // GPIO "clear-pin-level" address
   *    uint32_t dpDAddr; // GPIO "pin-direction" address, where 1 = output
   *    uint32_t dpShift; // Shift of GPIO pin in S/C/D/I addresses
   *
   *    // USB D+ line descriptor, as above
   *    uint32_t dnIAddr;
   *    uint32_t dnSAddr;
   *    uint32_t dnCAddr;
   *    uint32_t dnDAddr;
   *    uint32_t dnShift;
   *
   *    // USB masks
   *    uint32_t dpMask;  // Mask of GPIO pin in S/C/D/I addresses
   *    uint32_t dnMask;
   *
   *    // Optional extra data follows
   * };
   */
#endif

/* [r|w]usbphy offsets */
.equ dpIAddr,0x08
.equ dpSAddr,0x0c
.equ dpCAddr,0x10
.equ dpDAddr,0x14
.equ dpShift,0x18

.equ dnIAddr,0x1c
.equ dnSAddr,0x20
.equ dnCAddr,0x24
.equ dnDAddr,0x28
.equ dnShift,0x2c

.equ dpMask,0x30
.equ dnMask,0x34

  /*
   *
   * Each USB bit takes about 666 nS, which at a 48 MHz clock rate gives us
   * 32 cycles to read each bit.  This code runs on a Cortex M0+, where each
   * instruction is one cycle, except taken-branches are three cycles.
   *
   * A USB frame begins with the pattern KJKJ...KK.  A USB frame ends with
   * a double-SE0 state.  USB low-speed packets have an 8-bit sync period, and
   * a maximum of 11 bytes.  Thus, a USB low-speed packet looks like this:
   *
   *     KJKJKJKK | data | 00
   *
   * Our usbReadData() code will start by positioning ourselves in the
   * middle of a pulse.  We do that by samping the line, then waiting for
   * it to change, then waiting some number of cycles.
   *
   * Once we're positioned, we then start looking for the KK end-of-sync
   * indicator.  An interrupt takes 16 clock cycles, and it probably took
   * at least 32 clock cycles to get here, meaning we've already lost the
   * first KJ.  This is fine, as we just need to look for "KK" to indicate
   * the end of the sync period.
   *
   * Since a USB low-speed packet is at most 11 bytes, we can store this in
   * three 32-bit registers.  We chain three registers together in a shift-
   * chain by self-adding-with-carry on each of the three registers in
   * sequence to move the top bit from one into the bottom bit of the next.
   *
   * Add a 1 to the low register if the state is the same as the previous
   * state, and add a 0 to the low register if the state has changed.
   *
   * As a special case, when we get six consecutive bits in a row (i.e. six
   * ones or six zeroes), the host will "stuff" one bit and flip the state,
   * meaning we should ignore that 1-bit.  If this is the case, the shift
   * should not be processed.
   *
   * Continue reading bits in until we get a double-SE0.  Actually, any
   * SE0 should be considered end-of-frame.
   *
   */

rretval  .req r0  /* Return value (at the end) */

rusbphy  .req r0  /* Pointer to the USBPHY struct (stored on stack) */
rscratch .req r0  /* General-purpose scratch register (after premable) */
routptr  .req r1  /* Outgoing sample buffer */

rone     .req r2  /* The value 1 */
rreg     .req r3  /* Register to sample pin */
rval     .req r4  /* Currently-sampled value */
rmash    .req r5  /* Mask/shift to isolate required pin */

rsample  .req r6  /* Most recent byte */
rcounter .req r7  /* Number of bytes sampled */

rlastval .req r8  /* What value was the last pin? */
runstuff .req r9  /* Log of the last six bits, for unstuffing */

rdpshift .req r10 /* Shift amount for D+ */
rdpiaddr .req r11 /* Pointer to D+ sample address */
rdniaddr .req r12 /* Pointer to D- sample address */

/* Read Stack:
 * 0: dnMask
 * 4: rusbphy (currently unused)
 */
.equ rsDnMask,0x00
.equ rsUsbPhy,0x04

.func usbPhyReadI
.global usbPhyReadI

usb_phy_read__se0:
  add sp, #12
  pop {r2-r6}
  mov r8, r2
  mov r9, r3
  mov r10, r4
  mov r11, r5
  mov r12, r6

  mov rretval, #0
  sub rretval, #4                   // Return -4
  pop {r2-r7,pc}

/*int */usbPhyReadI/*(struct GrainuumUSB *usb, uint8_t samples[11])*/:
  push {r2-r7,lr}
  mov r2, r8
  mov r3, r9
  mov r4, r10
  mov r5, r11
  mov r6, r12
  push {r2-r6}                      // Save high registers
  sub sp, #12

  ldr rreg, [rusbphy, #dnIAddr]     // Grab the address for the data input reg.
  ldr rmash, [rusbphy, #dnMask]     // Grab the mask for the bit.

  str rusbphy, [sp, #rsUsbPhy]      // Save the USBPHY on the stack.
  str rmash, [sp, #rsDnMask]        // Save it on the stack for later.

  ldr rsample, [rusbphy, #dpIAddr]  // Also grab D+ address
  ldr rcounter, [rusbphy, #dpMask]  // And D+ mask

  /* Wait for the line to flip */
  ldr rval, [rreg]                  // Sample D-, to watch for it flipping
  ldr rone, [rsample]               // Sample D+ at the same time
  and rval, rmash                   // Mask off the interesting bit
  mov rlastval, rval                // Save the bit for use in looking for sync

  /* Check to see if it's SE0, in which case this is a keepalive pkt */
  and rone, rcounter                // Mask off the interesting bit
  add rone, rval                    // Combine D+ and D-.
  beq usb_phy_read__se0             // Exit if SE0 condition (both are 0).

  /* Clear out the register shift-chain */
  mov rsample, #0                   // Reset the sample byte value.
  mov rcounter, #0                  // Reset the "bits" counter.

  mov rone, #1                      // Actually load the value '1' into the reg.

  mov rval, #0b11
  mov runstuff, rval                // Load 0b11 into unstuff reg, as the header
                                    // ends with the pattern KK, which starts
                                    // a run of two.

  // The loop is 4 cycles on a failure.  One
  // pulse is 32 cycles.  Therefore, loop up
  // to 8 times before giving up.
.rept 8
  ldr rval, [rreg]                  // Sample USBDP
  and rval, rmash                   // Mask off the interesting bit
  cmp rval, rlastval                // Wait for it to change
  bne usb_phy_read__sync_wait       // When it changes, go wait for sync pulse
.endr
  b usb_phy_read__timeout           // It never changed, so return "timeout".

usb_phy_read__sync_wait:
  // Move us away from the start of the pulse, to avoid transition errors.
  bl usb_phy__wait_5_cycles

  // Wait for the end-of-header sync pulse, which is when the value
  // repeats itself.  This is the "KK" in the KJKJKJKK training sequence.
.rept 6
  ldr rval, [rreg]                    // Sample USBDP
  and rval, rmash                     // Mask off the interesting bit
  cmp rlastval, rval
  beq usb_phy_read__start_reading_usb
  mov rlastval, rval
  bl usb_phy__wait_27_cycles
.endr
  b usb_phy_read__sync_timeout

  /* We're synced to the middle of a pulse, and the clock sync / start-of-
   * -frame has been found.  Real packet data follows.
   */
usb_phy_read__start_reading_usb:
  // ?

  /* Adjust rlastval so that it's in the correct position -- we skip doing
     this above since we're only interested in the value changing, not in
     what the value is.  However, we're now interested in what the value
     is, so we now deal with shifts instead of masks, and always mask by
     #1.
   */
  mov rval, rlastval
  ldr rmash, [rusbphy, #dpShift]
  ror rval, rmash
  and rval, rone
  mov rlastval, rval
  // 6

  /* We have plenty of extra cycles here, because the first bit is a K,
   * and we simply need to wait for it to finish.
   */
  ldr rreg, [rusbphy, #dpIAddr]       // Cache the address of the D+ input bank
  mov rdpiaddr, rreg                  // to save one cycle.
  ldr rreg, [rusbphy, #dnIAddr]       // Cache the address of the D- input bank
  mov rdniaddr, rreg                  // to save another cycle.
  ldr rreg, [rusbphy, #dpShift]       // Cache the D+ shift, too.
  mov rdpshift, rreg
  // 9

  nop
  nop
  nop
  nop
  nop
  nop
  nop

usb_phy_read__get_usb_bit:
  mov rval, rdpiaddr                  // Get the address of the D+ input bank.
  mov rreg, rdniaddr                  // Get the address of the D- input bank.
  ldr rval, [rval]                    // Actually sample D+
  ldr rreg, [rreg]                    // Also sample D-

  mov rmash, rdpshift                 // Get the shift of the D+ bit
  ror rval, rmash                     // Rotate the value down to bit 1.
  and rval, rone                      // Mask off everything else.
  // 7

  /* xor this bit with the last bit and invert it, in order to get
   * the logical value */
  mov rmash, rlastval                 // Load up the previous bit
  mov rlastval, rval                  // Save the current bit for the next loop.
  eor rmash, rval                     // Check to see if the state has changed.
  mvn rmash, rmash                    // Invert, as 1 ^ 1 or 0 ^ 0 should be 1.
  and rmash, rone                     // Mask off everything but the last bit.

  orr rsample, rmash                  // Append the value to the sample.
  ror rsample, rone                   // Move it to the top of the buffer.

  add runstuff, runstuff              // Shift the "unstuff" value up by 1.
  add runstuff, rmash                 // "or" in the one-bit rval to the bottom.

  // 9

  // [16 total]

  /* If we've completed a byte, the rcounter will mask to 0.
   * If the byte is done, advance.
   * If the byte continues, check for SE0.
   */
  add rcounter, rone                  // Increment the total-bit counter.
  mov rscratch, #7                    // Prepare to mask by 0x7
  tst rcounter, rscratch              // Perform the mask
  beq usb_phy_read__advance_byte      // If the result is 0, advance the byte.
  // 4 (or 5, if branch taken)

  // The result is NOT 0, so see if it's an SE0.
usb_phy_read__check_se0:
  // Check for SE0
  ldr rscratch, [sp, #rsDnMask]
  and rreg, rscratch
  add rreg, rval                      // An end-of-frame is indicated by two
                                      // frames of SE0.  If this is the case,
                                      // then the result of adding these
                                      // together will result in 0.
  bne usb_phy_read__check_unstuff
  b usb_phy_read__exit                // Exit if so.
  // 6 (if not SE0)

usb_phy_read__advance_byte:
  lsr rsample, #24                    // Rotate the sample down to the byte list
  strb rsample, [routptr]             // Save the value to the out buffer.
  add routptr, rone                   // Advance the out pointer.
  mov rsample, #0                     // Reset sample accumulator.
  // 5

  // Figure out if we need to unstuff, or if we can just continue to the
  // next bit.
  // Six consecutive USB states will be followed by a dummy
  // state flip.  Ignore this.
usb_phy_read__check_unstuff:
  mov rreg, runstuff
  mov rval, #0b111111                 // Unstuff mask
  and rreg, rval
  cmp rreg, rval

  /* Loop again */
  bne usb_phy_read__get_usb_bit
  // 2 (if branch taken, 1 if unstuffing needs to happen)

usb_phy_read__unstuff:
  nop                                 // We get here when the current bit has
                                      // one more clock cycle left.  Add a nop
                                      // just to make the cycle-counting easier.

  /* --- New "loop" starts here --- */
  // NOTE that we don't increment "rcounter" here.
  mov runstuff, rone                  // We're skipping over one bit, which
                                      // results in a new run of one.
  add runstuff, rone
  // 2

  /* Invert the last value, since a false 0 was added to the stream */
  mov rreg, rlastval                  // Read the current last val into a lo reg.
  mvn rreg, rreg                      // Negate the value.
  and rreg, rreg, rone                // Mask it with 0b1
  mov rlastval, rreg                  // Save it back into a lo reg.
  // 4

  cmp rcounter, #88                   // Sanity check: see if we've read more
                                      // than 88 bytes (11 bits), because we've
                                      // got cycles to spare here.
  bgt usb_phy_read__exit              // Exit if so
  // 2

  bl usb_phy__wait_22_cycles

  b usb_phy_read__get_usb_bit
  // 2

usb_phy_read__exit:
  // a minimum of 10 cycles have elapsed since we got here

  /* Count the number of bytes read (rcounter / 8) */
  mov rretval, rcounter
  asr rretval, #3                 // Return number of bytes (not bits) read.
  cmp rretval, #11                // Error out if we read more than 11 bytes.
  bgt usb_phy_read__overflow_exit
  // 4

usb_phy_read__return:
  add sp, #12                     // Restore stack.
  pop {r2-r6}
  mov r8, r2
  mov r9, r3
  mov r10, r4
  mov r11, r5
  mov r12, r6
  // 13

  pop {r2-r7,pc}
  // 8

  /* Read too many bits, return -2 */
usb_phy_read__overflow_exit:
  mov rretval, #0
  sub rretval, #2
  b usb_phy_read__return

  /* Unable to find pulse end, return -3 */
usb_phy_read__sync_timeout:
  mov rretval, #0
  sub rretval, #3
  b usb_phy_read__return

  /* Timeout while reading, return -1 */
usb_phy_read__timeout:
  mov rretval, #0
  sub rretval, #1
  b usb_phy_read__return

.endfunc
.type usbPhyReadI, %function
.size usbPhyReadI, .-usbPhyReadI



usb_phy__wait_32_cycles: nop
usb_phy__wait_31_cycles: nop
usb_phy__wait_30_cycles: nop
usb_phy__wait_29_cycles: nop
usb_phy__wait_28_cycles: nop
usb_phy__wait_27_cycles: nop
usb_phy__wait_26_cycles: nop
usb_phy__wait_25_cycles: nop
usb_phy__wait_24_cycles: nop
usb_phy__wait_23_cycles: nop
usb_phy__wait_22_cycles: nop
usb_phy__wait_21_cycles: nop
usb_phy__wait_20_cycles: nop
usb_phy__wait_19_cycles: nop
usb_phy__wait_18_cycles: nop
usb_phy__wait_17_cycles: nop
usb_phy__wait_16_cycles: nop
usb_phy__wait_15_cycles: nop
usb_phy__wait_14_cycles: nop
usb_phy__wait_13_cycles: nop
usb_phy__wait_12_cycles: nop
usb_phy__wait_11_cycles: nop
usb_phy__wait_10_cycles: nop
usb_phy__wait_9_cycles:  nop
usb_phy__wait_8_cycles:  nop
usb_phy__wait_7_cycles:  nop
usb_phy__wait_6_cycles:  nop
usb_phy__wait_5_cycles:  mov pc, lr





/*
 * usbPhyWriteI
 * Register (arguments):
 *   r0: USBPHY
 *   r1: pointer to buffer data
 *   r2: number of bytes to write
 */
wusbphy   .req r0   /* Pointer to USBPHY value */

wlastsym  .req r1   /* The last symbol (0 = j, 1 = k) */
wpkt      .req r2   /* Current byte */
wleft     .req r3   /* Number of bits left before we need to reload wpkt */

/* These are used when writing values out.  May be repurposed. */
wpaddr    .req r4   /* Write "address" for D+ line, during normal operation */
wnaddr    .req r5   /* Write "address" for D- line, during normal operation */
wtmp1     .req r4
wtmp2     .req r5

/* These must remain unchanged during the whole operation. */
wpmask    .req r6   /* Write mask for D+ line */
wnmask    .req r7   /* Write mask for D- line */

wbytes    .req r8   /* Pointer to bytes (from r2 arg) */
wend      .req r9   /* End of the wbytes array (from r1+r2 arg) */
wdpsetreg .req r10
wdpclrreg .req r11
wdnclrreg .req r12
wstuff    .req r0   /* The last six bits, used for bit stuffing (reuses wusbphy) */

/* Stack data:
   0: D- set (wDnSAddr)
   4: wusbphy
 */

/* Indexes off of internal data */
.equ wDnSAddr,0x00      /* D- Set Addr */
.equ wDnCAddr,0x04      /* D- Clear Addr */
.equ wPkt1,0x08
.equ wPkt1Num,0x0c
.equ wPkt2,0x10
.equ wPkt2Num,0x14
.equ wPkt3,0x18
.equ wPkt3Num,0x1c
.equ wFirstPkt,0x20
.equ wSpSave,0x24

.thumb
.align  2
.thumb_func
.global usbPhyWriteI
.func usbPhyWriteI
/*void */usbPhyWriteI/*(struct GrainuumUSB *usb, const uint8_t buffer[11], uint32_t count)*/:
  push {r3-r7,lr}
  mov r3, r8
  mov r4, r9
  mov r5, r10
  mov r6, r11
  push {r3-r6}                      // Save other arguments.

  /* Allocate and populate the stack */
  sub sp, #8                        // Use 8 bytes of stack

  /* Cache D+ set and clear registers early on */
  ldr wtmp1, [wusbphy, #dpSAddr]    // Registers are faster than RAM, and we
  mov wdpsetreg, wtmp1              // only have two free registers, so pre-
  ldr wtmp1, [wusbphy, #dpCAddr]    // cache the D+ addresses to save one
  mov wdpclrreg, wtmp1              // clock cycle.
  ldr wtmp1, [wusbphy, #dnCAddr]    // Cache D- clr as well.
  mov wdnclrreg, wtmp1

  /* Load D+ and D- masks, used for direction and value setting */
  ldr wpmask, [wusbphy, #dpMask]    // USB D+ mask
  ldr wnmask, [wusbphy, #dnMask]    // USB D- mask

  /* Pre-set the lines to J-state to prevent glitching */
#if 0
  mov wpaddr, wdpsetreg             // D+ set
  ldr wnaddr, [wusbphy, #dnCAddr]   // D- clr
#else
  mov wpaddr, wdpclrreg             // D+ clr
  ldr wnaddr, [wusbphy, #dnSAddr]   // D- set
#endif
  str wpmask, [wpaddr]              // Write D+ value
  str wnmask, [wnaddr]              // Write D- value

  /* Set D+ line to OUTPUT */
  ldr wtmp1, [wusbphy, #dpDAddr]    // Get the direction address
  ldr wtmp2, [wtmp1]                // Get the direction value
  orr wtmp2, wtmp2, wpmask          // Set the direciton mask
  str wtmp2, [wtmp1]                // Set the direction for D+

  /* Set D- line to OUTPUT */
  ldr wtmp1, [wusbphy, #dnDAddr]    // Get the direction address
  ldr wtmp2, [wtmp1]                // Get the direction value
  orr wtmp2, wtmp2, wnmask          // Set the direciton mask
  str wtmp2, [wtmp1]                // Set the direction for D-

  /* Set K state.  This indicates the start of the packet. */
  mov wpaddr, wdpclrreg             // D+ clr
  ldr wnaddr, [wusbphy, #dnSAddr]   // D- set
  str wpmask, [wpaddr]              // Write D+ value
  str wnmask, [wnaddr]              // Write D- value

  /* Now that the packet has started, we have 30 cycles to complete setup. */

  ldr wtmp1, [wusbphy, #dnSAddr]    // Cache D- Set addr
  str wtmp1, [sp, #0]               // Save it on the stack
  // 4

  /* Save passed-in values on the stack, before we scribble over them. */
  str wusbphy, [sp, #4]             // Save wusbphy,
  mov wbytes, r1                    // byte pointer,
  add r2, r2, r1                    // calculate the end of the array
  mov wend, r2                      // and save it too.
  // 5

  /* Load the next byte into wpkt */
  mov wtmp1, wbytes                 // Load the byte pointer
  ldrb wpkt, [wtmp1]                // Get the actual byte
  add wtmp1, #1                     // Increase the byte pointer
  mov wbytes, wtmp1                 // Save the byte pointer back into wbytes
  // 5

  mvn wpkt, wpkt                    // Invert the value to make the tests work
  mov wleft, #8                     // Start over with 8 bytes.
  // 2

usb_phy_write__get_first_packet:
  mov wlastsym, #1                  // Last symbols were "KK" from the header,
  mov wstuff, #0b111100             // so load a run of 2 into the stuff value.
  // 2

  // usb start-of-frame header //
  /*bl usb_phy_write__state_k // K state entered above already */
  bl usb_phy__wait_6_cycles
  bl usb_phy_write__state_j
  bl usb_phy_write__state_k
  bl usb_phy_write__state_j
  bl usb_phy_write__state_k
  bl usb_phy_write__state_j
  bl usb_phy_write__state_k
  bl usb_phy__wait_26_cycles        // Hold k state for one more cycle.  Take
                                    // up the slack that would normally
                                    // follow this.
  // end of header //

usb_phy_write__top:

  mov wtmp1, #0                     // Clear wthisbit, so we can add later.
  lsr wpkt, #1                      // Shift the bottom bit into the carry bit.
  adc wtmp1, wtmp1                  // Pull the new bit out from the carry bit.

  add wstuff, wstuff, wstuff        // Shift up the stuff bit, to allow for
  add wstuff, wstuff, wtmp1         // adding the new bit in.

  add wlastsym, wlastsym, wtmp1     // Add the new bit to the last symbol.
                                    // Since we're only looking at the last
                                    // bit, this becomes an XOR.
  mov wtmp1, #0b1                   // Examine the last bit, to check for a
  tst wlastsym, wtmp1               // state transition or not.
  // 8

  /* Write the desired state out (each branch is balanced) */
  bne usb_phy_write__j
usb_phy_write__k:
  mov wpaddr, wdpsetreg             // D+ set
  mov wnaddr, wdnclrreg
  b usb_phy_write__out

usb_phy_write__j:
  mov wpaddr, wdpclrreg             // D+ clr
  ldr wnaddr, [sp, #0]              // D- set

usb_phy_write__out:
  str wpmask, [wpaddr]
  str wnmask, [wnaddr]
  // 7 (either branch taken)

  // 15 cycles total

  sub wleft, wleft, #1              // See how many bits we have left to write.
  bne usb_phy_write__continue_byte  // If nonzero, write another bit.
  // 2

  /* We just finished writing a byte.  Load the next byte, or exit. */
usb_phy_write__finished_byte:
  mov wtmp1, wbytes                 // Move byte array into a lo reg
  cmp wtmp1, wend                   // See if we've reached the end.
  beq usb_phy_write__send_eof       // Exit if it's now 0.
  ldrb wpkt, [wtmp1]                // Read the next byte into wpkt.
  add wtmp1, #1                     // Advance byte array by one.
  mov wbytes, wtmp1                 // ...or store byte addr back in the hi reg.
  // 7

usb_phy_write__calculate_next_pkt:
  mvn wpkt, wpkt                    // Invert it to make the math work.
  mov wleft, #8                     // Reset "bits left" to 8.
  // 2

  nop
  // 1

  /* If we just wrote "111111", then stuff one bit */
usb_phy_write__stuff_bit_maybe:
  mov wtmp2, #0b111111              // Compare it with the wstuff value
  and wtmp2, wstuff                 // AND the two together.  If they're the
  beq usb_phy_write__stuff_bit      // same, then stuff one bit.
  // 3

usb_phy_write__done_stuffing_bit:
  b usb_phy_write__top
  // 2

  /* We're still writing this byte, so there's nothing to do. */
usb_phy_write__continue_byte:
  bl usb_phy__wait_7_cycles
  b usb_phy_write__stuff_bit_maybe
  // 2

usb_phy_write__stuff_bit:
  /* When we get here, we are already into the packet. */
// Need 18 cycles until packet is written
  bl usb_phy__wait_6_cycles
  mov wstuff, #0b111111             // Clear out the bit-stuff rcounter
  // 2

  add wlastsym, wlastsym, #1        // Invert the last symbol.

  mov wtmp1, #0b1                   // See if we need to send j or k
  tst wlastsym, wtmp1
  // 3

  /* Write the desired state out (each branch is balanced) */
  bne usb_phy_write__stuff_j
usb_phy_write_stuff_k:
  mov wpaddr, wdpsetreg             // D+ set
  mov wnaddr, wdnclrreg             // D- clr
  b usb_phy_write__stuff_out

usb_phy_write__stuff_j:
  mov wpaddr, wdpclrreg             // D+ clr
  ldr wnaddr, [sp, #0]              // D- set

usb_phy_write__stuff_out:
  str wpmask, [wpaddr]
  str wnmask, [wnaddr]
  // 7 (either branch taken)

  bl usb_phy__wait_13_cycles
  b usb_phy_write__done_stuffing_bit

usb_phy_write__eof_stuff_bit:
  bl usb_phy__wait_12_cycles
  mov wstuff, #0b111111             // Clear out the bit-stuff rcounter
  // 2

  add wlastsym, wlastsym, #1        // Invert the last symbol.

  mov wtmp1, #0b1                   // See if we need to send j or k
  tst wlastsym, wtmp1
  // 3

  /* Write the desired state out (each branch is balanced) */
  bne usb_phy_write__eof_stuff_j
usb_phy_write__eof_stuff_k:
  mov wpaddr, wdpsetreg             // D+ set
  mov wnaddr, wdnclrreg             // D- clr
  b usb_phy_write__eof_stuff_out

usb_phy_write__eof_stuff_j:
  mov wpaddr, wdpclrreg             // D+ clr
  ldr wnaddr, [sp, #0]              // D- set

usb_phy_write__eof_stuff_out:
  str wpmask, [wpaddr]
  str wnmask, [wnaddr]
  bl usb_phy__wait_27_cycles
  b usb_phy_write__send_se0
  // 7 (either branch taken)

usb_phy_write__send_eof:
  mov wtmp2, #0b111111              // Compare it with the wstuff value
  and wtmp2, wstuff                 // AND the two together.  If they're the
  beq usb_phy_write__eof_stuff_bit  // same, then stuff one bit.

  bl usb_phy__wait_16_cycles
usb_phy_write__send_se0:
  bl usb_phy_write__state_se0
  bl usb_phy_write__state_se0

  /* Set J-state, as required by the spec */
#if 1
  bl usb_phy__wait_6_cycles
  mov wpaddr, wdpsetreg             // D+ set
  mov wnaddr, wdnclrreg             // D- clr
  str wpmask, [wpaddr]
  str wnmask, [wnaddr]
#else
#warning "Not setting J state (fix this before committing)"
#endif
  /* Cheat a bit on the end-of-packet time, since the following
   * instructions take roughly 10 cycles before the lines reset.
   */
  bl usb_phy__wait_28_cycles

  // --- Done Transmitting --- //

  // Restore sp, since we're done with it
  ldr wusbphy, [sp, #4]             // Restore wusbphy
  add sp, #8                        // Restore stack pointer.

  /* Now, set both lines back to INPUT */

  /* Set D+ line to INPUT */
  ldr wtmp1, [wusbphy, #dpDAddr]    // Get the direction address
  ldr wtmp2, [wtmp1]                // Get the direction value
  bic wtmp2, wtmp2, wpmask          // Clear the direciton mask
  str wtmp2, [wtmp1]                // Set the direction for D+

  /* Set D- line to INPUT */
  ldr wtmp1, [wusbphy, #dnDAddr]    // Get the direction address
  ldr wtmp2, [wtmp1]                // Get the direction value
  bic wtmp2, wtmp2, wnmask          // Clear the direciton mask
  str wtmp2, [wtmp1]                // Set the direction for D-

  pop {r3-r6}                       // Restore registers
  mov r11, r6
  mov r10, r5
  mov r9, r4
  mov r8, r3
  pop {r3-r7,pc}                    // Restore and return to caller.

  // Useful functions
usb_phy_write__state_se0:
  mov wpaddr, wdpclrreg             // D+ clr
  mov wnaddr, wdnclrreg             // D- clr
  b usb_phy_write__out_func
usb_phy_write__state_j:
  mov wpaddr, wdpsetreg             // D+ set
  mov wnaddr, wdnclrreg             // D- clr
  b usb_phy_write__out_func
usb_phy_write__state_k:
  mov wpaddr, wdpclrreg             // D+ clr
  ldr wnaddr, [sp, #0]              // D- set
  nop
usb_phy_write__out_func:
  str wpmask, [wpaddr]
  str wnmask, [wnaddr]
  b usb_phy__wait_25_cycles

.endfunc
.type usbPhyWriteI, %function
.size usbPhyWriteI, .-usbPhyWriteI


================================================
FILE: grainuum-phy.c
================================================
/****************************************************************************
 * 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"

__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;
}

/* --- */

__attribute__((section(".ramtext")))
void grainuum_receive_packet(struct GrainuumUSB *usb) {
  grainuumReceivePacket(usb);
}

__attribute__((section(".ramtext")))
void grainuumCaptureI(struct GrainuumUSB *usb, uint8_t *samples)
{
  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;
  }

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

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);

  /* Set both lines to 0 (clear both D+ and D-) to simulate unplug. */
  grainuumWritel(usb->usbdpMask, usb->usbdpCAddr);
  grainuumWritel(usb->usbdnMask, usb->usbdnCAddr);

  /* Set both lines to output */
  grainuumWritel(grainuumReadl(usb->usbdpDAddr) | usb->usbdpMask, usb->usbdpDAddr);
  grainuumWritel(grainuumReadl(usb->usbdnDAddr) | usb->usbdnMask, usb->usbdnDAddr);

  grainuumDisconnectPost(usb);
}

void grainuumConnect(struct GrainuumUSB *usb) {

  grainuumConnectPre(usb);

  /* Set both lines to input */
  grainuumWritel(grainuumReadl(usb->usbdpDAddr) & ~usb->usbdpMask, usb->usbdpDAddr);
  grainuumWritel(grainuumReadl(usb->usbdnDAddr) & ~usb->usbdnMask, usb->usbdnDAddr);

  grainuumConnectPost(usb);
}


================================================
FILE: grainuum-state.c
================================================
/****************************************************************************
 * 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;
  }
}


================================================
FILE: grainuum.h
================================================
/****************************************************************************
 * 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;

  uint8_t data_in[8];

  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
 */
void grainuumCaptureI(struct GrainuumUSB *usb, uint8_t packet[12]);

/**
 * @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 */
Download .txt
gitextract_9qw8xfqk/

├── LICENSE
├── README.md
├── grainuum-phy-ll.s
├── grainuum-phy.c
├── grainuum-state.c
└── grainuum.h
Download .txt
SYMBOL INDEX (69 symbols across 3 files)

FILE: grainuum-phy.c
  function grainuumConnectPre (line 34) | __attribute__((weak))
  function grainuumConnectPost (line 39) | __attribute__((weak))
  function grainuumDisconnectPre (line 45) | __attribute__((weak))
  function grainuumDisconnectPost (line 50) | __attribute__((weak))
  function grainuumReceivePacket (line 56) | __attribute__((weak))
  function grainuumInitPre (line 62) | __attribute__((weak))
  function grainuumInitPost (line 68) | __attribute__((weak))
  function grainuum_receive_packet (line 76) | __attribute__((section(".ramtext")))
  function grainuumCaptureI (line 81) | __attribute__((section(".ramtext")))
  function grainuumInitialized (line 139) | int grainuumInitialized(struct GrainuumUSB *usb)
  function grainuumWriteQueue (line 147) | void grainuumWriteQueue(struct GrainuumUSB *usb, int epnum,
  function grainuumInit (line 155) | void grainuumInit(struct GrainuumUSB *usb,
  function grainuumDisconnect (line 172) | void grainuumDisconnect(struct GrainuumUSB *usb) {
  function grainuumConnect (line 187) | void grainuumConnect(struct GrainuumUSB *usb) {

FILE: grainuum-state.c
  type usb_state_packet_type (line 39) | enum usb_state_packet_type {
  function grainuumSendWait (line 48) | __attribute__((weak))
  function crc16_add (line 58) | static uint16_t crc16_add(uint16_t crc, uint8_t c, uint16_t poly)
  function crc16 (line 72) | static uint16_t crc16(const uint8_t *data, uint32_t size,
  function grainuum_state_clear_tx (line 82) | static void grainuum_state_clear_tx(struct GrainuumState *state, int res...
  function grainuum_state_process_tx (line 95) | static void grainuum_state_process_tx(struct GrainuumState *state)
  function usbStateTransferSuccess (line 167) | static void usbStateTransferSuccess(struct GrainuumState *state)
  function grainuum_state_send_data (line 197) | static int grainuum_state_send_data(struct GrainuumState *state,
  function grainuumDropData (line 215) | void grainuumDropData(struct GrainuumUSB *usb)
  function grainuumDataQueued (line 222) | int grainuumDataQueued(struct GrainuumUSB *usb)
  function grainuumSendData (line 227) | int grainuumSendData(struct GrainuumUSB *usb, int epnum,
  function grainuum_state_process_setup (line 250) | static int grainuum_state_process_setup(struct GrainuumState *state, con...
  function grainuum_state_parse_data (line 276) | static void grainuum_state_parse_data(struct GrainuumState *state,
  function grainuum_state_parse_token (line 310) | static inline void grainuum_state_parse_token(struct GrainuumState *state,
  function grainuumProcess (line 318) | void grainuumProcess(struct GrainuumUSB *usb,

FILE: grainuum.h
  type usb_pids (line 72) | enum usb_pids {
  type GrainuumUSB (line 91) | struct GrainuumUSB
  type GrainuumState (line 92) | struct GrainuumState
  type GrainuumConfig (line 93) | struct GrainuumConfig
  type GrainuumUSB (line 103) | struct GrainuumUSB
  type GrainuumUSB (line 106) | struct GrainuumUSB
  type GrainuumUSB (line 114) | struct GrainuumUSB
  type GrainuumUSB (line 121) | struct GrainuumUSB
  type GrainuumUSB (line 135) | struct GrainuumUSB
  type GrainuumUSB (line 147) | struct GrainuumUSB
  type usb_packet (line 151) | struct usb_packet {
  type usb_setup_packet (line 178) | struct usb_setup_packet {
  type usb_device_descriptor (line 192) | struct usb_device_descriptor {
  type usb_configuration_descriptor (line 209) | struct usb_configuration_descriptor {
  type usb_string_descriptor (line 221) | struct usb_string_descriptor {
  type usb_interface_descriptor (line 227) | struct usb_interface_descriptor {
  type usb_endpoint_descriptor (line 239) | struct usb_endpoint_descriptor {
  type usb_hid_descriptor (line 248) | struct usb_hid_descriptor {
  type GrainuumConfig (line 304) | struct GrainuumConfig {
  type GrainuumState (line 315) | struct GrainuumState {
  type GrainuumUSB (line 394) | struct GrainuumUSB
  type GrainuumUSB (line 401) | struct GrainuumUSB
  type GrainuumUSB (line 408) | struct GrainuumUSB
  type GrainuumUSB (line 415) | struct GrainuumUSB
  type GrainuumUSB (line 422) | struct GrainuumUSB
  type GrainuumUSB (line 429) | struct GrainuumUSB
  type GrainuumUSB (line 438) | struct GrainuumUSB
  type GrainuumUSB (line 451) | struct GrainuumUSB
  type GrainuumUSB (line 467) | struct GrainuumUSB
  type GrainuumUSB (line 478) | struct GrainuumUSB
  type GrainuumUSB (line 489) | struct GrainuumUSB
  type GrainuumUSB (line 499) | struct GrainuumUSB
  type GrainuumUSB (line 515) | struct GrainuumUSB
  type GrainuumConfig (line 515) | struct GrainuumConfig
  type GrainuumUSB (line 529) | struct GrainuumUSB
  type GrainuumUSB (line 542) | struct GrainuumUSB
  type GrainuumUSB (line 554) | struct GrainuumUSB
  type GrainuumUSB (line 562) | struct GrainuumUSB
  type GrainuumUSB (line 577) | struct GrainuumUSB
  type GrainuumUSB (line 588) | struct GrainuumUSB
Condensed preview — 6 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (80K chars).
[
  {
    "path": "LICENSE",
    "chars": 1049,
    "preview": "Copyright (c) Sean Cross\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software "
  },
  {
    "path": "README.md",
    "chars": 5596,
    "preview": "Grainuum USB\n============\n\nA software implementation of a USB stack for small CPUs.\n\nGrainuum is designed to run on Cort"
  },
  {
    "path": "grainuum-phy-ll.s",
    "chars": 29216,
    "preview": "/****************************************************************************\n * Grainuum Software USB Stack            "
  },
  {
    "path": "grainuum-phy.c",
    "chars": 5998,
    "preview": "/****************************************************************************\r\n * Grainuum Software USB Stack           "
  },
  {
    "path": "grainuum-state.c",
    "chars": 11560,
    "preview": "/****************************************************************************\r\n * Grainuum Software USB Stack           "
  },
  {
    "path": "grainuum.h",
    "chars": 22766,
    "preview": "/****************************************************************************\r\n * Grainuum Software USB Stack           "
  }
]

About this extraction

This page contains the full source code of the xobs/grainuum GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 6 files (74.4 KB), approximately 19.8k tokens, and a symbol index with 69 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!