]> sigrok.org Git - sigrok-firmware-fx2lafw.git/commitdiff
Add FX2 firmware for the Hantek 6022BE USB scope.
authorUwe Hermann <redacted>
Thu, 10 Mar 2016 20:02:12 +0000 (21:02 +0100)
committerUwe Hermann <redacted>
Thu, 10 Mar 2016 21:32:11 +0000 (22:32 +0100)
This firmware was originally created by Jochen Hoenicke in 05/2015
for the Hantek6022API github project by Robert Cope. Full credit for
the implementation of the firmware goes to Jochen Hoenicke, thanks!

Github project:

  https://github.com/rpcope1/Hantek6022API

We're incorporating the firmware into the sigrok-firmware-fx2lafw
repo/tarball for convenience of sigrok users. E.g. so that they can
easily get it with the other fx2lafw firmware files from distro
packages, the build scripts in sigrok-util automatically build/install
it, the sigrok Windows installers automatically ship it, the sigrok
Android APKs automatically ship it, etc. etc.

The files we're integrating into fx2lafw from Hantek6022API are taken from
the PyHT6022/HantekFirmware/custom directory of that repo, using the state
as of 03/2016 (the files were last modified in 05/2015, though).

The git hash of the last relevant commit there was:

  0498e2ab239aabb1084c19e1e24faf56764b217e

Files integrated into fx2lafw:

 - hw/hantek-6022be/dscr.a51: Copied unmodified.

 - hantek_6022be.c: This file is created by appending the
   contents of the original device.c and fw.c together without
   other modifications.

The license of both files is LGPL 2.1 (or later).

hantek_6022be.c [new file with mode: 0644]
hw/hantek-6022be/dscr.a51 [new file with mode: 0644]

