[
  {
    "path": "LICENSE",
    "content": "Copyright (c) Sean Cross\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \"Software\"), to deal in the Software without\nrestriction, including without limitation the rights to use,\ncopy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\nOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\nHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "Grainuum USB\n============\n\nA software implementation of a USB stack for small CPUs.\n\nGrainuum is designed to run on Cortex-M0+ CPUs running at 48 MHz with\nsingle-cycle IO.  In practice, this means it runs well on a wide variety\nof Kinetis chips.\n\n\nUsage\n=======\n\nTo start with, create a GrainuumUSB object that defines your device's pin layout.\nSpecify the offsets for setting and clearing pins, sampling pins, changing the\npin direction, and the offsets of the two pins in the various banks.\n\nThe structure is defined as such:\n\n    static struct USBPHY {\n      // USB D- line descriptor \n      uint32_t dpIAddr; // GPIO \"sample-whole-bank\" address\n      uint32_t dpSAddr; // GPIO \"set-pin-level\" address\n      uint32_t dpCAddr; // GPIO \"clear-pin-level\" address\n      uint32_t dpDAddr; // GPIO \"pin-direction\" address, where 1 = output\n      uint32_t dpShift; // Shift of GPIO pin in S/C/D/I addresses\n      \n      // USB D+ line descriptor, as above\n      uint32_t dnIAddr;\n      uint32_t dnSAddr;\n      uint32_t dnCAddr;\n      uint32_t dnDAddr;\n      uint32_t dnShift;\n      \n      // USB masks\n      uint32_t dpMask;  // Mask of GPIO pin in S/C/D/I addresses\n      uint32_t dnMask;\n      ...\n    };\n\n\nFor example, if D+ was on pin PTA4 and D- was on PTB0, you might specify the\nfollowing layout:\n\n    static struct GrainuumUSB myUSB = {\n      /* PTB0 */\n      .usbdnIAddr = 0xf8000050, /* FGPIOB_PDIR */\n      .usbdnSAddr = 0xf8000044, /* FGPIOB_PSOR */\n      .usbdnCAddr = 0xf8000048, /* FGPIOB_PCOR */\n      .usbdnDAddr = 0xf8000054, /* FGPIOB_PDDR */\n      .usbdnMask  = (1 << 0),\n      .usbdnShift = 0,\n      \n      /* PTA4 */\n      .usbdpIAddr = 0xf8000010, /* FGPIOA_PDIR */\n      .usbdpSAddr = 0xf8000004, /* FGPIOA_PSOR */\n      .usbdpCAddr = 0xf8000008, /* FGPIOA_PCOR */\n      .usbdpDAddr = 0xf8000014, /* FGPIOA_PDDR */\n      .usbdpMask  = (1 << 4),\n      .usbdpShift = 4,\n    };\n\nYou should also set up a GrainuumConfig device to handle USB communication:\n\n    struct GrainuumConfig {\n        /* Called by GrainuumUSB to send descriptors to the host */\n      get_usb_descriptor_t      getDescriptor;\n\n        /* Called by GrainuumUSB when the host sets the configuration number */\n      usb_set_config_num_t      setConfigNum;\n\n        /* Called by GrainuumUSB to get space to store incoming data */\n      usb_get_buffer_t          getReceiveBuffer;\n\n        /* Called by GrainuumUSB when data has been received from the host */\n      usb_data_in_t             receiveData;\n      \n        /* Called by GrainuumUSB after sendData() has queued data, but before it is sent */\n      usb_data_out_start_t      sendDataStarted;\n\n        /* Called by GrainuumUSB after sendData() has sent the data to the host */\n      usb_data_out_finish_t     sendDataFinished;\n    } __attribute__((packed, aligned(4)));\n\nThe most important function to fill in is getDescriptor(), which will\nallow the USB system to respond to requests from the host.  Most other\nentries are optional.\n\nRegister these two objects with GrainuumUSB:\n\n  void grainuumInit(struct GrainuumUSB *usb, struct GrainuumConfig *cfg);\n\nThis will initialize the PHY and put it in \"Disconnected\" mode.  To connect, call grainuumConnect();\n\n  void grainuumConnect(struct GrainuumUSB *usb);\n\nNow you can hook your interrupt handler.  When an ISR hits, call grainuumCaptureI()\nwith a buffer big enough to hold one USB packet:\n\n  void grainuumCaptureI(struct GrainuumUSB *usb, uint8_t packet[12]);\n\nThen, sometime later once the interrupt is finished, pass the same buffer to grainuumProcess():\n\n  void grainuumProcess(struct GrainuumUSB *usb,\n                       const uint8_t packet[12]);\n\n*The packet that is passed to grainuumProcess() and grainuumCaptureI() MUST be aligned\nsuch that packet[1] is word-aligned.  One way to do this might be to define packet[16]\nas being aligned, and pass &packet[3] to these functions.  Or you can use Granuum Buffers,\nwhich are described below.*\n\nTo send data to the host, use grainuumSendData():\n\n  int grainuumSendData(struct GrainuumUSB *usb, int epnum, const void *data, int size);\n\n\nGrainuum Buffers\n----------------\n\nThe USB PHY uses a ring buffer to log all incoming data as it enters\nthe device.  This data has special alignment requirements.  You can use\nGrainuum Buffers to manage this data.\n\nGrainuum Buffers are a set of macros that wrap all of the alignment\nmagic.\n\nDeclare a Grainuum Buffer using the GRAINUUM_BUFFER macro, specifying the\nnumber of complete packets to buffer.  To declare a buffer named\n*usb_buffer* with four elements, write:\n\n    GRAINUUM_BUFFER(usb_buffer, 4);\n\nIn your program code, you must initialize the buffer before you use it:\n\n    GRAINUUM_BUFFER_INIT(usb_buffer);\n\nTo check if the buffer is empty, use is_empty:\n\n    if (!GRAINUUM_BUFFER_IS_EMPTY(usb_buffer)) {\n        ... work on the buffer ...\n    }\n\nYou'll generally want to get a pointer to the top of the buffer,\nand advance it only if the data is filled.  To get a pointer\nto the top of the buffer (and pass it to grainuumCaptureI()), type:\n\n    grainuumCaptureI(usb, GRAINUUM_BUFFER_ENTRY(usb_buffer));\n\nIf the buffer is filled, advance the buffer with advance():\n\n    GRAINUUM_BUFFER_ADVANCE(usb_buffer);\n\nTo get the oldest item in the queue, use top():\n\n    uint8_t *usb_pkt = GRAINUUM_BUFFER_TOP(usb_buffer);\n\nWhen you're done with the packet and want to advance tne end\nof the buffer (i.e. remove the oldest item), use remove():\n\n    GRAINUUM_BUFFER_REMOVE(usb_buffer);\n\nCallbacks and Hooks\n-------------------\n\nMost of the normal configuration is done through the GrainuumConfig structure."
  },
  {
    "path": "grainuum-phy-ll.s",
    "content": "/****************************************************************************\n * Grainuum Software USB Stack                                              *\n *                                                                          *\n * MIT License:                                                             *\n * Copyright (c) 2016 Sean Cross                                            *\n *                                                                          *\n * Permission is hereby granted, free of charge, to any person obtaining a  *\n * copy of this software and associated documentation files (the            *\n * \"Software\"), to deal in the Software without restriction, including      *\n * without limitation the rights to use, copy, modify, merge, publish,      *\n * distribute, distribute with modifications, sublicense, and/or sell       *\n * copies of the Software, and to permit persons to whom the Software is    *\n * furnished to do so, subject to the following conditions:                 *\n *                                                                          *\n * The above copyright notice and this permission notice shall be included  *\n * in all copies or substantial portions of the Software.                   *\n *                                                                          *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *\n * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *\n * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *\n * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *\n * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *\n * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *\n *                                                                          *\n * Except as contained in this notice, the name(s) of the above copyright   *\n * holders shall not be used in advertising or otherwise to promote the     *\n * sale, use or other dealings in this Software without prior written       *\n * authorization.                                                           *\n ****************************************************************************/\n\n#ifndef GRAINUUM_SECTION\n#define GRAINUUM_SECTION .ramtext\n#endif \n.section GRAINUUM_SECTION /* Can also run out of .section .text */\n\n.cpu    cortex-m0plus\n.fpu    softvfp\n\n#if 0\n  /***************************************************************************\n   * USB PHY low-level code\n   *\n   * Exports the following functions:\n   *\n   *    int usbPhyReadI(struct GrainuumUSB *usb, uint8_t buffer[11])\n   *    void usbPhyWriteI(struct GrainuumUSB *usb, const uint8_t buffer[11], uint32_t count)\n   *\n   * Interrupts are disabled during this code, since it is time-critical.\n   * Note that as a Kinetis \"feature\", jumps of more than 48 bytes can\n   * cause random amounts of jitter.  Make sure you don't do that.\n   *\n   * Both functions take the following struct as their first parameter:\n   *\n   *  static struct USBPHY {\n   *    // USB D- line descriptor \n   *    uint32_t dpIAddr; // GPIO \"sample-whole-bank\" address\n   *    uint32_t dpSAddr; // GPIO \"set-pin-level\" address\n   *    uint32_t dpCAddr; // GPIO \"clear-pin-level\" address\n   *    uint32_t dpDAddr; // GPIO \"pin-direction\" address, where 1 = output\n   *    uint32_t dpShift; // Shift of GPIO pin in S/C/D/I addresses\n   *\n   *    // USB D+ line descriptor, as above\n   *    uint32_t dnIAddr;\n   *    uint32_t dnSAddr;\n   *    uint32_t dnCAddr;\n   *    uint32_t dnDAddr;\n   *    uint32_t dnShift;\n   *\n   *    // USB masks\n   *    uint32_t dpMask;  // Mask of GPIO pin in S/C/D/I addresses\n   *    uint32_t dnMask;\n   *\n   *    // Optional extra data follows\n   * };\n   */\n#endif\n\n/* [r|w]usbphy offsets */\n.equ dpIAddr,0x08\n.equ dpSAddr,0x0c\n.equ dpCAddr,0x10\n.equ dpDAddr,0x14\n.equ dpShift,0x18\n\n.equ dnIAddr,0x1c\n.equ dnSAddr,0x20\n.equ dnCAddr,0x24\n.equ dnDAddr,0x28\n.equ dnShift,0x2c\n\n.equ dpMask,0x30\n.equ dnMask,0x34\n\n  /*\n   *\n   * Each USB bit takes about 666 nS, which at a 48 MHz clock rate gives us\n   * 32 cycles to read each bit.  This code runs on a Cortex M0+, where each\n   * instruction is one cycle, except taken-branches are three cycles.\n   *\n   * A USB frame begins with the pattern KJKJ...KK.  A USB frame ends with\n   * a double-SE0 state.  USB low-speed packets have an 8-bit sync period, and\n   * a maximum of 11 bytes.  Thus, a USB low-speed packet looks like this:\n   *\n   *     KJKJKJKK | data | 00\n   *\n   * Our usbReadData() code will start by positioning ourselves in the\n   * middle of a pulse.  We do that by samping the line, then waiting for\n   * it to change, then waiting some number of cycles.\n   *\n   * Once we're positioned, we then start looking for the KK end-of-sync\n   * indicator.  An interrupt takes 16 clock cycles, and it probably took\n   * at least 32 clock cycles to get here, meaning we've already lost the\n   * first KJ.  This is fine, as we just need to look for \"KK\" to indicate\n   * the end of the sync period.\n   *\n   * Since a USB low-speed packet is at most 11 bytes, we can store this in\n   * three 32-bit registers.  We chain three registers together in a shift-\n   * chain by self-adding-with-carry on each of the three registers in\n   * sequence to move the top bit from one into the bottom bit of the next.\n   *\n   * Add a 1 to the low register if the state is the same as the previous\n   * state, and add a 0 to the low register if the state has changed.\n   *\n   * As a special case, when we get six consecutive bits in a row (i.e. six\n   * ones or six zeroes), the host will \"stuff\" one bit and flip the state,\n   * meaning we should ignore that 1-bit.  If this is the case, the shift\n   * should not be processed.\n   *\n   * Continue reading bits in until we get a double-SE0.  Actually, any\n   * SE0 should be considered end-of-frame.\n   *\n   */\n\nrretval  .req r0  /* Return value (at the end) */\n\nrusbphy  .req r0  /* Pointer to the USBPHY struct (stored on stack) */\nrscratch .req r0  /* General-purpose scratch register (after premable) */\nroutptr  .req r1  /* Outgoing sample buffer */\n\nrone     .req r2  /* The value 1 */\nrreg     .req r3  /* Register to sample pin */\nrval     .req r4  /* Currently-sampled value */\nrmash    .req r5  /* Mask/shift to isolate required pin */\n\nrsample  .req r6  /* Most recent byte */\nrcounter .req r7  /* Number of bytes sampled */\n\nrlastval .req r8  /* What value was the last pin? */\nrunstuff .req r9  /* Log of the last six bits, for unstuffing */\n\nrdpshift .req r10 /* Shift amount for D+ */\nrdpiaddr .req r11 /* Pointer to D+ sample address */\nrdniaddr .req r12 /* Pointer to D- sample address */\n\n/* Read Stack:\n * 0: dnMask\n * 4: rusbphy (currently unused)\n */\n.equ rsDnMask,0x00\n.equ rsUsbPhy,0x04\n\n.func usbPhyReadI\n.global usbPhyReadI\n\nusb_phy_read__se0:\n  add sp, #12\n  pop {r2-r6}\n  mov r8, r2\n  mov r9, r3\n  mov r10, r4\n  mov r11, r5\n  mov r12, r6\n\n  mov rretval, #0\n  sub rretval, #4                   // Return -4\n  pop {r2-r7,pc}\n\n/*int */usbPhyReadI/*(struct GrainuumUSB *usb, uint8_t samples[11])*/:\n  push {r2-r7,lr}\n  mov r2, r8\n  mov r3, r9\n  mov r4, r10\n  mov r5, r11\n  mov r6, r12\n  push {r2-r6}                      // Save high registers\n  sub sp, #12\n\n  ldr rreg, [rusbphy, #dnIAddr]     // Grab the address for the data input reg.\n  ldr rmash, [rusbphy, #dnMask]     // Grab the mask for the bit.\n\n  str rusbphy, [sp, #rsUsbPhy]      // Save the USBPHY on the stack.\n  str rmash, [sp, #rsDnMask]        // Save it on the stack for later.\n\n  ldr rsample, [rusbphy, #dpIAddr]  // Also grab D+ address\n  ldr rcounter, [rusbphy, #dpMask]  // And D+ mask\n\n  /* Wait for the line to flip */\n  ldr rval, [rreg]                  // Sample D-, to watch for it flipping\n  ldr rone, [rsample]               // Sample D+ at the same time\n  and rval, rmash                   // Mask off the interesting bit\n  mov rlastval, rval                // Save the bit for use in looking for sync\n\n  /* Check to see if it's SE0, in which case this is a keepalive pkt */\n  and rone, rcounter                // Mask off the interesting bit\n  add rone, rval                    // Combine D+ and D-.\n  beq usb_phy_read__se0             // Exit if SE0 condition (both are 0).\n\n  /* Clear out the register shift-chain */\n  mov rsample, #0                   // Reset the sample byte value.\n  mov rcounter, #0                  // Reset the \"bits\" counter.\n\n  mov rone, #1                      // Actually load the value '1' into the reg.\n\n  mov rval, #0b11\n  mov runstuff, rval                // Load 0b11 into unstuff reg, as the header\n                                    // ends with the pattern KK, which starts\n                                    // a run of two.\n\n  // The loop is 4 cycles on a failure.  One\n  // pulse is 32 cycles.  Therefore, loop up\n  // to 8 times before giving up.\n.rept 8\n  ldr rval, [rreg]                  // Sample USBDP\n  and rval, rmash                   // Mask off the interesting bit\n  cmp rval, rlastval                // Wait for it to change\n  bne usb_phy_read__sync_wait       // When it changes, go wait for sync pulse\n.endr\n  b usb_phy_read__timeout           // It never changed, so return \"timeout\".\n\nusb_phy_read__sync_wait:\n  // Move us away from the start of the pulse, to avoid transition errors.\n  bl usb_phy__wait_5_cycles\n\n  // Wait for the end-of-header sync pulse, which is when the value\n  // repeats itself.  This is the \"KK\" in the KJKJKJKK training sequence.\n.rept 6\n  ldr rval, [rreg]                    // Sample USBDP\n  and rval, rmash                     // Mask off the interesting bit\n  cmp rlastval, rval\n  beq usb_phy_read__start_reading_usb\n  mov rlastval, rval\n  bl usb_phy__wait_27_cycles\n.endr\n  b usb_phy_read__sync_timeout\n\n  /* We're synced to the middle of a pulse, and the clock sync / start-of-\n   * -frame has been found.  Real packet data follows.\n   */\nusb_phy_read__start_reading_usb:\n  // ?\n\n  /* Adjust rlastval so that it's in the correct position -- we skip doing\n     this above since we're only interested in the value changing, not in\n     what the value is.  However, we're now interested in what the value\n     is, so we now deal with shifts instead of masks, and always mask by\n     #1.\n   */\n  mov rval, rlastval\n  ldr rmash, [rusbphy, #dpShift]\n  ror rval, rmash\n  and rval, rone\n  mov rlastval, rval\n  // 6\n\n  /* We have plenty of extra cycles here, because the first bit is a K,\n   * and we simply need to wait for it to finish.\n   */\n  ldr rreg, [rusbphy, #dpIAddr]       // Cache the address of the D+ input bank\n  mov rdpiaddr, rreg                  // to save one cycle.\n  ldr rreg, [rusbphy, #dnIAddr]       // Cache the address of the D- input bank\n  mov rdniaddr, rreg                  // to save another cycle.\n  ldr rreg, [rusbphy, #dpShift]       // Cache the D+ shift, too.\n  mov rdpshift, rreg\n  // 9\n\n  nop\n  nop\n  nop\n  nop\n  nop\n  nop\n  nop\n\nusb_phy_read__get_usb_bit:\n  mov rval, rdpiaddr                  // Get the address of the D+ input bank.\n  mov rreg, rdniaddr                  // Get the address of the D- input bank.\n  ldr rval, [rval]                    // Actually sample D+\n  ldr rreg, [rreg]                    // Also sample D-\n\n  mov rmash, rdpshift                 // Get the shift of the D+ bit\n  ror rval, rmash                     // Rotate the value down to bit 1.\n  and rval, rone                      // Mask off everything else.\n  // 7\n\n  /* xor this bit with the last bit and invert it, in order to get\n   * the logical value */\n  mov rmash, rlastval                 // Load up the previous bit\n  mov rlastval, rval                  // Save the current bit for the next loop.\n  eor rmash, rval                     // Check to see if the state has changed.\n  mvn rmash, rmash                    // Invert, as 1 ^ 1 or 0 ^ 0 should be 1.\n  and rmash, rone                     // Mask off everything but the last bit.\n\n  orr rsample, rmash                  // Append the value to the sample.\n  ror rsample, rone                   // Move it to the top of the buffer.\n\n  add runstuff, runstuff              // Shift the \"unstuff\" value up by 1.\n  add runstuff, rmash                 // \"or\" in the one-bit rval to the bottom.\n\n  // 9\n\n  // [16 total]\n\n  /* If we've completed a byte, the rcounter will mask to 0.\n   * If the byte is done, advance.\n   * If the byte continues, check for SE0.\n   */\n  add rcounter, rone                  // Increment the total-bit counter.\n  mov rscratch, #7                    // Prepare to mask by 0x7\n  tst rcounter, rscratch              // Perform the mask\n  beq usb_phy_read__advance_byte      // If the result is 0, advance the byte.\n  // 4 (or 5, if branch taken)\n\n  // The result is NOT 0, so see if it's an SE0.\nusb_phy_read__check_se0:\n  // Check for SE0\n  ldr rscratch, [sp, #rsDnMask]\n  and rreg, rscratch\n  add rreg, rval                      // An end-of-frame is indicated by two\n                                      // frames of SE0.  If this is the case,\n                                      // then the result of adding these\n                                      // together will result in 0.\n  bne usb_phy_read__check_unstuff\n  b usb_phy_read__exit                // Exit if so.\n  // 6 (if not SE0)\n\nusb_phy_read__advance_byte:\n  lsr rsample, #24                    // Rotate the sample down to the byte list\n  strb rsample, [routptr]             // Save the value to the out buffer.\n  add routptr, rone                   // Advance the out pointer.\n  mov rsample, #0                     // Reset sample accumulator.\n  // 5\n\n  // Figure out if we need to unstuff, or if we can just continue to the\n  // next bit.\n  // Six consecutive USB states will be followed by a dummy\n  // state flip.  Ignore this.\nusb_phy_read__check_unstuff:\n  mov rreg, runstuff\n  mov rval, #0b111111                 // Unstuff mask\n  and rreg, rval\n  cmp rreg, rval\n\n  /* Loop again */\n  bne usb_phy_read__get_usb_bit\n  // 2 (if branch taken, 1 if unstuffing needs to happen)\n\nusb_phy_read__unstuff:\n  nop                                 // We get here when the current bit has\n                                      // one more clock cycle left.  Add a nop\n                                      // just to make the cycle-counting easier.\n\n  /* --- New \"loop\" starts here --- */\n  // NOTE that we don't increment \"rcounter\" here.\n  mov runstuff, rone                  // We're skipping over one bit, which\n                                      // results in a new run of one.\n  add runstuff, rone\n  // 2\n\n  /* Invert the last value, since a false 0 was added to the stream */\n  mov rreg, rlastval                  // Read the current last val into a lo reg.\n  mvn rreg, rreg                      // Negate the value.\n  and rreg, rreg, rone                // Mask it with 0b1\n  mov rlastval, rreg                  // Save it back into a lo reg.\n  // 4\n\n  cmp rcounter, #88                   // Sanity check: see if we've read more\n                                      // than 88 bytes (11 bits), because we've\n                                      // got cycles to spare here.\n  bgt usb_phy_read__exit              // Exit if so\n  // 2\n\n  bl usb_phy__wait_22_cycles\n\n  b usb_phy_read__get_usb_bit\n  // 2\n\nusb_phy_read__exit:\n  // a minimum of 10 cycles have elapsed since we got here\n\n  /* Count the number of bytes read (rcounter / 8) */\n  mov rretval, rcounter\n  asr rretval, #3                 // Return number of bytes (not bits) read.\n  cmp rretval, #11                // Error out if we read more than 11 bytes.\n  bgt usb_phy_read__overflow_exit\n  // 4\n\nusb_phy_read__return:\n  add sp, #12                     // Restore stack.\n  pop {r2-r6}\n  mov r8, r2\n  mov r9, r3\n  mov r10, r4\n  mov r11, r5\n  mov r12, r6\n  // 13\n\n  pop {r2-r7,pc}\n  // 8\n\n  /* Read too many bits, return -2 */\nusb_phy_read__overflow_exit:\n  mov rretval, #0\n  sub rretval, #2\n  b usb_phy_read__return\n\n  /* Unable to find pulse end, return -3 */\nusb_phy_read__sync_timeout:\n  mov rretval, #0\n  sub rretval, #3\n  b usb_phy_read__return\n\n  /* Timeout while reading, return -1 */\nusb_phy_read__timeout:\n  mov rretval, #0\n  sub rretval, #1\n  b usb_phy_read__return\n\n.endfunc\n.type usbPhyReadI, %function\n.size usbPhyReadI, .-usbPhyReadI\n\n\n\nusb_phy__wait_32_cycles: nop\nusb_phy__wait_31_cycles: nop\nusb_phy__wait_30_cycles: nop\nusb_phy__wait_29_cycles: nop\nusb_phy__wait_28_cycles: nop\nusb_phy__wait_27_cycles: nop\nusb_phy__wait_26_cycles: nop\nusb_phy__wait_25_cycles: nop\nusb_phy__wait_24_cycles: nop\nusb_phy__wait_23_cycles: nop\nusb_phy__wait_22_cycles: nop\nusb_phy__wait_21_cycles: nop\nusb_phy__wait_20_cycles: nop\nusb_phy__wait_19_cycles: nop\nusb_phy__wait_18_cycles: nop\nusb_phy__wait_17_cycles: nop\nusb_phy__wait_16_cycles: nop\nusb_phy__wait_15_cycles: nop\nusb_phy__wait_14_cycles: nop\nusb_phy__wait_13_cycles: nop\nusb_phy__wait_12_cycles: nop\nusb_phy__wait_11_cycles: nop\nusb_phy__wait_10_cycles: nop\nusb_phy__wait_9_cycles:  nop\nusb_phy__wait_8_cycles:  nop\nusb_phy__wait_7_cycles:  nop\nusb_phy__wait_6_cycles:  nop\nusb_phy__wait_5_cycles:  mov pc, lr\n\n\n\n\n\n/*\n * usbPhyWriteI\n * Register (arguments):\n *   r0: USBPHY\n *   r1: pointer to buffer data\n *   r2: number of bytes to write\n */\nwusbphy   .req r0   /* Pointer to USBPHY value */\n\nwlastsym  .req r1   /* The last symbol (0 = j, 1 = k) */\nwpkt      .req r2   /* Current byte */\nwleft     .req r3   /* Number of bits left before we need to reload wpkt */\n\n/* These are used when writing values out.  May be repurposed. */\nwpaddr    .req r4   /* Write \"address\" for D+ line, during normal operation */\nwnaddr    .req r5   /* Write \"address\" for D- line, during normal operation */\nwtmp1     .req r4\nwtmp2     .req r5\n\n/* These must remain unchanged during the whole operation. */\nwpmask    .req r6   /* Write mask for D+ line */\nwnmask    .req r7   /* Write mask for D- line */\n\nwbytes    .req r8   /* Pointer to bytes (from r2 arg) */\nwend      .req r9   /* End of the wbytes array (from r1+r2 arg) */\nwdpsetreg .req r10\nwdpclrreg .req r11\nwdnclrreg .req r12\nwstuff    .req r0   /* The last six bits, used for bit stuffing (reuses wusbphy) */\n\n/* Stack data:\n   0: D- set (wDnSAddr)\n   4: wusbphy\n */\n\n/* Indexes off of internal data */\n.equ wDnSAddr,0x00      /* D- Set Addr */\n.equ wDnCAddr,0x04      /* D- Clear Addr */\n.equ wPkt1,0x08\n.equ wPkt1Num,0x0c\n.equ wPkt2,0x10\n.equ wPkt2Num,0x14\n.equ wPkt3,0x18\n.equ wPkt3Num,0x1c\n.equ wFirstPkt,0x20\n.equ wSpSave,0x24\n\n.thumb\n.align  2\n.thumb_func\n.global usbPhyWriteI\n.func usbPhyWriteI\n/*void */usbPhyWriteI/*(struct GrainuumUSB *usb, const uint8_t buffer[11], uint32_t count)*/:\n  push {r3-r7,lr}\n  mov r3, r8\n  mov r4, r9\n  mov r5, r10\n  mov r6, r11\n  push {r3-r6}                      // Save other arguments.\n\n  /* Allocate and populate the stack */\n  sub sp, #8                        // Use 8 bytes of stack\n\n  /* Cache D+ set and clear registers early on */\n  ldr wtmp1, [wusbphy, #dpSAddr]    // Registers are faster than RAM, and we\n  mov wdpsetreg, wtmp1              // only have two free registers, so pre-\n  ldr wtmp1, [wusbphy, #dpCAddr]    // cache the D+ addresses to save one\n  mov wdpclrreg, wtmp1              // clock cycle.\n  ldr wtmp1, [wusbphy, #dnCAddr]    // Cache D- clr as well.\n  mov wdnclrreg, wtmp1\n\n  /* Load D+ and D- masks, used for direction and value setting */\n  ldr wpmask, [wusbphy, #dpMask]    // USB D+ mask\n  ldr wnmask, [wusbphy, #dnMask]    // USB D- mask\n\n  /* Pre-set the lines to J-state to prevent glitching */\n#if 0\n  mov wpaddr, wdpsetreg             // D+ set\n  ldr wnaddr, [wusbphy, #dnCAddr]   // D- clr\n#else\n  mov wpaddr, wdpclrreg             // D+ clr\n  ldr wnaddr, [wusbphy, #dnSAddr]   // D- set\n#endif\n  str wpmask, [wpaddr]              // Write D+ value\n  str wnmask, [wnaddr]              // Write D- value\n\n  /* Set D+ line to OUTPUT */\n  ldr wtmp1, [wusbphy, #dpDAddr]    // Get the direction address\n  ldr wtmp2, [wtmp1]                // Get the direction value\n  orr wtmp2, wtmp2, wpmask          // Set the direciton mask\n  str wtmp2, [wtmp1]                // Set the direction for D+\n\n  /* Set D- line to OUTPUT */\n  ldr wtmp1, [wusbphy, #dnDAddr]    // Get the direction address\n  ldr wtmp2, [wtmp1]                // Get the direction value\n  orr wtmp2, wtmp2, wnmask          // Set the direciton mask\n  str wtmp2, [wtmp1]                // Set the direction for D-\n\n  /* Set K state.  This indicates the start of the packet. */\n  mov wpaddr, wdpclrreg             // D+ clr\n  ldr wnaddr, [wusbphy, #dnSAddr]   // D- set\n  str wpmask, [wpaddr]              // Write D+ value\n  str wnmask, [wnaddr]              // Write D- value\n\n  /* Now that the packet has started, we have 30 cycles to complete setup. */\n\n  ldr wtmp1, [wusbphy, #dnSAddr]    // Cache D- Set addr\n  str wtmp1, [sp, #0]               // Save it on the stack\n  // 4\n\n  /* Save passed-in values on the stack, before we scribble over them. */\n  str wusbphy, [sp, #4]             // Save wusbphy,\n  mov wbytes, r1                    // byte pointer,\n  add r2, r2, r1                    // calculate the end of the array\n  mov wend, r2                      // and save it too.\n  // 5\n\n  /* Load the next byte into wpkt */\n  mov wtmp1, wbytes                 // Load the byte pointer\n  ldrb wpkt, [wtmp1]                // Get the actual byte\n  add wtmp1, #1                     // Increase the byte pointer\n  mov wbytes, wtmp1                 // Save the byte pointer back into wbytes\n  // 5\n\n  mvn wpkt, wpkt                    // Invert the value to make the tests work\n  mov wleft, #8                     // Start over with 8 bytes.\n  // 2\n\nusb_phy_write__get_first_packet:\n  mov wlastsym, #1                  // Last symbols were \"KK\" from the header,\n  mov wstuff, #0b111100             // so load a run of 2 into the stuff value.\n  // 2\n\n  // usb start-of-frame header //\n  /*bl usb_phy_write__state_k // K state entered above already */\n  bl usb_phy__wait_6_cycles\n  bl usb_phy_write__state_j\n  bl usb_phy_write__state_k\n  bl usb_phy_write__state_j\n  bl usb_phy_write__state_k\n  bl usb_phy_write__state_j\n  bl usb_phy_write__state_k\n  bl usb_phy__wait_26_cycles        // Hold k state for one more cycle.  Take\n                                    // up the slack that would normally\n                                    // follow this.\n  // end of header //\n\nusb_phy_write__top:\n\n  mov wtmp1, #0                     // Clear wthisbit, so we can add later.\n  lsr wpkt, #1                      // Shift the bottom bit into the carry bit.\n  adc wtmp1, wtmp1                  // Pull the new bit out from the carry bit.\n\n  add wstuff, wstuff, wstuff        // Shift up the stuff bit, to allow for\n  add wstuff, wstuff, wtmp1         // adding the new bit in.\n\n  add wlastsym, wlastsym, wtmp1     // Add the new bit to the last symbol.\n                                    // Since we're only looking at the last\n                                    // bit, this becomes an XOR.\n  mov wtmp1, #0b1                   // Examine the last bit, to check for a\n  tst wlastsym, wtmp1               // state transition or not.\n  // 8\n\n  /* Write the desired state out (each branch is balanced) */\n  bne usb_phy_write__j\nusb_phy_write__k:\n  mov wpaddr, wdpsetreg             // D+ set\n  mov wnaddr, wdnclrreg\n  b usb_phy_write__out\n\nusb_phy_write__j:\n  mov wpaddr, wdpclrreg             // D+ clr\n  ldr wnaddr, [sp, #0]              // D- set\n\nusb_phy_write__out:\n  str wpmask, [wpaddr]\n  str wnmask, [wnaddr]\n  // 7 (either branch taken)\n\n  // 15 cycles total\n\n  sub wleft, wleft, #1              // See how many bits we have left to write.\n  bne usb_phy_write__continue_byte  // If nonzero, write another bit.\n  // 2\n\n  /* We just finished writing a byte.  Load the next byte, or exit. */\nusb_phy_write__finished_byte:\n  mov wtmp1, wbytes                 // Move byte array into a lo reg\n  cmp wtmp1, wend                   // See if we've reached the end.\n  beq usb_phy_write__send_eof       // Exit if it's now 0.\n  ldrb wpkt, [wtmp1]                // Read the next byte into wpkt.\n  add wtmp1, #1                     // Advance byte array by one.\n  mov wbytes, wtmp1                 // ...or store byte addr back in the hi reg.\n  // 7\n\nusb_phy_write__calculate_next_pkt:\n  mvn wpkt, wpkt                    // Invert it to make the math work.\n  mov wleft, #8                     // Reset \"bits left\" to 8.\n  // 2\n\n  nop\n  // 1\n\n  /* If we just wrote \"111111\", then stuff one bit */\nusb_phy_write__stuff_bit_maybe:\n  mov wtmp2, #0b111111              // Compare it with the wstuff value\n  and wtmp2, wstuff                 // AND the two together.  If they're the\n  beq usb_phy_write__stuff_bit      // same, then stuff one bit.\n  // 3\n\nusb_phy_write__done_stuffing_bit:\n  b usb_phy_write__top\n  // 2\n\n  /* We're still writing this byte, so there's nothing to do. */\nusb_phy_write__continue_byte:\n  bl usb_phy__wait_7_cycles\n  b usb_phy_write__stuff_bit_maybe\n  // 2\n\nusb_phy_write__stuff_bit:\n  /* When we get here, we are already into the packet. */\n// Need 18 cycles until packet is written\n  bl usb_phy__wait_6_cycles\n  mov wstuff, #0b111111             // Clear out the bit-stuff rcounter\n  // 2\n\n  add wlastsym, wlastsym, #1        // Invert the last symbol.\n\n  mov wtmp1, #0b1                   // See if we need to send j or k\n  tst wlastsym, wtmp1\n  // 3\n\n  /* Write the desired state out (each branch is balanced) */\n  bne usb_phy_write__stuff_j\nusb_phy_write_stuff_k:\n  mov wpaddr, wdpsetreg             // D+ set\n  mov wnaddr, wdnclrreg             // D- clr\n  b usb_phy_write__stuff_out\n\nusb_phy_write__stuff_j:\n  mov wpaddr, wdpclrreg             // D+ clr\n  ldr wnaddr, [sp, #0]              // D- set\n\nusb_phy_write__stuff_out:\n  str wpmask, [wpaddr]\n  str wnmask, [wnaddr]\n  // 7 (either branch taken)\n\n  bl usb_phy__wait_13_cycles\n  b usb_phy_write__done_stuffing_bit\n\nusb_phy_write__eof_stuff_bit:\n  bl usb_phy__wait_12_cycles\n  mov wstuff, #0b111111             // Clear out the bit-stuff rcounter\n  // 2\n\n  add wlastsym, wlastsym, #1        // Invert the last symbol.\n\n  mov wtmp1, #0b1                   // See if we need to send j or k\n  tst wlastsym, wtmp1\n  // 3\n\n  /* Write the desired state out (each branch is balanced) */\n  bne usb_phy_write__eof_stuff_j\nusb_phy_write__eof_stuff_k:\n  mov wpaddr, wdpsetreg             // D+ set\n  mov wnaddr, wdnclrreg             // D- clr\n  b usb_phy_write__eof_stuff_out\n\nusb_phy_write__eof_stuff_j:\n  mov wpaddr, wdpclrreg             // D+ clr\n  ldr wnaddr, [sp, #0]              // D- set\n\nusb_phy_write__eof_stuff_out:\n  str wpmask, [wpaddr]\n  str wnmask, [wnaddr]\n  bl usb_phy__wait_27_cycles\n  b usb_phy_write__send_se0\n  // 7 (either branch taken)\n\nusb_phy_write__send_eof:\n  mov wtmp2, #0b111111              // Compare it with the wstuff value\n  and wtmp2, wstuff                 // AND the two together.  If they're the\n  beq usb_phy_write__eof_stuff_bit  // same, then stuff one bit.\n\n  bl usb_phy__wait_16_cycles\nusb_phy_write__send_se0:\n  bl usb_phy_write__state_se0\n  bl usb_phy_write__state_se0\n\n  /* Set J-state, as required by the spec */\n#if 1\n  bl usb_phy__wait_6_cycles\n  mov wpaddr, wdpsetreg             // D+ set\n  mov wnaddr, wdnclrreg             // D- clr\n  str wpmask, [wpaddr]\n  str wnmask, [wnaddr]\n#else\n#warning \"Not setting J state (fix this before committing)\"\n#endif\n  /* Cheat a bit on the end-of-packet time, since the following\n   * instructions take roughly 10 cycles before the lines reset.\n   */\n  bl usb_phy__wait_28_cycles\n\n  // --- Done Transmitting --- //\n\n  // Restore sp, since we're done with it\n  ldr wusbphy, [sp, #4]             // Restore wusbphy\n  add sp, #8                        // Restore stack pointer.\n\n  /* Now, set both lines back to INPUT */\n\n  /* Set D+ line to INPUT */\n  ldr wtmp1, [wusbphy, #dpDAddr]    // Get the direction address\n  ldr wtmp2, [wtmp1]                // Get the direction value\n  bic wtmp2, wtmp2, wpmask          // Clear the direciton mask\n  str wtmp2, [wtmp1]                // Set the direction for D+\n\n  /* Set D- line to INPUT */\n  ldr wtmp1, [wusbphy, #dnDAddr]    // Get the direction address\n  ldr wtmp2, [wtmp1]                // Get the direction value\n  bic wtmp2, wtmp2, wnmask          // Clear the direciton mask\n  str wtmp2, [wtmp1]                // Set the direction for D-\n\n  pop {r3-r6}                       // Restore registers\n  mov r11, r6\n  mov r10, r5\n  mov r9, r4\n  mov r8, r3\n  pop {r3-r7,pc}                    // Restore and return to caller.\n\n  // Useful functions\nusb_phy_write__state_se0:\n  mov wpaddr, wdpclrreg             // D+ clr\n  mov wnaddr, wdnclrreg             // D- clr\n  b usb_phy_write__out_func\nusb_phy_write__state_j:\n  mov wpaddr, wdpsetreg             // D+ set\n  mov wnaddr, wdnclrreg             // D- clr\n  b usb_phy_write__out_func\nusb_phy_write__state_k:\n  mov wpaddr, wdpclrreg             // D+ clr\n  ldr wnaddr, [sp, #0]              // D- set\n  nop\nusb_phy_write__out_func:\n  str wpmask, [wpaddr]\n  str wnmask, [wnaddr]\n  b usb_phy__wait_25_cycles\n\n.endfunc\n.type usbPhyWriteI, %function\n.size usbPhyWriteI, .-usbPhyWriteI\n"
  },
  {
    "path": "grainuum-phy.c",
    "content": "/****************************************************************************\r\n * Grainuum Software USB Stack                                              *\r\n *                                                                          *\r\n * MIT License:                                                             *\r\n * Copyright (c) 2016 Sean Cross                                            *\r\n *                                                                          *\r\n * Permission is hereby granted, free of charge, to any person obtaining a  *\r\n * copy of this software and associated documentation files (the            *\r\n * \"Software\"), to deal in the Software without restriction, including      *\r\n * without limitation the rights to use, copy, modify, merge, publish,      *\r\n * distribute, distribute with modifications, sublicense, and/or sell       *\r\n * copies of the Software, and to permit persons to whom the Software is    *\r\n * furnished to do so, subject to the following conditions:                 *\r\n *                                                                          *\r\n * The above copyright notice and this permission notice shall be included  *\r\n * in all copies or substantial portions of the Software.                   *\r\n *                                                                          *\r\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *\r\n * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *\r\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *\r\n * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *\r\n * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *\r\n * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *\r\n * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *\r\n *                                                                          *\r\n * Except as contained in this notice, the name(s) of the above copyright   *\r\n * holders shall not be used in advertising or otherwise to promote the     *\r\n * sale, use or other dealings in this Software without prior written       *\r\n * authorization.                                                           *\r\n ****************************************************************************/\r\n \r\n #include \"grainuum.h\"\r\n\r\n__attribute__((weak))\r\nvoid grainuumConnectPre(struct GrainuumUSB *usb)\r\n{\r\n  (void)usb;\r\n}\r\n__attribute__((weak))\r\nvoid grainuumConnectPost(struct GrainuumUSB *usb)\r\n{\r\n  (void)usb;\r\n}\r\n\r\n__attribute__((weak))\r\nvoid grainuumDisconnectPre(struct GrainuumUSB *usb)\r\n{\r\n  (void)usb;\r\n}\r\n__attribute__((weak))\r\nvoid grainuumDisconnectPost(struct GrainuumUSB *usb)\r\n{\r\n  (void)usb;\r\n}\r\n\r\n__attribute__((weak))\r\nvoid grainuumReceivePacket(struct GrainuumUSB *usb)\r\n{\r\n  (void)usb;\r\n}\r\n\r\n__attribute__((weak))\r\nvoid grainuumInitPre(struct GrainuumUSB *usb)\r\n{\r\n  (void)usb;\r\n}\r\n\r\n__attribute__((weak))\r\nvoid grainuumInitPost(struct GrainuumUSB *usb)\r\n{\r\n  (void)usb;\r\n}\r\n\r\n/* --- */\r\n\r\n__attribute__((section(\".ramtext\")))\r\nvoid grainuum_receive_packet(struct GrainuumUSB *usb) {\r\n  grainuumReceivePacket(usb);\r\n}\r\n\r\n__attribute__((section(\".ramtext\")))\r\nvoid grainuumCaptureI(struct GrainuumUSB *usb, uint8_t *samples)\r\n{\r\n  int ret;\r\n  const uint8_t nak_pkt[] = {USB_PID_NAK};\r\n  const uint8_t ack_pkt[] = {USB_PID_ACK};\r\n\r\n  ret = usbPhyReadI(usb, samples);\r\n  if (ret <= 0) {\r\n    if (ret != -1)\r\n      usbPhyWriteI(usb, nak_pkt, sizeof(nak_pkt));\r\n    return;\r\n  }\r\n\r\n  /* Save the byte counter for later inspection */\r\n  samples[11] = ret;\r\n\r\n  switch (samples[0]) {\r\n  case USB_PID_IN:\r\n    /* Make sure we have queued data, and that it's for this particular EP */\r\n    if ((!usb->queued_size)\r\n    || (((((const uint16_t *)(samples+1))[0] >> 7) & 0xf) != usb->queued_epnum))\r\n    {\r\n      usbPhyWriteI(usb, nak_pkt, sizeof(nak_pkt));\r\n      break;\r\n    }\r\n\r\n    usbPhyWriteI(usb, usb->queued_data, usb->queued_size);\r\n    break;\r\n\r\n  case USB_PID_SETUP:\r\n    grainuum_receive_packet(usb);\r\n    break;\r\n\r\n  case USB_PID_OUT:\r\n    grainuum_receive_packet(usb);\r\n    break;\r\n\r\n  case USB_PID_ACK:\r\n    /* Allow the next byte to be sent */\r\n    usb->queued_size = 0;\r\n    grainuum_receive_packet(usb);\r\n    break;\r\n\r\n  case USB_PID_DATA0:\r\n  case USB_PID_DATA1:\r\n    usbPhyWriteI(usb, ack_pkt, sizeof(ack_pkt));\r\n    grainuum_receive_packet(usb);\r\n    break;\r\n\r\n  default:\r\n    usbPhyWriteI(usb, nak_pkt, sizeof(nak_pkt));\r\n    break;\r\n  }\r\n\r\n  return;\r\n}\r\n\r\nint grainuumInitialized(struct GrainuumUSB *usb)\r\n{\r\n  if (!usb)\r\n    return 0;\r\n\r\n  return usb->initialized;\r\n}\r\n\r\nvoid grainuumWriteQueue(struct GrainuumUSB *usb, int epnum,\r\n                        const void *buffer, int size)\r\n{\r\n  usb->queued_data = buffer;\r\n  usb->queued_epnum = epnum;\r\n  usb->queued_size = size;\r\n}\r\n\r\nvoid grainuumInit(struct GrainuumUSB *usb,\r\n                  struct GrainuumConfig *cfg) {\r\n\r\n  if (usb->initialized)\r\n    return;\r\n\r\n  grainuumInitPre(usb);\r\n\r\n  usb->cfg = cfg;\r\n  usb->state.usb = usb;\r\n  cfg->usb = usb;\r\n\r\n  usb->initialized = 1;\r\n\r\n  grainuumInitPost(usb);\r\n}\r\n\r\nvoid grainuumDisconnect(struct GrainuumUSB *usb) {\r\n\r\n  grainuumDisconnectPre(usb);\r\n\r\n  /* Set both lines to 0 (clear both D+ and D-) to simulate unplug. */\r\n  grainuumWritel(usb->usbdpMask, usb->usbdpCAddr);\r\n  grainuumWritel(usb->usbdnMask, usb->usbdnCAddr);\r\n\r\n  /* Set both lines to output */\r\n  grainuumWritel(grainuumReadl(usb->usbdpDAddr) | usb->usbdpMask, usb->usbdpDAddr);\r\n  grainuumWritel(grainuumReadl(usb->usbdnDAddr) | usb->usbdnMask, usb->usbdnDAddr);\r\n\r\n  grainuumDisconnectPost(usb);\r\n}\r\n\r\nvoid grainuumConnect(struct GrainuumUSB *usb) {\r\n\r\n  grainuumConnectPre(usb);\r\n\r\n  /* Set both lines to input */\r\n  grainuumWritel(grainuumReadl(usb->usbdpDAddr) & ~usb->usbdpMask, usb->usbdpDAddr);\r\n  grainuumWritel(grainuumReadl(usb->usbdnDAddr) & ~usb->usbdnMask, usb->usbdnDAddr);\r\n\r\n  grainuumConnectPost(usb);\r\n}\r\n"
  },
  {
    "path": "grainuum-state.c",
    "content": "/****************************************************************************\r\n * Grainuum Software USB Stack                                              *\r\n *                                                                          *\r\n * MIT License:                                                             *\r\n * Copyright (c) 2016 Sean Cross                                            *\r\n *                                                                          *\r\n * Permission is hereby granted, free of charge, to any person obtaining a  *\r\n * copy of this software and associated documentation files (the            *\r\n * \"Software\"), to deal in the Software without restriction, including      *\r\n * without limitation the rights to use, copy, modify, merge, publish,      *\r\n * distribute, distribute with modifications, sublicense, and/or sell       *\r\n * copies of the Software, and to permit persons to whom the Software is    *\r\n * furnished to do so, subject to the following conditions:                 *\r\n *                                                                          *\r\n * The above copyright notice and this permission notice shall be included  *\r\n * in all copies or substantial portions of the Software.                   *\r\n *                                                                          *\r\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *\r\n * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *\r\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *\r\n * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *\r\n * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *\r\n * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *\r\n * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *\r\n *                                                                          *\r\n * Except as contained in this notice, the name(s) of the above copyright   *\r\n * holders shall not be used in advertising or otherwise to promote the     *\r\n * sale, use or other dealings in this Software without prior written       *\r\n * authorization.                                                           *\r\n ****************************************************************************/\r\n #include \"grainuum.h\"\r\n\r\n#ifndef NULL\r\n#define NULL ((void *)0)\r\n#endif\r\n\r\nvoid *memcpy(void *dest, const void *src, unsigned int n);\r\n\r\nenum usb_state_packet_type {\r\n  packet_type_none,\r\n  packet_type_setup,\r\n  packet_type_setup_in,\r\n  packet_type_setup_out,\r\n  packet_type_in,\r\n  packet_type_out,\r\n};\r\n\r\n__attribute__((weak))\r\nvoid grainuumSendWait(struct GrainuumUSB *usb, int epnum,\r\n                      const void *data, int size)\r\n{\r\n  (void)usb;\r\n  (void)epnum;\r\n  (void)data;\r\n  (void)size;\r\n}\r\n\r\nstatic uint16_t crc16_add(uint16_t crc, uint8_t c, uint16_t poly)\r\n{\r\n  uint8_t  i;\r\n\r\n  for (i = 0; i < 8; i++) {\r\n    if ((crc ^ c) & 1)\r\n      crc = (crc >> 1) ^ poly;\r\n    else\r\n      crc >>= 1;\r\n    c >>= 1;\r\n  }\r\n  return crc;\r\n}\r\n\r\nstatic uint16_t crc16(const uint8_t *data, uint32_t size,\r\n                      uint16_t init, uint32_t poly)\r\n{\r\n\r\n  while (size--)\r\n    init = crc16_add(init, *data++, poly);\r\n\r\n  return init;\r\n}\r\n\r\nstatic void grainuum_state_clear_tx(struct GrainuumState *state, int result)\r\n{\r\n  struct GrainuumUSB *usb = state->usb;\r\n\r\n  /* If a thread is blocking, wake it up with a failure */\r\n  if (usb->cfg->sendDataFinished && state->packet_queued)\r\n    usb->cfg->sendDataFinished(usb, result);\r\n  state->data_out_left = 0;\r\n  state->data_out_max = 0;\r\n  state->data_out = NULL;\r\n  state->packet_queued = 0;\r\n}\r\n\r\nstatic void grainuum_state_process_tx(struct GrainuumState *state)\r\n{\r\n\r\n  uint16_t crc;\r\n  struct GrainuumUSB *usb = state->usb;\r\n\r\n  /* Don't allow us to re-prepare data */\r\n  if (state->packet_queued) {\r\n    return;\r\n  }\r\n  state->packet_queued = 1;\r\n\r\n  /* If there's no data to send, then don't send any */\r\n  if (!state->data_out) {\r\n    state->packet_queued = 0;\r\n    return;\r\n  }\r\n\r\n  /* If we've sent all of our data, then there's nothing else to send */\r\n  if ((state->data_out_left < 0) || (state->data_out_max < 0)) {\r\n    grainuum_state_clear_tx(state, 0);\r\n    return;\r\n  }\r\n\r\n  /* Pick the correct PID, DATA0 or DATA1 */\r\n  if (state->data_buffer & (1 << state->tok_epnum))\r\n    state->packet.pid = USB_PID_DATA1;\r\n  else\r\n    state->packet.pid = USB_PID_DATA0;\r\n\r\n  /* If there's no data, prepare a special NULL packet */\r\n  if ((state->data_out_left == 0) || (state->data_out_max == 0)) {\r\n\r\n    /* The special-null thing only happens for EP0 */\r\n    if (state->data_out_epnum != 0) {\r\n      grainuum_state_clear_tx(state, 0);\r\n      return;\r\n    }\r\n    state->packet.data[0] = 0;  /* CRC16 for empty packets is 0 */\r\n    state->packet.data[1] = 0;\r\n    state->packet.size = 2;\r\n    grainuumWriteQueue(usb, state->data_out_epnum,\r\n                         &state->packet, state->packet.size + 1);\r\n    return;\r\n  }\r\n\r\n  /* Keep the packet size to 8 bytes max */\r\n  if (state->data_out_left > 8)\r\n    state->packet.size = 8;\r\n  else\r\n    state->packet.size = state->data_out_left;\r\n\r\n  /* Limit the amount of data transferred to data_out_max */\r\n  if (state->packet.size > state->data_out_max)\r\n    state->packet.size = state->data_out_max;\r\n\r\n  /* Copy over data bytes */\r\n  memcpy(state->packet.data, state->data_out, state->packet.size);\r\n\r\n  /* Calculate and copy the crc16 */\r\n  crc = ~crc16(state->packet.data, state->packet.size, 0xffff, 0xa001);\r\n  state->packet.data[state->packet.size++] = crc;\r\n  state->packet.data[state->packet.size++] = crc >> 8;\r\n\r\n  /* Prepare the packet, including the PID at the end */\r\n  grainuumWriteQueue(usb, state->data_out_epnum,\r\n                       &state->packet, state->packet.size + 1);\r\n}\r\n\r\n/* Called when a packet is ACKed.\r\n * Updates the outgoing packet buffer.\r\n */\r\nstatic void usbStateTransferSuccess(struct GrainuumState *state)\r\n{\r\n\r\n  /* Reduce the amount of data left.\r\n   * If the packet is divisible by 8, this will cause one more call\r\n   * to this function with state->data_out_left == 0.  This will send\r\n   * a NULL packet, which indicates end-of-transfer.\r\n   */\r\n  state->data_out_left -= 8;\r\n  state->data_out_max -= 8;\r\n  state->data_out += 8;\r\n\r\n  if ((state->data_out_left < 0) || (state->data_out_max < 0)) {\r\n    grainuum_state_clear_tx(state, 0);\r\n\r\n    /* End of a State setup packet */\r\n    if (state->packet_type == packet_type_setup_out)\r\n      state->packet_type = packet_type_none;\r\n    if (state->packet_type == packet_type_setup_in)\r\n      state->packet_type = packet_type_none;\r\n    if (state->packet_type == packet_type_out)\r\n      state->packet_type = packet_type_none;\r\n  }\r\n\r\n  state->packet_queued = 0;\r\n}\r\n\r\n/* Send data down the wire, interrupting any existing\r\n * data that may be queued.\r\n */\r\nstatic int grainuum_state_send_data(struct GrainuumState *state,\r\n                             int epnum,\r\n                             const void *data,\r\n                             int size,\r\n                             int max)\r\n{\r\n\r\n  /* De-queue any data that may already be queued. */\r\n  grainuum_state_clear_tx(state, 1);\r\n\r\n  state->data_out_epnum = epnum;\r\n  state->data_out_left = size;\r\n  state->data_out_max = max;\r\n  state->data_out = data;\r\n\r\n  return 0;\r\n}\r\n\r\nvoid grainuumDropData(struct GrainuumUSB *usb)\r\n{\r\n  usb->state.packet_queued = 0;\r\n  usb->state.data_out = 0;\r\n  grainuumWriteQueue(usb, 0, NULL, 0);\r\n}\r\n\r\nint grainuumDataQueued(struct GrainuumUSB *usb)\r\n{\r\n  return (usb->state.data_out || usb->state.packet_queued);\r\n}\r\n\r\nint grainuumSendData(struct GrainuumUSB *usb, int epnum,\r\n                     const void *data, int size)\r\n{\r\n\r\n  struct GrainuumState *state = &usb->state;\r\n  int ret;\r\n\r\n  if (state->data_out || !state->address || state->packet_queued) {\r\n    return -11; /* EAGAIN */\r\n  }\r\n\r\n  ret = grainuum_state_send_data(state, epnum, data, size, size);\r\n  if (ret)\r\n    return ret;\r\n\r\n  grainuum_state_process_tx(state);\r\n\r\n  if (usb->cfg->sendDataStarted)\r\n    usb->cfg->sendDataStarted(usb, epnum, data, size);\r\n\r\n  return 0;\r\n}\r\n\r\nstatic int grainuum_state_process_setup(struct GrainuumState *state, const uint8_t packet[10])\r\n{\r\n\r\n  const struct usb_setup_packet *setup;\r\n  const void *response = (void *)-1;\r\n  uint32_t response_len = 0;\r\n  struct GrainuumUSB *usb = state->usb;\r\n  struct GrainuumConfig *cfg = usb->cfg;\r\n\r\n  setup = (const struct usb_setup_packet *)packet;\r\n\r\n  if ((setup->bmRequestType == 0x00) && (setup->bRequest == SET_ADDRESS)) {\r\n    state->address = setup->wValue;\r\n  }\r\n  else if ((setup->bmRequestType == 0x00) && (setup->bRequest == SET_CONFIGURATION)) {\r\n    if (cfg->setConfigNum)\r\n      cfg->setConfigNum(usb, setup->wValue);\r\n  }\r\n  else {\r\n    response_len = cfg->getDescriptor(usb, setup, &response);\r\n  }\r\n  grainuum_state_send_data(state, state->tok_epnum, response, response_len, setup->wLength);\r\n\r\n  return 0;\r\n}\r\n\r\nstatic void grainuum_state_parse_data(struct GrainuumState *state,\r\n                               const uint8_t packet[10],\r\n                               uint32_t size)\r\n{\r\n  (void)size;\r\n  struct GrainuumUSB *usb = state->usb;\r\n\r\n  switch (state->packet_type) {\r\n\r\n  case packet_type_setup:\r\n    grainuum_state_process_setup(state, packet);\r\n    grainuum_state_process_tx(state);\r\n    state->packet_type = packet_type_none;\r\n    break;\r\n\r\n  case packet_type_out:\r\n    // XXX HACK: An OUT packet gets generated (on Windows at least) when\r\n    // terminating a SETUP sequence.  This seems odd.\r\n    if (state->tok_epnum == 0)\r\n      break;\r\n    // Copy over the packet, minus the CRC16\r\n    memcpy(state->tok_buf + state->tok_pos, packet, size - 2);\r\n    state->tok_pos += (size - 2);\r\n    if (!usb->cfg->receiveData(usb, state->tok_epnum, size - 2, packet))\r\n      state->packet_type = packet_type_none;\r\n    break;\r\n\r\n  case packet_type_in:\r\n  case packet_type_none:\r\n  default:\r\n    break;\r\n  }\r\n}\r\n\r\nstatic inline void grainuum_state_parse_token(struct GrainuumState *state,\r\n                                       const uint8_t packet[2])\r\n{\r\n\r\n  state->tok_epnum = (((const uint16_t *)packet)[0] >> 7) & 0xf;\r\n  /*state->tok_addr  = (((const uint16_t *)packet)[0] >> 11) & 0x1f; // Field unused in this code*/\r\n}\r\n\r\nvoid grainuumProcess(struct GrainuumUSB *usb,\r\n                     const uint8_t packet[12])\r\n{\r\n\r\n  uint32_t size = packet[11];\r\n  struct GrainuumState *state = &usb->state;\r\n  switch(packet[0]) {\r\n  case USB_PID_SETUP:\r\n    state->packet_type = packet_type_setup;\r\n    grainuum_state_clear_tx(state, 1);\r\n    grainuum_state_parse_token(state, packet + 1);\r\n    break;\r\n\r\n  case USB_PID_DATA0:\r\n    state->data_buffer |= (1 << state->tok_epnum);\r\n    grainuum_state_parse_data(state, packet + 1, size - 1);\r\n    break;\r\n\r\n  case USB_PID_DATA1:\r\n    state->data_buffer &= ~(1 << state->tok_epnum);\r\n    grainuum_state_parse_data(state, packet + 1, size - 1);\r\n    break;\r\n\r\n  case USB_PID_OUT:\r\n    grainuum_state_parse_token(state, packet + 1);\r\n    state->packet_type = packet_type_out;\r\n    state->tok_pos = 0;\r\n    state->tok_buf = usb->cfg->getReceiveBuffer(usb, state->tok_epnum, NULL);\r\n  break;\r\n\r\n  case USB_PID_ACK:\r\n    state->data_buffer ^= (1 << state->tok_epnum);\r\n    usbStateTransferSuccess(state);\r\n    if (state->data_out) {\r\n      grainuum_state_process_tx(state);\r\n    }\r\n    else {\r\n      grainuum_state_clear_tx(state, 0);\r\n    }\r\n    break;\r\n\r\n  default:\r\n    break;\r\n  }\r\n}\r\n"
  },
  {
    "path": "grainuum.h",
    "content": "/****************************************************************************\r\n * Grainuum Software USB Stack                                              *\r\n *                                                                          *\r\n * MIT License:                                                             *\r\n * Copyright (c) 2016 Sean Cross                                            *\r\n *                                                                          *\r\n * Permission is hereby granted, free of charge, to any person obtaining a  *\r\n * copy of this software and associated documentation files (the            *\r\n * \"Software\"), to deal in the Software without restriction, including      *\r\n * without limitation the rights to use, copy, modify, merge, publish,      *\r\n * distribute, distribute with modifications, sublicense, and/or sell       *\r\n * copies of the Software, and to permit persons to whom the Software is    *\r\n * furnished to do so, subject to the following conditions:                 *\r\n *                                                                          *\r\n * The above copyright notice and this permission notice shall be included  *\r\n * in all copies or substantial portions of the Software.                   *\r\n *                                                                          *\r\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *\r\n * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *\r\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *\r\n * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *\r\n * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *\r\n * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *\r\n * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *\r\n *                                                                          *\r\n * Except as contained in this notice, the name(s) of the above copyright   *\r\n * holders shall not be used in advertising or otherwise to promote the     *\r\n * sale, use or other dealings in this Software without prior written       *\r\n * authorization.                                                           *\r\n ****************************************************************************/\r\n\r\n#ifndef _GRAINUUM_H\r\n#define _GRAINUUM_H\r\n\r\n#include <stdint.h>\r\n\r\n/**\r\n * @brief   Extra fields for GrainuumState struct.\r\n * @note    You probably can ignore this.\r\n */\r\n#ifndef GRAINUUM_STATE_EXTRA\r\n#define GRAINUUM_STATE_EXTRA\r\n#endif /* GRAINUUM_STATE_EXTRA */\r\n\r\n/**\r\n * @brief   Extra fields for GrainuumUSB struct.\r\n * @note    Use this to store context and thread information.\r\n */\r\n#ifndef GRAINUUM_EXTRA\r\n#define GRAINUUM_EXTRA\r\n#endif /* GRAINUUM_EXTRA */\r\n\r\n#define GET_STATUS 0\r\n#define CLEAR_FEATURE 1\r\n#define SET_FEATURE 3\r\n#define SET_ADDRESS 5\r\n#define GET_DESCRIPTOR 6\r\n#define SET_DESCRIPTOR 7\r\n#define GET_CONFIGURATION 8\r\n#define SET_CONFIGURATION 9\r\n#define GET_INTERFACE 10\r\n#define SET_INTERFACE 11\r\n#define SYNC_FRAME 12\r\n\r\n#define GET_REPORT 1\r\n#define GET_IDLE 2\r\n#define GET_PROTOCOL 3\r\n#define SET_REPORT 9\r\n#define SET_IDLE 10\r\n#define SET_PROTOCOL 11\r\n\r\nenum usb_pids {\r\n  USB_PID_RESERVED = 0xf0,\r\n  USB_PID_OUT = 0xe1,\r\n  USB_PID_ACK = 0xd2,\r\n  USB_PID_DATA0 = 0xc3,\r\n  USB_PID_PING = 0xb4,\r\n  USB_PID_SOF = 0xa5,\r\n  USB_PID_NYET = 0x96,\r\n  USB_PID_DATA2 = 0x87,\r\n  USB_PID_SPLIT = 0x78,\r\n  USB_PID_IN = 0x69,\r\n  USB_PID_NAK = 0x5a,\r\n  USB_PID_DATA1 = 0x4b,\r\n  USB_PID_ERR = 0x3c,\r\n  USB_PID_SETUP = 0x2d,\r\n  USB_PID_STALL = 0x1e,\r\n  USB_PID_MDATA = 0x0f,\r\n};\r\n\r\nstruct GrainuumUSB;\r\nstruct GrainuumState;\r\nstruct GrainuumConfig;\r\n\r\n/* Function callbacks */\r\n\r\n/* Each of these functions are called by the USB system to get a buffer.\r\n * On return, *data will point to the buffer, and the number of bytes\r\n * in the buffer will be returned.\r\n *\r\n * If the data does not exist, return 0.\r\n */\r\ntypedef int (*get_usb_descriptor_t)(struct GrainuumUSB *usb,\r\n                                    const void *pkt,\r\n                                    const void **data);\r\ntypedef void (*usb_set_config_num_t)(struct GrainuumUSB *usb,\r\n                                     int configNum);\r\n\r\n/*\r\n * Called when doing an OUT xfer (data to device) to get a buffer for\r\n * the specified endpoint.\r\n * It is up to the user to ensure the buffer is large enough.\r\n */\r\ntypedef void * (*usb_get_buffer_t)(struct GrainuumUSB *usb,\r\n                                   uint8_t epnum,\r\n                                   int32_t *size);\r\n\r\n/*\r\n * When data is received (i.e. OUT EP), this function will be called.\r\n */\r\ntypedef int (*usb_data_in_t)(struct GrainuumUSB *usb,\r\n                             uint8_t epnum,\r\n                             uint32_t bytes,\r\n                             const void *data);\r\n\r\n/**\r\n * @brief   Called immediately after @p grainuumSendData() has queued data.\r\n * @note    This function can be used to e.g. sleep a thread.\r\n * @param[in] usb     pointer to the @p GrainuumUSB object\r\n * @param[in] epnum   endpoint number of the transfer\r\n * @param[in] data    pointer to the data being written\r\n * @param[in] size   number of bytes being written\r\n * @api\r\n */\r\ntypedef void (*usb_data_out_start_t)(struct GrainuumUSB *usb,\r\n                                     int epnum,\r\n                                     const void *data,\r\n                                     int size);\r\n\r\n/**\r\n * @brief   Called once all data has been sent.\r\n * @note    This function can be used to e.g. wake up a thread.\r\n * @param[out] usb     pointer to the @p GrainuumUSB object\r\n * @param[out] result  whether the transfer was successful (0), or had an error.\r\n * @api\r\n */\r\ntypedef int (*usb_data_out_finish_t)(struct GrainuumUSB *usb,\r\n                                     int result);\r\n\r\n/* Structure of a USB packet on the wire, plus size field */\r\nstruct usb_packet {\r\n  union {\r\n    struct {\r\n      uint8_t pid;\r\n      uint8_t data[10]; /* Including CRC */\r\n    } __attribute((packed, aligned(4)));\r\n    uint8_t raw_data[11];\r\n  } __attribute((packed, aligned(4)));\r\n  uint8_t size; /* Not including pid (so may be 0) */\r\n  /* Checksum omitted */\r\n} __attribute__((packed, aligned(4)));\r\n\r\n/* USB Descriptors */\r\n\r\n#define DT_DEVICE 0x01\r\n#define DT_CONFIGURATION 0x02\r\n#define DT_STRING 0x03\r\n#define DT_INTERFACE 0x04\r\n#define DT_ENDPOINT 0x05\r\n#define DT_DEVICE_QUALIFIER 0x06\r\n#define DT_OTHER_SPEED_CONFIGURATION 0x07\r\n#define DT_INTERFACE_POWER 0x08\r\n\r\n#define DT_HID 0x21\r\n#define DT_HID_REPORT 0x22\r\n#define DT_PID 0x23\r\n\r\nstruct usb_setup_packet {\r\n  uint8_t bmRequestType;\r\n  uint8_t bRequest;\r\n  union {\r\n    uint16_t wValue;\r\n    struct {\r\n      uint8_t wValueL;\r\n      uint8_t wValueH;\r\n    };\r\n  };\r\n  uint16_t wIndex;\r\n  uint16_t wLength;\r\n} __attribute__((packed, aligned(4)));\r\n\r\nstruct usb_device_descriptor {\r\n  uint8_t  bLength;\r\n  uint8_t  bDescriptorType;\r\n  uint16_t bcdUSB;\r\n  uint8_t  bDeviceClass;\r\n  uint8_t  bDeviceSubClass;\r\n  uint8_t  bDeviceProtocol;\r\n  uint8_t  bMaxPacketSize0;\r\n  uint16_t idVendor;\r\n  uint16_t idProduct;\r\n  uint16_t bcdDevice;\r\n  uint8_t  iManufacturer;\r\n  uint8_t  iProduct;\r\n  uint8_t  iSerialNumber;\r\n  uint8_t  bNumConfigurations;\r\n} __attribute__((packed, aligned(4)));\r\n\r\nstruct usb_configuration_descriptor {\r\n  uint8_t  bLength;             /* Size of this descriptor, in bytes (9) */\r\n  uint8_t  bDescriptorType;     /* DT_CONFIGURATION (2) */\r\n  uint16_t wTotalLength;        /* Total length of this, plus sizeof(data) */\r\n  uint8_t  bNumInterfaces;      /* Number of interfaces supported by config */\r\n  uint8_t  bConfigurationValue; /* Value used by Set Configuration */\r\n  uint8_t  iConfiguration;      /* index of string descriptor for config */\r\n  uint8_t  bmAttributes;        /* Bitmap of attributes.  D7 must be 1. */\r\n  uint8_t  bMaxPower;           /* Maximum power, in units of 2mA */\r\n  uint8_t  data[];              /* Remaining descriptors */\r\n} __attribute__((packed, aligned(4)));\r\n\r\nstruct usb_string_descriptor {\r\n  uint8_t bLength;          /* sizeof(usb_string_descriptor) + sizeof(data) */\r\n  uint8_t bDescriptorType;  /* DT_STRING (3) */\r\n  uint8_t data[];           /* UTF-16LE string data or lang data(for string 0 */\r\n} __attribute__((packed, aligned(4)));\r\n\r\nstruct usb_interface_descriptor {\r\n  uint8_t bLength;            /* sizeof(usb_interface_descriptor) (9) */\r\n  uint8_t bDescriptorType;    /* DT_INTERFACE (4) */\r\n  uint8_t bInterfaceNumber;   /* Which interface this describes.  Usually 0. */\r\n  uint8_t bAlternateSetting;  /* ??? */\r\n  uint8_t bNumEndpoints;      /* Number of endpoints, minus 1 */\r\n  uint8_t bInterfaceClass;    /* Class code */\r\n  uint8_t bInterfaceSubclass; /* Class sub-code */\r\n  uint8_t bInterfaceProtocol; /* Protocol code, assigned by USB */\r\n  uint8_t iInterface;         /* Index of string for this interface */\r\n} __attribute__((packed, aligned(4)));\r\n\r\nstruct usb_endpoint_descriptor {\r\n  uint8_t  bLength;           /* sizeof(usb_endpoint_descriptor) (7) */\r\n  uint8_t  bDescriptorType;   /* DT_ENDPOINT (5) */\r\n  uint8_t  bEndpointAddress;  /* High bit 1:IN, 0:OUT. Lower 4-bits are EP# */\r\n  uint8_t  bmAttributes;      /* 0=control, 2=bulk, 3=interrupt */\r\n  uint16_t wMaxPacketSize;    /* Max packet size for this EP */\r\n  uint8_t  bInterval;         /* Polling rate (in 1ms units) */\r\n} __attribute__((packed, aligned(4)));\r\n\r\nstruct usb_hid_descriptor {\r\n  uint8_t  bLength;           /* sizeof(usb_hid_descriptor) (9) */\r\n  uint8_t  bDescriptorType;   /* DT_HID (0x21) */\r\n  uint16_t bcdHID;            /* HID class version number, in BCD */\r\n  uint8_t  bCountryCode;      /* Target country (usually 0) */\r\n  uint8_t  bNumDescriptors;   /* Number of HID class descriptors (usually 1) */\r\n  uint8_t  bReportDescriptorType;   /* Report descriptor type (usually 0x22) */\r\n  uint16_t wReportDescriptorLength; /* Length of the HID/PID report descriptor */\r\n} __attribute__((packed, aligned(4)));\r\n\r\n#define GRAINUUM_BUFFER_ELEMENT_SIZE 12 /* 1 PID, 8 data, 2 CRC16, 1 size */\r\n/* grainuum_buffer is aligned such that its first byte is on a word boundary.\r\n * This is because the first byte of every packet is a PID, which is\r\n * immediately discarded.  This leaves the remainder of the packet\r\n * word-aligned.\r\n */\r\n\r\n#define GRAINUUM_BUFFER(name, sz)                           \\\r\nstruct {                                                    \\\r\n  uint8_t  head;                                            \\\r\n  uint8_t  tail;                                            \\\r\n  uint8_t  padding;                                         \\\r\n  union {                                                   \\\r\n    uint8_t  buffer[(sz) * GRAINUUM_BUFFER_ELEMENT_SIZE];   \\\r\n    uint8_t  elements[sz][GRAINUUM_BUFFER_ELEMENT_SIZE];    \\\r\n  };                                                        \\\r\n} name __attribute__((aligned(4)));                         \\\r\nuint8_t * name ## _head_ptr;\r\n#define GRAINUUM_BUFFER_INIT(name)                          \\\r\n  do {                                                      \\\r\n    (name).head = 0;                                        \\\r\n    (name).tail = 0;                                        \\\r\n    name ## _head_ptr = (name).buffer;                      \\\r\n  } while(0)\r\n#define GRAINUUM_BUFFER_ADVANCE(name)                       \\\r\n  do {                                                      \\\r\n    (name).head += GRAINUUM_BUFFER_ELEMENT_SIZE;            \\\r\n    if ((name).head >= sizeof((name).buffer))               \\\r\n      (name).head = 0;                                      \\\r\n    name ## _head_ptr = ((name).buffer + (name).head);      \\\r\n  } while(0)\r\n#define GRAINUUM_BUFFER_TOP(name)                           \\\r\n  (&((name).buffer[(name).tail]))\r\n#define GRAINUUM_BUFFER_REMOVE(name)                        \\\r\n  do {                                                      \\\r\n    (name).tail += GRAINUUM_BUFFER_ELEMENT_SIZE;            \\\r\n  if ((name).tail >= sizeof((name).buffer))                 \\\r\n    (name).tail = 0;                                        \\\r\n  } while(0)\r\n#define GRAINUUM_BUFFER_IS_EMPTY(name)                      \\\r\n  ((name).head == (name).tail)\r\n#define GRAINUUM_BUFFER_ENTRY(name)                         \\\r\n  name ## _head_ptr\r\n\r\n/* Grainuum Structs */\r\n\r\nstruct GrainuumConfig {\r\n  get_usb_descriptor_t      getDescriptor;\r\n  usb_set_config_num_t      setConfigNum;\r\n  usb_get_buffer_t          getReceiveBuffer;\r\n  usb_data_in_t             receiveData;\r\n  usb_data_out_start_t      sendDataStarted;\r\n  usb_data_out_finish_t     sendDataFinished;\r\n  void                     *data;\r\n  struct GrainuumUSB       *usb;\r\n} __attribute__((packed, aligned(4)));\r\n\r\nstruct GrainuumState {\r\n  struct GrainuumUSB *usb;\r\n\r\n  uint8_t data_in[8];\r\n\r\n  const void *data_out;     /* Pointer to the data that's being sent */\r\n  int32_t data_out_left;    /* How much data has yet to be sent */\r\n  int32_t data_out_max;     /* The maximum number of bytes to send */\r\n  int32_t data_out_epnum;   /* Which endpoint the data is for */\r\n\r\n  struct usb_packet packet; /* Currently-queued packet */\r\n  int packet_queued;        /* Whether a packet is queued */\r\n\r\n  uint32_t tok_pos;         /* Position within the current token */\r\n  void *tok_buf;            /* Buffer storing current token's data */\r\n  uint8_t tok_epnum;        /* Last token's endpoint */\r\n\r\n  uint8_t data_buffer;      /* Whether we're sending DATA0 or DATA1 */\r\n  uint8_t packet_type;      /* PACKET_SETUP, PACKET_IN, or PACKET_OUT */\r\n\r\n  uint8_t address;          /* Our configured address */\r\n\r\n  GRAINUUM_STATE_EXTRA\r\n} __attribute__((packed, aligned(4)));\r\n\r\nstruct GrainuumUSB {\r\n\r\n  struct GrainuumConfig *cfg; /* Callbacks */\r\n  int initialized;\r\n\r\n  /* USB D- pin specification */\r\n  uint32_t usbdnIAddr;\r\n  uint32_t usbdnSAddr;\r\n  uint32_t usbdnCAddr;\r\n  uint32_t usbdnDAddr;\r\n  uint32_t usbdnShift;\r\n\r\n  /* USB D+ pin specification */\r\n  uint32_t usbdpIAddr;\r\n  uint32_t usbdpSAddr;\r\n  uint32_t usbdpCAddr;\r\n  uint32_t usbdpDAddr;\r\n  uint32_t usbdpShift;\r\n\r\n  uint32_t usbdnMask;\r\n  uint32_t usbdpMask;\r\n\r\n  uint32_t queued_size;\r\n  uint32_t queued_epnum;\r\n  const void *queued_data;\r\n\r\n  struct GrainuumState state;     /* Associated state */\r\n\r\n  GRAINUUM_EXTRA\r\n} __attribute__((packed, aligned(4)));\r\n\r\n#ifdef __cplusplus\r\nextern \"C\" {\r\n#endif\r\n\r\nstatic inline void grainuumWritel(uint32_t value, uint32_t addr)\r\n{\r\n  *((volatile uint32_t *)addr) = value;\r\n}\r\n\r\nstatic inline uint32_t grainuumReadl(uint32_t addr)\r\n{\r\n  return *(volatile uint32_t *)addr;\r\n}\r\n\r\n/*===========================================================================*/\r\n/* Weak hook functions.                                                      */\r\n/*===========================================================================*/\r\n\r\n/**\r\n * @brief   Called just before the USB device is plugged in.\r\n * @param[in] usb     pointer to the @p GrainuumUSB object\r\n * @api\r\n */\r\nvoid grainuumConnectPre(struct GrainuumUSB *usb);\r\n\r\n/**\r\n * @brief   Called just after the USB device is plugged in.\r\n * @param[in] usb     pointer to the @p GrainuumUSB object\r\n * @api\r\n */\r\nvoid grainuumConnectPost(struct GrainuumUSB *usb);\r\n\r\n/**\r\n * @brief   Called just before the USB device is unplugged.\r\n * @param[in] usb     pointer to the @p GrainuumUSB object\r\n * @api\r\n */\r\nvoid grainuumDisconnectPre(struct GrainuumUSB *usb);\r\n\r\n/**\r\n * @brief   Called just after the USB device is unplugged.\r\n * @param[in] usb     pointer to the @p GrainuumUSB object\r\n * @api\r\n */\r\nvoid grainuumDisconnectPost(struct GrainuumUSB *usb);\r\n\r\n/**\r\n * @brief   Called just before the USB device is first initialized.\r\n * @param[in] usb     pointer to the @p GrainuumUSB object\r\n * @api\r\n */\r\nvoid grainuumInitPre(struct GrainuumUSB *usb);\r\n\r\n/**\r\n * @brief   Called just before the USB device is first initialized.\r\n * @param[in] usb     pointer to the @p GrainuumUSB object\r\n * @api\r\n */\r\nvoid grainuumInitPost(struct GrainuumUSB *usb);\r\n\r\n/**\r\n * @brief   Called immediately after a packet has been received.\r\n * @note    This is called from an interrupt context.  Data will\r\n *          be stored in the buffer that was passed to @p grainuumCaptureI()\r\n * @param[in] usb     pointer to the @p GrainuumUSB object\r\n * @iclass\r\n */\r\nvoid grainuumReceivePacket(struct GrainuumUSB *usb);\r\n\r\n/*===========================================================================*/\r\n/* External declarations.                                                    */\r\n/*===========================================================================*/\r\n\r\n/**\r\n * @brief   Returns nonzero if Grainuum has been initialized.\r\n * @param[in] usb     pointer to the @p GrainuumUSB object.\r\n * @return            nonzero if @p GrainuumUSB is initialized.\r\n * @retval 0          Object is not initilized.\r\n * @api\r\n */\r\nint grainuumInitialized(struct GrainuumUSB *usb);\r\n\r\n/**\r\n * @brief   Queues some data to be sent to the host.\r\n * @note    After the first 8 bytes, @p data must remain valid\r\n *          until the transfer has completed.  This generally\r\n *          means you can send const data stored in the text\r\n *          section, or small 8-byte packets.\r\n * @param[in] usb     pointer to the @p GrainuumUSB object.\r\n * @param[in] epnum   endpoint number of the transfer.\r\n * @param[in] data    pointer to the data being written.\r\n * @param[in] size    number of bytes being written.\r\n * @return            0 if the transfer completed successfully.\r\n * @retval 0          Transfer completed successfully.\r\n * @api\r\n */\r\nint grainuumSendData(struct GrainuumUSB *usb, int epnum, const void *data, int size);\r\n\r\n/**\r\n * @brief   Clears the send buffer, if not empty.\r\n * @note    If data has already been queued for the PHY, then\r\n *          this will not prevent it from being sent.\r\n *          This function is intended to be used to prevent\r\n *          grainuumSendData() from returning -EAGAIN.\r\n * @param[in] usb     pointer to the @p GrainuumUSB object.\r\n * @api\r\n */\r\nvoid grainuumDropData(struct GrainuumUSB *usb);\r\n\r\n/**\r\n * @brief   Determines if data is already queued.\r\n * @note    If data has been queued, then this will return\r\n *          nonzero.  If this returns zero, then you can\r\n *          trust grainuumSendData() will succeed.\r\n * @param[in] usb     pointer to the @p GrainuumUSB object.\r\n * @return            Nonzero if data is already queued.\r\n * @api\r\n */\r\nint grainuumDataQueued(struct GrainuumUSB *usb);\r\n\r\n/**\r\n * @brief   Process one received packet through the Grainuum state machine.\r\n * @note    This feeds USB packets into the state machine.  It should not\r\n *          be called as part of an interrupt.\r\n * @param[in] usb     pointer to the @p GrainuumUSB object.\r\n * @param[in] packet  The USB packet that was most recently received, with byte 12 holding the size.\r\n * @api\r\n */\r\nvoid grainuumProcess(struct GrainuumUSB *usb,\r\n                     const uint8_t packet[12]);\r\n\r\n/**\r\n * @brief   Initialize the Grainuum USB system.\r\n * @note    This is meant to run as part of an interrupt.  Pass\r\n *          the storage buffer in as @p samples.  The number\r\n *          of bytes that were read will be stored in the last\r\n *          byte of the array.  For best performance, make\r\n *          sure that @p sample is on byte 3 of a 4-byte boundary,\r\n *          so that samples[1] is on a word boundary. The @p GrainuumUSB\r\n *          object will start out disconnected.\r\n * @param[in] usb   Pointer to the @p GrainuumUSB object to initialize.\r\n * @param[in] link  Pointer to the @p GrainuumConfig object to use.\r\n * @api\r\n */\r\nvoid grainuumInit(struct GrainuumUSB *usb, struct GrainuumConfig *link);\r\n\r\n/**\r\n * @brief   Capture a USB packet from the wire.\r\n * @note    This is meant to run as part of an interrupt.  Pass\r\n *          the storage buffer in as @p samples.  The number\r\n *          of bytes that were read will be stored in the last\r\n *          byte of the array.  For best performance, make\r\n *          sure that @p sample is on byte 3 of a 4-byte boundary,\r\n *          so that samples[1] is on a  word boundary.\r\n * @param[in] usb     pointer to the @p GrainuumUSB object.\r\n * @param[in] packet  Buffer to store the read samples.\r\n * @api\r\n */\r\nvoid grainuumCaptureI(struct GrainuumUSB *usb, uint8_t packet[12]);\r\n\r\n/**\r\n * @brief   Internal function. Queues 8 bytes to be sent by the phy.\r\n * @note    This is an internal function, and is not meant to be called.\r\n *          It is meant to queue properly-formatted USB packets complete\r\n *          with CRC-16 (if required).\r\n * @param[in] usb     pointer to the @p GrainuumUSB object.\r\n * @param[in] epnum   The endpoint number to queue data for.\r\n * @param[in] buffer  The data to queue.\r\n * @param[in] size    The number of bytes that are queued.\r\n * @notapi\r\n */\r\nvoid grainuumWriteQueue(struct GrainuumUSB *usb, int epnum,\r\n                        const void *buffer, int size);\r\n\r\n/**\r\n * @brief   Simulates plugging the device into USB.\r\n * @note    All USB Connect hooks will be called.\r\n *          The default USB state is \"disconnected\",\r\n *          so @p grainuumConnect() must be called\r\n *          to start communications.\r\n * @param[in] usb     pointer to the @p GrainuumUSB object.\r\n * @api\r\n */\r\nvoid grainuumConnect(struct GrainuumUSB *usb);\r\n\r\n/**\r\n * @brief   Simulates unplugging the device from USB.\r\n * @note    All USB Disconnect hooks will be called.\r\n * @param[in] usb     pointer to the @p GrainuumUSB object.\r\n * @api\r\n */\r\nvoid grainuumDisconnect(struct GrainuumUSB *usb);\r\n\r\n/**\r\n * @brief   Reads one packet from the wire.\r\n * @note    This must be called from an interrupt context with\r\n *          interrupts disabled.\r\n * @param[in] usb       Pointer to the @p GrainuumUSB object.\r\n * @param[out] samples  Buffer where the samples will be stored.\r\n * @return              The number of bytes read, or negative on error\r\n * @retval -1           Timeout while reading.\r\n * @retval -2           Read too many bits.\r\n * @retval -3           Unable to find sync end.\r\n * @retval -4           Probably a keepalive packet.\r\n * @notapi\r\n */\r\nint usbPhyReadI(const struct GrainuumUSB *usb, uint8_t samples[11]);\r\n\r\n/**\r\n * @brief   Writes one packet from the wire.\r\n * @note    This must be called from an interrupt context with\r\n *          interrupts disabled.\r\n * @param[in] usb       Pointer to the @p GrainuumUSB object.\r\n * @param[in] samples   Buffer where the samples will be stored.\r\n * @param[in] size      Number of bytes to write.\r\n * @notapi\r\n */\r\nvoid usbPhyWriteI(const struct GrainuumUSB *usb, const void *buffer, uint32_t size);\r\n\r\n#ifdef __cplusplus\r\n};\r\n#endif\r\n\r\n#endif /* _GRAINUUM_H */\r\n"
  }
]