diff --git a/hantek_6022be.c b/hantek_6022be.c
new file mode 100644 (file)
index 0000000..8a73cc0
--- /dev/null
@@ -0,0 +1,511 @@
+/**
+ * Copyright (C) 2009 Ubixum, Inc. 
+ * Copyright (C) 2015 Jochen Hoenicke
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ **/
+
+#include <fx2macros.h>
+#include <fx2ints.h>
+#include <autovector.h>
+#include <delay.h>
+#include <setupdat.h>
+
+#ifdef DEBUG_FIRMWARE 
+#include <serial.h>
+#include <stdio.h>
+#else
+#define printf(...)
+#endif
+
+volatile WORD ledcounter = 0;
+
+
+volatile __bit dosud=FALSE;
+volatile __bit dosuspend=FALSE;
+
+// custom functions
+extern void main_loop();
+extern void main_init();
+
+void main() {
+
+ SETCPUFREQ(CLK_12M); // save energy
+
+ main_init();
+
+ // set up interrupts.
+ USE_USB_INTS();
+ ENABLE_SUDAV();
+ ENABLE_USBRESET();
+ ENABLE_HISPEED(); 
+ ENABLE_SUSPEND();
+ ENABLE_RESUME();
+
+ EA=1;
+
+ // init timer2
+    RCAP2L = -500 & 0xff;
+    RCAP2H = (-500 >> 8) & 0xff;
+    T2CON = 0;
+    ET2 = 1;
+    TR2 = 1;
+
+// iic files (c2 load) don't need to renumerate/delay
+// trm 3.6
+#ifndef NORENUM
+ RENUMERATE();
+#else
+ USBCS &= ~bmDISCON;
+#endif
+    PORTCCFG = 0;
+    PORTACFG = 0;
+    OEC = 0xff;
+    OEA = 0x80;
+
+ while(TRUE) {
+
+     main_loop();
+
+     if (dosud) {
+       dosud=FALSE;
+       handle_setupdata();
+     }
+
+     if (dosuspend) {
+        dosuspend=FALSE;
+        do {
+           printf ( "I'm going to Suspend.\n" );
+           WAKEUPCS |= bmWU|bmWU2; // make sure ext wakeups are cleared
+           SUSPEND=1;
+           PCON |= 1;
+           __asm
+           nop
+           nop
+           nop
+           nop
+           nop
+           nop
+           nop
+           __endasm;
+        } while ( !remote_wakeup_allowed && REMOTE_WAKEUP()); 
+        printf ( "I'm going to wake up.\n");
+
+        // resume
+        // trm 6.4
+        if ( REMOTE_WAKEUP() ) {
+            delay(5);
+            USBCS |= bmSIGRESUME;
+            delay(15);
+            USBCS &= ~bmSIGRESUME;
+        }
+
+     }
+
+ } // end while
+
+} // end main
+
+void resume_isr() __interrupt RESUME_ISR {
+ CLEAR_RESUME();
+}
+  
+void sudav_isr() __interrupt SUDAV_ISR {
+ dosud=TRUE;
+ CLEAR_SUDAV();
+}
+void usbreset_isr() __interrupt USBRESET_ISR {
+ handle_hispeed(FALSE);
+ CLEAR_USBRESET();
+}
+void hispeed_isr() __interrupt HISPEED_ISR {
+ handle_hispeed(TRUE);
+ CLEAR_HISPEED();
+}
+
+void suspend_isr() __interrupt SUSPEND_ISR {
+ dosuspend=TRUE;
+ CLEAR_SUSPEND();
+}
+
+void timer2_isr() __interrupt TF2_ISR {
+  PA7 = !PA7;
+  if (ledcounter) {
+    if (--ledcounter == 0) {
+      // clear LED
+      PC0 = 1;
+      PC1 = 1;
+    }
+  }
+  TF2 = 0;
+}
+/**
+ * Copyright (C) 2009 Ubixum, Inc. 
+ * Copyright (C) 2015 Jochen Hoenicke
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ **/
+
+#include <fx2macros.h>
+#include <delay.h>
+
+#ifdef DEBUG_FIRMWARE
+#include <stdio.h>
+#else
+#define printf(...)
+#endif
+
+// change to support as many interfaces as you need
+BYTE altiface = 0; // alt interface
+extern volatile WORD ledcounter;
+
+
+
+/* This sets three bits for each channel, one channel at a time.
+ * For channel 0 we want to set bits 5, 6 & 7
+ * For channel 1 we want to set bits 2, 3 & 4
+ *
+ * We convert the input values that are strange due to original firmware code into the value of the three bits as follows:
+ * val -> bits
+ * 1  -> 010b
+ * 2  -> 001b
+ * 5  -> 000b
+ * 10 -> 011b
+ *
+ * The third bit is always zero since there are only four outputs connected in the serial selector chip.
+ *
+ * The multiplication of the converted value by 0x24 sets the relevant bits in
+ * both channels and then we mask it out to only affect the channel currently
+ * requested.
+ */
+BOOL set_voltage(BYTE channel, BYTE val)
+{
+    BYTE bits, mask;
+    switch (val) {
+    case 1:
+       bits = 0x24 * 2;
+       break;
+    case 2:
+       bits = 0x24 * 1;
+       break;
+    case 5:
+       bits = 0x24 * 0;
+       break;
+    case 10:
+       bits = 0x24 * 3;
+       break;
+    default:
+       return FALSE;
+    }
+
+    mask = channel ? 0xe0 : 0x1c;
+    IOC = (IOC & ~mask) | (bits & mask);
+    return TRUE;
+}
+
+BOOL set_numchannels(BYTE numchannels)
+{
+    if (numchannels == 1 || numchannels == 2) {
+       BYTE fifocfg = 7 + numchannels;
+       EP2FIFOCFG = fifocfg;
+       EP6FIFOCFG = fifocfg;
+       return TRUE;
+    }
+    return FALSE;
+}
+
+void clear_fifo()
+{
+    GPIFABORT = 0xff;
+    SYNCDELAY3;
+    FIFORESET = 0x80;
+    SYNCDELAY3;
+    FIFORESET = 0x82;
+    SYNCDELAY3;
+    FIFORESET = 0x86;
+    SYNCDELAY3;
+    FIFORESET = 0;
+}
+
+void stop_sampling()
+{
+    GPIFABORT = 0xff;
+    SYNCDELAY3;
+    if (altiface == 0) {
+       INPKTEND = 6;
+    } else {
+       INPKTEND = 2;
+    }
+}
+
+void start_sampling()
+{
+    int i;
+    clear_fifo();
+
+    for (i = 0; i < 1000; i++);
+    while (!(GPIFTRIG & 0x80)) {
+       ;
+    }
+    SYNCDELAY3;
+    GPIFTCB1 = 0x28;
+    SYNCDELAY3;
+    GPIFTCB0 = 0;
+    if (altiface == 0)
+       GPIFTRIG = 6;
+    else
+       GPIFTRIG = 4;
+
+    // set green led
+    // don't clear led
+    ledcounter = 0;
+    PC0 = 1;
+    PC1 = 0;
+}
+
+extern __code BYTE highspd_dscr;
+extern __code BYTE fullspd_dscr;
+void select_interface(BYTE alt)
+{
+    const BYTE *pPacketSize = (USBCS & bmHSM ? &highspd_dscr : &fullspd_dscr)
+       + (9 + 16*alt + 9 + 4);
+    altiface = alt;
+    if (alt == 0) {
+       // bulk on port 6
+       EP2CFG = 0x00;
+       EP6CFG = 0xe0;
+       EP6GPIFFLGSEL = 1;
+
+       EP6AUTOINLENL = pPacketSize[0];
+       EP6AUTOINLENH = pPacketSize[1];
+    } else {
+       // iso on port 2
+       EP2CFG = 0xd8;
+       EP6CFG = 0x00;
+       EP2GPIFFLGSEL = 1;
+
+       EP2AUTOINLENL = pPacketSize[0];
+       EP2AUTOINLENH = pPacketSize[1] & 0x7;
+       EP2ISOINPKTS = (pPacketSize[1] >> 3) + 1;
+    }
+}
+
+const struct samplerate_info {
+    BYTE rate;
+    BYTE wait0;
+    BYTE wait1;
+    BYTE opc0;
+    BYTE opc1;
+    BYTE out0;
+    BYTE ifcfg;
+} samplerates[] = {
+    { 48,0x80,   0, 3, 0, 0x00, 0xea },
+    { 30,0x80,   0, 3, 0, 0x00, 0xaa },
+    { 24,   1,   0, 2, 1, 0x40, 0xca },
+    { 16,   1,   1, 2, 0, 0x40, 0xca },
+    { 12,   2,   1, 2, 0, 0x40, 0xca },
+    {  8,   3,   2, 2, 0, 0x40, 0xca },
+    {  4,   6,   5, 2, 0, 0x40, 0xca },
+    {  2,  12,  11, 2, 0, 0x40, 0xca },
+    {  1,  24,  23, 2, 0, 0x40, 0xca },
+    { 50,  48,  47, 2, 0, 0x40, 0xca },
+    { 20, 120, 119, 2, 0, 0x40, 0xca },
+    { 10, 240, 239, 2, 0, 0x40, 0xca }
+};
+
+BOOL set_samplerate(BYTE rate)
+{
+    BYTE i = 0;
+    while (samplerates[i].rate != rate) {
+       i++;
+       if (i == sizeof(samplerates)/sizeof(samplerates[0]))
+           return FALSE;
+    }
+
+    IFCONFIG = samplerates[i].ifcfg;
+
+    AUTOPTRSETUP = 7;
+    AUTOPTRH2 = 0xE4;
+    AUTOPTRL2 = 0x00;
+
+    /* The program for low-speed, e.g. 1 MHz, is
+     * wait 24, CTL2=0, FIFO
+     * wait 23, CTL2=1
+     * jump 0, CTL2=1
+     *
+     * The program for 24 MHz is
+     * wait 1, CTL2=0, FIFO
+     * jump 0, CTL2=1
+     *
+     * The program for 30/48 MHz is:
+     * jump 0, CTL2=Z, FIFO, LOOP
+     */
+
+    EXTAUTODAT2 = samplerates[i].wait0;
+    EXTAUTODAT2 = samplerates[i].wait1;
+    EXTAUTODAT2 = 1;
+    EXTAUTODAT2 = 0;
+    EXTAUTODAT2 = 0;
+    EXTAUTODAT2 = 0;
+    EXTAUTODAT2 = 0;
+    EXTAUTODAT2 = 0;
+
+    EXTAUTODAT2 = samplerates[i].opc0;
+    EXTAUTODAT2 = samplerates[i].opc1;
+    EXTAUTODAT2 = 1;
+    EXTAUTODAT2 = 0;
+    EXTAUTODAT2 = 0;
+    EXTAUTODAT2 = 0;
+    EXTAUTODAT2 = 0;
+    EXTAUTODAT2 = 0;
+
+    EXTAUTODAT2 = samplerates[i].out0;
+    EXTAUTODAT2 = 0x44;
+    EXTAUTODAT2 = 0x44;
+    EXTAUTODAT2 = 0x00;
+    EXTAUTODAT2 = 0x00;
+    EXTAUTODAT2 = 0x00;
+    EXTAUTODAT2 = 0x00;
+    EXTAUTODAT2 = 0x00;
+
+    EXTAUTODAT2 = 0;
+    EXTAUTODAT2 = 0;
+    EXTAUTODAT2 = 0;
+    EXTAUTODAT2 = 0;
+    EXTAUTODAT2 = 0;
+    EXTAUTODAT2 = 0;
+    EXTAUTODAT2 = 0;
+    EXTAUTODAT2 = 0;
+
+    for (i = 0; i < 96; i++)
+       EXTAUTODAT2 = 0;
+    return TRUE;
+}
+
+BOOL handle_get_descriptor() {
+  return FALSE;
+}
+
+//************************** Configuration Handlers *****************************
+
+// set *alt_ifc to the current alt interface for ifc
+BOOL handle_get_interface(BYTE ifc, BYTE* alt_ifc) {
+    (void) ifc; // ignore unused parameter
+    *alt_ifc=altiface;
+    return TRUE;
+}
+// return TRUE if you set the interface requested
+// NOTE this function should reconfigure and reset the endpoints
+// according to the interface descriptors you provided.
+BOOL handle_set_interface(BYTE ifc,BYTE alt_ifc) {  
+    printf ( "Set Interface.\n" );
+    if (ifc == 0) {
+      select_interface(alt_ifc);
+    }
+    return TRUE;
+}
+
+// handle getting and setting the configuration
+// 1 is the default.  We don't support multiple configurations.
+BYTE handle_get_configuration() { 
+    return 0;
+}
+
+BOOL handle_set_configuration(BYTE cfg) { 
+    (void) cfg; // ignore unused parameter
+    return TRUE;
+}
+
+
+//******************* VENDOR COMMAND HANDLERS **************************
+
+BOOL handle_vendorcommand(BYTE cmd) {
+    stop_sampling();
+    // Set Red LED
+    PC0 = 0;
+    PC1 = 1;
+    ledcounter = 1000;
+    switch (cmd) {
+    case 0xe0:
+    case 0xe1:
+       EP0BCH=0;
+       EP0BCL=0;
+       while (EP0CS & bmEPBUSY);
+       set_voltage(cmd - 0xe0, EP0BUF[0]);
+       return TRUE;
+    case 0xe2:
+       EP0BCH=0;
+       EP0BCL=0;
+       while (EP0CS & bmEPBUSY);
+       set_samplerate(EP0BUF[0]);
+       return TRUE;
+    case 0xe3:
+       EP0BCH=0;
+       EP0BCL=0;
+       while (EP0CS & bmEPBUSY);
+       if (EP0BUF[0] == 1)
+           start_sampling();
+       return TRUE;
+    case 0xe4:
+       EP0BCH=0;
+       EP0BCL=0;
+       while (EP0CS & bmEPBUSY);
+       set_numchannels(EP0BUF[0]);
+       return TRUE;
+    }
+    return FALSE; // not handled by handlers
+}
+
+//********************  INIT ***********************
+
+void main_init() {
+    EP4CFG = 0;
+    EP8CFG = 0;
+
+    // in idle mode tristate all outputs
+    GPIFIDLECTL = 0x00;
+    GPIFCTLCFG = 0x80;
+    GPIFWFSELECT = 0x00;
+    GPIFREADYSTAT = 0x00;
+
+    stop_sampling();
+    set_voltage(0, 1);
+    set_voltage(1, 1);
+    set_samplerate(1);
+    set_numchannels(2);
+    select_interface(0);
+
+    printf ( "Initialization Done.\n" );
+}
+
+
+void main_loop() {
+}
+
+
diff --git a/hw/hantek-6022be/dscr.a51 b/hw/hantek-6022be/dscr.a51
new file mode 100644 (file)
index 0000000..d282fdc
--- /dev/null
@@ -0,0 +1,358 @@
+; Copyright (C) 2009 Ubixum, Inc. 
+;
+; This library is free software; you can redistribute it and/or
+; modify it under the terms of the GNU Lesser General Public
+; License as published by the Free Software Foundation; either
+; version 2.1 of the License, or (at your option) any later version.
+; 
+; This library is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+; Lesser General Public License for more details.
+; 
+; You should have received a copy of the GNU Lesser General Public
+; License along with this library; if not, write to the Free Software
+; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+; this is a the default 
+; full speed and high speed 
+; descriptors found in the TRM
+; change however you want but leave 
+; the descriptor pointers so the setupdat.c file works right
+
+.module DEV_DSCR 
+
+; descriptor types
+; same as setupdat.h
+DSCR_DEVICE_TYPE=1
+DSCR_CONFIG_TYPE=2
+DSCR_STRING_TYPE=3
+DSCR_INTERFACE_TYPE=4
+DSCR_ENDPOINT_TYPE=5
+DSCR_DEVQUAL_TYPE=6
+
+; for the repeating interfaces
+DSCR_INTERFACE_LEN=9
+DSCR_ENDPOINT_LEN=7
+
+; endpoint types
+ENDPOINT_TYPE_CONTROL=0
+ENDPOINT_TYPE_ISO=1
+ENDPOINT_TYPE_BULK=2
+ENDPOINT_TYPE_INT=3
+
+    .globl     _dev_dscr, _dev_qual_dscr, _highspd_dscr, _fullspd_dscr, _dev_strings, _dev_strings_end
+; These need to be in code memory.  If
+; they aren't you'll have to manully copy them somewhere
+; in code memory otherwise SUDPTRH:L don't work right
+    .area      DSCR_AREA       (CODE)
+
+_dev_dscr:
+       .db     dev_dscr_end-_dev_dscr    ; len
+       .db     DSCR_DEVICE_TYPE                  ; type
+       .dw     0x0002                                    ; usb 2.0
+       .db     0xff                                      ; class (vendor specific)
+       .db     0xff                                      ; subclass (vendor specific)
+       .db     0xff                                      ; protocol (vendor specific)
+       .db     64                                                ; packet size (ep0)
+       .dw     0xB504                                    ; vendor id 
+       .dw     0x2260                                    ; product id
+       .dw     0x0000                                    ; version id
+       .db     1                                 ; manufacturure str idx                               
+       .db     2                                         ; product str idx     
+       .db     0                                         ; serial str idx 
+       .db     1                                     ; n configurations
+dev_dscr_end:
+
+_dev_qual_dscr:
+       .db     dev_qualdscr_end-_dev_qual_dscr
+       .db     DSCR_DEVQUAL_TYPE
+       .dw     0x0002                              ; usb 2.0
+       .db     0
+       .db     0
+       .db     0
+       .db     64                                  ; max packet
+       .db     1                                                                       ; n configs
+       .db     0                                                                       ; extra reserved byte
+dev_qualdscr_end:
+
+_highspd_dscr:
+       .db     highspd_dscr_end-_highspd_dscr      ; dscr len                                                                                  ;; Descriptor length
+       .db     DSCR_CONFIG_TYPE
+    ; can't use .dw because byte order is different
+       .db     (highspd_dscr_realend-_highspd_dscr) % 256 ; total length of config lsb
+       .db     (highspd_dscr_realend-_highspd_dscr) / 256 ; total length of config msb
+       .db     1                                                                ; n interfaces
+       .db     1                                                                ; config number
+       .db     0                                                                ; config string
+       .db     0x80                             ; attrs = bus powered, no wakeup
+       .db     55                               ; max power = 110mA
+highspd_dscr_end:
+
+; all the interfaces next 
+; BULK interface
+       .db     DSCR_INTERFACE_LEN
+       .db     DSCR_INTERFACE_TYPE
+       .db     0                                ; index
+       .db     0                                ; alt setting idx
+       .db     1                                ; n endpoints  
+       .db     0xff                     ; class
+       .db     0
+       .db     0
+       .db     0                    ; string index     
+
+; endpoint 6 in 
+       .db     DSCR_ENDPOINT_LEN
+       .db     DSCR_ENDPOINT_TYPE
+       .db     0x86                            ;  ep1 dir=in and address
+       .db     ENDPOINT_TYPE_BULK      ; type
+       .db     0x00                            ; max packet LSB
+       .db     0x02                            ; max packet size=512 bytes
+       .db     0x00                            ; polling interval
+
+; ISOCHRONOUS interface
+       .db     DSCR_INTERFACE_LEN
+       .db     DSCR_INTERFACE_TYPE
+       .db     0                                ; index
+       .db     1                                ; alt setting idx
+       .db     1                                ; n endpoints  
+       .db     0xff                     ; class
+       .db     0
+       .db     1
+       .db     0                    ; string index     
+
+; endpoint 2 in 
+       .db     DSCR_ENDPOINT_LEN
+       .db     DSCR_ENDPOINT_TYPE
+       .db     0x82                            ;  ep1 dir=in and address
+       .db     ENDPOINT_TYPE_ISO       ; type
+       .db     0x00                            ; max packet LSB
+       .db     0x14                            ; max packet size=3*1024 bytes
+       .db     0x01                            ; polling interval
+
+; ISOCHRONOUS interface  16MB/s
+       .db     DSCR_INTERFACE_LEN
+       .db     DSCR_INTERFACE_TYPE
+       .db     0                                ; index
+       .db     2                                ; alt setting idx
+       .db     1                                ; n endpoints  
+       .db     0xff                     ; class
+       .db     0
+       .db     1
+       .db     0                    ; string index     
+
+; endpoint 2 in 
+       .db     DSCR_ENDPOINT_LEN
+       .db     DSCR_ENDPOINT_TYPE
+       .db     0x82                            ;  ep1 dir=in and address
+       .db     ENDPOINT_TYPE_ISO       ; type
+       .db     0x00                            ; max packet LSB
+       .db     0x0c                            ; max packet size=2*1024 bytes
+       .db     0x01                            ; polling interval
+
+; ISOCHRONOUS interface  8MB/s
+       .db     DSCR_INTERFACE_LEN
+       .db     DSCR_INTERFACE_TYPE
+       .db     0                                ; index
+       .db     3                                ; alt setting idx
+       .db     1                                ; n endpoints  
+       .db     0xff                     ; class
+       .db     0
+       .db     1
+       .db     0                    ; string index     
+
+; endpoint 2 in 
+       .db     DSCR_ENDPOINT_LEN
+       .db     DSCR_ENDPOINT_TYPE
+       .db     0x82                            ;  ep1 dir=in and address
+       .db     ENDPOINT_TYPE_ISO       ; type
+       .db     0x00                            ; max packet LSB
+       .db     0x04                            ; max packet size=1024 bytes
+       .db     0x01                            ; polling interval
+
+; ISOCHRONOUS interface  4MB/s
+       .db     DSCR_INTERFACE_LEN
+       .db     DSCR_INTERFACE_TYPE
+       .db     0                                ; index
+       .db     4                                ; alt setting idx
+       .db     1                                ; n endpoints  
+       .db     0xff                     ; class
+       .db     0
+       .db     1
+       .db     0                    ; string index     
+
+; endpoint 2 in 
+       .db     DSCR_ENDPOINT_LEN
+       .db     DSCR_ENDPOINT_TYPE
+       .db     0x82                            ;  ep1 dir=in and address
+       .db     ENDPOINT_TYPE_ISO       ; type
+       .db     0x00                            ; max packet LSB
+       .db     0x04                            ; max packet size=1024 bytes
+       .db     0x02                            ; polling interval
+
+
+; ISOCHRONOUS interface  2MB/s
+       .db     DSCR_INTERFACE_LEN
+       .db     DSCR_INTERFACE_TYPE
+       .db     0                                ; index
+       .db     5                                ; alt setting idx
+       .db     1                                ; n endpoints  
+       .db     0xff                     ; class
+       .db     0
+       .db     1
+       .db     0                    ; string index     
+
+; endpoint 2 in 
+       .db     DSCR_ENDPOINT_LEN
+       .db     DSCR_ENDPOINT_TYPE
+       .db     0x82                            ;  ep1 dir=in and address
+       .db     ENDPOINT_TYPE_ISO       ; type
+       .db     0x00                            ; max packet LSB
+       .db     0x04                            ; max packet size=1024 bytes
+       .db     0x03                            ; polling interval
+
+; ISOCHRONOUS interface  1MB/s
+       .db     DSCR_INTERFACE_LEN
+       .db     DSCR_INTERFACE_TYPE
+       .db     0                                ; index
+       .db     6                                ; alt setting idx
+       .db     1                                ; n endpoints  
+       .db     0xff                     ; class
+       .db     0
+       .db     1
+       .db     0                    ; string index     
+
+; endpoint 2 in 
+       .db     DSCR_ENDPOINT_LEN
+       .db     DSCR_ENDPOINT_TYPE
+       .db     0x82                            ;  ep1 dir=in and address
+       .db     ENDPOINT_TYPE_ISO       ; type
+       .db     0x00                            ; max packet LSB
+       .db     0x04                            ; max packet size=1024 bytes
+       .db     0x04                            ; polling interval
+
+; ISOCHRONOUS interface 500 kB/s
+       .db     DSCR_INTERFACE_LEN
+       .db     DSCR_INTERFACE_TYPE
+       .db     0                                ; index
+       .db     7                                ; alt setting idx
+       .db     1                                ; n endpoints  
+       .db     0xff                     ; class
+       .db     0
+       .db     1
+       .db     0                    ; string index     
+
+; endpoint 2 in 
+       .db     DSCR_ENDPOINT_LEN
+       .db     DSCR_ENDPOINT_TYPE
+       .db     0x82                            ;  ep1 dir=in and address
+       .db     ENDPOINT_TYPE_ISO       ; type
+       .db     0x00                            ; max packet LSB
+       .db     0x02                            ; max packet size=512 bytes
+       .db     0x04                            ; polling interval
+
+
+highspd_dscr_realend:
+
+.even
+_fullspd_dscr:
+       .db     fullspd_dscr_end-_fullspd_dscr      ; dscr len
+       .db     DSCR_CONFIG_TYPE
+    ; can't use .dw because byte order is different
+       .db     (fullspd_dscr_realend-_fullspd_dscr) % 256 ; total length of config lsb
+       .db     (fullspd_dscr_realend-_fullspd_dscr) / 256 ; total length of config msb
+       .db     2                                                                ; n interfaces
+       .db     1                                                                ; config number
+       .db     0                                                                ; config string
+       .db     0x80                             ; attrs = bus powered, no wakeup
+       .db     55                               ; max power = 110mA
+fullspd_dscr_end:
+
+
+; all the interfaces next 
+; BULK interface
+       .db     DSCR_INTERFACE_LEN
+       .db     DSCR_INTERFACE_TYPE
+       .db     0                                ; index
+       .db     0                                ; alt setting idx
+       .db     1                                ; n endpoints  
+       .db     0xff                     ; class
+       .db     0
+       .db     0
+       .db     0                    ; string index     
+
+; endpoint 6 in 
+       .db     DSCR_ENDPOINT_LEN
+       .db     DSCR_ENDPOINT_TYPE
+       .db     0x86                            ;  ep1 dir=in and address
+       .db     ENDPOINT_TYPE_BULK      ; type
+       .db     0x40                            ; max packet LSB
+       .db     0x00                            ; max packet size=512 bytes
+       .db     0x00                            ; polling interval
+
+; ISOCHRONOUS interface 1 MB/s
+       .db     DSCR_INTERFACE_LEN
+       .db     DSCR_INTERFACE_TYPE
+       .db     0                                ; index
+       .db     1                                ; alt setting idx
+       .db     1                                ; n endpoints  
+       .db     0xff                     ; class
+       .db     0
+       .db     1
+       .db     0                    ; string index     
+
+; endpoint 2 in 
+       .db     DSCR_ENDPOINT_LEN
+       .db     DSCR_ENDPOINT_TYPE
+       .db     0x82                            ;  ep1 dir=in and address
+       .db     ENDPOINT_TYPE_ISO       ; type
+       .db     0xff                            ; max packet LSB
+       .db     0x03                            ; max packet size=1023 bytes
+       .db     0x01                            ; polling interval
+
+; ISOCHRONOUS interface 500 kB/s
+       .db     DSCR_INTERFACE_LEN
+       .db     DSCR_INTERFACE_TYPE
+       .db     0                                ; index
+       .db     2                                ; alt setting idx
+       .db     1                                ; n endpoints  
+       .db     0xff                     ; class
+       .db     0
+       .db     1
+       .db     0                    ; string index     
+
+; endpoint 2 in 
+       .db     DSCR_ENDPOINT_LEN
+       .db     DSCR_ENDPOINT_TYPE
+       .db     0x82                            ;  ep1 dir=in and address
+       .db     ENDPOINT_TYPE_ISO       ; type
+       .db     0x00                            ; max packet LSB
+       .db     0x02                            ; max packet size=512 bytes
+       .db     0x01                            ; polling interval
+
+fullspd_dscr_realend:
+
+.even
+_dev_strings:
+; sample string
+_string0:
+       .db     string0end-_string0 ; len
+       .db     DSCR_STRING_TYPE
+    .db 0x09, 0x04 ; 0x0409 is the language code for English.  Possible to add more codes after this. 
+string0end:
+; add more strings here
+_string1:
+       .db     string1end-_string1 ; len
+       .db     DSCR_STRING_TYPE
+    .ascii 'O\0D\0M\0'
+string1end:
+_string2:
+       .db     string2end-_string2 ; len
+       .db     DSCR_STRING_TYPE
+    .ascii 'H\0a\0n\0t\0e\0k\0D\0S\0O\0006\0000\0002\0002\0B\0E\0'
+string2end:
+
+
+_dev_strings_end:
+    .dw 0x0000  ; in case you wanted to look at memory between _dev_strings and _dev_strings_end